Merge "Remove radio ISAP HIDL entry in compat matrix."
diff --git a/audio/aidl/TEST_MAPPING b/audio/aidl/TEST_MAPPING
index a166e61..6a545c1 100644
--- a/audio/aidl/TEST_MAPPING
+++ b/audio/aidl/TEST_MAPPING
@@ -13,6 +13,9 @@
       "name": "VtsHalDownmixTargetTest"
     },
     {
+      "name": "VtsHalDynamicsProcessingTargetTest"
+    },
+    {
       "name": "VtsHalEnvironmentalReverbTargetTest"
     },
     {
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/CommandId.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/CommandId.aidl
index 79299ee..86b69fa 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/CommandId.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/CommandId.aidl
@@ -37,14 +37,14 @@
   START = 0,
   STOP = 1,
   RESET = 2,
-  VENDOR_COMMAND_0 = 256,
-  VENDOR_COMMAND_1 = 257,
-  VENDOR_COMMAND_2 = 258,
-  VENDOR_COMMAND_3 = 259,
-  VENDOR_COMMAND_4 = 260,
-  VENDOR_COMMAND_5 = 261,
-  VENDOR_COMMAND_6 = 262,
-  VENDOR_COMMAND_7 = 263,
-  VENDOR_COMMAND_8 = 264,
-  VENDOR_COMMAND_9 = 265,
+  VENDOR_COMMAND_0 = 0x100,
+  VENDOR_COMMAND_1,
+  VENDOR_COMMAND_2,
+  VENDOR_COMMAND_3,
+  VENDOR_COMMAND_4,
+  VENDOR_COMMAND_5,
+  VENDOR_COMMAND_6,
+  VENDOR_COMMAND_7,
+  VENDOR_COMMAND_8,
+  VENDOR_COMMAND_9,
 }
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Downmix.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Downmix.aidl
index 76f8ce5..402441d 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Downmix.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Downmix.aidl
@@ -47,7 +47,7 @@
   }
   @VintfStability
   enum Type {
-    STRIP = 0,
-    FOLD = 1,
+    STRIP,
+    FOLD,
   }
 }
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/DynamicsProcessing.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/DynamicsProcessing.aidl
index ed4dc80..8e5b719 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/DynamicsProcessing.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/DynamicsProcessing.aidl
@@ -36,14 +36,14 @@
 union DynamicsProcessing {
   android.hardware.audio.effect.VendorExtension vendorExtension;
   android.hardware.audio.effect.DynamicsProcessing.EngineArchitecture engineArchitecture;
-  android.hardware.audio.effect.DynamicsProcessing.BandChannelConfig preEq;
-  android.hardware.audio.effect.DynamicsProcessing.BandChannelConfig postEq;
-  android.hardware.audio.effect.DynamicsProcessing.EqBandConfig preEqBand;
-  android.hardware.audio.effect.DynamicsProcessing.EqBandConfig postEqBand;
-  android.hardware.audio.effect.DynamicsProcessing.BandChannelConfig mbc;
-  android.hardware.audio.effect.DynamicsProcessing.MbcBandConfig mbcBand;
-  android.hardware.audio.effect.DynamicsProcessing.LimiterConfig limiter;
-  float inputGainDb;
+  android.hardware.audio.effect.DynamicsProcessing.ChannelConfig[] preEq;
+  android.hardware.audio.effect.DynamicsProcessing.ChannelConfig[] postEq;
+  android.hardware.audio.effect.DynamicsProcessing.EqBandConfig[] preEqBand;
+  android.hardware.audio.effect.DynamicsProcessing.EqBandConfig[] postEqBand;
+  android.hardware.audio.effect.DynamicsProcessing.ChannelConfig[] mbc;
+  android.hardware.audio.effect.DynamicsProcessing.MbcBandConfig[] mbcBand;
+  android.hardware.audio.effect.DynamicsProcessing.LimiterConfig[] limiter;
+  android.hardware.audio.effect.DynamicsProcessing.InputGain[] inputGain;
   @VintfStability
   union Id {
     int vendorExtensionTag;
@@ -52,37 +52,39 @@
   @VintfStability
   parcelable Capability {
     ParcelableHolder extension;
+    float minCutOffFreq;
+    float maxCutOffFreq;
   }
   enum ResolutionPreference {
-    FAVOR_FREQUENCY_RESOLUTION = 0,
-    FAVOR_TIME_RESOLUTION = 1,
+    FAVOR_FREQUENCY_RESOLUTION,
+    FAVOR_TIME_RESOLUTION,
   }
   @VintfStability
-  parcelable BandEnablement {
+  parcelable StageEnablement {
     boolean inUse;
     int bandCount;
   }
   @VintfStability
   parcelable EngineArchitecture {
     android.hardware.audio.effect.DynamicsProcessing.ResolutionPreference resolutionPreference = android.hardware.audio.effect.DynamicsProcessing.ResolutionPreference.FAVOR_FREQUENCY_RESOLUTION;
-    float preferredFrameDurationMs;
-    android.hardware.audio.effect.DynamicsProcessing.BandEnablement preEqBand;
-    android.hardware.audio.effect.DynamicsProcessing.BandEnablement postEqBand;
-    android.hardware.audio.effect.DynamicsProcessing.BandEnablement mbcBand;
+    float preferredProcessingDurationMs;
+    android.hardware.audio.effect.DynamicsProcessing.StageEnablement preEqStage;
+    android.hardware.audio.effect.DynamicsProcessing.StageEnablement postEqStage;
+    android.hardware.audio.effect.DynamicsProcessing.StageEnablement mbcStage;
     boolean limiterInUse;
   }
   @VintfStability
-  parcelable BandChannelConfig {
+  parcelable ChannelConfig {
     int channel;
-    android.hardware.audio.effect.DynamicsProcessing.BandEnablement enablement;
+    boolean enable;
   }
   @VintfStability
   parcelable EqBandConfig {
     int channel;
     int band;
     boolean enable;
-    float cutoffFrequency;
-    float gain;
+    float cutoffFrequencyHz;
+    float gainDb;
   }
   @VintfStability
   parcelable MbcBandConfig {
@@ -90,7 +92,6 @@
     int band;
     boolean enable;
     float cutoffFrequencyHz;
-    float gainDb;
     float attackTimeMs;
     float releaseTimeMs;
     float ratio;
@@ -105,7 +106,6 @@
   parcelable LimiterConfig {
     int channel;
     boolean enable;
-    boolean inUse;
     int linkGroup;
     float attackTimeMs;
     float releaseTimeMs;
@@ -113,4 +113,9 @@
     float thresholdDb;
     float postGainDb;
   }
+  @VintfStability
+  parcelable InputGain {
+    int channel;
+    float gainDb;
+  }
 }
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/EnvironmentalReverb.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/EnvironmentalReverb.aidl
index 0e61932..9edad09 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/EnvironmentalReverb.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/EnvironmentalReverb.aidl
@@ -44,22 +44,6 @@
   int diffusionPm;
   int densityPm;
   boolean bypass;
-  const int MIN_ROOM_LEVEL_MB = -6000;
-  const int MAX_ROOM_LEVEL_MB = 0;
-  const int MIN_ROOM_HF_LEVEL_MB = -4000;
-  const int MAX_ROOM_HF_LEVEL_MB = 0;
-  const int MIN_DECAY_TIME_MS = 100;
-  const int MAX_DECAY_TIME_MS = 20000;
-  const int MIN_DECAY_HF_RATIO_PM = 100;
-  const int MAX_DECAY_HF_RATIO_PM = 1000;
-  const int MIN_LEVEL_MB = -6000;
-  const int MAX_LEVEL_MB = 0;
-  const int MIN_DELAY_MS = 0;
-  const int MAX_DELAY_MS = 65;
-  const int MIN_DIFFUSION_PM = 0;
-  const int MAX_DIFFUSION_PM = 1000;
-  const int MIN_DENSITY_PM = 0;
-  const int MAX_DENSITY_PM = 1000;
   @VintfStability
   union Id {
     int vendorExtensionTag;
@@ -68,6 +52,17 @@
   @VintfStability
   parcelable Capability {
     android.hardware.audio.effect.VendorExtension extension;
+    int minRoomLevelMb;
+    int maxRoomLevelMb;
+    int minRoomHfLevelMb;
+    int maxRoomHfLevelMb;
     int maxDecayTimeMs;
+    int minDecayHfRatioPm;
+    int maxDecayHfRatioPm;
+    int minLevelMb;
+    int maxLevelMb;
+    int maxDelayMs;
+    int maxDiffusionPm;
+    int maxDensityPm;
   }
 }
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/HapticGenerator.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/HapticGenerator.aidl
index 959594b..20f7e02 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/HapticGenerator.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/HapticGenerator.aidl
@@ -48,9 +48,9 @@
   }
   @Backing(type="int") @VintfStability
   enum VibratorScale {
-    MUTE = -100,
-    VERY_LOW = -2,
-    LOW = -1,
+    MUTE = (-100),
+    VERY_LOW = (-2),
+    LOW = (-1),
     NONE = 0,
     HIGH = 1,
     VERY_HIGH = 2,
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/NoiseSuppression.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/NoiseSuppression.aidl
index 223d95a..397f897 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/NoiseSuppression.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/NoiseSuppression.aidl
@@ -47,8 +47,8 @@
   }
   @Backing(type="int") @VintfStability
   enum Level {
-    LOW = 0,
-    MEDIUM = 1,
-    HIGH = 2,
+    LOW,
+    MEDIUM,
+    HIGH,
   }
 }
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/PresetReverb.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/PresetReverb.aidl
index 24a9ce1..4651742 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/PresetReverb.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/PresetReverb.aidl
@@ -38,13 +38,13 @@
   android.hardware.audio.effect.PresetReverb.Presets preset;
   @Backing(type="int") @VintfStability
   enum Presets {
-    NONE = 0,
-    SMALLROOM = 1,
-    MEDIUMROOM = 2,
-    LARGEROOM = 3,
-    MEDIUMHALL = 4,
-    LARGEHALL = 5,
-    PLATE = 6,
+    NONE,
+    SMALLROOM,
+    MEDIUMROOM,
+    LARGEROOM,
+    MEDIUMHALL,
+    LARGEHALL,
+    PLATE,
   }
   @VintfStability
   union Id {
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/State.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/State.aidl
index 3176b01..17f9814 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/State.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/State.aidl
@@ -34,7 +34,7 @@
 package android.hardware.audio.effect;
 @Backing(type="byte") @VintfStability
 enum State {
-  INIT = 0,
-  IDLE = 1,
-  PROCESSING = 2,
+  INIT,
+  IDLE,
+  PROCESSING,
 }
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Visualizer.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Visualizer.aidl
index 25f0b73..c8cb551 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Visualizer.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Visualizer.aidl
@@ -62,12 +62,12 @@
   @VintfStability
   enum ScalingMode {
     NORMALIZED = 0,
-    AS_PLAYED = 1,
+    AS_PLAYED,
   }
   @VintfStability
   enum MeasurementMode {
     NONE = 0,
-    PEAK_RMS = 1,
+    PEAK_RMS,
   }
   @VintfStability
   union GetOnlyParameters {
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
index e25b15a..501dc01 100644
--- a/audio/aidl/android/hardware/audio/core/stream-out-async-sm.gv
+++ b/audio/aidl/android/hardware/audio/core/stream-out-async-sm.gv
@@ -30,6 +30,7 @@
     STANDBY -> PAUSED [label="burst"];                // producer -> active
     IDLE -> STANDBY [label="standby"];                // consumer -> passive
     IDLE -> TRANSFERRING [label="burst"];             // producer -> active
+    IDLE -> ACTIVE [label="burst"];                   // full write
     ACTIVE -> PAUSED [label="pause"];                 // consumer -> passive (not consuming)
     ACTIVE -> DRAINING [label="drain"];               // producer -> passive
     ACTIVE -> TRANSFERRING [label="burst"];           // early unblocking
@@ -45,6 +46,7 @@
     PAUSED -> IDLE [label="flush"];                   // producer -> passive, buffer is cleared
     DRAINING -> IDLE [label="←IStreamCallback.onDrainReady"];
     DRAINING -> TRANSFERRING [label="burst"];         // producer -> active
+    DRAINING -> ACTIVE [label="burst"];               // full write
     DRAINING -> DRAIN_PAUSED [label="pause"];         // consumer -> passive (not consuming)
     DRAIN_PAUSED -> DRAINING [label="start"];         // consumer -> active
     DRAIN_PAUSED -> TRANSFER_PAUSED [label="burst"];  // producer -> active
diff --git a/audio/aidl/android/hardware/audio/core/stream-out-sm.gv b/audio/aidl/android/hardware/audio/core/stream-out-sm.gv
index 6aa5c61..47e7fda 100644
--- a/audio/aidl/android/hardware/audio/core/stream-out-sm.gv
+++ b/audio/aidl/android/hardware/audio/core/stream-out-sm.gv
@@ -31,6 +31,7 @@
     ACTIVE -> ACTIVE [label="burst"];
     ACTIVE -> PAUSED [label="pause"];          // consumer -> passive (not consuming)
     ACTIVE -> DRAINING [label="drain"];        // producer -> passive
+    ACTIVE -> IDLE [label="drain"];            // synchronous drain
     PAUSED -> PAUSED [label="burst"];
     PAUSED -> ACTIVE [label="start"];          // consumer -> active
     PAUSED -> IDLE [label="flush"];            // producer -> passive, buffer is cleared
diff --git a/audio/aidl/android/hardware/audio/effect/DynamicsProcessing.aidl b/audio/aidl/android/hardware/audio/effect/DynamicsProcessing.aidl
index ee5dcad..6db3338 100644
--- a/audio/aidl/android/hardware/audio/effect/DynamicsProcessing.aidl
+++ b/audio/aidl/android/hardware/audio/effect/DynamicsProcessing.aidl
@@ -51,6 +51,14 @@
          * capability definition not enough.
          */
         ParcelableHolder extension;
+        /**
+         * Min Cut off frequency (in Hz) for all Bands.
+         */
+        float minCutOffFreq;
+        /**
+         * Max Cut off frequency (in Hz) for all Bands.
+         */
+        float maxCutOffFreq;
     }
 
     /**
@@ -68,16 +76,16 @@
     }
 
     /**
-     * Band enablement configuration.
+     * Stage enablement configuration.
      */
     @VintfStability
-    parcelable BandEnablement {
+    parcelable StageEnablement {
         /**
-         * True if multi-band stage is in use.
+         * True if stage is in use.
          */
         boolean inUse;
         /**
-         * Number of bands configured for this stage.
+         * Number of bands configured for this stage. Must be positive when inUse is true.
          */
         int bandCount;
     }
@@ -92,21 +100,22 @@
          */
         ResolutionPreference resolutionPreference = ResolutionPreference.FAVOR_FREQUENCY_RESOLUTION;
         /**
-         * Preferred frame duration in milliseconds (ms).
+         * Preferred processing duration in milliseconds (ms). Must not be negative, 0 means no
+         * preference.
          */
-        float preferredFrameDurationMs;
+        float preferredProcessingDurationMs;
         /**
          * PreEq stage (Multi-band Equalizer) configuration.
          */
-        BandEnablement preEqBand;
+        StageEnablement preEqStage;
         /**
          * PostEq stage (Multi-band Equalizer) configuration.
          */
-        BandEnablement postEqBand;
+        StageEnablement postEqStage;
         /**
          * MBC stage (Multi-band Compressor) configuration.
          */
-        BandEnablement mbcBand;
+        StageEnablement mbcStage;
         /**
          * True if Limiter stage is in use.
          */
@@ -114,18 +123,19 @@
     }
 
     /**
-     * Band enablement configuration for a specific channel.
+     * Enablement configuration for a specific channel.
      */
     @VintfStability
-    parcelable BandChannelConfig {
+    parcelable ChannelConfig {
         /**
-         * Channel index.
+         * Channel index. Must not be negative, and not exceed the channel count calculated from
+         * Parameter.common.input.base.channelMask.
          */
         int channel;
         /**
-         * Channel index.
+         * Channel enablement configuration. Can not be true if corresponding stage is not in use.
          */
-        BandEnablement enablement;
+        boolean enable;
     }
 
     /**
@@ -134,7 +144,8 @@
     @VintfStability
     parcelable EqBandConfig {
         /**
-         * Channel index.
+         * Channel index. Must not be negative, and not exceed the channel count calculated from
+         * Parameter.common.input.base.channelMask.
          */
         int channel;
         /**
@@ -142,17 +153,20 @@
          */
         int band;
         /**
-         * True if EQ stage is enabled.
+         * True if EQ band is enabled.
+         * If EngineArchitecture EQ stage inUse was set to false, then enable can not be set to
+         * true.
          */
         boolean enable;
         /**
-         * Topmost frequency number (in Hz) this band will process.
+         * Topmost frequency number (in Hz) this band will process. Must be in the range of
+         * [minCutOffFreq, maxCutOffFreq] defined in Capability.
          */
-        float cutoffFrequency;
+        float cutoffFrequencyHz;
         /**
          * Gain factor in decibels (dB).
          */
-        float gain;
+        float gainDb;
     }
 
     /**
@@ -161,51 +175,53 @@
     @VintfStability
     parcelable MbcBandConfig {
         /**
-         * Channel index.
+         * Channel index. Must not be negative, and not exceed the channel count calculated from
+         * Parameter.common.input.base.channelMask.
          */
         int channel;
         /**
-         * Band index, must in the range of [0, bandCount-1].
+         * Band index. Must be in the range of [0, bandCount-1].
          */
         int band;
         /**
-         * True if MBC stage is enabled.
+         * True if MBC band is enabled.
+         * If EngineArchitecture MBC inUse was set to false, then enable here can not be set to
+         * true.
          */
         boolean enable;
         /**
-         * Topmost frequency number (in Hz) this band will process.
+         * Topmost frequency number (in Hz) this band will process. Must be in the range of
+         * [minCutOffFreq, maxCutOffFreq] defined in Capability.
          */
         float cutoffFrequencyHz;
         /**
-         * Gain factor in decibels (dB).
-         */
-        float gainDb;
-        /**
-         * Attack Time for compressor in milliseconds (ms).
+         * Attack Time for compressor in milliseconds (ms). Must not be negative.
          */
         float attackTimeMs;
         /**
-         * Release Time for compressor in milliseconds (ms).
+         * Release Time for compressor in milliseconds (ms). Must not be negative.
          */
         float releaseTimeMs;
         /**
-         * Compressor ratio (N:1) (input:output).
+         * Compressor ratio (N:1) (input:output). Must not be negative.
          */
         float ratio;
         /**
-         * Compressor threshold measured in decibels (dB) from 0 dB Full Scale (dBFS).
+         * Compressor threshold measured in decibels (dB) from 0 dB Full Scale (dBFS).  Must not be
+         * positive.
          */
         float thresholdDb;
         /**
-         * Width in decibels (dB) around compressor threshold point.
+         * Width in decibels (dB) around compressor threshold point. Must not be negative.
          */
         float kneeWidthDb;
         /**
-         * Noise gate threshold in decibels (dB) from 0 dB Full Scale (dBFS).
+         * Noise gate threshold in decibels (dB) from 0 dB Full Scale (dBFS). Must not be positive.
          */
         float noiseGateThresholdDb;
         /**
-         * Expander ratio (1:N) (input:output) for signals below the Noise Gate Threshold.
+         * Expander ratio (1:N) (input:output) for signals below the Noise Gate Threshold. Must not
+         * be negative.
          */
         float expanderRatio;
         /**
@@ -224,36 +240,35 @@
     @VintfStability
     parcelable LimiterConfig {
         /**
-         * Channel index.
+         * Channel index. Must not be negative, and not exceed the channel count calculated from
+         * Parameter.common.input.base.channelMask.
          */
         int channel;
         /**
-         * True if Limiter stage is enabled.
+         * True if Limiter band is enabled.
+         * If EngineArchitecture limiterInUse was set to false, then enable can not be set to true.
          */
         boolean enable;
         /**
-         * True if Limiter stage is in use.
-         */
-        boolean inUse;
-        /**
          * Index of group assigned to this Limiter. Only limiters that share the same linkGroup
          * index will react together.
          */
         int linkGroup;
         /**
-         * Attack Time for compressor in milliseconds (ms).
+         * Attack Time for compressor in milliseconds (ms). Must not be negative.
          */
         float attackTimeMs;
         /**
-         * Release Time for compressor in milliseconds (ms).
+         * Release Time for compressor in milliseconds (ms). Must not be negative.
          */
         float releaseTimeMs;
         /**
-         * Compressor ratio (N:1) (input:output).
+         * Compressor ratio (N:1) (input:output). Must not be negative.
          */
         float ratio;
         /**
-         * Compressor threshold measured in decibels (dB) from 0 dB Full Scale (dBFS).
+         * Compressor threshold measured in decibels (dB) from 0 dB Full Scale (dBFS). Must not be
+         * positive.
          */
         float thresholdDb;
         /**
@@ -263,39 +278,55 @@
     }
 
     /**
+     * Input gain for a channel (specified by the channel index).
+     */
+    @VintfStability
+    parcelable InputGain {
+        /**
+         * Channel index. Must not be negative, and not exceed the channel count calculated from
+         * Parameter.common.input.base.channelMask.
+         */
+        int channel;
+        /**
+         * Gain applied to the input signal in decibels (dB). 0 dB means no change in level.
+         */
+        float gainDb;
+    }
+
+    /**
      * Effect engine architecture.
      */
     EngineArchitecture engineArchitecture;
     /**
-     * PreEq stage per channel configuration.
+     * PreEq stage per channel configuration. Only valid when pre EQ stage inUse is true.
      */
-    BandChannelConfig preEq;
+    ChannelConfig[] preEq;
     /**
-     * PostEq stage per channel configuration.
+     * PostEq stage per channel configuration. Only valid when post EQ stage inUse is true.
      */
-    BandChannelConfig postEq;
+    ChannelConfig[] postEq;
     /**
-     * PreEq stage per band configuration.
+     * PreEq stage per band configuration. Only valid when pre EQ stage inUse is true.
      */
-    EqBandConfig preEqBand;
+    EqBandConfig[] preEqBand;
     /**
-     * PostEq stage per band configuration.
+     * PostEq stage per band configuration. Only valid when post EQ stage inUse is true.
      */
-    EqBandConfig postEqBand;
+    EqBandConfig[] postEqBand;
     /**
-     * MBC stage per channel configuration.
+     * MBC stage per channel configuration. Only valid when MBC stage inUse is true.
      */
-    BandChannelConfig mbc;
+    ChannelConfig[] mbc;
     /**
-     * PostEq stage per band configuration.
+     * PostEq stage per band configuration. Only valid when MBC stage inUse is true.
      */
-    MbcBandConfig mbcBand;
+    MbcBandConfig[] mbcBand;
     /**
-     * Limiter stage configuration.
+     * Limiter stage configuration. Only valid when limiter stage inUse is true.
      */
-    LimiterConfig limiter;
+    LimiterConfig[] limiter;
     /**
-     * Input gain factor in decibels (dB). 0 dB means no change in level.
+     * Input gain factor.
      */
-    float inputGainDb;
+    InputGain[] inputGain;
 }
diff --git a/audio/aidl/android/hardware/audio/effect/EnvironmentalReverb.aidl b/audio/aidl/android/hardware/audio/effect/EnvironmentalReverb.aidl
index 81c0dde..fc98fe6 100644
--- a/audio/aidl/android/hardware/audio/effect/EnvironmentalReverb.aidl
+++ b/audio/aidl/android/hardware/audio/effect/EnvironmentalReverb.aidl
@@ -50,112 +50,96 @@
         VendorExtension extension;
 
         /**
+         * Minimal possible room level in millibels.
+         */
+        int minRoomLevelMb;
+        /**
+         * Maximum possible room level in millibels.
+         */
+        int maxRoomLevelMb;
+        /**
+         * Minimal possible room hf level in millibels.
+         */
+        int minRoomHfLevelMb;
+        /**
+         * Maximum possible room hf level in millibels.
+         */
+        int maxRoomHfLevelMb;
+        /**
          * Max decay time supported in millisecond.
          */
         int maxDecayTimeMs;
+        /**
+         * Minimal possible per mille decay hf ratio.
+         */
+        int minDecayHfRatioPm;
+        /**
+         * Maximum possible per mille decay hf ratio.
+         */
+        int maxDecayHfRatioPm;
+        /**
+         * Minimal possible room level in millibels.
+         */
+        int minLevelMb;
+        /**
+         * Maximum possible room level in millibels.
+         */
+        int maxLevelMb;
+        /**
+         * Maximum possible delay time in milliseconds.
+         */
+        int maxDelayMs;
+        /**
+         * Maximum possible per mille diffusion.
+         */
+        int maxDiffusionPm;
+        /**
+         * Maximum possible per mille density.
+         */
+        int maxDensityPm;
     }
 
     /**
-     * Minimal possible room level in millibels.
-     */
-    const int MIN_ROOM_LEVEL_MB = -6000;
-    /**
-     * Maximum possible room level in millibels.
-     */
-    const int MAX_ROOM_LEVEL_MB = 0;
-    /**
-     * Room level apply to the reverb effect in millibels.
+     * Room level apply to the reverb effect in millibels. The value of the roomLevelMb must be in
+     * range of the value specified by the 'minRoomLevelMb' capability and the 'maxRoomLevelMb'
+     * capability.
      */
     int roomLevelMb;
-
     /**
-     * Minimal possible room hf level in millibels.
-     */
-    const int MIN_ROOM_HF_LEVEL_MB = -4000;
-    /**
-     * Maximum possible room hf level in millibels.
-     */
-    const int MAX_ROOM_HF_LEVEL_MB = 0;
-    /**
-     * Room HF level apply to the reverb effect in millibels.
+     * Room HF level apply to the reverb effect in millibels. The value of the roomHfLevelMb must be
+     * in range of the value specified by the 'minRoomHfLevelMb' capability and the
+     * 'maxRoomHfLevelMb' capability.
      */
     int roomHfLevelMb;
-
     /**
-     * Minimal possible decay time in milliseconds.
-     */
-    const int MIN_DECAY_TIME_MS = 100;
-    /**
-     * Maximum possible decay time in milliseconds.
-     */
-    const int MAX_DECAY_TIME_MS = 20000;
-    /**
-     * Delay time apply to the reverb effect in milliseconds.
+     * Delay time apply to the reverb effect in milliseconds.The value of the decayTimeMs must
+     * be non-negative and not exceed the value specified by the 'maxDecayTimeMs' capability.
      */
     int decayTimeMs;
-
     /**
-     * Minimal possible per mille decay hf ratio.
-     */
-    const int MIN_DECAY_HF_RATIO_PM = 100;
-    /**
-     * Maximum possible per mille decay hf ratio.
-     */
-    const int MAX_DECAY_HF_RATIO_PM = 1000;
-    /**
-     * HF decay ratio in permilles.
+     * HF decay ratio in permilles. The value of the decayHfRatioPm must be in range
+     * of the value specified by the 'minDecayHfRatioPm' capability and the 'maxDecayHfRatioPm'
+     * capability.
      */
     int decayHfRatioPm;
-
     /**
-     * Minimal possible room level in millibels.
-     */
-    const int MIN_LEVEL_MB = -6000;
-    /**
-     * Maximum possible room level in millibels.
-     */
-    const int MAX_LEVEL_MB = 0;
-    /**
-     * Reverb level in millibels.
+     * Reverb level in millibels. The value of the levelMb must be in range
+     * of the value specified by the 'minLevelMb' capability and the 'maxLevelMb' capability.
      */
     int levelMb;
-
     /**
-     * Minimal possible delay time in milliseconds.
-     */
-    const int MIN_DELAY_MS = 0;
-    /**
-     * Maximum possible delay time in milliseconds.
-     */
-    const int MAX_DELAY_MS = 65;
-    /**
-     * Reverb delay in milliseconds.
+     * Reverb delay in milliseconds. The value of the delayMs must be non-negative and not
+     * exceed the value specified by the 'maxDelayMs' capability.
      */
     int delayMs;
-
     /**
-     * Minimal possible per mille diffusion.
-     */
-    const int MIN_DIFFUSION_PM = 0;
-    /**
-     * Maximum possible per mille diffusion.
-     */
-    const int MAX_DIFFUSION_PM = 1000;
-    /**
-     * Diffusion in permilles.
+     * Diffusion in permilles. The value of the diffusionPm must be non-negative and not
+     * exceed the value specified by the 'maxDiffusionPm' capability.
      */
     int diffusionPm;
-
     /**
-     * Minimal possible per mille density.
-     */
-    const int MIN_DENSITY_PM = 0;
-    /**
-     * Maximum possible per mille density.
-     */
-    const int MAX_DENSITY_PM = 1000;
-    /**
-     * Density in permilles.
+     * Density in permilles. The value of the densityPm must be non-negative and not
+     * exceed the value specified by the 'maxDensityPm' capability.
      */
     int densityPm;
 
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index 6f23636..1e6785f 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -156,7 +156,7 @@
         "libbassboostsw",
         "libbundleaidl",
         "libdownmixaidl",
-        "libdynamicsprocessingsw",
+        "libdynamicsprocessingaidl",
         "libenvreverbsw",
         "libequalizersw",
         "libhapticgeneratoraidl",
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index e8b5bfc..13b04cd 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -46,6 +46,7 @@
 using aidl::android::media::audio::common::AudioPortConfig;
 using aidl::android::media::audio::common::AudioPortExt;
 using aidl::android::media::audio::common::AudioProfile;
+using aidl::android::media::audio::common::Boolean;
 using aidl::android::media::audio::common::Int;
 using aidl::android::media::audio::common::PcmType;
 using android::hardware::audio::common::getFrameSizeInBytes;
@@ -138,12 +139,15 @@
         (flags.getTag() == AudioIoFlags::Tag::output &&
          !isBitPositionFlagSet(flags.get<AudioIoFlags::Tag::output>(),
                                AudioOutputFlags::MMAP_NOIRQ))) {
+        StreamContext::DebugParameters params{mDebug.streamTransientStateDelayMs,
+                                              mVendorDebug.forceTransientBurst,
+                                              mVendorDebug.forceSynchronousDrain};
         StreamContext temp(
                 std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
                 std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
                 portConfigIt->format.value(), portConfigIt->channelMask.value(),
                 std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames),
-                asyncCallback, mDebug.streamTransientStateDelayMs);
+                asyncCallback, params);
         if (temp.isValid()) {
             *out_context = std::move(temp);
         } else {
@@ -976,18 +980,69 @@
     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 }
 
+const std::string Module::VendorDebug::kForceTransientBurstName = "aosp.forceTransientBurst";
+const std::string Module::VendorDebug::kForceSynchronousDrainName = "aosp.forceSynchronousDrain";
+
 ndk::ScopedAStatus Module::getVendorParameters(const std::vector<std::string>& in_ids,
                                                std::vector<VendorParameter>* _aidl_return) {
     LOG(DEBUG) << __func__ << ": id count: " << in_ids.size();
-    (void)_aidl_return;
-    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    bool allParametersKnown = true;
+    for (const auto& id : in_ids) {
+        if (id == VendorDebug::kForceTransientBurstName) {
+            VendorParameter forceTransientBurst{.id = id};
+            forceTransientBurst.ext.setParcelable(Boolean{mVendorDebug.forceTransientBurst});
+            _aidl_return->push_back(std::move(forceTransientBurst));
+        } else if (id == VendorDebug::kForceSynchronousDrainName) {
+            VendorParameter forceSynchronousDrain{.id = id};
+            forceSynchronousDrain.ext.setParcelable(Boolean{mVendorDebug.forceSynchronousDrain});
+            _aidl_return->push_back(std::move(forceSynchronousDrain));
+        } else {
+            allParametersKnown = false;
+            LOG(ERROR) << __func__ << ": unrecognized parameter \"" << id << "\"";
+        }
+    }
+    if (allParametersKnown) return ndk::ScopedAStatus::ok();
+    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
 }
 
+namespace {
+
+template <typename W>
+bool extractParameter(const VendorParameter& p, decltype(W::value)* v) {
+    std::optional<W> value;
+    binder_status_t result = p.ext.getParcelable(&value);
+    if (result == STATUS_OK && value.has_value()) {
+        *v = value.value().value;
+        return true;
+    }
+    LOG(ERROR) << __func__ << ": failed to read the value of the parameter \"" << p.id
+               << "\": " << result;
+    return false;
+}
+
+}  // namespace
+
 ndk::ScopedAStatus Module::setVendorParameters(const std::vector<VendorParameter>& in_parameters,
                                                bool in_async) {
     LOG(DEBUG) << __func__ << ": parameter count " << in_parameters.size()
                << ", async: " << in_async;
-    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    bool allParametersKnown = true;
+    for (const auto& p : in_parameters) {
+        if (p.id == VendorDebug::kForceTransientBurstName) {
+            if (!extractParameter<Boolean>(p, &mVendorDebug.forceTransientBurst)) {
+                return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+            }
+        } else if (p.id == VendorDebug::kForceSynchronousDrainName) {
+            if (!extractParameter<Boolean>(p, &mVendorDebug.forceSynchronousDrain)) {
+                return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+            }
+        } else {
+            allParametersKnown = false;
+            LOG(ERROR) << __func__ << ": unrecognized parameter \"" << p.id << "\"";
+        }
+    }
+    if (allParametersKnown) return ndk::ScopedAStatus::ok();
+    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
 }
 
 ndk::ScopedAStatus Module::addDeviceEffect(
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index a490a2a..0520cba 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -402,7 +402,11 @@
                     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);
+                    if (mState == StreamDescriptor::State::ACTIVE && mForceSynchronousDrain) {
+                        mState = StreamDescriptor::State::IDLE;
+                    } else {
+                        switchToTransientState(StreamDescriptor::State::DRAINING);
+                    }
                 } else if (mState == StreamDescriptor::State::TRANSFER_PAUSED) {
                     mState = StreamDescriptor::State::DRAIN_PAUSED;
                     populateReply(&reply, mIsConnected);
@@ -467,14 +471,19 @@
 
 bool StreamOutWorkerLogic::write(size_t clientSize, StreamDescriptor::Reply* reply) {
     const size_t readByteCount = mDataMQ->availableToRead();
-    // Amount of data that the HAL module is going to actually use.
-    const size_t byteCount = std::min({clientSize, readByteCount, mDataBufferSize});
     bool fatal = false;
     if (bool success = readByteCount > 0 ? mDataMQ->read(&mDataBuffer[0], readByteCount) : true) {
         const bool isConnected = mIsConnected;
         LOG(DEBUG) << __func__ << ": reading of " << readByteCount << " bytes from data MQ"
                    << " succeeded; connected? " << isConnected;
-        // Frames are consumed and counted regardless of connection status.
+        // Amount of data that the HAL module is going to actually use.
+        size_t byteCount = std::min({clientSize, readByteCount, mDataBufferSize});
+        if (byteCount >= mFrameSize && mForceTransientBurst) {
+            // In order to prevent the state machine from going to ACTIVE state,
+            // simulate partial write.
+            byteCount -= mFrameSize;
+        }
+        // Frames are consumed and counted regardless of the connection status.
         reply->fmqByteCount += byteCount;
         mFrameCount += byteCount / mFrameSize;
         populateReply(reply, isConnected);
diff --git a/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.cpp b/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.cpp
index 7f06013..40b46e0 100644
--- a/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.cpp
+++ b/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.cpp
@@ -54,7 +54,6 @@
         return EX_ILLEGAL_ARGUMENT;
     }
     *_aidl_return = AcousticEchoCancelerSw::kDescriptor;
-    LOG(ERROR) << __func__ << "xxx " << _aidl_return->toString();
     return EX_NONE;
 }
 
diff --git a/audio/aidl/default/audio_effects_config.xml b/audio/aidl/default/audio_effects_config.xml
index 9670e9c..e460f89 100644
--- a/audio/aidl/default/audio_effects_config.xml
+++ b/audio/aidl/default/audio_effects_config.xml
@@ -33,7 +33,7 @@
         <library name="bassboostsw" path="libbassboostsw.so"/>
         <library name="bundle" path="libbundleaidl.so"/>
         <library name="downmix" path="libdownmixaidl.so"/>
-        <library name="dynamics_processingsw" path="libdynamicsprocessingsw.so"/>
+        <library name="dynamics_processing" path="libdynamicsprocessingaidl.so"/>
         <library name="equalizersw" path="libequalizersw.so"/>
         <library name="haptic_generator" path="libhapticgeneratoraidl.so"/>
         <library name="loudness_enhancer" path="libloudnessenhanceraidl.so"/>
@@ -72,7 +72,7 @@
             <libsw library="bundle" uuid="8631f300-72e2-11df-b57e-0002a5d5c51b"/>
         </effectProxy>
         <effect name="downmix" library="downmix" uuid="93f04452-e4fe-41cc-91f9-e475b6d1d69f"/>
-        <effect name="dynamics_processing" library="dynamics_processingsw" uuid="fa818d78-588b-11ed-9b6a-0242ac120002"/>
+        <effect name="dynamics_processing" library="dynamics_processing" uuid="e0e6539b-1781-7261-676f-6d7573696340"/>
         <effect name="haptic_generator" library="haptic_generator" uuid="97c4acd1-8b82-4f2f-832e-c2fe5d7a9931"/>
         <effect name="loudness_enhancer" library="loudness_enhancer" uuid="fa415329-2034-4bea-b5dc-5b381c8d1e2c"/>
         <effect name="env_reverb" library="env_reverbsw" uuid="fa819886-588b-11ed-9b6a-0242ac120002"/>
diff --git a/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp b/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp
index 39345a9..0ffbaa1 100644
--- a/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp
+++ b/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "AHAL_DynamicsProcessingSw"
 #include <Utils.h>
 #include <algorithm>
+#include <set>
 #include <unordered_set>
 
 #include <android-base/logging.h>
@@ -60,7 +61,8 @@
 namespace aidl::android::hardware::audio::effect {
 
 const std::string DynamicsProcessingSw::kEffectName = "DynamicsProcessingSw";
-const DynamicsProcessing::Capability DynamicsProcessingSw::kCapability;
+const DynamicsProcessing::Capability DynamicsProcessingSw::kCapability = {.minCutOffFreq = 220,
+                                                                          .maxCutOffFreq = 20000};
 const Descriptor DynamicsProcessingSw::kDescriptor = {
         .common = {.id = {.type = kDynamicsProcessingTypeUUID,
                           .uuid = kDynamicsProcessingSwImplUUID,
@@ -83,16 +85,143 @@
     RETURN_IF(Parameter::Specific::dynamicsProcessing != specific.getTag(), EX_ILLEGAL_ARGUMENT,
               "EffectNotSupported");
 
-    mSpecificParam = specific.get<Parameter::Specific::dynamicsProcessing>();
-    LOG(DEBUG) << __func__ << " success with: " << specific.toString();
-    return ndk::ScopedAStatus::ok();
+    RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+
+    LOG(INFO) << __func__ << specific.toString();
+    auto& dpParam = specific.get<Parameter::Specific::dynamicsProcessing>();
+    auto tag = dpParam.getTag();
+    switch (tag) {
+        case DynamicsProcessing::engineArchitecture: {
+            RETURN_IF(mContext->setEngineArchitecture(
+                              dpParam.get<DynamicsProcessing::engineArchitecture>()) !=
+                              RetCode::SUCCESS,
+                      EX_ILLEGAL_ARGUMENT, "setEngineArchitectureFailed");
+            return ndk::ScopedAStatus::ok();
+        }
+        case DynamicsProcessing::preEq: {
+            RETURN_IF(mContext->setPreEqChannelCfgs(dpParam.get<DynamicsProcessing::preEq>()) !=
+                              RetCode::SUCCESS,
+                      EX_ILLEGAL_ARGUMENT, "setPreEqChannelCfgsFailed");
+            return ndk::ScopedAStatus::ok();
+        }
+        case DynamicsProcessing::postEq: {
+            RETURN_IF(mContext->setPostEqChannelCfgs(dpParam.get<DynamicsProcessing::postEq>()) !=
+                              RetCode::SUCCESS,
+                      EX_ILLEGAL_ARGUMENT, "setPostEqChannelCfgsFailed");
+            return ndk::ScopedAStatus::ok();
+        }
+        case DynamicsProcessing::mbc: {
+            RETURN_IF(mContext->setMbcChannelCfgs(dpParam.get<DynamicsProcessing::mbc>()) !=
+                              RetCode::SUCCESS,
+                      EX_ILLEGAL_ARGUMENT, "setMbcChannelCfgsFailed");
+            return ndk::ScopedAStatus::ok();
+        }
+        case DynamicsProcessing::preEqBand: {
+            RETURN_IF(mContext->setPreEqBandCfgs(dpParam.get<DynamicsProcessing::preEqBand>()) !=
+                              RetCode::SUCCESS,
+                      EX_ILLEGAL_ARGUMENT, "setPreEqBandCfgsFailed");
+            return ndk::ScopedAStatus::ok();
+        }
+        case DynamicsProcessing::postEqBand: {
+            RETURN_IF(mContext->setPostEqBandCfgs(dpParam.get<DynamicsProcessing::postEqBand>()) !=
+                              RetCode::SUCCESS,
+                      EX_ILLEGAL_ARGUMENT, "setPostEqBandCfgsFailed");
+            return ndk::ScopedAStatus::ok();
+        }
+        case DynamicsProcessing::mbcBand: {
+            RETURN_IF(mContext->setMbcBandCfgs(dpParam.get<DynamicsProcessing::mbcBand>()) !=
+                              RetCode::SUCCESS,
+                      EX_ILLEGAL_ARGUMENT, "setMbcBandCfgsFailed");
+            return ndk::ScopedAStatus::ok();
+        }
+        case DynamicsProcessing::limiter: {
+            RETURN_IF(mContext->setLimiterCfgs(dpParam.get<DynamicsProcessing::limiter>()) !=
+                              RetCode::SUCCESS,
+                      EX_ILLEGAL_ARGUMENT, "limiterCfgsFailed");
+            return ndk::ScopedAStatus::ok();
+        }
+        case DynamicsProcessing::inputGain: {
+            RETURN_IF(mContext->setInputGainCfgs(dpParam.get<DynamicsProcessing::inputGain>()) !=
+                              RetCode::SUCCESS,
+                      EX_ILLEGAL_ARGUMENT, "inputGainCfgFailed");
+            return ndk::ScopedAStatus::ok();
+        }
+        case DynamicsProcessing::vendorExtension: {
+            LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag);
+            return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+                    EX_ILLEGAL_ARGUMENT, "DynamicsProcessingTagNotSupported");
+        }
+    }
 }
 
 ndk::ScopedAStatus DynamicsProcessingSw::getParameterSpecific(const Parameter::Id& id,
                                                               Parameter::Specific* specific) {
     auto tag = id.getTag();
     RETURN_IF(Parameter::Id::dynamicsProcessingTag != tag, EX_ILLEGAL_ARGUMENT, "wrongIdTag");
-    specific->set<Parameter::Specific::dynamicsProcessing>(mSpecificParam);
+    auto dpId = id.get<Parameter::Id::dynamicsProcessingTag>();
+    auto dpIdTag = dpId.getTag();
+    switch (dpIdTag) {
+        case DynamicsProcessing::Id::commonTag:
+            return getParameterDynamicsProcessing(dpId.get<DynamicsProcessing::Id::commonTag>(),
+                                                  specific);
+        case DynamicsProcessing::Id::vendorExtensionTag:
+            LOG(ERROR) << __func__ << " unsupported tag: " << toString(dpIdTag);
+            return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+                    EX_ILLEGAL_ARGUMENT, "DynamicsProcessingTagNotSupported");
+    }
+}
+
+ndk::ScopedAStatus DynamicsProcessingSw::getParameterDynamicsProcessing(
+        const DynamicsProcessing::Tag& tag, Parameter::Specific* specific) {
+    RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+
+    DynamicsProcessing dpParam;
+    switch (tag) {
+        case DynamicsProcessing::Tag::engineArchitecture: {
+            dpParam.set<DynamicsProcessing::engineArchitecture>(mContext->getEngineArchitecture());
+            break;
+        }
+        case DynamicsProcessing::Tag::preEq: {
+            dpParam.set<DynamicsProcessing::preEq>(mContext->getPreEqChannelCfgs());
+            break;
+        }
+        case DynamicsProcessing::Tag::postEq: {
+            dpParam.set<DynamicsProcessing::postEq>(mContext->getPostEqChannelCfgs());
+            break;
+        }
+        case DynamicsProcessing::Tag::mbc: {
+            dpParam.set<DynamicsProcessing::mbc>(mContext->getMbcChannelCfgs());
+            break;
+        }
+        case DynamicsProcessing::Tag::preEqBand: {
+            dpParam.set<DynamicsProcessing::preEqBand>(mContext->getPreEqBandCfgs());
+            break;
+        }
+        case DynamicsProcessing::Tag::postEqBand: {
+            dpParam.set<DynamicsProcessing::postEqBand>(mContext->getPostEqBandCfgs());
+            break;
+        }
+        case DynamicsProcessing::Tag::mbcBand: {
+            dpParam.set<DynamicsProcessing::mbcBand>(mContext->getMbcBandCfgs());
+            break;
+        }
+        case DynamicsProcessing::Tag::limiter: {
+            dpParam.set<DynamicsProcessing::limiter>(mContext->getLimiterCfgs());
+            break;
+        }
+        case DynamicsProcessing::Tag::inputGain: {
+            dpParam.set<DynamicsProcessing::inputGain>(mContext->getInputGainCfgs());
+            break;
+        }
+        case DynamicsProcessing::vendorExtension: {
+            LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag);
+            return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+                    EX_ILLEGAL_ARGUMENT, "DynamicsProcessingTagNotSupported");
+        }
+    }
+
+    specific->set<Parameter::Specific::dynamicsProcessing>(dpParam);
+    LOG(INFO) << __func__ << specific->toString();
     return ndk::ScopedAStatus::ok();
 }
 
@@ -127,4 +256,252 @@
     return {STATUS_OK, samples, samples};
 }
 
+RetCode DynamicsProcessingSwContext::setCommon(const Parameter::Common& common) {
+    mCommon = common;
+    mChannelCount =
+            ::android::hardware::audio::common::getChannelCount(common.input.base.channelMask);
+    resizeChannels();
+    resizeBands();
+    LOG(INFO) << __func__ << mCommon.toString();
+    return RetCode::SUCCESS;
+}
+
+RetCode DynamicsProcessingSwContext::setEngineArchitecture(
+        const DynamicsProcessing::EngineArchitecture& cfg) {
+    RETURN_VALUE_IF(!validateEngineConfig(cfg), RetCode::ERROR_ILLEGAL_PARAMETER,
+                    "illegalEngineConfig");
+
+    if (mEngineSettings == cfg) {
+        LOG(INFO) << __func__ << " not change in engine, do nothing";
+        return RetCode::SUCCESS;
+    }
+    mEngineSettings = cfg;
+    resizeBands();
+    return RetCode::SUCCESS;
+}
+
+RetCode DynamicsProcessingSwContext::setChannelCfgs(
+        const std::vector<DynamicsProcessing::ChannelConfig>& cfgs,
+        std::vector<DynamicsProcessing::ChannelConfig>& targetCfgs,
+        const DynamicsProcessing::StageEnablement& stage) {
+    RETURN_VALUE_IF(!stage.inUse, RetCode::ERROR_ILLEGAL_PARAMETER, "stageNotInUse");
+
+    RetCode ret = RetCode::SUCCESS;
+    std::unordered_set<int> channelSet;
+    for (auto& cfg : cfgs) {
+        if (cfg.channel < 0 || (size_t)cfg.channel >= mChannelCount) {
+            LOG(ERROR) << __func__ << " skip illegal channel config " << cfg.toString();
+            ret = RetCode::ERROR_ILLEGAL_PARAMETER;
+            continue;
+        }
+        if (0 != channelSet.count(cfg.channel)) {
+            LOG(WARNING) << __func__ << " duplicated channel " << cfg.channel;
+            ret = RetCode::ERROR_ILLEGAL_PARAMETER;
+        } else {
+            channelSet.insert(cfg.channel);
+        }
+        targetCfgs[cfg.channel] = cfg;
+    }
+    return ret;
+}
+
+RetCode DynamicsProcessingSwContext::setPreEqChannelCfgs(
+        const std::vector<DynamicsProcessing::ChannelConfig>& cfgs) {
+    return setChannelCfgs(cfgs, mPreEqChCfgs, mEngineSettings.preEqStage);
+}
+
+RetCode DynamicsProcessingSwContext::setPostEqChannelCfgs(
+        const std::vector<DynamicsProcessing::ChannelConfig>& cfgs) {
+    return setChannelCfgs(cfgs, mPostEqChCfgs, mEngineSettings.postEqStage);
+}
+
+RetCode DynamicsProcessingSwContext::setMbcChannelCfgs(
+        const std::vector<DynamicsProcessing::ChannelConfig>& cfgs) {
+    return setChannelCfgs(cfgs, mMbcChCfgs, mEngineSettings.mbcStage);
+}
+
+RetCode DynamicsProcessingSwContext::setEqBandCfgs(
+        const std::vector<DynamicsProcessing::EqBandConfig>& cfgs,
+        std::vector<DynamicsProcessing::EqBandConfig>& targetCfgs,
+        const DynamicsProcessing::StageEnablement& stage,
+        const std::vector<DynamicsProcessing::ChannelConfig>& channelConfig) {
+    RETURN_VALUE_IF(!stage.inUse, RetCode::ERROR_ILLEGAL_PARAMETER, "eqStageNotInUse");
+
+    RetCode ret = RetCode::SUCCESS;
+    std::set<std::pair<int /* channel */, int /* band */>> bandSet;
+
+    for (auto& cfg : cfgs) {
+        if (0 != bandSet.count({cfg.channel, cfg.band})) {
+            LOG(WARNING) << __func__ << " duplicated band " << cfg.toString();
+            ret = RetCode::ERROR_ILLEGAL_PARAMETER;
+        } else {
+            bandSet.insert({cfg.channel, cfg.band});
+        }
+        if (!validateEqBandConfig(cfg, mChannelCount, stage.bandCount, channelConfig)) {
+            LOG(WARNING) << __func__ << " skip invalid band " << cfg.toString();
+            ret = RetCode::ERROR_ILLEGAL_PARAMETER;
+            continue;
+            ;
+        }
+        targetCfgs[cfg.channel * stage.bandCount + cfg.band] = cfg;
+    }
+    return ret;
+}
+
+RetCode DynamicsProcessingSwContext::setPreEqBandCfgs(
+        const std::vector<DynamicsProcessing::EqBandConfig>& cfgs) {
+    return setEqBandCfgs(cfgs, mPreEqChBands, mEngineSettings.preEqStage, mPreEqChCfgs);
+}
+
+RetCode DynamicsProcessingSwContext::setPostEqBandCfgs(
+        const std::vector<DynamicsProcessing::EqBandConfig>& cfgs) {
+    return setEqBandCfgs(cfgs, mPostEqChBands, mEngineSettings.postEqStage, mPostEqChCfgs);
+}
+
+RetCode DynamicsProcessingSwContext::setMbcBandCfgs(
+        const std::vector<DynamicsProcessing::MbcBandConfig>& cfgs) {
+    RETURN_VALUE_IF(!mEngineSettings.mbcStage.inUse, RetCode::ERROR_ILLEGAL_PARAMETER,
+                    "mbcNotInUse");
+
+    RetCode ret = RetCode::SUCCESS;
+    std::set<std::pair<int /* channel */, int /* band */>> bandSet;
+
+    int bandCount = mEngineSettings.mbcStage.bandCount;
+    std::vector<bool> filled(mChannelCount * bandCount, false);
+    for (auto& it : cfgs) {
+        if (0 != bandSet.count({it.channel, it.band})) {
+            LOG(WARNING) << __func__ << " duplicated band " << it.toString();
+            ret = RetCode::ERROR_ILLEGAL_PARAMETER;
+        } else {
+            bandSet.insert({it.channel, it.band});
+        }
+        if (!validateMbcBandConfig(it, mChannelCount, mEngineSettings.mbcStage.bandCount,
+                                   mMbcChCfgs)) {
+            LOG(WARNING) << __func__ << " skip invalid band " << it.toString();
+            ret = RetCode::ERROR_ILLEGAL_PARAMETER;
+            continue;
+            ;
+        }
+        mMbcChBands[it.channel * bandCount + it.band] = it;
+    }
+    return ret;
+}
+
+RetCode DynamicsProcessingSwContext::setLimiterCfgs(
+        const std::vector<DynamicsProcessing::LimiterConfig>& cfgs) {
+    RETURN_VALUE_IF(!mEngineSettings.limiterInUse, RetCode::ERROR_ILLEGAL_PARAMETER,
+                    "limiterNotInUse");
+
+    RetCode ret = RetCode::SUCCESS;
+    std::unordered_set<int> channelSet;
+
+    for (auto& it : cfgs) {
+        if (0 != channelSet.count(it.channel)) {
+            LOG(WARNING) << __func__ << " duplicated channel " << it.channel;
+            ret = RetCode::ERROR_ILLEGAL_PARAMETER;
+        } else {
+            channelSet.insert(it.channel);
+        }
+        if (!validateLimiterConfig(it, mChannelCount)) {
+            LOG(WARNING) << __func__ << " skip invalid limiter " << it.toString();
+            ret = RetCode::ERROR_ILLEGAL_PARAMETER;
+            continue;
+        }
+        mLimiterCfgs[it.channel] = it;
+    }
+    return ret;
+}
+
+void DynamicsProcessingSwContext::resizeChannels() {
+    if (mPreEqChCfgs.size() != mChannelCount) {
+        mPreEqChCfgs.resize(mChannelCount, {.channel = kInvalidChannelId});
+    }
+    if (mPostEqChCfgs.size() != mChannelCount) {
+        mPostEqChCfgs.resize(mChannelCount, {.channel = kInvalidChannelId});
+    }
+    if (mMbcChCfgs.size() != mChannelCount) {
+        mMbcChCfgs.resize(mChannelCount, {.channel = kInvalidChannelId});
+    }
+    if (mLimiterCfgs.size() != mChannelCount) {
+        mLimiterCfgs.resize(mChannelCount, {.channel = kInvalidChannelId});
+    }
+    if (mInputGainCfgs.size() != mChannelCount) {
+        mInputGainCfgs.resize(mChannelCount, {.channel = kInvalidChannelId});
+    }
+}
+
+void DynamicsProcessingSwContext::resizeBands() {
+    if (mPreEqChBands.size() != (size_t)(mChannelCount * mEngineSettings.preEqStage.bandCount)) {
+        mPreEqChBands.resize(mChannelCount * mEngineSettings.preEqStage.bandCount,
+                             {.channel = kInvalidChannelId});
+    }
+    if (mPostEqChBands.size() != (size_t)(mChannelCount * mEngineSettings.postEqStage.bandCount)) {
+        mPostEqChBands.resize(mChannelCount * mEngineSettings.postEqStage.bandCount,
+                              {.channel = kInvalidChannelId});
+    }
+    if (mMbcChBands.size() != (size_t)(mChannelCount * mEngineSettings.mbcStage.bandCount)) {
+        mMbcChBands.resize(mChannelCount * mEngineSettings.mbcStage.bandCount,
+                           {.channel = kInvalidChannelId});
+    }
+}
+
+RetCode DynamicsProcessingSwContext::setInputGainCfgs(
+        const std::vector<DynamicsProcessing::InputGain>& cfgs) {
+    for (const auto& cfg : cfgs) {
+        RETURN_VALUE_IF(cfg.channel < 0 || (size_t)cfg.channel >= mChannelCount,
+                        RetCode::ERROR_ILLEGAL_PARAMETER, "invalidChannel");
+        mInputGainCfgs[cfg.channel] = cfg;
+    }
+    return RetCode::SUCCESS;
+}
+
+std::vector<DynamicsProcessing::InputGain> DynamicsProcessingSwContext::getInputGainCfgs() {
+    std::vector<DynamicsProcessing::InputGain> ret;
+    std::copy_if(mInputGainCfgs.begin(), mInputGainCfgs.end(), std::back_inserter(ret),
+                 [&](const auto& gain) { return gain.channel != kInvalidChannelId; });
+    return ret;
+}
+
+bool DynamicsProcessingSwContext::validateCutoffFrequency(float freq) {
+    return freq >= DynamicsProcessingSw::kCapability.minCutOffFreq &&
+           freq <= DynamicsProcessingSw::kCapability.maxCutOffFreq;
+}
+
+bool DynamicsProcessingSwContext::validateStageEnablement(
+        const DynamicsProcessing::StageEnablement& enablement) {
+    return !enablement.inUse || (enablement.inUse && enablement.bandCount > 0);
+}
+
+bool DynamicsProcessingSwContext::validateEngineConfig(
+        const DynamicsProcessing::EngineArchitecture& engine) {
+    return engine.preferredProcessingDurationMs >= 0 &&
+           validateStageEnablement(engine.preEqStage) &&
+           validateStageEnablement(engine.postEqStage) && validateStageEnablement(engine.mbcStage);
+}
+
+bool DynamicsProcessingSwContext::validateEqBandConfig(
+        const DynamicsProcessing::EqBandConfig& band, int maxChannel, int maxBand,
+        const std::vector<DynamicsProcessing::ChannelConfig>& channelConfig) {
+    return band.channel >= 0 && band.channel < maxChannel &&
+           (size_t)band.channel < channelConfig.size() && channelConfig[band.channel].enable &&
+           band.band >= 0 && band.band < maxBand && validateCutoffFrequency(band.cutoffFrequencyHz);
+}
+
+bool DynamicsProcessingSwContext::validateMbcBandConfig(
+        const DynamicsProcessing::MbcBandConfig& band, int maxChannel, int maxBand,
+        const std::vector<DynamicsProcessing::ChannelConfig>& channelConfig) {
+    return band.channel >= 0 && band.channel < maxChannel &&
+           (size_t)band.channel < channelConfig.size() && channelConfig[band.channel].enable &&
+           band.band >= 0 && band.band < maxBand &&
+           validateCutoffFrequency(band.cutoffFrequencyHz) && band.attackTimeMs >= 0 &&
+           band.releaseTimeMs >= 0 && band.ratio >= 0 && band.thresholdDb <= 0 &&
+           band.kneeWidthDb <= 0 && band.noiseGateThresholdDb <= 0 && band.expanderRatio >= 0;
+}
+
+bool DynamicsProcessingSwContext::validateLimiterConfig(
+        const DynamicsProcessing::LimiterConfig& limiter, int maxChannel) {
+    return limiter.channel >= 0 && limiter.channel < maxChannel && limiter.attackTimeMs >= 0 &&
+           limiter.releaseTimeMs >= 0 && limiter.ratio >= 0 && limiter.thresholdDb <= 0;
+}
+
 }  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.h b/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.h
index 2ae46c5..e336df7 100644
--- a/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.h
+++ b/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.h
@@ -29,11 +29,77 @@
 class DynamicsProcessingSwContext final : public EffectContext {
   public:
     DynamicsProcessingSwContext(int statusDepth, const Parameter::Common& common)
-        : EffectContext(statusDepth, common) {
+        : EffectContext(statusDepth, common),
+          mChannelCount(::android::hardware::audio::common::getChannelCount(
+                  common.input.base.channelMask)),
+          mPreEqChCfgs(mChannelCount, {.channel = kInvalidChannelId}),
+          mPostEqChCfgs(mChannelCount, {.channel = kInvalidChannelId}),
+          mMbcChCfgs(mChannelCount, {.channel = kInvalidChannelId}),
+          mLimiterCfgs(mChannelCount, {.channel = kInvalidChannelId}) {
         LOG(DEBUG) << __func__;
     }
-    // TODO: add specific context here
-};
+
+    // utils
+    RetCode setChannelCfgs(const std::vector<DynamicsProcessing::ChannelConfig>& cfgs,
+                           std::vector<DynamicsProcessing::ChannelConfig>& targetCfgs,
+                           const DynamicsProcessing::StageEnablement& engineSetting);
+
+    RetCode setEqBandCfgs(const std::vector<DynamicsProcessing::EqBandConfig>& cfgs,
+                          std::vector<DynamicsProcessing::EqBandConfig>& targetCfgs,
+                          const DynamicsProcessing::StageEnablement& stage,
+                          const std::vector<DynamicsProcessing::ChannelConfig>& channelConfig);
+
+    // set params
+    RetCode setCommon(const Parameter::Common& common) override;
+    RetCode setEngineArchitecture(const DynamicsProcessing::EngineArchitecture& cfg);
+    RetCode setPreEqChannelCfgs(const std::vector<DynamicsProcessing::ChannelConfig>& cfgs);
+    RetCode setPostEqChannelCfgs(const std::vector<DynamicsProcessing::ChannelConfig>& cfgs);
+    RetCode setMbcChannelCfgs(const std::vector<DynamicsProcessing::ChannelConfig>& cfgs);
+    RetCode setPreEqBandCfgs(const std::vector<DynamicsProcessing::EqBandConfig>& cfgs);
+    RetCode setPostEqBandCfgs(const std::vector<DynamicsProcessing::EqBandConfig>& cfgs);
+    RetCode setMbcBandCfgs(const std::vector<DynamicsProcessing::MbcBandConfig>& cfgs);
+    RetCode setLimiterCfgs(const std::vector<DynamicsProcessing::LimiterConfig>& cfgs);
+    RetCode setInputGainCfgs(const std::vector<DynamicsProcessing::InputGain>& cfgs);
+
+    // get params
+    DynamicsProcessing::EngineArchitecture getEngineArchitecture() { return mEngineSettings; }
+    std::vector<DynamicsProcessing::ChannelConfig> getPreEqChannelCfgs() { return mPreEqChCfgs; }
+    std::vector<DynamicsProcessing::ChannelConfig> getPostEqChannelCfgs() { return mPostEqChCfgs; }
+    std::vector<DynamicsProcessing::ChannelConfig> getMbcChannelCfgs() { return mMbcChCfgs; }
+    std::vector<DynamicsProcessing::EqBandConfig> getPreEqBandCfgs() { return mPreEqChBands; }
+    std::vector<DynamicsProcessing::EqBandConfig> getPostEqBandCfgs() { return mPostEqChBands; }
+    std::vector<DynamicsProcessing::MbcBandConfig> getMbcBandCfgs() { return mMbcChBands; }
+    std::vector<DynamicsProcessing::LimiterConfig> getLimiterCfgs() { return mLimiterCfgs; }
+    std::vector<DynamicsProcessing::InputGain> getInputGainCfgs();
+
+  private:
+    static constexpr int32_t kInvalidChannelId = -1;
+    size_t mChannelCount = 0;
+    DynamicsProcessing::EngineArchitecture mEngineSettings;
+    // Channel config vector with size of mChannelCount
+    std::vector<DynamicsProcessing::ChannelConfig> mPreEqChCfgs;
+    std::vector<DynamicsProcessing::ChannelConfig> mPostEqChCfgs;
+    std::vector<DynamicsProcessing::ChannelConfig> mMbcChCfgs;
+    std::vector<DynamicsProcessing::LimiterConfig> mLimiterCfgs;
+    std::vector<DynamicsProcessing::InputGain> mInputGainCfgs;
+    // Band config vector with size of mChannelCount * bandCount
+    std::vector<DynamicsProcessing::EqBandConfig> mPreEqChBands;
+    std::vector<DynamicsProcessing::EqBandConfig> mPostEqChBands;
+    std::vector<DynamicsProcessing::MbcBandConfig> mMbcChBands;
+
+    bool validateCutoffFrequency(float freq);
+    bool validateStageEnablement(const DynamicsProcessing::StageEnablement& enablement);
+    bool validateEngineConfig(const DynamicsProcessing::EngineArchitecture& engine);
+    bool validateEqBandConfig(const DynamicsProcessing::EqBandConfig& band, int maxChannel,
+                              int maxBand,
+                              const std::vector<DynamicsProcessing::ChannelConfig>& channelConfig);
+    bool validateMbcBandConfig(const DynamicsProcessing::MbcBandConfig& band, int maxChannel,
+                               int maxBand,
+                               const std::vector<DynamicsProcessing::ChannelConfig>& channelConfig);
+    bool validateLimiterConfig(const DynamicsProcessing::LimiterConfig& limiter, int maxChannel);
+    void resizeChannels();
+    void resizeBands();
+};  // DynamicsProcessingSwContext
 
 class DynamicsProcessingSw final : public EffectImpl {
   public:
@@ -60,7 +126,9 @@
 
   private:
     std::shared_ptr<DynamicsProcessingSwContext> mContext;
-    /* parameters */
-    DynamicsProcessing mSpecificParam;
-};
+    ndk::ScopedAStatus getParameterDynamicsProcessing(const DynamicsProcessing::Tag& tag,
+                                                      Parameter::Specific* specific);
+
+};  // DynamicsProcessingSw
+
 }  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/envReverb/EnvReverbSw.cpp b/audio/aidl/default/envReverb/EnvReverbSw.cpp
index 9d7159a..905dba4 100644
--- a/audio/aidl/default/envReverb/EnvReverbSw.cpp
+++ b/audio/aidl/default/envReverb/EnvReverbSw.cpp
@@ -60,8 +60,18 @@
 namespace aidl::android::hardware::audio::effect {
 
 const std::string EnvReverbSw::kEffectName = "EnvReverbSw";
-const EnvironmentalReverb::Capability EnvReverbSw::kCapability = {
-        .maxDecayTimeMs = EnvironmentalReverb::MAX_DECAY_TIME_MS};
+const EnvironmentalReverb::Capability EnvReverbSw::kCapability = {.minRoomLevelMb = -6000,
+                                                                  .maxRoomLevelMb = 0,
+                                                                  .minRoomHfLevelMb = -4000,
+                                                                  .maxRoomHfLevelMb = 0,
+                                                                  .maxDecayTimeMs = 7000,
+                                                                  .minDecayHfRatioPm = 100,
+                                                                  .maxDecayHfRatioPm = 2000,
+                                                                  .minLevelMb = -6000,
+                                                                  .maxLevelMb = 0,
+                                                                  .maxDelayMs = 65,
+                                                                  .maxDiffusionPm = 1000,
+                                                                  .maxDensityPm = 1000};
 const Descriptor EnvReverbSw::kDescriptor = {
         .common = {.id = {.type = kEnvReverbTypeUUID,
                           .uuid = kEnvReverbSwImplUUID,
@@ -251,4 +261,88 @@
     return {STATUS_OK, samples, samples};
 }
 
+RetCode EnvReverbSwContext::setErRoomLevel(int roomLevel) {
+    if (roomLevel < EnvReverbSw::kCapability.minRoomLevelMb ||
+        roomLevel > EnvReverbSw::kCapability.maxRoomLevelMb) {
+        LOG(ERROR) << __func__ << " invalid roomLevel: " << roomLevel;
+        return RetCode::ERROR_ILLEGAL_PARAMETER;
+    }
+    // TODO : Add implementation to apply new room level
+    mRoomLevel = roomLevel;
+    return RetCode::SUCCESS;
+}
+
+RetCode EnvReverbSwContext::setErRoomHfLevel(int roomHfLevel) {
+    if (roomHfLevel < EnvReverbSw::kCapability.minRoomHfLevelMb ||
+        roomHfLevel > EnvReverbSw::kCapability.maxRoomHfLevelMb) {
+        LOG(ERROR) << __func__ << " invalid roomHfLevel: " << roomHfLevel;
+        return RetCode::ERROR_ILLEGAL_PARAMETER;
+    }
+    // TODO : Add implementation to apply new room HF level
+    mRoomHfLevel = roomHfLevel;
+    return RetCode::SUCCESS;
+}
+
+RetCode EnvReverbSwContext::setErDecayTime(int decayTime) {
+    if (decayTime < 0 || decayTime > EnvReverbSw::kCapability.maxDecayTimeMs) {
+        LOG(ERROR) << __func__ << " invalid decayTime: " << decayTime;
+        return RetCode::ERROR_ILLEGAL_PARAMETER;
+    }
+    // TODO : Add implementation to apply new decay time
+    mDecayTime = decayTime;
+    return RetCode::SUCCESS;
+}
+
+RetCode EnvReverbSwContext::setErDecayHfRatio(int decayHfRatio) {
+    if (decayHfRatio < EnvReverbSw::kCapability.minDecayHfRatioPm ||
+        decayHfRatio > EnvReverbSw::kCapability.maxDecayHfRatioPm) {
+        LOG(ERROR) << __func__ << " invalid decayHfRatio: " << decayHfRatio;
+        return RetCode::ERROR_ILLEGAL_PARAMETER;
+    }
+    // TODO : Add implementation to apply new decay HF ratio
+    mDecayHfRatio = decayHfRatio;
+    return RetCode::SUCCESS;
+}
+
+RetCode EnvReverbSwContext::setErLevel(int level) {
+    if (level < EnvReverbSw::kCapability.minLevelMb ||
+        level > EnvReverbSw::kCapability.maxLevelMb) {
+        LOG(ERROR) << __func__ << " invalid level: " << level;
+        return RetCode::ERROR_ILLEGAL_PARAMETER;
+    }
+    // TODO : Add implementation to apply new level
+    mLevel = level;
+    return RetCode::SUCCESS;
+}
+
+RetCode EnvReverbSwContext::setErDelay(int delay) {
+    if (delay < 0 || delay > EnvReverbSw::kCapability.maxDelayMs) {
+        LOG(ERROR) << __func__ << " invalid delay: " << delay;
+        return RetCode::ERROR_ILLEGAL_PARAMETER;
+    }
+    // TODO : Add implementation to apply new delay
+    mDelay = delay;
+    return RetCode::SUCCESS;
+}
+
+RetCode EnvReverbSwContext::setErDiffusion(int diffusion) {
+    if (diffusion < 0 || diffusion > EnvReverbSw::kCapability.maxDiffusionPm) {
+        LOG(ERROR) << __func__ << " invalid diffusion: " << diffusion;
+        return RetCode::ERROR_ILLEGAL_PARAMETER;
+    }
+    // TODO : Add implementation to apply new diffusion
+    mDiffusion = diffusion;
+    return RetCode::SUCCESS;
+}
+
+RetCode EnvReverbSwContext::setErDensity(int density) {
+    if (density < 0 || density > EnvReverbSw::kCapability.maxDensityPm) {
+        LOG(ERROR) << __func__ << " invalid density: " << density;
+        return RetCode::ERROR_ILLEGAL_PARAMETER;
+    }
+    // TODO : Add implementation to apply new density
+    mDensity = density;
+    return RetCode::SUCCESS;
+}
+
 }  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/envReverb/EnvReverbSw.h b/audio/aidl/default/envReverb/EnvReverbSw.h
index f521215..77f384e 100644
--- a/audio/aidl/default/envReverb/EnvReverbSw.h
+++ b/audio/aidl/default/envReverb/EnvReverbSw.h
@@ -33,100 +33,28 @@
         LOG(DEBUG) << __func__;
     }
 
-    RetCode setErRoomLevel(int roomLevel) {
-        if (roomLevel < EnvironmentalReverb::MIN_ROOM_LEVEL_MB ||
-            roomLevel > EnvironmentalReverb::MAX_ROOM_LEVEL_MB) {
-            LOG(ERROR) << __func__ << " invalid roomLevel: " << roomLevel;
-            return RetCode::ERROR_ILLEGAL_PARAMETER;
-        }
-        // TODO : Add implementation to apply new room level
-        mRoomLevel = roomLevel;
-        return RetCode::SUCCESS;
-    }
+    RetCode setErRoomLevel(int roomLevel);
     int getErRoomLevel() const { return mRoomLevel; }
 
-    RetCode setErRoomHfLevel(int roomHfLevel) {
-        if (roomHfLevel < EnvironmentalReverb::MIN_ROOM_HF_LEVEL_MB ||
-            roomHfLevel > EnvironmentalReverb::MAX_ROOM_HF_LEVEL_MB) {
-            LOG(ERROR) << __func__ << " invalid roomHfLevel: " << roomHfLevel;
-            return RetCode::ERROR_ILLEGAL_PARAMETER;
-        }
-        // TODO : Add implementation to apply new room HF level
-        mRoomHfLevel = roomHfLevel;
-        return RetCode::SUCCESS;
-    }
+    RetCode setErRoomHfLevel(int roomHfLevel);
     int getErRoomHfLevel() const { return mRoomHfLevel; }
 
-    RetCode setErDecayTime(int decayTime) {
-        if (decayTime < EnvironmentalReverb::MIN_DECAY_TIME_MS ||
-            decayTime > EnvironmentalReverb::MAX_DECAY_TIME_MS) {
-            LOG(ERROR) << __func__ << " invalid decayTime: " << decayTime;
-            return RetCode::ERROR_ILLEGAL_PARAMETER;
-        }
-        // TODO : Add implementation to apply new decay time
-        mDecayTime = decayTime;
-        return RetCode::SUCCESS;
-    }
+    RetCode setErDecayTime(int decayTime);
     int getErDecayTime() const { return mDecayTime; }
 
-    RetCode setErDecayHfRatio(int decayHfRatio) {
-        if (decayHfRatio < EnvironmentalReverb::MIN_DECAY_HF_RATIO_PM ||
-            decayHfRatio > EnvironmentalReverb::MAX_DECAY_HF_RATIO_PM) {
-            LOG(ERROR) << __func__ << " invalid decayHfRatio: " << decayHfRatio;
-            return RetCode::ERROR_ILLEGAL_PARAMETER;
-        }
-        // TODO : Add implementation to apply new decay HF ratio
-        mDecayHfRatio = decayHfRatio;
-        return RetCode::SUCCESS;
-    }
+    RetCode setErDecayHfRatio(int decayHfRatio);
     int getErDecayHfRatio() const { return mDecayHfRatio; }
 
-    RetCode setErLevel(int level) {
-        if (level < EnvironmentalReverb::MIN_LEVEL_MB ||
-            level > EnvironmentalReverb::MAX_LEVEL_MB) {
-            LOG(ERROR) << __func__ << " invalid level: " << level;
-            return RetCode::ERROR_ILLEGAL_PARAMETER;
-        }
-        // TODO : Add implementation to apply new level
-        mLevel = level;
-        return RetCode::SUCCESS;
-    }
+    RetCode setErLevel(int level);
     int getErLevel() const { return mLevel; }
 
-    RetCode setErDelay(int delay) {
-        if (delay < EnvironmentalReverb::MIN_DELAY_MS ||
-            delay > EnvironmentalReverb::MAX_DELAY_MS) {
-            LOG(ERROR) << __func__ << " invalid delay: " << delay;
-            return RetCode::ERROR_ILLEGAL_PARAMETER;
-        }
-        // TODO : Add implementation to apply new delay
-        mDelay = delay;
-        return RetCode::SUCCESS;
-    }
+    RetCode setErDelay(int delay);
     int getErDelay() const { return mDelay; }
 
-    RetCode setErDiffusion(int diffusion) {
-        if (diffusion < EnvironmentalReverb::MIN_DIFFUSION_PM ||
-            diffusion > EnvironmentalReverb::MAX_DIFFUSION_PM) {
-            LOG(ERROR) << __func__ << " invalid diffusion: " << diffusion;
-            return RetCode::ERROR_ILLEGAL_PARAMETER;
-        }
-        // TODO : Add implementation to apply new diffusion
-        mDiffusion = diffusion;
-        return RetCode::SUCCESS;
-    }
+    RetCode setErDiffusion(int diffusion);
     int getErDiffusion() const { return mDiffusion; }
 
-    RetCode setErDensity(int density) {
-        if (density < EnvironmentalReverb::MIN_DENSITY_PM ||
-            density > EnvironmentalReverb::MAX_DENSITY_PM) {
-            LOG(ERROR) << __func__ << " invalid density: " << density;
-            return RetCode::ERROR_ILLEGAL_PARAMETER;
-        }
-        // TODO : Add implementation to apply new density
-        mDensity = density;
-        return RetCode::SUCCESS;
-    }
+    RetCode setErDensity(int density);
     int getErDensity() const { return mDensity; }
 
     RetCode setErBypass(bool bypass) {
@@ -137,14 +65,14 @@
     bool getErBypass() const { return mBypass; }
 
   private:
-    int mRoomLevel = EnvironmentalReverb::MIN_ROOM_LEVEL_MB;       // Default room level
-    int mRoomHfLevel = EnvironmentalReverb::MAX_ROOM_HF_LEVEL_MB;  // Default room hf level
+    int mRoomLevel = -6000;                                        // Default room level
+    int mRoomHfLevel = 0;                                          // Default room hf level
     int mDecayTime = 1000;                                         // Default decay time
     int mDecayHfRatio = 500;                                       // Default decay hf ratio
-    int mLevel = EnvironmentalReverb::MIN_LEVEL_MB;                // Default level
+    int mLevel = -6000;                                            // Default level
     int mDelay = 40;                                               // Default delay
-    int mDiffusion = EnvironmentalReverb::MAX_DIFFUSION_PM;        // Default diffusion
-    int mDensity = EnvironmentalReverb::MAX_DENSITY_PM;            // Default density
+    int mDiffusion = 1000;                                         // Default diffusion
+    int mDensity = 1000;                                           // Default density
     bool mBypass = false;                                          // Default bypass
 };
 
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index e9f43d8..000a704 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -36,6 +36,13 @@
     explicit Module(Type type) : mType(type) {}
 
   private:
+    struct VendorDebug {
+        static const std::string kForceTransientBurstName;
+        static const std::string kForceSynchronousDrainName;
+        bool forceTransientBurst = false;
+        bool forceSynchronousDrain = false;
+    };
+
     ndk::ScopedAStatus setModuleDebug(
             const ::aidl::android::hardware::audio::core::ModuleDebug& in_debug) override;
     ndk::ScopedAStatus getTelephony(std::shared_ptr<ITelephony>* _aidl_return) override;
@@ -128,6 +135,7 @@
     const Type mType;
     std::unique_ptr<internal::Configuration> mConfig;
     ModuleDebug mDebug;
+    VendorDebug mVendorDebug;
     // For the interfaces requiring to return the same instance, we need to hold them
     // via a strong pointer. The binder token is retained for a call to 'setMinSchedulerPolicy'.
     std::shared_ptr<ITelephony> mTelephony;
diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h
index 5abd4de..2cf5951 100644
--- a/audio/aidl/default/include/core-impl/Stream.h
+++ b/audio/aidl/default/include/core-impl/Stream.h
@@ -62,12 +62,21 @@
     // Ensure that this value is not used by any of StreamDescriptor.State enums
     static constexpr int32_t STATE_CLOSED = -1;
 
+    struct DebugParameters {
+        // An extra delay for transient states, in ms.
+        int transientStateDelayMs = 0;
+        // Force the "burst" command to move the SM to the TRANSFERRING state.
+        bool forceTransientBurst = false;
+        // Force the "drain" command to be synchronous, going directly to the IDLE state.
+        bool forceSynchronousDrain = false;
+    };
+
     StreamContext() = default;
     StreamContext(std::unique_ptr<CommandMQ> commandMQ, std::unique_ptr<ReplyMQ> replyMQ,
                   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)
+                  DebugParameters debugParameters)
         : mCommandMQ(std::move(commandMQ)),
           mInternalCommandCookie(std::rand()),
           mReplyMQ(std::move(replyMQ)),
@@ -75,7 +84,7 @@
           mChannelLayout(channelLayout),
           mDataMQ(std::move(dataMQ)),
           mAsyncCallback(asyncCallback),
-          mTransientStateDelayMs(transientStateDelayMs) {}
+          mDebugParameters(debugParameters) {}
     StreamContext(StreamContext&& other)
         : mCommandMQ(std::move(other.mCommandMQ)),
           mInternalCommandCookie(other.mInternalCommandCookie),
@@ -83,8 +92,8 @@
           mFormat(other.mFormat),
           mChannelLayout(other.mChannelLayout),
           mDataMQ(std::move(other.mDataMQ)),
-          mAsyncCallback(other.mAsyncCallback),
-          mTransientStateDelayMs(other.mTransientStateDelayMs) {}
+          mAsyncCallback(std::move(other.mAsyncCallback)),
+          mDebugParameters(std::move(other.mDebugParameters)) {}
     StreamContext& operator=(StreamContext&& other) {
         mCommandMQ = std::move(other.mCommandMQ);
         mInternalCommandCookie = other.mInternalCommandCookie;
@@ -92,8 +101,8 @@
         mFormat = std::move(other.mFormat);
         mChannelLayout = std::move(other.mChannelLayout);
         mDataMQ = std::move(other.mDataMQ);
-        mAsyncCallback = other.mAsyncCallback;
-        mTransientStateDelayMs = other.mTransientStateDelayMs;
+        mAsyncCallback = std::move(other.mAsyncCallback);
+        mDebugParameters = std::move(other.mDebugParameters);
         return *this;
     }
 
@@ -107,10 +116,12 @@
     ::aidl::android::media::audio::common::AudioFormatDescription getFormat() const {
         return mFormat;
     }
+    bool getForceTransientBurst() const { return mDebugParameters.forceTransientBurst; }
+    bool getForceSynchronousDrain() const { return mDebugParameters.forceSynchronousDrain; }
     size_t getFrameSize() const;
     int getInternalCommandCookie() const { return mInternalCommandCookie; }
     ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
-    int getTransientStateDelayMs() const { return mTransientStateDelayMs; }
+    int getTransientStateDelayMs() const { return mDebugParameters.transientStateDelayMs; }
     bool isValid() const;
     void reset();
 
@@ -122,7 +133,7 @@
     ::aidl::android::media::audio::common::AudioChannelLayout mChannelLayout;
     std::unique_ptr<DataMQ> mDataMQ;
     std::shared_ptr<IStreamCallback> mAsyncCallback;
-    int mTransientStateDelayMs;
+    DebugParameters mDebugParameters;
 };
 
 class StreamWorkerCommonLogic : public ::android::hardware::audio::common::StreamLogic {
@@ -141,7 +152,9 @@
           mReplyMQ(context.getReplyMQ()),
           mDataMQ(context.getDataMQ()),
           mAsyncCallback(context.getAsyncCallback()),
-          mTransientStateDelayMs(context.getTransientStateDelayMs()) {}
+          mTransientStateDelayMs(context.getTransientStateDelayMs()),
+          mForceTransientBurst(context.getForceTransientBurst()),
+          mForceSynchronousDrain(context.getForceSynchronousDrain()) {}
     std::string init() override;
     void populateReply(StreamDescriptor::Reply* reply, bool isConnected) const;
     void populateReplyWrongState(StreamDescriptor::Reply* reply,
@@ -164,6 +177,8 @@
     std::shared_ptr<IStreamCallback> mAsyncCallback;
     const std::chrono::duration<int, std::milli> mTransientStateDelayMs;
     std::chrono::time_point<std::chrono::steady_clock> mTransientStateStart;
+    const bool mForceTransientBurst;
+    const bool mForceSynchronousDrain;
     // 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;
diff --git a/audio/aidl/default/include/effect-impl/EffectContext.h b/audio/aidl/default/include/effect-impl/EffectContext.h
index 1f39db0..a3e7ff2 100644
--- a/audio/aidl/default/include/effect-impl/EffectContext.h
+++ b/audio/aidl/default/include/effect-impl/EffectContext.h
@@ -119,11 +119,11 @@
 
     virtual RetCode setCommon(const Parameter::Common& common) {
         mCommon = common;
-        LOG(ERROR) << __func__ << mCommon.toString();
+        LOG(INFO) << __func__ << mCommon.toString();
         return RetCode::SUCCESS;
     }
     virtual Parameter::Common getCommon() {
-        LOG(ERROR) << __func__ << mCommon.toString();
+        LOG(INFO) << __func__ << mCommon.toString();
         return mCommon;
     }
 
diff --git a/audio/aidl/default/include/effect-impl/EffectTypes.h b/audio/aidl/default/include/effect-impl/EffectTypes.h
index 58c8672..b100a2e 100644
--- a/audio/aidl/default/include/effect-impl/EffectTypes.h
+++ b/audio/aidl/default/include/effect-impl/EffectTypes.h
@@ -98,12 +98,13 @@
         }                                                                              \
     } while (0)
 
-#define RETURN_VALUE_IF(expr, ret, log)                                                          \
-    do {                                                                                         \
-        if (expr) {                                                                              \
-            LOG(ERROR) << __func__ << ":" << __LINE__ << " return with expr " << #expr << (log); \
-            return ret;                                                                          \
-        }                                                                                        \
+#define RETURN_VALUE_IF(expr, ret, log)                                                  \
+    do {                                                                                 \
+        if (expr) {                                                                      \
+            LOG(ERROR) << __func__ << ":" << __LINE__ << " return with expr \"" << #expr \
+                       << "\":" << (log);                                                \
+            return ret;                                                                  \
+        }                                                                                \
     } while (0)
 
 #define RETURN_IF_BINDER_EXCEPTION(functor)                                 \
diff --git a/audio/aidl/default/include/effect-impl/EffectUUID.h b/audio/aidl/default/include/effect-impl/EffectUUID.h
index 6eec29e..1a60829 100644
--- a/audio/aidl/default/include/effect-impl/EffectUUID.h
+++ b/audio/aidl/default/include/effect-impl/EffectUUID.h
@@ -135,6 +135,12 @@
                                                         0x11ed,
                                                         0x9b6a,
                                                         {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
+// e0e6539b-1781-7261-676f-6d7573696340
+static const AudioUuid kDynamicsProcessingImplUUID = {static_cast<int32_t>(0xe0e6539b),
+                                                      0x1781,
+                                                      0x7261,
+                                                      0x676f,
+                                                      {0x6d, 0x75, 0x73, 0x69, 0x63, 0x40}};
 // 1411e6d6-aecd-4021-a1cf-a6aceb0d71e5
 static const AudioUuid kHapticGeneratorTypeUUID = {static_cast<int32_t>(0x1411e6d6),
                                                    0xaecd,
diff --git a/audio/aidl/vts/Android.bp b/audio/aidl/vts/Android.bp
index becdf1b..f9d12dd 100644
--- a/audio/aidl/vts/Android.bp
+++ b/audio/aidl/vts/Android.bp
@@ -81,6 +81,12 @@
 }
 
 cc_test {
+    name: "VtsHalDynamicsProcessingTargetTest",
+    defaults: ["VtsHalAudioTargetTestDefaults"],
+    srcs: ["VtsHalDynamicsProcessingTest.cpp"],
+}
+
+cc_test {
     name: "VtsHalEnvironmentalReverbTargetTest",
     defaults: ["VtsHalAudioTargetTestDefaults"],
     srcs: ["VtsHalEnvironmentalReverbTargetTest.cpp"],
diff --git a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
index 8da475e..d4f2811 100644
--- a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
@@ -18,6 +18,7 @@
 #include <chrono>
 #include <cmath>
 #include <condition_variable>
+#include <forward_list>
 #include <limits>
 #include <memory>
 #include <mutex>
@@ -85,6 +86,7 @@
 using aidl::android::media::audio::common::AudioPortExt;
 using aidl::android::media::audio::common::AudioSource;
 using aidl::android::media::audio::common::AudioUsage;
+using aidl::android::media::audio::common::Boolean;
 using aidl::android::media::audio::common::Float;
 using aidl::android::media::audio::common::Int;
 using aidl::android::media::audio::common::Void;
@@ -126,7 +128,7 @@
         return WithDebugFlags(parent.mFlags);
     }
 
-    WithDebugFlags() {}
+    WithDebugFlags() = default;
     explicit WithDebugFlags(const ModuleDebug& initial) : mInitial(initial), mFlags(initial) {}
     WithDebugFlags(const WithDebugFlags&) = delete;
     WithDebugFlags& operator=(const WithDebugFlags&) = delete;
@@ -135,7 +137,10 @@
             EXPECT_IS_OK(mModule->setModuleDebug(mInitial));
         }
     }
-    void SetUp(IModule* module) { ASSERT_IS_OK(module->setModuleDebug(mFlags)); }
+    void SetUp(IModule* module) {
+        ASSERT_IS_OK(module->setModuleDebug(mFlags));
+        mModule = module;
+    }
     ModuleDebug& flags() { return mFlags; }
 
   private:
@@ -144,13 +149,65 @@
     IModule* mModule = nullptr;
 };
 
+template <typename T>
+class WithModuleParameter {
+  public:
+    WithModuleParameter(const std::string parameterId, const T& value)
+        : mParameterId(parameterId), mValue(value) {}
+    WithModuleParameter(const WithModuleParameter&) = delete;
+    WithModuleParameter& operator=(const WithModuleParameter&) = delete;
+    ~WithModuleParameter() {
+        if (mModule != nullptr) {
+            VendorParameter parameter{.id = mParameterId};
+            parameter.ext.setParcelable(mInitial);
+            EXPECT_IS_OK(mModule->setVendorParameters({parameter}, false));
+        }
+    }
+    ScopedAStatus SetUpNoChecks(IModule* module, bool failureExpected) {
+        std::vector<VendorParameter> parameters;
+        ScopedAStatus result = module->getVendorParameters({mParameterId}, &parameters);
+        if (result.isOk() && parameters.size() == 1) {
+            std::optional<T> maybeInitial;
+            binder_status_t status = parameters[0].ext.getParcelable(&maybeInitial);
+            if (status == STATUS_OK && maybeInitial.has_value()) {
+                mInitial = maybeInitial.value();
+                VendorParameter parameter{.id = mParameterId};
+                parameter.ext.setParcelable(mValue);
+                result = module->setVendorParameters({parameter}, false);
+                if (result.isOk()) {
+                    LOG(INFO) << __func__ << ": overriding parameter \"" << mParameterId
+                              << "\" with " << mValue.toString()
+                              << ", old value: " << mInitial.toString();
+                    mModule = module;
+                }
+            } else {
+                LOG(ERROR) << __func__ << ": error while retrieving the value of \"" << mParameterId
+                           << "\"";
+                return ScopedAStatus::fromStatus(status);
+            }
+        }
+        if (!result.isOk()) {
+            LOG(failureExpected ? INFO : ERROR)
+                    << __func__ << ": can not override vendor parameter \"" << mParameterId << "\""
+                    << result;
+        }
+        return result;
+    }
+
+  private:
+    const std::string mParameterId;
+    const T mValue;
+    IModule* mModule = nullptr;
+    T mInitial;
+};
+
 // For consistency, WithAudioPortConfig can start both with a non-existent
 // port config, and with an existing one. Existence is determined by the
 // id of the provided config. If it's not 0, then WithAudioPortConfig is
 // essentially a no-op wrapper.
 class WithAudioPortConfig {
   public:
-    WithAudioPortConfig() {}
+    WithAudioPortConfig() = default;
     explicit WithAudioPortConfig(const AudioPortConfig& config) : mInitialConfig(config) {}
     WithAudioPortConfig(const WithAudioPortConfig&) = delete;
     WithAudioPortConfig& operator=(const WithAudioPortConfig&) = delete;
@@ -302,26 +359,31 @@
 
     void SetUpImpl(const std::string& moduleName) {
         ASSERT_NO_FATAL_FAILURE(ConnectToService(moduleName));
-        debug.flags().simulateDeviceConnections = true;
-        ASSERT_NO_FATAL_FAILURE(debug.SetUp(module.get()));
     }
 
-    void TearDownImpl() {
-        if (module != nullptr) {
-            EXPECT_IS_OK(module->setModuleDebug(ModuleDebug{}));
-        }
-    }
+    void TearDownImpl() { debug.reset(); }
 
     void ConnectToService(const std::string& moduleName) {
+        ASSERT_EQ(module, nullptr);
+        ASSERT_EQ(debug, nullptr);
         module = IModule::fromBinder(binderUtil.connectToService(moduleName));
         ASSERT_NE(module, nullptr);
+        ASSERT_NO_FATAL_FAILURE(SetUpDebug());
     }
 
     void RestartService() {
         ASSERT_NE(module, nullptr);
         moduleConfig.reset();
+        debug.reset();
         module = IModule::fromBinder(binderUtil.restartService());
         ASSERT_NE(module, nullptr);
+        ASSERT_NO_FATAL_FAILURE(SetUpDebug());
+    }
+
+    void SetUpDebug() {
+        debug.reset(new WithDebugFlags());
+        debug->flags().simulateDeviceConnections = true;
+        ASSERT_NO_FATAL_FAILURE(debug->SetUp(module.get()));
     }
 
     void ApplyEveryConfig(const std::vector<AudioPortConfig>& configs) {
@@ -390,7 +452,7 @@
     std::shared_ptr<IModule> module;
     std::unique_ptr<ModuleConfig> moduleConfig;
     AudioHalBinderServiceUtil binderUtil;
-    WithDebugFlags debug;
+    std::unique_ptr<WithDebugFlags> debug;
 };
 
 class AudioCoreModule : public AudioCoreModuleBase, public testing::TestWithParam<std::string> {
@@ -465,6 +527,7 @@
     size_t getBufferSizeFrames() const { return mBufferSizeFrames; }
     CommandMQ* getCommandMQ() const { return mCommandMQ.get(); }
     DataMQ* getDataMQ() const { return mDataMQ.get(); }
+    size_t getFrameSizeBytes() const { return mFrameSizeBytes; }
     ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
 
   private:
@@ -504,10 +567,48 @@
     return std::to_string(static_cast<int32_t>(event));
 }
 
+// Note: we use a reference wrapper, not a pointer, because methods of std::*list
+// return references to inserted elements. This way, we can put a returned reference
+// into the children vector without any type conversions, and this makes DAG creation
+// code more clear.
+template <typename T>
+struct DagNode : public std::pair<T, std::vector<std::reference_wrapper<DagNode<T>>>> {
+    using Children = std::vector<std::reference_wrapper<DagNode>>;
+    DagNode(const T& t, const Children& c) : std::pair<T, Children>(t, c) {}
+    DagNode(T&& t, Children&& c) : std::pair<T, Children>(std::move(t), std::move(c)) {}
+    const T& datum() const { return this->first; }
+    Children& children() { return this->second; }
+    const Children& children() const { return this->second; }
+};
+// Since DagNodes do contain references to next nodes, node links provided
+// by the list are not used. Thus, the order of the nodes in the list is not
+// important, except that the starting node must be at the front of the list,
+// which means, it must always be added last.
+template <typename T>
+struct Dag : public std::forward_list<DagNode<T>> {
+    Dag() = default;
+    // We prohibit copying and moving Dag instances because implementing that
+    // is not trivial due to references between nodes.
+    Dag(const Dag&) = delete;
+    Dag(Dag&&) = delete;
+    Dag& operator=(const Dag&) = delete;
+    Dag& operator=(Dag&&) = delete;
+};
+
 // 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>;
+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 StateSequence {
     virtual ~StateSequence() = default;
     virtual void rewind() = 0;
@@ -517,6 +618,10 @@
     virtual void advance(StreamDescriptor::State state) = 0;
 };
 
+// Defines the current state and the trigger to transfer to the next one,
+// thus "state" is the "from" state.
+using StateTransitionFrom = std::pair<StreamDescriptor::State, TransitionTrigger>;
+
 static const StreamDescriptor::Command kGetStatusCommand =
         StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::getStatus>(Void{});
 static const StreamDescriptor::Command kStartCommand =
@@ -542,66 +647,65 @@
         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 {
+struct StateDag : public Dag<StateTransitionFrom> {
+    using Node = StateDag::reference;
+    using NextStates = StateDag::value_type::Children;
+
+    template <typename... Next>
+    Node makeNode(StreamDescriptor::State s, TransitionTrigger t, Next&&... next) {
+        return emplace_front(std::make_pair(s, t), NextStates{std::forward<Next>(next)...});
+    }
+    Node makeNodes(const std::vector<StateTransitionFrom>& v, Node last) {
+        auto helper = [&](auto i, auto&& h) -> Node {
+            if (i == v.end()) return last;
+            return makeNode(i->first, i->second, h(++i, h));
+        };
+        return helper(v.begin(), helper);
+    }
+    Node makeNodes(const std::vector<StateTransitionFrom>& v, StreamDescriptor::State f) {
+        return makeNodes(v, makeFinalNode(f));
+    }
+    Node makeFinalNode(StreamDescriptor::State s) {
+        // The actual command used here is irrelevant. Since it's the final node
+        // in the test sequence, no commands sent after reaching it.
+        return emplace_front(std::make_pair(s, kGetStatusCommand), NextStates{});
+    }
+};
+
+class StateSequenceFollower : 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; }
+    explicit StateSequenceFollower(std::unique_ptr<StateDag> steps)
+        : mSteps(std::move(steps)), mCurrent(mSteps->front()) {}
+    void rewind() override { mCurrent = mSteps->front(); }
+    bool done() const override { return current().children().empty(); }
+    TransitionTrigger getTrigger() override { return current().datum().second; }
     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);
-        }
+        std::set<StreamDescriptor::State> result;
+        std::transform(current().children().cbegin(), current().children().cend(),
+                       std::inserter(result, result.begin()),
+                       [](const auto& node) { return node.get().datum().first; });
+        LOG(DEBUG) << __func__ << ": " << ::android::internal::ToString(result);
         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++;
+        if (auto it = std::find_if(
+                    current().children().cbegin(), current().children().cend(),
+                    [&](const auto& node) { return node.get().datum().first == state; });
+            it != current().children().cend()) {
+            LOG(DEBUG) << __func__ << ": " << toString(mCurrent.get().datum().first) << " -> "
+                       << toString(it->get().datum().first);
+            mCurrent = *it;
+        } else {
+            LOG(FATAL) << __func__ << ": state " << toString(state) << " is unexpected";
         }
-        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;
+    StateDag::const_reference current() const { return mCurrent.get(); }
+    std::unique_ptr<StateDag> mSteps;
+    std::reference_wrapper<StateDag::value_type> mCurrent;
 };
 
-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.
@@ -927,7 +1031,7 @@
         return common->close();
     }
 
-    WithStream() {}
+    WithStream() = default;
     explicit WithStream(const AudioPortConfig& portConfig) : mPortConfig(portConfig) {}
     WithStream(const WithStream&) = delete;
     WithStream& operator=(const WithStream&) = delete;
@@ -1032,7 +1136,7 @@
 
 class WithAudioPatch {
   public:
-    WithAudioPatch() {}
+    WithAudioPatch() = default;
     WithAudioPatch(const AudioPortConfig& srcPortConfig, const AudioPortConfig& sinkPortConfig)
         : mSrcPortConfig(srcPortConfig), mSinkPortConfig(sinkPortConfig) {}
     WithAudioPatch(bool sinkIsCfg1, const AudioPortConfig& portConfig1,
@@ -1473,7 +1577,7 @@
         GTEST_SKIP() << "No external devices in the module.";
     }
     AudioPort ignored;
-    WithDebugFlags doNotSimulateConnections = WithDebugFlags::createNested(debug);
+    WithDebugFlags doNotSimulateConnections = WithDebugFlags::createNested(*debug);
     doNotSimulateConnections.flags().simulateDeviceConnections = false;
     ASSERT_NO_FATAL_FAILURE(doNotSimulateConnections.SetUp(module.get()));
     for (const auto& port : ports) {
@@ -1493,7 +1597,7 @@
     }
     WithDevicePortConnectedState portConnected(*ports.begin(), GenerateUniqueDeviceAddress());
     ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get()));
-    ModuleDebug midwayDebugChange = debug.flags();
+    ModuleDebug midwayDebugChange = debug->flags();
     midwayDebugChange.simulateDeviceConnections = false;
     EXPECT_STATUS(EX_ILLEGAL_STATE, module->setModuleDebug(midwayDebugChange))
             << "when trying to disable connections simulation while having a connected device";
@@ -2716,8 +2820,8 @@
 
 class StreamLogicDefaultDriver : public StreamLogicDriver {
   public:
-    explicit StreamLogicDefaultDriver(std::shared_ptr<StateSequence> commands)
-        : mCommands(commands) {
+    StreamLogicDefaultDriver(std::shared_ptr<StateSequence> commands, size_t frameSizeBytes)
+        : mCommands(commands), mFrameSizeBytes(frameSizeBytes) {
         mCommands->rewind();
     }
 
@@ -2736,7 +2840,10 @@
                 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--;
+                    if (maxDataSize > static_cast<int>(mFrameSizeBytes)) {
+                        LOG(DEBUG) << __func__ << ": reducing data size by " << mFrameSizeBytes;
+                        maxDataSize -= mFrameSizeBytes;
+                    }
                     *actualSize = maxDataSize;
                 }
                 command->set<StreamDescriptor::Command::Tag::burst>(maxDataSize);
@@ -2782,6 +2889,7 @@
 
   protected:
     std::shared_ptr<StateSequence> mCommands;
+    const size_t mFrameSizeBytes;
     std::optional<StreamDescriptor::State> mPreviousState;
     std::optional<int64_t> mPreviousFrames;
     bool mObservablePositionIncrease = false;
@@ -2830,7 +2938,7 @@
                 (!isNonBlocking && streamType == StreamTypeFilter::ASYNC)) {
                 continue;
             }
-            WithDebugFlags delayTransientStates = WithDebugFlags::createNested(debug);
+            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()));
@@ -2841,6 +2949,40 @@
             } else {
                 ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq2(portConfig, commandsAndStates));
             }
+            if (isNonBlocking) {
+                // Also try running the same sequence with "aosp.forceTransientBurst" set.
+                // This will only work with the default implementation. When it works, the stream
+                // tries always to move to the 'TRANSFERRING' state after a burst.
+                // This helps to check more paths for our test scenarios.
+                WithModuleParameter forceTransientBurst("aosp.forceTransientBurst", Boolean{true});
+                if (forceTransientBurst.SetUpNoChecks(module.get(), true /*failureExpected*/)
+                            .isOk()) {
+                    if (!std::get<PARAM_SETUP_SEQ>(GetParam())) {
+                        ASSERT_NO_FATAL_FAILURE(
+                                RunStreamIoCommandsImplSeq1(portConfig, commandsAndStates));
+                    } else {
+                        ASSERT_NO_FATAL_FAILURE(
+                                RunStreamIoCommandsImplSeq2(portConfig, commandsAndStates));
+                    }
+                }
+            } else if (!IOTraits<Stream>::is_input) {
+                // Also try running the same sequence with "aosp.forceSynchronousDrain" set.
+                // This will only work with the default implementation. When it works, the stream
+                // tries always to move to the 'IDLE' state after a drain.
+                // This helps to check more paths for our test scenarios.
+                WithModuleParameter forceSynchronousDrain("aosp.forceSynchronousDrain",
+                                                          Boolean{true});
+                if (forceSynchronousDrain.SetUpNoChecks(module.get(), true /*failureExpected*/)
+                            .isOk()) {
+                    if (!std::get<PARAM_SETUP_SEQ>(GetParam())) {
+                        ASSERT_NO_FATAL_FAILURE(
+                                RunStreamIoCommandsImplSeq1(portConfig, commandsAndStates));
+                    } else {
+                        ASSERT_NO_FATAL_FAILURE(
+                                RunStreamIoCommandsImplSeq2(portConfig, commandsAndStates));
+                    }
+                }
+            }
         }
     }
 
@@ -2861,7 +3003,8 @@
 
         WithStream<Stream> stream(patch.getPortConfig(IOTraits<Stream>::is_input));
         ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
-        StreamLogicDefaultDriver driver(commandsAndStates);
+        StreamLogicDefaultDriver driver(commandsAndStates,
+                                        stream.getContext()->getFrameSizeBytes());
         typename IOTraits<Stream>::Worker worker(*stream.getContext(), &driver,
                                                  stream.getEventReceiver());
 
@@ -2882,7 +3025,8 @@
                                      std::shared_ptr<StateSequence> commandsAndStates) {
         WithStream<Stream> stream(portConfig);
         ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
-        StreamLogicDefaultDriver driver(commandsAndStates);
+        StreamLogicDefaultDriver driver(commandsAndStates,
+                                        stream.getContext()->getFrameSizeBytes());
         typename IOTraits<Stream>::Worker worker(*stream.getContext(), &driver,
                                                  stream.getEventReceiver());
 
@@ -3219,38 +3363,52 @@
 
 // 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());
+std::shared_ptr<StateSequence> makeBurstCommands(bool isSync) {
+    using State = StreamDescriptor::State;
+    auto d = std::make_unique<StateDag>();
+    StateDag::Node last = d->makeFinalNode(State::ACTIVE);
+    StateDag::Node active = d->makeNode(State::ACTIVE, kBurstCommand, last);
+    StateDag::Node idle = d->makeNode(State::IDLE, kBurstCommand, active);
+    if (!isSync) {
+        // Allow optional routing via the TRANSFERRING state on bursts.
+        active.children().push_back(d->makeNode(State::TRANSFERRING, kTransferReadyEvent, last));
+        idle.children().push_back(d->makeNode(State::TRANSFERRING, kTransferReadyEvent, active));
     }
-    return std::make_shared<SmartStateSequence>(result);
+    d->makeNode(State::STANDBY, kStartCommand, idle);
+    return std::make_shared<StateSequenceFollower>(std::move(d));
 }
 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::make_tuple(std::string("Read"), 0, StreamTypeFilter::ANY, makeBurstCommands(true));
+static const NamedCommandSequence kWriteSyncSeq =
+        std::make_tuple(std::string("Write"), 0, StreamTypeFilter::SYNC, makeBurstCommands(true));
+static const NamedCommandSequence kWriteAsyncSeq =
+        std::make_tuple(std::string("Write"), 0, StreamTypeFilter::ASYNC, makeBurstCommands(false));
 
 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)});
+    using State = StreamDescriptor::State;
+    auto d = std::make_unique<StateDag>();
+    if (isInput) {
+        d->makeNodes({std::make_pair(State::STANDBY, kStartCommand),
+                      std::make_pair(State::IDLE, kBurstCommand),
+                      std::make_pair(State::ACTIVE, kDrainInCommand),
+                      std::make_pair(State::DRAINING, kStartCommand),
+                      std::make_pair(State::ACTIVE, kDrainInCommand)},
+                     State::DRAINING);
+    } else {
+        StateDag::Node draining =
+                d->makeNodes({std::make_pair(State::DRAINING, kBurstCommand),
+                              std::make_pair(State::TRANSFERRING, kDrainOutAllCommand)},
+                             State::DRAINING);
+        StateDag::Node idle =
+                d->makeNodes({std::make_pair(State::IDLE, kBurstCommand),
+                              std::make_pair(State::TRANSFERRING, kDrainOutAllCommand)},
+                             draining);
+        // If we get straight into ACTIVE on burst, no further testing is possible.
+        draining.children().push_back(d->makeFinalNode(State::ACTIVE));
+        idle.children().push_back(d->makeFinalNode(State::ACTIVE));
+        d->makeNode(State::STANDBY, kStartCommand, idle);
+    }
+    return std::make_shared<StateSequenceFollower>(std::move(d));
 }
 static const NamedCommandSequence kWriteDrainAsyncSeq =
         std::make_tuple(std::string("WriteDrain"), kStreamTransientStateTransitionDelayMs,
@@ -3259,58 +3417,92 @@
         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)});
+    using State = StreamDescriptor::State;
+    auto d = std::make_unique<StateDag>();
+    StateDag::Node last = d->makeFinalNode(State::IDLE);
+    StateDag::Node active = d->makeNodes(
+            {std::make_pair(State::ACTIVE, kDrainOutAllCommand),
+             std::make_pair(State::DRAINING, isSync ? TransitionTrigger(kGetStatusCommand)
+                                                    : TransitionTrigger(kDrainReadyEvent))},
+            last);
+    StateDag::Node idle = d->makeNode(State::IDLE, kBurstCommand, active);
+    if (!isSync) {
+        idle.children().push_back(d->makeNode(State::TRANSFERRING, kTransferReadyEvent, active));
+    } else {
+        active.children().push_back(last);
+    }
+    d->makeNode(State::STANDBY, kStartCommand, idle);
+    return std::make_shared<StateSequenceFollower>(std::move(d));
 }
 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)});
+std::shared_ptr<StateSequence> makeDrainPauseOutCommands(bool isSync) {
+    using State = StreamDescriptor::State;
+    auto d = std::make_unique<StateDag>();
+    StateDag::Node draining = d->makeNodes({std::make_pair(State::DRAINING, kPauseCommand),
+                                            std::make_pair(State::DRAIN_PAUSED, kStartCommand),
+                                            std::make_pair(State::DRAINING, kPauseCommand),
+                                            std::make_pair(State::DRAIN_PAUSED, kBurstCommand)},
+                                           isSync ? State::PAUSED : State::TRANSFER_PAUSED);
+    StateDag::Node active = d->makeNode(State::ACTIVE, kDrainOutAllCommand, draining);
+    StateDag::Node idle = d->makeNode(State::IDLE, kBurstCommand, active);
+    if (!isSync) {
+        idle.children().push_back(d->makeNode(State::TRANSFERRING, kDrainOutAllCommand, draining));
+    } else {
+        // If we get straight into IDLE on drain, no further testing is possible.
+        active.children().push_back(d->makeFinalNode(State::IDLE));
+    }
+    d->makeNode(State::STANDBY, kStartCommand, idle);
+    return std::make_shared<StateSequenceFollower>(std::move(d));
 }
 static const NamedCommandSequence kDrainPauseOutSyncSeq =
         std::make_tuple(std::string("DrainPause"), kStreamTransientStateTransitionDelayMs,
-                        StreamTypeFilter::SYNC, makeDrainOutPauseCommands(true));
+                        StreamTypeFilter::SYNC, makeDrainPauseOutCommands(true));
 static const NamedCommandSequence kDrainPauseOutAsyncSeq =
         std::make_tuple(std::string("DrainPause"), kStreamTransientStateTransitionDelayMs,
-                        StreamTypeFilter::ASYNC, makeDrainOutPauseCommands(false));
+                        StreamTypeFilter::ASYNC, makeDrainPauseOutCommands(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)});
+    using State = StreamDescriptor::State;
+    auto d = std::make_unique<StateDag>();
+    if (isInput) {
+        d->makeNodes({std::make_pair(State::STANDBY, kStartCommand),
+                      std::make_pair(State::IDLE, kStandbyCommand),
+                      std::make_pair(State::STANDBY, kStartCommand),
+                      std::make_pair(State::IDLE, kBurstCommand),
+                      std::make_pair(State::ACTIVE, kPauseCommand),
+                      std::make_pair(State::PAUSED, kFlushCommand),
+                      std::make_pair(State::STANDBY, kStartCommand),
+                      std::make_pair(State::IDLE, kBurstCommand)},
+                     State::ACTIVE);
+    } else {
+        StateDag::Node idle3 =
+                d->makeNode(State::IDLE, kBurstCommand, d->makeFinalNode(State::ACTIVE));
+        StateDag::Node idle2 = d->makeNodes({std::make_pair(State::IDLE, kStandbyCommand),
+                                             std::make_pair(State::STANDBY, kStartCommand)},
+                                            idle3);
+        StateDag::Node active = d->makeNodes({std::make_pair(State::ACTIVE, kPauseCommand),
+                                              std::make_pair(State::PAUSED, kFlushCommand)},
+                                             idle2);
+        StateDag::Node idle = d->makeNode(State::IDLE, kBurstCommand, active);
+        if (!isSync) {
+            idle3.children().push_back(d->makeFinalNode(State::TRANSFERRING));
+            StateDag::Node transferring =
+                    d->makeNodes({std::make_pair(State::TRANSFERRING, kPauseCommand),
+                                  std::make_pair(State::TRANSFER_PAUSED, kFlushCommand)},
+                                 idle2);
+            idle.children().push_back(transferring);
+        }
+        d->makeNodes({std::make_pair(State::STANDBY, kStartCommand),
+                      std::make_pair(State::IDLE, kStandbyCommand),
+                      std::make_pair(State::STANDBY, kStartCommand)},
+                     idle);
+    }
+    return std::make_shared<StateSequenceFollower>(std::move(d));
 }
 static const NamedCommandSequence kStandbyInSeq = std::make_tuple(
         std::string("Standby"), 0, StreamTypeFilter::ANY, makeStandbyCommands(true, false));
@@ -3320,50 +3512,71 @@
         std::make_tuple(std::string("Standby"), kStreamTransientStateTransitionDelayMs,
                         StreamTypeFilter::ASYNC, makeStandbyCommands(false, false));
 
-static const NamedCommandSequence kPauseInSeq =
-        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> makePauseCommands(bool isInput, bool isSync) {
+    using State = StreamDescriptor::State;
+    auto d = std::make_unique<StateDag>();
+    if (isInput) {
+        d->makeNodes({std::make_pair(State::STANDBY, kStartCommand),
+                      std::make_pair(State::IDLE, kBurstCommand),
+                      std::make_pair(State::ACTIVE, kPauseCommand),
+                      std::make_pair(State::PAUSED, kBurstCommand),
+                      std::make_pair(State::ACTIVE, kPauseCommand),
+                      std::make_pair(State::PAUSED, kFlushCommand)},
+                     State::STANDBY);
+    } else {
+        StateDag::Node idle = d->makeNodes({std::make_pair(State::IDLE, kBurstCommand),
+                                            std::make_pair(State::ACTIVE, kPauseCommand),
+                                            std::make_pair(State::PAUSED, kStartCommand),
+                                            std::make_pair(State::ACTIVE, kPauseCommand),
+                                            std::make_pair(State::PAUSED, kBurstCommand),
+                                            std::make_pair(State::PAUSED, kStartCommand),
+                                            std::make_pair(State::ACTIVE, kPauseCommand)},
+                                           State::PAUSED);
+        if (!isSync) {
+            idle.children().push_back(
+                    d->makeNodes({std::make_pair(State::TRANSFERRING, kPauseCommand),
+                                  std::make_pair(State::TRANSFER_PAUSED, kStartCommand),
+                                  std::make_pair(State::TRANSFERRING, kPauseCommand),
+                                  std::make_pair(State::TRANSFER_PAUSED, kDrainOutAllCommand),
+                                  std::make_pair(State::DRAIN_PAUSED, kBurstCommand)},
+                                 State::TRANSFER_PAUSED));
+        }
+        d->makeNode(State::STANDBY, kStartCommand, idle);
+    }
+    return std::make_shared<StateSequenceFollower>(std::move(d));
+}
+static const NamedCommandSequence kPauseInSeq = std::make_tuple(
+        std::string("Pause"), 0, StreamTypeFilter::ANY, makePauseCommands(true, false));
+static const NamedCommandSequence kPauseOutSyncSeq = std::make_tuple(
+        std::string("Pause"), 0, StreamTypeFilter::SYNC, makePauseCommands(false, true));
+static const NamedCommandSequence kPauseOutAsyncSeq =
+        std::make_tuple(std::string("Pause"), kStreamTransientStateTransitionDelayMs,
+                        StreamTypeFilter::ASYNC, makePauseCommands(false, false));
 
 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)});
+    using State = StreamDescriptor::State;
+    auto d = std::make_unique<StateDag>();
+    if (isInput) {
+        d->makeNodes({std::make_pair(State::STANDBY, kStartCommand),
+                      std::make_pair(State::IDLE, kBurstCommand),
+                      std::make_pair(State::ACTIVE, kPauseCommand),
+                      std::make_pair(State::PAUSED, kFlushCommand)},
+                     State::STANDBY);
+    } else {
+        StateDag::Node last = d->makeFinalNode(State::IDLE);
+        StateDag::Node idle = d->makeNodes({std::make_pair(State::IDLE, kBurstCommand),
+                                            std::make_pair(State::ACTIVE, kPauseCommand),
+                                            std::make_pair(State::PAUSED, kFlushCommand)},
+                                           last);
+        if (!isSync) {
+            idle.children().push_back(
+                    d->makeNodes({std::make_pair(State::TRANSFERRING, kPauseCommand),
+                                  std::make_pair(State::TRANSFER_PAUSED, kFlushCommand)},
+                                 last));
+        }
+        d->makeNode(State::STANDBY, kStartCommand, idle);
+    }
+    return std::make_shared<StateSequenceFollower>(std::move(d));
 }
 static const NamedCommandSequence kFlushInSeq = std::make_tuple(
         std::string("Flush"), 0, StreamTypeFilter::ANY, makeFlushCommands(true, false));
@@ -3374,13 +3587,21 @@
                         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)});
+    using State = StreamDescriptor::State;
+    auto d = std::make_unique<StateDag>();
+    StateDag::Node draining = d->makeNodes({std::make_pair(State::DRAINING, kPauseCommand),
+                                            std::make_pair(State::DRAIN_PAUSED, kFlushCommand)},
+                                           State::IDLE);
+    StateDag::Node active = d->makeNode(State::ACTIVE, kDrainOutAllCommand, draining);
+    StateDag::Node idle = d->makeNode(State::IDLE, kBurstCommand, active);
+    if (!isSync) {
+        idle.children().push_back(d->makeNode(State::TRANSFERRING, kDrainOutAllCommand, draining));
+    } else {
+        // If we get straight into IDLE on drain, no further testing is possible.
+        active.children().push_back(d->makeFinalNode(State::IDLE));
+    }
+    d->makeNode(State::STANDBY, kStartCommand, idle);
+    return std::make_shared<StateSequenceFollower>(std::move(d));
 }
 static const NamedCommandSequence kDrainPauseFlushOutSyncSeq =
         std::make_tuple(std::string("DrainPauseFlush"), kStreamTransientStateTransitionDelayMs,
diff --git a/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
new file mode 100644
index 0000000..9feff91
--- /dev/null
+++ b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
@@ -0,0 +1,1115 @@
+/*
+ * Copyright (C) 2023 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 <aidl/Vintf.h>
+
+#define LOG_TAG "VtsHalDynamicsProcessingTest"
+
+#include <set>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+#include <Utils.h>
+#include "EffectHelper.h"
+
+using namespace android;
+
+using aidl::android::hardware::audio::effect::Capability;
+using aidl::android::hardware::audio::effect::Descriptor;
+using aidl::android::hardware::audio::effect::DynamicsProcessing;
+using aidl::android::hardware::audio::effect::IEffect;
+using aidl::android::hardware::audio::effect::IFactory;
+using aidl::android::hardware::audio::effect::kDynamicsProcessingTypeUUID;
+using aidl::android::hardware::audio::effect::Parameter;
+
+/**
+ * Here we focus on specific parameter checking, general IEffect interfaces testing performed in
+ * VtsAudioEffectTargetTest.
+ */
+class DynamicsProcessingTestHelper : public EffectHelper {
+  public:
+    DynamicsProcessingTestHelper(std::pair<std::shared_ptr<IFactory>, Descriptor> pair,
+                                 int32_t channelLayOut = AudioChannelLayout::LAYOUT_STEREO) {
+        std::tie(mFactory, mDescriptor) = pair;
+        mChannelLayout = channelLayOut;
+        mChannelCount = ::android::hardware::audio::common::getChannelCount(
+                AudioChannelLayout::make<AudioChannelLayout::layoutMask>(mChannelLayout));
+    }
+
+    // setup
+    void SetUpDynamicsProcessingEffect() {
+        ASSERT_NE(nullptr, mFactory);
+        ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
+
+        Parameter::Specific specific = getDefaultParamSpecific();
+        Parameter::Common common = EffectHelper::createParamCommon(
+                0 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, 44100 /* oSampleRate */,
+                0x100 /* iFrameCount */, 0x100 /* oFrameCount */,
+                AudioChannelLayout::make<AudioChannelLayout::layoutMask>(mChannelLayout),
+                AudioChannelLayout::make<AudioChannelLayout::layoutMask>(mChannelLayout));
+        IEffect::OpenEffectReturn ret;
+        ASSERT_NO_FATAL_FAILURE(open(mEffect, common, specific, &ret, EX_NONE));
+        ASSERT_NE(nullptr, mEffect);
+        mEngineConfigApplied = mEngineConfigPreset;
+    }
+
+    Parameter::Specific getDefaultParamSpecific() {
+        DynamicsProcessing dp = DynamicsProcessing::make<DynamicsProcessing::engineArchitecture>(
+                mEngineConfigPreset);
+        Parameter::Specific specific =
+                Parameter::Specific::make<Parameter::Specific::dynamicsProcessing>(dp);
+        return specific;
+    }
+
+    // teardown
+    void TearDownDynamicsProcessingEffect() {
+        ASSERT_NO_FATAL_FAILURE(close(mEffect));
+        ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
+    }
+
+    // utils functions for parameter checking
+    bool isParamValid(const DynamicsProcessing::Tag& tag, const DynamicsProcessing& dp,
+                      const Descriptor& desc);
+    bool isParamEqual(const DynamicsProcessing::Tag& tag, const DynamicsProcessing& dpRef,
+                      const DynamicsProcessing& dpTest);
+
+    bool isEnablementValid(const DynamicsProcessing::StageEnablement& enablement);
+    bool isEngineConfigValid(const DynamicsProcessing::EngineArchitecture& cfg);
+
+    bool isCutoffFrequencyValid(float freq, const DynamicsProcessing::Capability& cap);
+    bool isChannelConfigValid(const std::vector<DynamicsProcessing::ChannelConfig>& cfgs,
+                              bool stageInUse);
+
+    bool isPreEqBandConfigValid(const DynamicsProcessing::Capability& cap,
+                                const std::vector<DynamicsProcessing::EqBandConfig>& cfgs,
+                                bool stageInUse, int bandCount);
+    bool isPostEqBandConfigValid(const DynamicsProcessing::Capability& cap,
+                                 const std::vector<DynamicsProcessing::EqBandConfig>& cfgs,
+                                 bool stageInUse, int bandCount);
+    bool isMbcBandConfigValid(const DynamicsProcessing::Capability& cap,
+                              const std::vector<DynamicsProcessing::MbcBandConfig>& cfgs,
+                              bool stageInUse, int bandCount);
+    bool isLimiterConfigValid(const std::vector<DynamicsProcessing::LimiterConfig>& cfgs,
+                              bool stageInUse);
+    bool isInputGainValid(const std::vector<DynamicsProcessing::InputGain>& cfgs);
+
+    bool isEngineConfigEqual(const DynamicsProcessing::EngineArchitecture& refCfg,
+                             const DynamicsProcessing::EngineArchitecture& testCfg);
+
+    template <typename T>
+    std::vector<T> filterEnabledVector(const std::vector<T>& vec);
+
+    template <typename T>
+    bool isAidlVectorEqualAfterFilter(const std::vector<T>& source, const std::vector<T>& target);
+
+    template <typename T>
+    bool isAidlVectorEqual(const std::vector<T>& source, const std::vector<T>& target);
+
+    // get set params and validate
+    void SetAndGetDynamicsProcessingParameters();
+
+    // enqueue test parameters
+    void addEngineConfig(const DynamicsProcessing::EngineArchitecture& cfg);
+    void addPreEqChannelConfig(const std::vector<DynamicsProcessing::ChannelConfig>& cfg);
+    void addPostEqChannelConfig(const std::vector<DynamicsProcessing::ChannelConfig>& cfg);
+    void addMbcChannelConfig(const std::vector<DynamicsProcessing::ChannelConfig>& cfg);
+    void addPreEqBandConfigs(const std::vector<DynamicsProcessing::EqBandConfig>& cfgs);
+    void addPostEqBandConfigs(const std::vector<DynamicsProcessing::EqBandConfig>& cfgs);
+    void addMbcBandConfigs(const std::vector<DynamicsProcessing::MbcBandConfig>& cfgs);
+    void addLimiterConfig(const std::vector<DynamicsProcessing::LimiterConfig>& cfg);
+    void addInputGain(const std::vector<DynamicsProcessing::InputGain>& inputGain);
+
+    static constexpr float kPreferredProcessingDurationMs = 10.0f;
+    static constexpr int kBandCount = 5;
+    std::shared_ptr<IFactory> mFactory;
+    std::shared_ptr<IEffect> mEffect;
+    Descriptor mDescriptor;
+    DynamicsProcessing::EngineArchitecture mEngineConfigApplied;
+    DynamicsProcessing::EngineArchitecture mEngineConfigPreset{
+            .resolutionPreference =
+                    DynamicsProcessing::ResolutionPreference::FAVOR_FREQUENCY_RESOLUTION,
+            .preferredProcessingDurationMs = kPreferredProcessingDurationMs,
+            .preEqStage = {.inUse = true, .bandCount = kBandCount},
+            .postEqStage = {.inUse = true, .bandCount = kBandCount},
+            .mbcStage = {.inUse = true, .bandCount = kBandCount},
+            .limiterInUse = true,
+    };
+
+    std::unordered_set<int /* channelId */> mPreEqChannelEnable;
+    std::unordered_set<int /* channelId */> mPostEqChannelEnable;
+    std::unordered_set<int /* channelId */> mMbcChannelEnable;
+    std::unordered_set<int /* channelId */> mLimiterChannelEnable;
+    static const std::set<std::vector<DynamicsProcessing::ChannelConfig>> kChannelConfigTestSet;
+    static const std::set<DynamicsProcessing::StageEnablement> kStageEnablementTestSet;
+    static const std::set<std::vector<DynamicsProcessing::InputGain>> kInputGainTestSet;
+
+  private:
+    int32_t mChannelLayout;
+    int mChannelCount;
+    std::vector<std::pair<DynamicsProcessing::Tag, DynamicsProcessing>> mTags;
+    void CleanUp() {
+        mTags.clear();
+        mPreEqChannelEnable.clear();
+        mPostEqChannelEnable.clear();
+        mMbcChannelEnable.clear();
+        mLimiterChannelEnable.clear();
+    }
+};
+
+// test value set for DynamicsProcessing::StageEnablement
+const std::set<DynamicsProcessing::StageEnablement>
+        DynamicsProcessingTestHelper::kStageEnablementTestSet = {
+                {.inUse = true, .bandCount = DynamicsProcessingTestHelper::kBandCount},
+                {.inUse = true, .bandCount = 0},
+                {.inUse = true, .bandCount = -1},
+                {.inUse = false, .bandCount = DynamicsProcessingTestHelper::kBandCount}};
+
+// test value set for DynamicsProcessing::ChannelConfig
+const std::set<std::vector<DynamicsProcessing::ChannelConfig>>
+        DynamicsProcessingTestHelper::kChannelConfigTestSet = {
+                {{.channel = -1, .enable = false},
+                 {.channel = 0, .enable = true},
+                 {.channel = 1, .enable = false},
+                 {.channel = 2, .enable = true}},
+
+                {{.channel = -1, .enable = false}, {.channel = 2, .enable = true}},
+
+                {{.channel = 0, .enable = true}, {.channel = 1, .enable = true}}};
+
+// test value set for DynamicsProcessing::InputGain
+const std::set<std::vector<DynamicsProcessing::InputGain>>
+        DynamicsProcessingTestHelper::kInputGainTestSet = {
+                {{.channel = 0, .gainDb = 10.f},
+                 {.channel = 1, .gainDb = 0.f},
+                 {.channel = 2, .gainDb = -10.f}},
+
+                {{.channel = -1, .gainDb = -10.f}, {.channel = -2, .gainDb = 10.f}},
+
+                {{.channel = -1, .gainDb = 10.f}, {.channel = 0, .gainDb = -10.f}}};
+
+bool DynamicsProcessingTestHelper::isParamValid(const DynamicsProcessing::Tag& tag,
+                                                const DynamicsProcessing& dp,
+                                                const Descriptor& desc) {
+    const DynamicsProcessing::Capability& dpCap =
+            desc.capability.get<Capability::dynamicsProcessing>();
+    switch (tag) {
+        case DynamicsProcessing::engineArchitecture: {
+            return isEngineConfigValid(dp.get<DynamicsProcessing::engineArchitecture>());
+        }
+        case DynamicsProcessing::preEq: {
+            return isChannelConfigValid(dp.get<DynamicsProcessing::preEq>(),
+                                        mEngineConfigApplied.preEqStage.inUse);
+        }
+        case DynamicsProcessing::postEq: {
+            return isChannelConfigValid(dp.get<DynamicsProcessing::postEq>(),
+                                        mEngineConfigApplied.postEqStage.inUse);
+        }
+        case DynamicsProcessing::mbc: {
+            return isChannelConfigValid(dp.get<DynamicsProcessing::mbc>(),
+                                        mEngineConfigApplied.mbcStage.inUse);
+        }
+        case DynamicsProcessing::preEqBand: {
+            return isPreEqBandConfigValid(dpCap, dp.get<DynamicsProcessing::preEqBand>(),
+                                          mEngineConfigApplied.preEqStage.inUse,
+                                          mEngineConfigApplied.preEqStage.bandCount);
+        }
+        case DynamicsProcessing::postEqBand: {
+            return isPostEqBandConfigValid(dpCap, dp.get<DynamicsProcessing::postEqBand>(),
+                                           mEngineConfigApplied.postEqStage.inUse,
+                                           mEngineConfigApplied.postEqStage.bandCount);
+        }
+        case DynamicsProcessing::mbcBand: {
+            return isMbcBandConfigValid(dpCap, dp.get<DynamicsProcessing::mbcBand>(),
+                                        mEngineConfigApplied.mbcStage.inUse,
+                                        mEngineConfigApplied.mbcStage.bandCount);
+        }
+        case DynamicsProcessing::limiter: {
+            return isLimiterConfigValid(dp.get<DynamicsProcessing::limiter>(),
+                                        mEngineConfigApplied.limiterInUse);
+        }
+        case DynamicsProcessing::inputGain: {
+            return isInputGainValid(dp.get<DynamicsProcessing::inputGain>());
+        }
+        case DynamicsProcessing::vendorExtension: {
+            return true;
+        }
+    }
+    return true;
+}
+
+bool DynamicsProcessingTestHelper::isParamEqual(const DynamicsProcessing::Tag& tag,
+                                                const DynamicsProcessing& dpRef,
+                                                const DynamicsProcessing& dpTest) {
+    switch (tag) {
+        case DynamicsProcessing::engineArchitecture: {
+            return isEngineConfigEqual(dpRef.get<DynamicsProcessing::engineArchitecture>(),
+                                       dpTest.get<DynamicsProcessing::engineArchitecture>());
+        }
+        case DynamicsProcessing::preEq: {
+            const auto& source = dpRef.get<DynamicsProcessing::preEq>();
+            const auto& target = dpTest.get<DynamicsProcessing::preEq>();
+            return isAidlVectorEqualAfterFilter<DynamicsProcessing::ChannelConfig>(source, target);
+        }
+        case DynamicsProcessing::postEq: {
+            return isAidlVectorEqualAfterFilter<DynamicsProcessing::ChannelConfig>(
+                    dpRef.get<DynamicsProcessing::postEq>(),
+                    dpTest.get<DynamicsProcessing::postEq>());
+        }
+        case DynamicsProcessing::mbc: {
+            return isAidlVectorEqualAfterFilter<DynamicsProcessing::ChannelConfig>(
+                    dpRef.get<DynamicsProcessing::mbc>(), dpTest.get<DynamicsProcessing::mbc>());
+        }
+        case DynamicsProcessing::preEqBand: {
+            return isAidlVectorEqualAfterFilter<DynamicsProcessing::EqBandConfig>(
+                    dpRef.get<DynamicsProcessing::preEqBand>(),
+                    dpTest.get<DynamicsProcessing::preEqBand>());
+        }
+        case DynamicsProcessing::postEqBand: {
+            return isAidlVectorEqualAfterFilter<DynamicsProcessing::EqBandConfig>(
+                    dpRef.get<DynamicsProcessing::postEqBand>(),
+                    dpTest.get<DynamicsProcessing::postEqBand>());
+        }
+        case DynamicsProcessing::mbcBand: {
+            return isAidlVectorEqualAfterFilter<DynamicsProcessing::MbcBandConfig>(
+                    dpRef.get<DynamicsProcessing::mbcBand>(),
+                    dpTest.get<DynamicsProcessing::mbcBand>());
+        }
+        case DynamicsProcessing::limiter: {
+            return isAidlVectorEqualAfterFilter<DynamicsProcessing::LimiterConfig>(
+                    dpRef.get<DynamicsProcessing::limiter>(),
+                    dpTest.get<DynamicsProcessing::limiter>());
+        }
+        case DynamicsProcessing::inputGain: {
+            return isAidlVectorEqual<DynamicsProcessing::InputGain>(
+                    dpRef.get<DynamicsProcessing::inputGain>(),
+                    dpTest.get<DynamicsProcessing::inputGain>());
+        }
+        case DynamicsProcessing::vendorExtension: {
+            return false;
+        }
+    }
+}
+
+bool DynamicsProcessingTestHelper::isEnablementValid(
+        const DynamicsProcessing::StageEnablement& enablement) {
+    return !enablement.inUse || (enablement.inUse && enablement.bandCount > 0);
+}
+
+bool DynamicsProcessingTestHelper::isEngineConfigValid(
+        const DynamicsProcessing::EngineArchitecture& cfg) {
+    return cfg.preferredProcessingDurationMs >= 0 && isEnablementValid(cfg.preEqStage) &&
+           isEnablementValid(cfg.postEqStage) && isEnablementValid(cfg.mbcStage);
+}
+
+bool DynamicsProcessingTestHelper::isChannelConfigValid(
+        const std::vector<DynamicsProcessing::ChannelConfig>& cfgs, bool stageInUse) {
+    std::unordered_set<int> channelSet;
+    if (!stageInUse) return false;
+    for (auto cfg : cfgs) {
+        if (cfg.channel < 0 || cfg.channel >= mChannelCount || 0 != channelSet.count(cfg.channel)) {
+            return false;
+        }
+        channelSet.insert(cfg.channel);
+    }
+    return true;
+}
+
+bool DynamicsProcessingTestHelper::isCutoffFrequencyValid(
+        float freq, const DynamicsProcessing::Capability& cap) {
+    return freq >= cap.minCutOffFreq && freq <= cap.maxCutOffFreq;
+}
+
+bool DynamicsProcessingTestHelper::isPreEqBandConfigValid(
+        const DynamicsProcessing::Capability& cap,
+        const std::vector<DynamicsProcessing::EqBandConfig>& cfgs, bool stageInUse, int bandCount) {
+    std::set<std::pair<int /* channelID */, int /* bandID */>> bandSet;
+    if (!stageInUse) return false;
+    for (auto cfg : cfgs) {
+        if (0 == mPreEqChannelEnable.count(cfg.channel) || cfg.channel < 0 ||
+            cfg.channel >= mChannelCount || cfg.band < 0 || cfg.band >= bandCount ||
+            !isCutoffFrequencyValid(cfg.cutoffFrequencyHz, cap) ||
+            0 != bandSet.count({cfg.channel, cfg.band})) {
+            return false;
+        }
+        bandSet.insert({cfg.channel, cfg.band});
+    }
+    return true;
+}
+
+bool DynamicsProcessingTestHelper::isPostEqBandConfigValid(
+        const DynamicsProcessing::Capability& cap,
+        const std::vector<DynamicsProcessing::EqBandConfig>& cfgs, bool stageInUse, int bandCount) {
+    std::set<std::pair<int /* channelID */, int /* bandID */>> bandSet;
+    // not able to set/get parameter when stage not in use.
+    if (!stageInUse) return false;
+    for (auto cfg : cfgs) {
+        if (0 == mPostEqChannelEnable.count(cfg.channel) || cfg.channel < 0 ||
+            cfg.channel >= mChannelCount || cfg.band < 0 || cfg.band >= bandCount ||
+            !isCutoffFrequencyValid(cfg.cutoffFrequencyHz, cap) ||
+            0 != bandSet.count({cfg.channel, cfg.band})) {
+            return false;
+        }
+        bandSet.insert({cfg.channel, cfg.band});
+    }
+    return true;
+}
+
+bool DynamicsProcessingTestHelper::isMbcBandConfigValid(
+        const DynamicsProcessing::Capability& cap,
+        const std::vector<DynamicsProcessing::MbcBandConfig>& cfgs, bool stageInUse,
+        int bandCount) {
+    std::set<std::pair<int /* channelID */, int /* bandID */>> bandSet;
+    if (!stageInUse) return false;
+    for (auto cfg : cfgs) {
+        if (0 == mMbcChannelEnable.count(cfg.channel) || cfg.channel < 0 ||
+            cfg.channel >= mChannelCount || cfg.band < 0 || cfg.band >= bandCount ||
+            (cfg.attackTimeMs < 0) || cfg.releaseTimeMs < 0 || cfg.ratio < 0 ||
+            cfg.thresholdDb > 0 || cfg.kneeWidthDb < 0 || cfg.noiseGateThresholdDb > 0 ||
+            cfg.expanderRatio < 0 || !isCutoffFrequencyValid(cfg.cutoffFrequencyHz, cap) ||
+            0 != bandSet.count({cfg.channel, cfg.band})) {
+            return false;
+        }
+        bandSet.insert({cfg.channel, cfg.band});
+    }
+    return true;
+}
+
+bool DynamicsProcessingTestHelper::isLimiterConfigValid(
+        const std::vector<DynamicsProcessing::LimiterConfig>& cfgs, bool stageInUse) {
+    std::set<int> channelSet;
+    if (!stageInUse) return false;
+    for (auto cfg : cfgs) {
+        if (0 == mLimiterChannelEnable.count(cfg.channel) || cfg.channel < 0 ||
+            cfg.channel >= mChannelCount || cfg.attackTimeMs < 0 || cfg.releaseTimeMs < 0 ||
+            cfg.ratio < 0 || cfg.thresholdDb > 0 || 0 != channelSet.count(cfg.channel)) {
+            return false;
+        }
+        channelSet.insert(cfg.channel);
+    }
+    return true;
+}
+
+bool DynamicsProcessingTestHelper::isInputGainValid(
+        const std::vector<DynamicsProcessing::InputGain>& cfgs) {
+    std::set<int> channelSet;
+    for (auto cfg : cfgs) {
+        if (cfg.channel < 0 || cfg.channel >= mChannelCount || 0 != channelSet.count(cfg.channel)) {
+            return false;
+        }
+        channelSet.insert(cfg.channel);
+    }
+    return true;
+}
+
+bool DynamicsProcessingTestHelper::isEngineConfigEqual(
+        const DynamicsProcessing::EngineArchitecture& ref,
+        const DynamicsProcessing::EngineArchitecture& test) {
+    return ref == test;
+}
+
+template <typename T>
+std::vector<T> DynamicsProcessingTestHelper::filterEnabledVector(const std::vector<T>& vec) {
+    std::vector<T> ret;
+    std::copy_if(vec.begin(), vec.end(), std::back_inserter(ret),
+                 [](const auto& v) { return v.enable; });
+    return ret;
+}
+
+template <typename T>
+bool DynamicsProcessingTestHelper::isAidlVectorEqual(const std::vector<T>& source,
+                                                     const std::vector<T>& target) {
+    if (source.size() != target.size()) return false;
+
+    auto tempS = source;
+    auto tempT = target;
+    std::sort(tempS.begin(), tempS.end());
+    std::sort(tempT.begin(), tempT.end());
+    return tempS == tempT;
+}
+
+template <typename T>
+bool DynamicsProcessingTestHelper::isAidlVectorEqualAfterFilter(const std::vector<T>& source,
+                                                                const std::vector<T>& target) {
+    return isAidlVectorEqual<T>(filterEnabledVector<T>(source), filterEnabledVector<T>(target));
+}
+
+void DynamicsProcessingTestHelper::SetAndGetDynamicsProcessingParameters() {
+    for (auto& it : mTags) {
+        auto& tag = it.first;
+        auto& dp = it.second;
+
+        // validate parameter
+        Descriptor desc;
+        ASSERT_STATUS(EX_NONE, mEffect->getDescriptor(&desc));
+        const bool valid = isParamValid(tag, dp, desc);
+        const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT;
+
+        // set parameter
+        Parameter expectParam;
+        Parameter::Specific specific;
+        specific.set<Parameter::Specific::dynamicsProcessing>(dp);
+        expectParam.set<Parameter::specific>(specific);
+        ASSERT_STATUS(expected, mEffect->setParameter(expectParam)) << expectParam.toString();
+
+        // only get if parameter in range and set success
+        if (expected == EX_NONE) {
+            Parameter getParam;
+            Parameter::Id id;
+            DynamicsProcessing::Id dpId;
+            dpId.set<DynamicsProcessing::Id::commonTag>(tag);
+            id.set<Parameter::Id::dynamicsProcessingTag>(dpId);
+            // if set success, then get should match
+            EXPECT_STATUS(expected, mEffect->getParameter(id, &getParam));
+            Parameter::Specific specificTest = getParam.get<Parameter::specific>();
+            const auto& target = specificTest.get<Parameter::Specific::dynamicsProcessing>();
+            EXPECT_TRUE(isParamEqual(tag, dp, target)) << dp.toString() << "\n"
+                                                       << target.toString();
+            // update mEngineConfigApplied after setting successfully
+            if (tag == DynamicsProcessing::engineArchitecture) {
+                mEngineConfigApplied = target.get<DynamicsProcessing::engineArchitecture>();
+            }
+        }
+    }
+}
+
+void DynamicsProcessingTestHelper::addEngineConfig(
+        const DynamicsProcessing::EngineArchitecture& cfg) {
+    DynamicsProcessing dp;
+    dp.set<DynamicsProcessing::engineArchitecture>(cfg);
+    mTags.push_back({DynamicsProcessing::engineArchitecture, dp});
+}
+
+void DynamicsProcessingTestHelper::addPreEqChannelConfig(
+        const std::vector<DynamicsProcessing::ChannelConfig>& cfgs) {
+    DynamicsProcessing dp;
+    dp.set<DynamicsProcessing::preEq>(cfgs);
+    mTags.push_back({DynamicsProcessing::preEq, dp});
+    for (auto& cfg : cfgs) {
+        if (cfg.enable) mPreEqChannelEnable.insert(cfg.channel);
+    }
+}
+
+void DynamicsProcessingTestHelper::addPostEqChannelConfig(
+        const std::vector<DynamicsProcessing::ChannelConfig>& cfgs) {
+    DynamicsProcessing dp;
+    dp.set<DynamicsProcessing::postEq>(cfgs);
+    mTags.push_back({DynamicsProcessing::postEq, dp});
+    for (auto& cfg : cfgs) {
+        if (cfg.enable) mPostEqChannelEnable.insert(cfg.channel);
+    }
+}
+
+void DynamicsProcessingTestHelper::addMbcChannelConfig(
+        const std::vector<DynamicsProcessing::ChannelConfig>& cfgs) {
+    DynamicsProcessing dp;
+    dp.set<DynamicsProcessing::mbc>(cfgs);
+    mTags.push_back({DynamicsProcessing::mbc, dp});
+    for (auto& cfg : cfgs) {
+        if (cfg.enable) mMbcChannelEnable.insert(cfg.channel);
+    }
+}
+
+void DynamicsProcessingTestHelper::addPreEqBandConfigs(
+        const std::vector<DynamicsProcessing::EqBandConfig>& cfgs) {
+    DynamicsProcessing dp;
+    dp.set<DynamicsProcessing::preEqBand>(cfgs);
+    mTags.push_back({DynamicsProcessing::preEqBand, dp});
+}
+
+void DynamicsProcessingTestHelper::addPostEqBandConfigs(
+        const std::vector<DynamicsProcessing::EqBandConfig>& cfgs) {
+    DynamicsProcessing dp;
+    dp.set<DynamicsProcessing::postEqBand>(cfgs);
+    mTags.push_back({DynamicsProcessing::postEqBand, dp});
+}
+
+void DynamicsProcessingTestHelper::addMbcBandConfigs(
+        const std::vector<DynamicsProcessing::MbcBandConfig>& cfgs) {
+    DynamicsProcessing dp;
+    dp.set<DynamicsProcessing::mbcBand>(cfgs);
+    mTags.push_back({DynamicsProcessing::mbcBand, dp});
+}
+
+void DynamicsProcessingTestHelper::addLimiterConfig(
+        const std::vector<DynamicsProcessing::LimiterConfig>& cfgs) {
+    DynamicsProcessing dp;
+    dp.set<DynamicsProcessing::limiter>(cfgs);
+    mTags.push_back({DynamicsProcessing::limiter, dp});
+    for (auto& cfg : cfgs) {
+        if (cfg.enable) mLimiterChannelEnable.insert(cfg.channel);
+    }
+}
+
+void DynamicsProcessingTestHelper::addInputGain(
+        const std::vector<DynamicsProcessing::InputGain>& inputGains) {
+    DynamicsProcessing dp;
+    dp.set<DynamicsProcessing::inputGain>(inputGains);
+    mTags.push_back({DynamicsProcessing::inputGain, dp});
+}
+
+/**
+ * Test DynamicsProcessing Engine Configuration
+ */
+enum EngineArchitectureTestParamName {
+    ENGINE_TEST_INSTANCE_NAME,
+    ENGINE_TEST_RESOLUTION_PREFERENCE,
+    ENGINE_TEST_PREFERRED_DURATION,
+    ENGINE_TEST_STAGE_ENABLEMENT,
+    ENGINE_TEST_LIMITER_IN_USE
+};
+using EngineArchitectureTestParams = std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>,
+                                                DynamicsProcessing::ResolutionPreference, float,
+                                                DynamicsProcessing::StageEnablement, bool>;
+
+void fillEngineArchConfig(DynamicsProcessing::EngineArchitecture& cfg,
+                          const EngineArchitectureTestParams& params) {
+    cfg.resolutionPreference = std::get<ENGINE_TEST_RESOLUTION_PREFERENCE>(params);
+    cfg.preferredProcessingDurationMs = std::get<ENGINE_TEST_PREFERRED_DURATION>(params);
+    cfg.preEqStage = cfg.postEqStage = cfg.mbcStage =
+            std::get<ENGINE_TEST_STAGE_ENABLEMENT>(params);
+    cfg.limiterInUse = std::get<ENGINE_TEST_LIMITER_IN_USE>(params);
+}
+
+class DynamicsProcessingTestEngineArchitecture
+    : public ::testing::TestWithParam<EngineArchitectureTestParams>,
+      public DynamicsProcessingTestHelper {
+  public:
+    DynamicsProcessingTestEngineArchitecture()
+        : DynamicsProcessingTestHelper(std::get<ENGINE_TEST_INSTANCE_NAME>(GetParam())) {
+        fillEngineArchConfig(mCfg, GetParam());
+    };
+
+    void SetUp() override { SetUpDynamicsProcessingEffect(); }
+
+    void TearDown() override { TearDownDynamicsProcessingEffect(); }
+
+    DynamicsProcessing::EngineArchitecture mCfg;
+};
+
+TEST_P(DynamicsProcessingTestEngineArchitecture, SetAndGetEngineArch) {
+    EXPECT_NO_FATAL_FAILURE(addEngineConfig(mCfg));
+    SetAndGetDynamicsProcessingParameters();
+}
+
+INSTANTIATE_TEST_SUITE_P(
+        DynamicsProcessingTest, DynamicsProcessingTestEngineArchitecture,
+        ::testing::Combine(
+                testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
+                        IFactory::descriptor, kDynamicsProcessingTypeUUID)),
+                testing::Values(DynamicsProcessing::ResolutionPreference::FAVOR_TIME_RESOLUTION,
+                                DynamicsProcessing::ResolutionPreference::
+                                        FAVOR_FREQUENCY_RESOLUTION),  // variant
+                testing::Values(-10.f, 0.f, 10.f),                    // processing duration
+                testing::ValuesIn(
+                        DynamicsProcessingTestHelper::kStageEnablementTestSet),  // preEQ/postEQ/mbc
+                testing::Bool()),                                                // limiter enable
+        [](const auto& info) {
+            auto descriptor = std::get<ENGINE_TEST_INSTANCE_NAME>(info.param).second;
+            DynamicsProcessing::EngineArchitecture cfg;
+            fillEngineArchConfig(cfg, info.param);
+            std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
+                               descriptor.common.name + "_UUID_" +
+                               descriptor.common.id.uuid.toString() + "_Cfg_" + cfg.toString();
+            std::replace_if(
+                    name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
+            return name;
+        });
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DynamicsProcessingTestEngineArchitecture);
+
+/**
+ * Test DynamicsProcessing Input Gain
+ */
+enum InputGainTestParamName {
+    INPUT_GAIN_INSTANCE_NAME,
+    INPUT_GAIN_PARAM,
+};
+class DynamicsProcessingTestInputGain
+    : public ::testing::TestWithParam<std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>,
+                                                 std::vector<DynamicsProcessing::InputGain>>>,
+      public DynamicsProcessingTestHelper {
+  public:
+    DynamicsProcessingTestInputGain()
+        : DynamicsProcessingTestHelper(std::get<INPUT_GAIN_INSTANCE_NAME>(GetParam())),
+          mInputGain(std::get<INPUT_GAIN_PARAM>(GetParam())){};
+
+    void SetUp() override { SetUpDynamicsProcessingEffect(); }
+
+    void TearDown() override { TearDownDynamicsProcessingEffect(); }
+
+    const std::vector<DynamicsProcessing::InputGain> mInputGain;
+};
+
+TEST_P(DynamicsProcessingTestInputGain, SetAndGetInputGain) {
+    EXPECT_NO_FATAL_FAILURE(addInputGain(mInputGain));
+    SetAndGetDynamicsProcessingParameters();
+}
+
+INSTANTIATE_TEST_SUITE_P(
+        DynamicsProcessingTest, DynamicsProcessingTestInputGain,
+        ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
+                                   IFactory::descriptor, kDynamicsProcessingTypeUUID)),
+                           testing::ValuesIn(DynamicsProcessingTestInputGain::kInputGainTestSet)),
+        [](const auto& info) {
+            auto descriptor = std::get<INPUT_GAIN_INSTANCE_NAME>(info.param).second;
+            std::string gains =
+                    ::android::internal::ToString(std::get<INPUT_GAIN_PARAM>(info.param));
+            std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
+                               descriptor.common.name + "_UUID_" +
+                               descriptor.common.id.uuid.toString() + "_inputGains_" + gains;
+            std::replace_if(
+                    name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
+            return name;
+        });
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DynamicsProcessingTestInputGain);
+
+/**
+ * Test DynamicsProcessing Limiter Config
+ */
+enum LimiterConfigTestParamName {
+    LIMITER_INSTANCE_NAME,
+    LIMITER_CHANNEL,
+    LIMITER_ENABLE,
+    LIMITER_LINK_GROUP,
+    LIMITER_ENGINE_IN_USE,
+    LIMITER_ADDITIONAL,
+};
+enum LimiterConfigTestAdditionalParam {
+    LIMITER_ATTACK_TIME,
+    LIMITER_RELEASE_TIME,
+    LIMITER_RATIO,
+    LIMITER_THRESHOLD,
+    LIMITER_POST_GAIN,
+    LIMITER_MAX_NUM,
+};
+using LimiterConfigTestAdditional = std::array<float, LIMITER_MAX_NUM>;
+// attachTime, releaseTime, ratio, thresh, postGain
+static constexpr std::array<LimiterConfigTestAdditional, 4> kLimiterConfigTestAdditionalParam = {
+        {{-1, -60, -2.5, -2, -3.14},
+         {-1, 60, -2.5, 2, -3.14},
+         {1, -60, 2.5, -2, 3.14},
+         {1, 60, 2.5, 2, 3.14}}};
+
+using LimiterConfigTestParams =
+        std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int32_t, bool, int32_t, bool,
+                   LimiterConfigTestAdditional>;
+
+void fillLimiterConfig(DynamicsProcessing::LimiterConfig& cfg,
+                       const LimiterConfigTestParams& params) {
+    const std::array<float, LIMITER_MAX_NUM> additional = std::get<LIMITER_ADDITIONAL>(params);
+    cfg.channel = std::get<LIMITER_CHANNEL>(params);
+    cfg.enable = std::get<LIMITER_ENABLE>(params);
+    cfg.linkGroup = std::get<LIMITER_LINK_GROUP>(params);
+    cfg.attackTimeMs = additional[LIMITER_ATTACK_TIME];
+    cfg.releaseTimeMs = additional[LIMITER_RELEASE_TIME];
+    cfg.ratio = additional[LIMITER_RATIO];
+    cfg.thresholdDb = additional[LIMITER_THRESHOLD];
+    cfg.postGainDb = additional[LIMITER_POST_GAIN];
+}
+
+class DynamicsProcessingTestLimiterConfig
+    : public ::testing::TestWithParam<LimiterConfigTestParams>,
+      public DynamicsProcessingTestHelper {
+  public:
+    DynamicsProcessingTestLimiterConfig()
+        : DynamicsProcessingTestHelper(std::get<LIMITER_INSTANCE_NAME>(GetParam())),
+          mLimiterInUseEngine(std::get<LIMITER_ENGINE_IN_USE>(GetParam())) {
+        fillLimiterConfig(mCfg, GetParam());
+    }
+
+    void SetUp() override { SetUpDynamicsProcessingEffect(); }
+
+    void TearDown() override { TearDownDynamicsProcessingEffect(); }
+
+    DynamicsProcessing::LimiterConfig mCfg;
+    bool mLimiterInUseEngine;
+};
+
+TEST_P(DynamicsProcessingTestLimiterConfig, SetAndGetLimiterConfig) {
+    mEngineConfigPreset.limiterInUse = mLimiterInUseEngine;
+    EXPECT_NO_FATAL_FAILURE(addEngineConfig(mEngineConfigPreset));
+    EXPECT_NO_FATAL_FAILURE(addLimiterConfig({mCfg}));
+    SetAndGetDynamicsProcessingParameters();
+}
+
+INSTANTIATE_TEST_SUITE_P(
+        DynamicsProcessingTest, DynamicsProcessingTestLimiterConfig,
+        ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
+                                   IFactory::descriptor, kDynamicsProcessingTypeUUID)),
+                           testing::Values(-1, 0, 1, 2),  // channel count
+                           testing::Bool(),               // enable
+                           testing::Values(3),            // link group
+                           testing::Bool(),               // engine limiter enable
+                           testing::ValuesIn(kLimiterConfigTestAdditionalParam)),  // Additional
+        [](const auto& info) {
+            auto descriptor = std::get<LIMITER_INSTANCE_NAME>(info.param).second;
+            DynamicsProcessing::LimiterConfig cfg;
+            fillLimiterConfig(cfg, info.param);
+            std::string engineLimiterInUse =
+                    std::to_string(std::get<LIMITER_ENGINE_IN_USE>(info.param));
+            std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
+                               descriptor.common.name + "_UUID_" +
+                               descriptor.common.id.uuid.toString() + "_limiterConfig_" +
+                               cfg.toString() + "_engineSetting_" + engineLimiterInUse;
+            std::replace_if(
+                    name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
+            return name;
+        });
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DynamicsProcessingTestLimiterConfig);
+
+/**
+ * Test DynamicsProcessing ChannelConfig
+ */
+enum ChannelConfigTestParamName {
+    BAND_CHANNEL_TEST_INSTANCE_NAME,
+    BAND_CHANNEL_TEST_CHANNEL_CONFIG,
+    BAND_CHANNEL_TEST_ENGINE_IN_USE
+};
+using ChannelConfigTestParams = std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>,
+                                           std::vector<DynamicsProcessing::ChannelConfig>, bool>;
+
+class DynamicsProcessingTestChannelConfig
+    : public ::testing::TestWithParam<ChannelConfigTestParams>,
+      public DynamicsProcessingTestHelper {
+  public:
+    DynamicsProcessingTestChannelConfig()
+        : DynamicsProcessingTestHelper(std::get<BAND_CHANNEL_TEST_INSTANCE_NAME>(GetParam())),
+          mCfg(std::get<BAND_CHANNEL_TEST_CHANNEL_CONFIG>(GetParam())),
+          mInUseEngine(std::get<BAND_CHANNEL_TEST_ENGINE_IN_USE>(GetParam())) {}
+
+    void SetUp() override { SetUpDynamicsProcessingEffect(); }
+
+    void TearDown() override { TearDownDynamicsProcessingEffect(); }
+
+    std::vector<DynamicsProcessing::ChannelConfig> mCfg;
+    const bool mInUseEngine;
+};
+
+TEST_P(DynamicsProcessingTestChannelConfig, SetAndGetPreEqChannelConfig) {
+    mEngineConfigPreset.preEqStage.inUse = mInUseEngine;
+    EXPECT_NO_FATAL_FAILURE(addEngineConfig(mEngineConfigPreset));
+    EXPECT_NO_FATAL_FAILURE(addPreEqChannelConfig(mCfg));
+    SetAndGetDynamicsProcessingParameters();
+}
+
+TEST_P(DynamicsProcessingTestChannelConfig, SetAndGetPostEqChannelConfig) {
+    mEngineConfigPreset.postEqStage.inUse = mInUseEngine;
+    EXPECT_NO_FATAL_FAILURE(addEngineConfig(mEngineConfigPreset));
+    EXPECT_NO_FATAL_FAILURE(addPostEqChannelConfig(mCfg));
+    SetAndGetDynamicsProcessingParameters();
+}
+
+TEST_P(DynamicsProcessingTestChannelConfig, SetAndGetMbcChannelConfig) {
+    mEngineConfigPreset.mbcStage.inUse = mInUseEngine;
+    EXPECT_NO_FATAL_FAILURE(addEngineConfig(mEngineConfigPreset));
+    EXPECT_NO_FATAL_FAILURE(addMbcChannelConfig(mCfg));
+    SetAndGetDynamicsProcessingParameters();
+}
+
+INSTANTIATE_TEST_SUITE_P(
+        DynamicsProcessingTest, DynamicsProcessingTestChannelConfig,
+        ::testing::Combine(
+                testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
+                        IFactory::descriptor, kDynamicsProcessingTypeUUID)),
+                testing::ValuesIn(
+                        DynamicsProcessingTestHelper::kChannelConfigTestSet),  // channel config
+                testing::Bool()),                                              // Engine inUse
+        [](const auto& info) {
+            auto descriptor = std::get<BAND_CHANNEL_TEST_INSTANCE_NAME>(info.param).second;
+            std::string engineInUse =
+                    std::to_string(std::get<BAND_CHANNEL_TEST_ENGINE_IN_USE>(info.param));
+            std::string channelConfig = ::android::internal::ToString(
+                    std::get<BAND_CHANNEL_TEST_CHANNEL_CONFIG>(info.param));
+
+            std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
+                               descriptor.common.name + "_UUID_" +
+                               descriptor.common.id.uuid.toString() + "_" + channelConfig +
+                               "_engineInUse_" + engineInUse;
+            std::replace_if(
+                    name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
+            return name;
+        });
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DynamicsProcessingTestChannelConfig);
+
+/**
+ * Test DynamicsProcessing EqBandConfig
+ */
+enum EqBandConfigTestParamName {
+    EQ_BAND_INSTANCE_NAME,
+    EQ_BAND_CHANNEL,
+    EQ_BAND_CHANNEL_ENABLE,
+    EQ_BAND_ENABLE,
+    EQ_BAND_CUT_OFF_FREQ,
+    EQ_BAND_GAIN,
+    EQ_BAND_STAGE_IN_USE
+};
+using EqBandConfigTestParams = std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int32_t,
+                                          std::vector<DynamicsProcessing::ChannelConfig>, bool,
+                                          std::vector<std::pair<int, float>>, float, bool>;
+
+void fillEqBandConfig(std::vector<DynamicsProcessing::EqBandConfig>& cfgs,
+                      const EqBandConfigTestParams& params) {
+    const std::vector<std::pair<int, float>> cutOffFreqs = std::get<EQ_BAND_CUT_OFF_FREQ>(params);
+    int bandCount = cutOffFreqs.size();
+    cfgs.resize(bandCount);
+    for (int i = 0; i < bandCount; i++) {
+        cfgs[i].channel = std::get<EQ_BAND_CHANNEL>(params);
+        cfgs[i].band = cutOffFreqs[i].first;
+        cfgs[i].enable = std::get<EQ_BAND_ENABLE>(params);
+        cfgs[i].cutoffFrequencyHz = cutOffFreqs[i].second;
+        cfgs[i].gainDb = std::get<EQ_BAND_GAIN>(params);
+    }
+}
+
+class DynamicsProcessingTestEqBandConfig : public ::testing::TestWithParam<EqBandConfigTestParams>,
+                                           public DynamicsProcessingTestHelper {
+  public:
+    DynamicsProcessingTestEqBandConfig()
+        : DynamicsProcessingTestHelper(std::get<EQ_BAND_INSTANCE_NAME>(GetParam())),
+          mStageInUse(std::get<EQ_BAND_STAGE_IN_USE>(GetParam())),
+          mChannelConfig(std::get<EQ_BAND_CHANNEL_ENABLE>(GetParam())) {
+        fillEqBandConfig(mCfgs, GetParam());
+    }
+
+    void SetUp() override { SetUpDynamicsProcessingEffect(); }
+
+    void TearDown() override { TearDownDynamicsProcessingEffect(); }
+
+    std::vector<DynamicsProcessing::EqBandConfig> mCfgs;
+    const bool mStageInUse;
+    const std::vector<DynamicsProcessing::ChannelConfig> mChannelConfig;
+};
+
+TEST_P(DynamicsProcessingTestEqBandConfig, SetAndGetPreEqBandConfig) {
+    mEngineConfigPreset.preEqStage.inUse = mStageInUse;
+    mEngineConfigPreset.preEqStage.bandCount = mCfgs.size();
+    EXPECT_NO_FATAL_FAILURE(addEngineConfig(mEngineConfigPreset));
+    EXPECT_NO_FATAL_FAILURE(addPreEqChannelConfig(mChannelConfig));
+    EXPECT_NO_FATAL_FAILURE(addPreEqBandConfigs(mCfgs));
+    SetAndGetDynamicsProcessingParameters();
+}
+
+TEST_P(DynamicsProcessingTestEqBandConfig, SetAndGetPostEqBandConfig) {
+    mEngineConfigPreset.postEqStage.inUse = mStageInUse;
+    mEngineConfigPreset.postEqStage.bandCount = mCfgs.size();
+    EXPECT_NO_FATAL_FAILURE(addEngineConfig(mEngineConfigPreset));
+    EXPECT_NO_FATAL_FAILURE(addPostEqChannelConfig(mChannelConfig));
+    EXPECT_NO_FATAL_FAILURE(addPostEqBandConfigs(mCfgs));
+    SetAndGetDynamicsProcessingParameters();
+}
+
+std::vector<std::vector<std::pair<int, float>>> kBands{
+        {
+                {0, 600},
+                {1, 2000},
+                {2, 6000},
+                {3, 10000},
+                {4, 16000},
+        },  // 5 bands
+        {
+                {0, 800},
+                {3, 15000},
+                {2, 6000},
+                {1, 2000},
+        },  // 4 bands, unsorted
+        {
+                {0, 650},
+                {1, 2000},
+                {2, 6000},
+                {3, 10000},
+                {3, 16000},
+        },  // 5 bands, missing band
+        {
+                {0, 900},
+                {1, 8000},
+                {2, 4000},
+                {3, 12000},
+        },  // 4 bands, cutoff freq not increasing
+        {
+                {0, 450},
+                {1, 2000},
+                {7, 6000},
+                {3, 10000},
+                {4, 16000},
+        },  // bad band index
+        {
+                {0, 1},
+                {1, 8000},
+        },  // too low cutoff freq
+        {
+                {0, 1200},
+                {1, 80000},
+        },  // too high cutoff freq
+};
+
+INSTANTIATE_TEST_SUITE_P(
+        DynamicsProcessingTest, DynamicsProcessingTestEqBandConfig,
+        ::testing::Combine(
+                testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
+                        IFactory::descriptor, kDynamicsProcessingTypeUUID)),
+                testing::Values(-1, 0, 10),  // channel ID
+                testing::ValuesIn(
+                        DynamicsProcessingTestHelper::kChannelConfigTestSet),  // channel enable
+                testing::Bool(),                                               // band enable
+                testing::ValuesIn(kBands),       // cut off frequencies
+                testing::Values(-3.14f, 3.14f),  // gain
+                testing::Bool()),                // stage in use
+        [](const auto& info) {
+            auto descriptor = std::get<EQ_BAND_INSTANCE_NAME>(info.param).second;
+            std::vector<DynamicsProcessing::EqBandConfig> cfgs;
+            fillEqBandConfig(cfgs, info.param);
+            std::string enable =
+                    ::android::internal::ToString(std::get<EQ_BAND_CHANNEL_ENABLE>(info.param));
+            std::string bands = ::android::internal::ToString(cfgs);
+            std::string stageInUse = std::to_string(std::get<EQ_BAND_STAGE_IN_USE>(info.param));
+            std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
+                               descriptor.common.name + "_UUID_" +
+                               descriptor.common.id.uuid.toString() + "_" + enable + "_bands_" +
+                               bands + "_stageInUse_" + stageInUse;
+            std::replace_if(
+                    name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
+            return name;
+        });
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DynamicsProcessingTestEqBandConfig);
+
+/**
+ * Test DynamicsProcessing MbcBandConfig
+ */
+
+enum MbcBandConfigParamName {
+    MBC_BAND_INSTANCE_NAME,
+    MBC_BAND_CHANNEL,
+    MBC_BAND_CHANNEL_CONFIG,
+    MBC_BAND_ENABLE,
+    MBC_BAND_CUTOFF_FREQ,
+    MBC_BAND_STAGE_IN_USE,
+    MBC_BAND_ADDITIONAL
+};
+enum MbcBandConfigAdditional {
+    MBC_ADD_ATTACK_TIME,
+    MBC_ADD_RELEASE_TIME,
+    MBC_ADD_RATIO,
+    MBC_ADD_THRESHOLD,
+    MBC_ADD_KNEE_WIDTH,
+    MBC_ADD_NOISE_GATE_THRESHOLD,
+    MBC_ADD_EXPENDER_RATIO,
+    MBC_ADD_PRE_GAIN,
+    MBC_ADD_POST_GAIN,
+    MBC_ADD_MAX_NUM
+};
+using TestParamsMbcBandConfigAdditional = std::array<float, MBC_ADD_MAX_NUM>;
+
+// attachTime, releaseTime, ratio, thresh, kneeWidth, noise, expander, preGain, postGain
+static constexpr std::array<TestParamsMbcBandConfigAdditional, 4> kMbcBandConfigAdditionalParam = {
+        {{-3, -10, -2, -2, -5, -90, -2.5, -2, -2},
+         {0, 0, 0, 0, 0, 0, 0, 0, 0},
+         {-3, 10, -2, 2, -5, 90, -2.5, 2, -2},
+         {3, 10, 2, 2, 5, 90, 2.5, 2, 2}}};
+
+using TestParamsMbcBandConfig =
+        std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int32_t,
+                   std::vector<DynamicsProcessing::ChannelConfig>, bool,
+                   std::vector<std::pair<int, float>>, bool, TestParamsMbcBandConfigAdditional>;
+
+void fillMbcBandConfig(std::vector<DynamicsProcessing::MbcBandConfig>& cfgs,
+                       const TestParamsMbcBandConfig& params) {
+    const std::vector<std::pair<int, float>> cutOffFreqs = std::get<MBC_BAND_CUTOFF_FREQ>(params);
+    const std::array<float, MBC_ADD_MAX_NUM> additional = std::get<MBC_BAND_ADDITIONAL>(params);
+    int bandCount = cutOffFreqs.size();
+    cfgs.resize(bandCount);
+    for (int i = 0; i < bandCount; i++) {
+        cfgs[i] = DynamicsProcessing::MbcBandConfig{
+                .channel = std::get<MBC_BAND_CHANNEL>(params),
+                .band = cutOffFreqs[i].first,
+                .enable = std::get<MBC_BAND_ENABLE>(params),
+                .cutoffFrequencyHz = cutOffFreqs[i].second,
+                .attackTimeMs = additional[MBC_ADD_ATTACK_TIME],
+                .releaseTimeMs = additional[MBC_ADD_RELEASE_TIME],
+                .ratio = additional[MBC_ADD_RATIO],
+                .thresholdDb = additional[MBC_ADD_THRESHOLD],
+                .kneeWidthDb = additional[MBC_ADD_KNEE_WIDTH],
+                .noiseGateThresholdDb = additional[MBC_ADD_NOISE_GATE_THRESHOLD],
+                .expanderRatio = additional[MBC_ADD_EXPENDER_RATIO],
+                .preGainDb = additional[MBC_ADD_PRE_GAIN],
+                .postGainDb = additional[MBC_ADD_POST_GAIN]};
+    }
+}
+
+class DynamicsProcessingTestMbcBandConfig
+    : public ::testing::TestWithParam<TestParamsMbcBandConfig>,
+      public DynamicsProcessingTestHelper {
+  public:
+    DynamicsProcessingTestMbcBandConfig()
+        : DynamicsProcessingTestHelper(std::get<MBC_BAND_INSTANCE_NAME>(GetParam())),
+          mStageInUse(std::get<MBC_BAND_STAGE_IN_USE>(GetParam())),
+          mChannelConfig(std::get<MBC_BAND_CHANNEL_CONFIG>(GetParam())) {
+        fillMbcBandConfig(mCfgs, GetParam());
+    }
+
+    void SetUp() override { SetUpDynamicsProcessingEffect(); }
+
+    void TearDown() override { TearDownDynamicsProcessingEffect(); }
+
+    std::vector<DynamicsProcessing::MbcBandConfig> mCfgs;
+    const bool mStageInUse;
+    const std::vector<DynamicsProcessing::ChannelConfig> mChannelConfig;
+};
+
+TEST_P(DynamicsProcessingTestMbcBandConfig, SetAndGetMbcBandConfig) {
+    mEngineConfigPreset.mbcStage.inUse = mStageInUse;
+    mEngineConfigPreset.mbcStage.bandCount = mCfgs.size();
+    EXPECT_NO_FATAL_FAILURE(addEngineConfig(mEngineConfigPreset));
+    EXPECT_NO_FATAL_FAILURE(addMbcChannelConfig(mChannelConfig));
+    EXPECT_NO_FATAL_FAILURE(addMbcBandConfigs(mCfgs));
+    SetAndGetDynamicsProcessingParameters();
+}
+
+INSTANTIATE_TEST_SUITE_P(
+        DynamicsProcessingTest, DynamicsProcessingTestMbcBandConfig,
+        ::testing::Combine(
+                testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
+                        IFactory::descriptor, kDynamicsProcessingTypeUUID)),
+                testing::Values(-1, 0, 10),  // channel count
+                testing::ValuesIn(
+                        DynamicsProcessingTestHelper::kChannelConfigTestSet),  // channel config
+                testing::Bool(),                                               // enable
+                testing::ValuesIn(kBands),                          // cut off frequencies
+                testing::Bool(),                                    // stage in use
+                testing::ValuesIn(kMbcBandConfigAdditionalParam)),  // Additional
+        [](const auto& info) {
+            auto descriptor = std::get<MBC_BAND_INSTANCE_NAME>(info.param).second;
+            std::vector<DynamicsProcessing::MbcBandConfig> cfgs;
+            fillMbcBandConfig(cfgs, info.param);
+            std::string enable =
+                    ::android::internal::ToString(std::get<MBC_BAND_CHANNEL_CONFIG>(info.param));
+            std::string mbcBands = ::android::internal::ToString(cfgs);
+            std::string stageInUse = std::to_string(std::get<MBC_BAND_STAGE_IN_USE>(info.param));
+            std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
+                               descriptor.common.name + "_UUID_" +
+                               descriptor.common.id.uuid.toString() + "_enable_" + enable +
+                               "_bands_" + mbcBands + "_stageInUse_" + stageInUse;
+            std::replace_if(
+                    name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
+            return name;
+        });
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DynamicsProcessingTestMbcBandConfig);
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ABinderProcess_setThreadPoolMaxThreadCount(1);
+    ABinderProcess_startThreadPool();
+    return RUN_ALL_TESTS();
+}
diff --git a/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp b/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp
index e99c4a4..82c8757 100644
--- a/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp
@@ -18,6 +18,7 @@
 
 #include <Utils.h>
 #include <aidl/Vintf.h>
+#include <unordered_set>
 #include "EffectHelper.h"
 
 using namespace android;
@@ -38,30 +39,6 @@
  * any index supported value test expects EX_NONE from IEffect.setParameter(), otherwise expects
  * EX_ILLEGAL_ARGUMENT.
  */
-const std::vector<int> kRoomLevelValues = {
-        EnvironmentalReverb::MIN_ROOM_LEVEL_MB - 1, EnvironmentalReverb::MIN_ROOM_LEVEL_MB,
-        EnvironmentalReverb::MAX_ROOM_LEVEL_MB, EnvironmentalReverb::MAX_ROOM_LEVEL_MB + 1};
-const std::vector<int> kRoomHfLevelValues = {
-        EnvironmentalReverb::MIN_ROOM_HF_LEVEL_MB - 1, EnvironmentalReverb::MIN_ROOM_HF_LEVEL_MB,
-        EnvironmentalReverb::MAX_ROOM_HF_LEVEL_MB, EnvironmentalReverb::MAX_ROOM_HF_LEVEL_MB + 1};
-const std::vector<int> kDecayTimeValues = {
-        EnvironmentalReverb::MIN_DECAY_TIME_MS - 1, EnvironmentalReverb::MIN_DECAY_TIME_MS,
-        EnvironmentalReverb::MAX_DECAY_TIME_MS, EnvironmentalReverb::MAX_DECAY_TIME_MS + 1};
-const std::vector<int> kDecayHfRatioValues = {
-        EnvironmentalReverb::MIN_DECAY_HF_RATIO_PM - 1, EnvironmentalReverb::MIN_DECAY_HF_RATIO_PM,
-        EnvironmentalReverb::MAX_DECAY_HF_RATIO_PM, EnvironmentalReverb::MAX_DECAY_HF_RATIO_PM + 1};
-const std::vector<int> kLevelValues = {
-        EnvironmentalReverb::MIN_LEVEL_MB - 1, EnvironmentalReverb::MIN_LEVEL_MB,
-        EnvironmentalReverb::MAX_LEVEL_MB, EnvironmentalReverb::MAX_LEVEL_MB + 1};
-const std::vector<int> kDelayValues = {
-        EnvironmentalReverb::MIN_DELAY_MS - 1, EnvironmentalReverb::MIN_DELAY_MS,
-        EnvironmentalReverb::MAX_DELAY_MS, EnvironmentalReverb::MAX_DELAY_MS + 1};
-const std::vector<int> kDiffusionValues = {
-        EnvironmentalReverb::MIN_DIFFUSION_PM - 1, EnvironmentalReverb::MIN_DIFFUSION_PM,
-        EnvironmentalReverb::MAX_DIFFUSION_PM, EnvironmentalReverb::MAX_DIFFUSION_PM + 1};
-const std::vector<int> kDensityValues = {
-        EnvironmentalReverb::MIN_DENSITY_PM - 1, EnvironmentalReverb::MIN_DENSITY_PM,
-        EnvironmentalReverb::MAX_DENSITY_PM, EnvironmentalReverb::MAX_DENSITY_PM + 1};
 
 class EnvironmentalReverbHelper : public EffectHelper {
   public:
@@ -88,8 +65,7 @@
     }
 
     Parameter::Specific getDefaultParamSpecific() {
-        EnvironmentalReverb er = EnvironmentalReverb::make<EnvironmentalReverb::roomLevelMb>(
-                EnvironmentalReverb::MIN_ROOM_LEVEL_MB);
+        EnvironmentalReverb er = EnvironmentalReverb::make<EnvironmentalReverb::roomLevelMb>(-6000);
         Parameter::Specific specific =
                 Parameter::Specific::make<Parameter::Specific::environmentalReverb>(er);
         return specific;
@@ -99,14 +75,14 @@
     std::shared_ptr<IFactory> mFactory;
     std::shared_ptr<IEffect> mEffect;
     Descriptor mDescriptor;
-    int mRoomLevel = EnvironmentalReverb::MIN_ROOM_LEVEL_MB;
-    int mRoomHfLevel = EnvironmentalReverb::MAX_ROOM_HF_LEVEL_MB;
+    int mRoomLevel = -6000;
+    int mRoomHfLevel = 0;
     int mDecayTime = 1000;
     int mDecayHfRatio = 500;
-    int mLevel = EnvironmentalReverb::MIN_LEVEL_MB;
+    int mLevel = -6000;
     int mDelay = 40;
-    int mDiffusion = EnvironmentalReverb::MAX_DIFFUSION_PM;
-    int mDensity = EnvironmentalReverb::MAX_DENSITY_PM;
+    int mDiffusion = 1000;
+    int mDensity = 1000;
     bool mBypass = false;
 
     void SetAndGetReverbParameters() {
@@ -202,11 +178,11 @@
         switch (tag) {
             case EnvironmentalReverb::roomLevelMb: {
                 int roomLevel = er.get<EnvironmentalReverb::roomLevelMb>();
-                return isRoomLevelInRange(roomLevel);
+                return isRoomLevelInRange(erCap, roomLevel);
             }
             case EnvironmentalReverb::roomHfLevelMb: {
                 int roomHfLevel = er.get<EnvironmentalReverb::roomHfLevelMb>();
-                return isRoomHfLevelInRange(roomHfLevel);
+                return isRoomHfLevelInRange(erCap, roomHfLevel);
             }
             case EnvironmentalReverb::decayTimeMs: {
                 int decayTime = er.get<EnvironmentalReverb::decayTimeMs>();
@@ -214,23 +190,23 @@
             }
             case EnvironmentalReverb::decayHfRatioPm: {
                 int decayHfRatio = er.get<EnvironmentalReverb::decayHfRatioPm>();
-                return isDecayHfRatioInRange(decayHfRatio);
+                return isDecayHfRatioInRange(erCap, decayHfRatio);
             }
             case EnvironmentalReverb::levelMb: {
                 int level = er.get<EnvironmentalReverb::levelMb>();
-                return isLevelInRange(level);
+                return isLevelInRange(erCap, level);
             }
             case EnvironmentalReverb::delayMs: {
                 int delay = er.get<EnvironmentalReverb::delayMs>();
-                return isDelayInRange(delay);
+                return isDelayInRange(erCap, delay);
             }
             case EnvironmentalReverb::diffusionPm: {
                 int diffusion = er.get<EnvironmentalReverb::diffusionPm>();
-                return isDiffusionInRange(diffusion);
+                return isDiffusionInRange(erCap, diffusion);
             }
             case EnvironmentalReverb::densityPm: {
                 int density = er.get<EnvironmentalReverb::densityPm>();
-                return isDensityInRange(density);
+                return isDensityInRange(erCap, density);
             }
             case EnvironmentalReverb::bypass: {
                 return true;
@@ -241,45 +217,191 @@
         return false;
     }
 
-    bool isRoomLevelInRange(int roomLevel) const {
-        return roomLevel >= EnvironmentalReverb::MIN_ROOM_LEVEL_MB &&
-               roomLevel <= EnvironmentalReverb::MAX_ROOM_LEVEL_MB;
+    bool isRoomLevelInRange(const EnvironmentalReverb::Capability& cap, int roomLevel) const {
+        return roomLevel >= cap.minRoomLevelMb && roomLevel <= cap.maxRoomLevelMb;
     }
 
-    bool isRoomHfLevelInRange(int roomHfLevel) const {
-        return roomHfLevel >= EnvironmentalReverb::MIN_ROOM_HF_LEVEL_MB &&
-               roomHfLevel <= EnvironmentalReverb::MAX_ROOM_HF_LEVEL_MB;
+    bool isRoomHfLevelInRange(const EnvironmentalReverb::Capability& cap, int roomHfLevel) const {
+        return roomHfLevel >= cap.minRoomHfLevelMb && roomHfLevel <= cap.maxRoomHfLevelMb;
     }
 
     bool isDecayTimeInRange(const EnvironmentalReverb::Capability& cap, int decayTime) const {
-        return decayTime >= EnvironmentalReverb::MIN_DECAY_TIME_MS &&
-               decayTime <= EnvironmentalReverb::MAX_DECAY_TIME_MS &&
-               decayTime <= cap.maxDecayTimeMs;
+        return decayTime >= 0 && decayTime <= cap.maxDecayTimeMs;
     }
 
-    bool isDecayHfRatioInRange(int decayHfRatio) const {
-        return decayHfRatio >= EnvironmentalReverb::MIN_DECAY_HF_RATIO_PM &&
-               decayHfRatio <= EnvironmentalReverb::MAX_DECAY_HF_RATIO_PM;
+    bool isDecayHfRatioInRange(const EnvironmentalReverb::Capability& cap, int decayHfRatio) const {
+        return decayHfRatio >= cap.minDecayHfRatioPm && decayHfRatio <= cap.maxDecayHfRatioPm;
     }
 
-    bool isLevelInRange(int level) const {
-        return level >= EnvironmentalReverb::MIN_LEVEL_MB &&
-               level <= EnvironmentalReverb::MAX_LEVEL_MB;
+    bool isLevelInRange(const EnvironmentalReverb::Capability& cap, int level) const {
+        return level >= cap.minLevelMb && level <= cap.maxLevelMb;
     }
 
-    bool isDelayInRange(int delay) const {
-        return delay >= EnvironmentalReverb::MIN_DELAY_MS &&
-               delay <= EnvironmentalReverb::MAX_DELAY_MS;
+    bool isDelayInRange(const EnvironmentalReverb::Capability& cap, int delay) const {
+        return delay >= 0 && delay <= cap.maxDelayMs;
     }
 
-    bool isDiffusionInRange(int diffusion) const {
-        return diffusion >= EnvironmentalReverb::MIN_DIFFUSION_PM &&
-               diffusion <= EnvironmentalReverb::MAX_DIFFUSION_PM;
+    bool isDiffusionInRange(const EnvironmentalReverb::Capability& cap, int diffusion) const {
+        return diffusion >= 0 && diffusion <= cap.maxDiffusionPm;
     }
 
-    bool isDensityInRange(int density) const {
-        return density >= EnvironmentalReverb::MIN_DENSITY_PM &&
-               density <= EnvironmentalReverb::MAX_DENSITY_PM;
+    bool isDensityInRange(const EnvironmentalReverb::Capability& cap, int density) const {
+        return density >= 0 && density <= cap.maxDensityPm;
+    }
+
+    static std::unordered_set<int> getRoomLevelValues() {
+        auto descList = EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor,
+                                                                     kEnvReverbTypeUUID);
+        int minRoomLevelMb = std::numeric_limits<int>::max();
+        int maxRoomLevelMb = std::numeric_limits<int>::min();
+        for (const auto& it : descList) {
+            maxRoomLevelMb = std::max(
+                    it.second.capability.get<Capability::environmentalReverb>().maxRoomLevelMb,
+                    maxRoomLevelMb);
+            minRoomLevelMb = std::min(
+                    it.second.capability.get<Capability::environmentalReverb>().minRoomLevelMb,
+                    minRoomLevelMb);
+        }
+        return {std::numeric_limits<int>::min(),        minRoomLevelMb - 1, minRoomLevelMb,
+                (minRoomLevelMb + maxRoomLevelMb) >> 1, maxRoomLevelMb,     maxRoomLevelMb + 1,
+                std::numeric_limits<int>::max()};
+    }
+
+    static std::unordered_set<int> getRoomHfLevelValues() {
+        auto descList = EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor,
+                                                                     kEnvReverbTypeUUID);
+        int minRoomHfLevelMb = std::numeric_limits<int>::max();
+        int maxRoomHfLevelMb = std::numeric_limits<int>::min();
+        for (const auto& it : descList) {
+            maxRoomHfLevelMb = std::max(
+                    it.second.capability.get<Capability::environmentalReverb>().maxRoomHfLevelMb,
+                    maxRoomHfLevelMb);
+            minRoomHfLevelMb = std::min(
+                    it.second.capability.get<Capability::environmentalReverb>().minRoomHfLevelMb,
+                    minRoomHfLevelMb);
+        }
+        return {std::numeric_limits<int>::min(),
+                minRoomHfLevelMb - 1,
+                minRoomHfLevelMb,
+                (minRoomHfLevelMb + maxRoomHfLevelMb) >> 1,
+                maxRoomHfLevelMb,
+                maxRoomHfLevelMb + 1,
+                std::numeric_limits<int>::max()};
+    }
+
+    static std::unordered_set<int> getDecayTimeValues() {
+        auto descList = EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor,
+                                                                     kEnvReverbTypeUUID);
+        const auto max = std::max_element(
+                descList.begin(), descList.end(),
+                [](const std::pair<std::shared_ptr<IFactory>, Descriptor>& a,
+                   const std::pair<std::shared_ptr<IFactory>, Descriptor>& b) {
+                    return a.second.capability.get<Capability::environmentalReverb>()
+                                   .maxDecayTimeMs <
+                           b.second.capability.get<Capability::environmentalReverb>()
+                                   .maxDecayTimeMs;
+                });
+        if (max == descList.end()) {
+            return {0};
+        }
+        int maxDecayTimeMs =
+                max->second.capability.get<Capability::environmentalReverb>().maxDecayTimeMs;
+        return {-1, 0, maxDecayTimeMs >> 1, maxDecayTimeMs - 1, maxDecayTimeMs, maxDecayTimeMs + 1};
+    }
+
+    static std::unordered_set<int> getDecayHfRatioValues() {
+        auto descList = EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor,
+                                                                     kEnvReverbTypeUUID);
+        int minDecayHfRatioPm = std::numeric_limits<int>::max();
+        int maxDecayHfRatioPm = std::numeric_limits<int>::min();
+        for (const auto& it : descList) {
+            maxDecayHfRatioPm = std::max(
+                    it.second.capability.get<Capability::environmentalReverb>().maxDecayHfRatioPm,
+                    maxDecayHfRatioPm);
+            minDecayHfRatioPm = std::min(
+                    it.second.capability.get<Capability::environmentalReverb>().minDecayHfRatioPm,
+                    minDecayHfRatioPm);
+        }
+        return {std::numeric_limits<int>::min(),
+                minDecayHfRatioPm - 1,
+                minDecayHfRatioPm,
+                (minDecayHfRatioPm + maxDecayHfRatioPm) >> 1,
+                maxDecayHfRatioPm,
+                maxDecayHfRatioPm + 1,
+                std::numeric_limits<int>::max()};
+    }
+
+    static std::unordered_set<int> getLevelValues() {
+        auto descList = EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor,
+                                                                     kEnvReverbTypeUUID);
+        int minLevelMb = std::numeric_limits<int>::max();
+        int maxLevelMb = std::numeric_limits<int>::min();
+        for (const auto& it : descList) {
+            maxLevelMb =
+                    std::max(it.second.capability.get<Capability::environmentalReverb>().maxLevelMb,
+                             maxLevelMb);
+            minLevelMb =
+                    std::min(it.second.capability.get<Capability::environmentalReverb>().minLevelMb,
+                             minLevelMb);
+        }
+        return {std::numeric_limits<int>::min(), minLevelMb - 1, minLevelMb,
+                (minLevelMb + maxLevelMb) >> 1,  maxLevelMb,     maxLevelMb + 1,
+                std::numeric_limits<int>::max()};
+    }
+
+    static std::unordered_set<int> getDelayValues() {
+        auto descList = EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor,
+                                                                     kEnvReverbTypeUUID);
+        const auto max = std::max_element(
+                descList.begin(), descList.end(),
+                [](const std::pair<std::shared_ptr<IFactory>, Descriptor>& a,
+                   const std::pair<std::shared_ptr<IFactory>, Descriptor>& b) {
+                    return a.second.capability.get<Capability::environmentalReverb>().maxDelayMs <
+                           b.second.capability.get<Capability::environmentalReverb>().maxDelayMs;
+                });
+        if (max == descList.end()) {
+            return {0};
+        }
+        int maxDelayMs = max->second.capability.get<Capability::environmentalReverb>().maxDelayMs;
+        return {-1, 0, maxDelayMs >> 1, maxDelayMs - 1, maxDelayMs, maxDelayMs + 1};
+    }
+
+    static std::unordered_set<int> getDiffusionValues() {
+        auto descList = EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor,
+                                                                     kEnvReverbTypeUUID);
+        const auto max = std::max_element(
+                descList.begin(), descList.end(),
+                [](const std::pair<std::shared_ptr<IFactory>, Descriptor>& a,
+                   const std::pair<std::shared_ptr<IFactory>, Descriptor>& b) {
+                    return a.second.capability.get<Capability::environmentalReverb>()
+                                   .maxDiffusionPm <
+                           b.second.capability.get<Capability::environmentalReverb>()
+                                   .maxDiffusionPm;
+                });
+        if (max == descList.end()) {
+            return {0};
+        }
+        int maxDiffusionPm =
+                max->second.capability.get<Capability::environmentalReverb>().maxDiffusionPm;
+        return {-1, 0, maxDiffusionPm >> 1, maxDiffusionPm - 1, maxDiffusionPm, maxDiffusionPm + 1};
+    }
+
+    static std::unordered_set<int> getDensityValues() {
+        auto descList = EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor,
+                                                                     kEnvReverbTypeUUID);
+        const auto max = std::max_element(
+                descList.begin(), descList.end(),
+                [](const std::pair<std::shared_ptr<IFactory>, Descriptor>& a,
+                   const std::pair<std::shared_ptr<IFactory>, Descriptor>& b) {
+                    return a.second.capability.get<Capability::environmentalReverb>().maxDensityPm <
+                           b.second.capability.get<Capability::environmentalReverb>().maxDensityPm;
+                });
+        if (max == descList.end()) {
+            return {0};
+        }
+        int maxDensityPm =
+                max->second.capability.get<Capability::environmentalReverb>().maxDensityPm;
+        return {-1, 0, maxDensityPm >> 1, maxDensityPm - 1, maxDensityPm, maxDensityPm + 1};
     }
 
   private:
@@ -310,7 +432,7 @@
         EnvironmentalReverbTest, EnvironmentalReverbRoomLevelTest,
         ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
                                    IFactory::descriptor, kEnvReverbTypeUUID)),
-                           testing::ValuesIn(kRoomLevelValues)),
+                           testing::ValuesIn(EnvironmentalReverbHelper::getRoomLevelValues())),
         [](const testing::TestParamInfo<EnvironmentalReverbRoomLevelTest::ParamType>& info) {
             auto descriptor = std::get<0>(info.param).second;
             std::string roomLevel = std::to_string(std::get<1>(info.param));
@@ -347,7 +469,7 @@
         EnvironmentalReverbTest, EnvironmentalReverbRoomHfLevelTest,
         ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
                                    IFactory::descriptor, kEnvReverbTypeUUID)),
-                           testing::ValuesIn(kRoomHfLevelValues)),
+                           testing::ValuesIn(EnvironmentalReverbHelper::getRoomHfLevelValues())),
         [](const testing::TestParamInfo<EnvironmentalReverbRoomHfLevelTest::ParamType>& info) {
             auto descriptor = std::get<0>(info.param).second;
             std::string roomHfLevel = std::to_string(std::get<1>(info.param));
@@ -384,7 +506,7 @@
         EnvironmentalReverbTest, EnvironmentalReverbDecayTimeTest,
         ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
                                    IFactory::descriptor, kEnvReverbTypeUUID)),
-                           testing::ValuesIn(kDecayTimeValues)),
+                           testing::ValuesIn(EnvironmentalReverbHelper::getDecayTimeValues())),
         [](const testing::TestParamInfo<EnvironmentalReverbDecayTimeTest::ParamType>& info) {
             auto descriptor = std::get<0>(info.param).second;
             std::string decayTime = std::to_string(std::get<1>(info.param));
@@ -421,7 +543,7 @@
         EnvironmentalReverbTest, EnvironmentalReverbDecayHfRatioTest,
         ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
                                    IFactory::descriptor, kEnvReverbTypeUUID)),
-                           testing::ValuesIn(kDecayHfRatioValues)),
+                           testing::ValuesIn(EnvironmentalReverbHelper::getDecayHfRatioValues())),
         [](const testing::TestParamInfo<EnvironmentalReverbDecayHfRatioTest::ParamType>& info) {
             auto descriptor = std::get<0>(info.param).second;
             std::string decayHfRatio = std::to_string(std::get<1>(info.param));
@@ -459,7 +581,7 @@
         EnvironmentalReverbTest, EnvironmentalReverbLevelTest,
         ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
                                    IFactory::descriptor, kEnvReverbTypeUUID)),
-                           testing::ValuesIn(kLevelValues)),
+                           testing::ValuesIn(EnvironmentalReverbHelper::getLevelValues())),
         [](const testing::TestParamInfo<EnvironmentalReverbDecayHfRatioTest::ParamType>& info) {
             auto descriptor = std::get<0>(info.param).second;
             std::string level = std::to_string(std::get<1>(info.param));
@@ -496,7 +618,7 @@
         EnvironmentalReverbTest, EnvironmentalReverbDelayTest,
         ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
                                    IFactory::descriptor, kEnvReverbTypeUUID)),
-                           testing::ValuesIn(kDelayValues)),
+                           testing::ValuesIn(EnvironmentalReverbHelper::getDelayValues())),
         [](const testing::TestParamInfo<EnvironmentalReverbDelayTest::ParamType>& info) {
             auto descriptor = std::get<0>(info.param).second;
             std::string delay = std::to_string(std::get<1>(info.param));
@@ -533,7 +655,7 @@
         EnvironmentalReverbTest, EnvironmentalReverbDiffusionTest,
         ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
                                    IFactory::descriptor, kEnvReverbTypeUUID)),
-                           testing::ValuesIn(kDiffusionValues)),
+                           testing::ValuesIn(EnvironmentalReverbHelper::getDiffusionValues())),
         [](const testing::TestParamInfo<EnvironmentalReverbDiffusionTest::ParamType>& info) {
             auto descriptor = std::get<0>(info.param).second;
             std::string diffusion = std::to_string(std::get<1>(info.param));
@@ -570,7 +692,7 @@
         EnvironmentalReverbTest, EnvironmentalReverbDensityTest,
         ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
                                    IFactory::descriptor, kEnvReverbTypeUUID)),
-                           testing::ValuesIn(kDensityValues)),
+                           testing::ValuesIn(EnvironmentalReverbHelper::getDensityValues())),
         [](const testing::TestParamInfo<EnvironmentalReverbDensityTest::ParamType>& info) {
             auto descriptor = std::get<0>(info.param).second;
             std::string density = std::to_string(std::get<1>(info.param));
diff --git a/automotive/can/aidl/default/tools/configurator/canprototools.cpp b/automotive/can/aidl/default/tools/configurator/canprototools.cpp
index 2aea315..84edd94 100644
--- a/automotive/can/aidl/default/tools/configurator/canprototools.cpp
+++ b/automotive/can/aidl/default/tools/configurator/canprototools.cpp
@@ -51,15 +51,20 @@
     return std::string(buff, 0, std::min(n, got));
 }
 
-std::optional<CanBusConfig> parseConfigFile(const std::string& filepath) {
-    std::ifstream cfg_stream(filepath);
-
-    // text headers that would be present in a plaintext proto config file.
+/*
+  parseConfigFile *used to* contain the body of parseConfigStream. However, it seems there's some
+  sort of odd behavior with IstreamInputStream and/or TextFormat::Parse, which causes HW Address
+  Sanitizer to flag a "tag-mismatch" in this function. Having the ifstream defined in a wrapper
+  function seems to solve this problem. The exact cause of this problem is yet unknown, but probably
+  lies somewhere in the protobuf implementation.
+*/
+static __attribute__((noinline)) std::optional<CanBusConfig> parseConfigStream(
+        std::ifstream& cfg_stream) {
     static const std::array<std::string, 3> text_headers = {"buses", "#", "controller"};
     auto cfg_file_snippet = readString(cfg_stream, 10);
 
     if (!cfg_file_snippet.has_value()) {
-        LOG(ERROR) << "Can't open " << filepath << " for reading";
+        LOG(ERROR) << "Can't read config from stream (maybe failed to open file?)";
         return std::nullopt;
     }
     cfg_stream.seekg(0);
@@ -77,16 +82,25 @@
     if (text_format) {
         google::protobuf::io::IstreamInputStream pb_stream(&cfg_stream);
         if (!google::protobuf::TextFormat::Parse(&pb_stream, &config)) {
-            LOG(ERROR) << "Failed to parse (text format) " << filepath;
+            LOG(ERROR) << "Parsing text format config failed";
             return std::nullopt;
         }
     } else if (!config.ParseFromIstream(&cfg_stream)) {
-        LOG(ERROR) << "Failed to parse (binary format) " << filepath;
+        LOG(ERROR) << "Parsing binary format config failed";
         return std::nullopt;
     }
     return config;
 }
 
+std::optional<CanBusConfig> parseConfigFile(const std::string& filepath) {
+    std::ifstream cfg_stream(filepath);
+    auto cfg_maybe = parseConfigStream(cfg_stream);
+    if (!cfg_maybe.has_value()) {
+        LOG(ERROR) << "Failed to parse " << filepath;
+    }
+    return cfg_maybe;
+}
+
 std::optional<BusConfig> fromPbBus(const Bus& pb_bus) {
     BusConfig bus_cfg = {};
     bus_cfg.name = pb_bus.name();
diff --git a/automotive/vehicle/Android.bp b/automotive/vehicle/Android.bp
new file mode 100644
index 0000000..f4a7cd1
--- /dev/null
+++ b/automotive/vehicle/Android.bp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 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_defaults {
+    name: "VehicleHalInterfaceDefaults",
+    static_libs: [
+        "android.hardware.automotive.vehicle-V2-ndk",
+    ],
+}
diff --git a/automotive/vehicle/aidl/impl/Android.bp b/automotive/vehicle/aidl/impl/Android.bp
index 73f7df0..d2c5145 100644
--- a/automotive/vehicle/aidl/impl/Android.bp
+++ b/automotive/vehicle/aidl/impl/Android.bp
@@ -19,13 +19,6 @@
 }
 
 cc_defaults {
-    name: "VehicleHalInterfaceDefaults",
-    static_libs: [
-        "android.hardware.automotive.vehicle-V2-ndk",
-    ],
-}
-
-cc_defaults {
     name: "VehicleHalDefaults",
     static_libs: [
         "android-automotive-large-parcelable-lib",
diff --git a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
index 80c299d..8181769 100644
--- a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
+++ b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
@@ -1744,6 +1744,77 @@
             }
         },
         {
+            "property": "VehicleProperty::HW_KEY_INPUT_V2",
+            "defaultValue": {
+                "int32Values": [
+                    0,
+                    0,
+                    0,
+                    0
+                ],
+                "int64Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT"
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT"
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT"
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT"
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER"
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::HW_MOTION_INPUT",
+            "defaultValue": {
+                "int32Values": [
+                    0,
+                    0,
+                    0,
+                    0,
+                    1,
+                    0,
+                    0
+                ],
+                "floatValues": [
+                    0,
+                    0,
+                    0,
+                    0
+                ],
+                "int64Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT"
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT"
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT"
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT"
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER"
+                }
+            ]
+        },
+        {
             "property": "VehicleProperty::HW_ROTARY_INPUT",
             "defaultValue": {
                 "int32Values": [
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
index e515bad..956c7c0 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
@@ -194,7 +194,7 @@
             const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value) const;
     ValueResultType getEchoReverseBytes(
             const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value) const;
-    bool isHvacPropAndHvacNotAvailable(int32_t propId);
+    bool isHvacPropAndHvacNotAvailable(int32_t propId) const;
 
     std::unordered_map<int32_t, ConfigDeclaration> loadConfigDeclarations();
 
@@ -236,10 +236,19 @@
             const aidl::android::hardware::automotive::vehicle::SetValueRequest& request);
 
     std::string genFakeDataCommand(const std::vector<std::string>& options);
+    void sendHvacPropertiesCurrentValues();
 
     static aidl::android::hardware::automotive::vehicle::VehiclePropValue createHwInputKeyProp(
             aidl::android::hardware::automotive::vehicle::VehicleHwKeyInputAction action,
             int32_t keyCode, int32_t targetDisplay);
+    static aidl::android::hardware::automotive::vehicle::VehiclePropValue createHwKeyInputV2Prop(
+            int32_t area, int32_t targetDisplay, int32_t keyCode, int32_t action,
+            int32_t repeatCount);
+    static aidl::android::hardware::automotive::vehicle::VehiclePropValue createHwMotionInputProp(
+            int32_t area, int32_t display, int32_t inputType, int32_t action, int32_t buttonState,
+            int32_t pointerCount, int32_t pointerId[], int32_t toolType[], float xData[],
+            float yData[], float pressure[], float size[]);
+
     static std::string genFakeDataHelp();
     static std::string parseErrMsg(std::string fieldName, std::string value, std::string type);
 };
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 dd76524..a9dced9 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
@@ -295,7 +295,7 @@
     return {};
 }
 
-bool FakeVehicleHardware::isHvacPropAndHvacNotAvailable(int32_t propId) {
+bool FakeVehicleHardware::isHvacPropAndHvacNotAvailable(int32_t propId) const {
     std::unordered_set<int32_t> powerProps(std::begin(HVAC_POWER_PROPERTIES),
                                            std::end(HVAC_POWER_PROPERTIES));
     if (powerProps.count(propId)) {
@@ -366,6 +366,11 @@
         return getUserHalProp(value);
     }
 
+    if (isHvacPropAndHvacNotAvailable(propId)) {
+        *isSpecialValue = true;
+        return StatusError(StatusCode::NOT_AVAILABLE) << "hvac not available";
+    }
+
     switch (propId) {
         case OBD2_FREEZE_FRAME:
             *isSpecialValue = true;
@@ -408,6 +413,27 @@
     return std::move(gotValue);
 }
 
+void FakeVehicleHardware::sendHvacPropertiesCurrentValues() {
+    for (size_t i = 0; i < sizeof(HVAC_POWER_PROPERTIES) / sizeof(int32_t); i++) {
+        int powerPropId = HVAC_POWER_PROPERTIES[i];
+        auto powerPropResults = mServerSidePropStore->readValuesForProperty(powerPropId);
+        if (!powerPropResults.ok()) {
+            ALOGW("failed to get power prop 0x%x, error: %s", powerPropId,
+                  getErrorMsg(powerPropResults).c_str());
+            continue;
+        }
+        auto& powerPropValues = powerPropResults.value();
+        for (size_t j = 0; j < powerPropValues.size(); j++) {
+            auto powerPropValue = std::move(powerPropValues[j]);
+            powerPropValue->status = VehiclePropertyStatus::AVAILABLE;
+            powerPropValue->timestamp = elapsedRealtimeNano();
+            // This will trigger a property change event for the current hvac property value.
+            mServerSidePropStore->writeValue(std::move(powerPropValue), /*updateStatus=*/true,
+                                             VehiclePropertyStore::EventMode::ALWAYS);
+        }
+    }
+}
+
 VhalResult<void> FakeVehicleHardware::maybeSetSpecialValue(const VehiclePropValue& value,
                                                            bool* isSpecialValue) {
     *isSpecialValue = false;
@@ -419,6 +445,13 @@
         return setUserHalProp(value);
     }
 
+    if (propId == toInt(VehicleProperty::HVAC_POWER_ON) && value.value.int32Values.size() == 1 &&
+        value.value.int32Values[0] == 1) {
+        // If we are turning HVAC power on, send current hvac property values through on change
+        // event.
+        sendHvacPropertiesCurrentValues();
+    }
+
     if (isHvacPropAndHvacNotAvailable(propId)) {
         *isSpecialValue = true;
         return StatusError(StatusCode::NOT_AVAILABLE) << "hvac not available";
@@ -672,6 +705,14 @@
 
 --genfakedata --keypress [keyCode(int32)] [display[int32]]: Generate key press.
 
+--genfakedata --keyinputv2 [area(int32)] [display(int32)] [keyCode[int32]] [action[int32]]
+  [repeatCount(int32)]
+
+--genfakedata --motioninput [area(int32)] [display(int32)] [inputType[int32]] [action[int32]]
+  [buttonState(int32)] --pointer [pointerId(int32)] [toolType(int32)] [xData(float)] [yData(float)]
+  [pressure(float)] [size(float)]
+  Generate a motion input event. --pointer option can be specified multiple times.
+
 )";
 }
 
@@ -802,6 +843,135 @@
         onValueChangeCallback(
                 createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_UP, keyCode, display));
         return "keypress event generated successfully";
+    } else if (command == "--keyinputv2") {
+        int32_t area;
+        int32_t display;
+        int32_t keyCode;
+        int32_t action;
+        int32_t repeatCount;
+        // --genfakedata --keyinputv2 [area(int32)] [display(int32)] [keyCode[int32]]
+        // [action[int32]] [repeatCount(int32)]
+        if (options.size() != 7) {
+            return "incorrect argument count, need 7 arguments for --genfakedata --keyinputv2\n";
+        }
+        if (!android::base::ParseInt(options[2], &area)) {
+            return parseErrMsg("area", options[2], "int");
+        }
+        if (!android::base::ParseInt(options[3], &display)) {
+            return parseErrMsg("display", options[3], "int");
+        }
+        if (!android::base::ParseInt(options[4], &keyCode)) {
+            return parseErrMsg("keyCode", options[4], "int");
+        }
+        if (!android::base::ParseInt(options[5], &action)) {
+            return parseErrMsg("action", options[5], "int");
+        }
+        if (!android::base::ParseInt(options[6], &repeatCount)) {
+            return parseErrMsg("repeatCount", options[6], "int");
+        }
+        // Send back to HAL
+        onValueChangeCallback(createHwKeyInputV2Prop(area, display, keyCode, action, repeatCount));
+        return StringPrintf(
+                "keyinputv2 event generated successfully with area:%d, display:%d,"
+                " keyCode:%d, action:%d, repeatCount:%d",
+                area, display, keyCode, action, repeatCount);
+
+    } else if (command == "--motioninput") {
+        int32_t area;
+        int32_t display;
+        int32_t inputType;
+        int32_t action;
+        int32_t buttonState;
+        int32_t pointerCount;
+
+        // --genfakedata --motioninput [area(int32)] [display(int32)] [inputType[int32]]
+        // [action[int32]] [buttonState(int32)] [pointerCount(int32)]
+        // --pointer [pointerId(int32)] [toolType(int32)] [xData(float)] [yData(float)]
+        // [pressure(float)] [size(float)]
+        int optionsSize = (int)options.size();
+        if (optionsSize / 7 < 2) {
+            return "incorrect argument count, need at least 14 arguments for --genfakedata "
+                   "--motioninput including at least 1 --pointer\n";
+        }
+
+        if (optionsSize % 7 != 0) {
+            return "incorrect argument count, need 6 arguments for every --pointer\n";
+        }
+        pointerCount = (int)optionsSize / 7 - 1;
+
+        if (!android::base::ParseInt(options[2], &area)) {
+            return parseErrMsg("area", options[2], "int");
+        }
+        if (!android::base::ParseInt(options[3], &display)) {
+            return parseErrMsg("display", options[3], "int");
+        }
+        if (!android::base::ParseInt(options[4], &inputType)) {
+            return parseErrMsg("inputType", options[4], "int");
+        }
+        if (!android::base::ParseInt(options[5], &action)) {
+            return parseErrMsg("action", options[5], "int");
+        }
+        if (!android::base::ParseInt(options[6], &buttonState)) {
+            return parseErrMsg("buttonState", options[6], "int");
+        }
+
+        int32_t pointerId[pointerCount];
+        int32_t toolType[pointerCount];
+        float xData[pointerCount];
+        float yData[pointerCount];
+        float pressure[pointerCount];
+        float size[pointerCount];
+
+        for (int i = 7, pc = 0; i < optionsSize; i += 7, pc += 1) {
+            int offset = i;
+            if (options[offset] != "--pointer") {
+                return "--pointer is needed for the motion input\n";
+            }
+            offset += 1;
+            if (!android::base::ParseInt(options[offset], &pointerId[pc])) {
+                return parseErrMsg("pointerId", options[offset], "int");
+            }
+            offset += 1;
+            if (!android::base::ParseInt(options[offset], &toolType[pc])) {
+                return parseErrMsg("toolType", options[offset], "int");
+            }
+            offset += 1;
+            if (!android::base::ParseFloat(options[offset], &xData[pc])) {
+                return parseErrMsg("xData", options[offset], "float");
+            }
+            offset += 1;
+            if (!android::base::ParseFloat(options[offset], &yData[pc])) {
+                return parseErrMsg("yData", options[offset], "float");
+            }
+            offset += 1;
+            if (!android::base::ParseFloat(options[offset], &pressure[pc])) {
+                return parseErrMsg("pressure", options[offset], "float");
+            }
+            offset += 1;
+            if (!android::base::ParseFloat(options[offset], &size[pc])) {
+                return parseErrMsg("size", options[offset], "float");
+            }
+        }
+
+        // Send back to HAL
+        onValueChangeCallback(createHwMotionInputProp(area, display, inputType, action, buttonState,
+                                                      pointerCount, pointerId, toolType, xData,
+                                                      yData, pressure, size));
+
+        std::string successMessage = StringPrintf(
+                "motion event generated successfully with area:%d, display:%d,"
+                " inputType:%d, action:%d, buttonState:%d, pointerCount:%d\n",
+                area, display, inputType, action, buttonState, pointerCount);
+        for (int i = 0; i < pointerCount; i++) {
+            successMessage += StringPrintf(
+                    "Pointer #%d {\n"
+                    " id:%d , tooltype:%d \n"
+                    " x:%f , y:%f\n"
+                    " pressure: %f, data: %f\n"
+                    "}\n",
+                    i, pointerId[i], toolType[i], xData[i], yData[i], pressure[i], size[i]);
+        }
+        return successMessage;
     }
 
     return StringPrintf("Unknown command: \"%s\"\n%s", command.c_str(), genFakeDataHelp().c_str());
@@ -819,6 +989,59 @@
     return value;
 }
 
+VehiclePropValue FakeVehicleHardware::createHwKeyInputV2Prop(int32_t area, int32_t targetDisplay,
+                                                             int32_t keyCode, int32_t action,
+                                                             int32_t repeatCount) {
+    VehiclePropValue value = {.prop = toInt(VehicleProperty::HW_KEY_INPUT_V2),
+                              .areaId = area,
+                              .timestamp = elapsedRealtimeNano(),
+                              .status = VehiclePropertyStatus::AVAILABLE,
+                              .value.int32Values = {targetDisplay, keyCode, action, repeatCount},
+                              .value.int64Values = {elapsedRealtimeNano()}};
+    return value;
+}
+
+VehiclePropValue FakeVehicleHardware::createHwMotionInputProp(
+        int32_t area, int32_t display, int32_t inputType, int32_t action, int32_t buttonState,
+        int32_t pointerCount, int32_t pointerId[], int32_t toolType[], float xData[], float yData[],
+        float pressure[], float size[]) {
+    std::vector<int> intValues;
+    intValues.push_back(display);
+    intValues.push_back(inputType);
+    intValues.push_back(action);
+    intValues.push_back(buttonState);
+    intValues.push_back(pointerCount);
+    for (int i = 0; i < pointerCount; i++) {
+        intValues.push_back(pointerId[i]);
+    }
+    for (int i = 0; i < pointerCount; i++) {
+        intValues.push_back(toolType[i]);
+    }
+
+    std::vector<float> floatValues;
+    for (int i = 0; i < pointerCount; i++) {
+        floatValues.push_back(xData[i]);
+    }
+    for (int i = 0; i < pointerCount; i++) {
+        floatValues.push_back(yData[i]);
+    }
+    for (int i = 0; i < pointerCount; i++) {
+        floatValues.push_back(pressure[i]);
+    }
+    for (int i = 0; i < pointerCount; i++) {
+        floatValues.push_back(size[i]);
+    }
+
+    VehiclePropValue value = {.prop = toInt(VehicleProperty::HW_MOTION_INPUT),
+                              .areaId = area,
+                              .timestamp = elapsedRealtimeNano(),
+                              .status = VehiclePropertyStatus::AVAILABLE,
+                              .value.int32Values = intValues,
+                              .value.floatValues = floatValues,
+                              .value.int64Values = {elapsedRealtimeNano()}};
+    return value;
+}
+
 void FakeVehicleHardware::eventFromVehicleBus(const VehiclePropValue& value) {
     mServerSidePropStore->writeValue(mValuePool->obtain(value));
 }
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 0184462..67b1aa4 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
@@ -61,6 +61,7 @@
 using ::android::base::ScopedLockAssertion;
 using ::android::base::StringPrintf;
 using ::android::base::unexpected;
+using ::testing::AnyOfArray;
 using ::testing::ContainerEq;
 using ::testing::ContainsRegex;
 using ::testing::Eq;
@@ -1140,6 +1141,71 @@
     EXPECT_EQ(getValueResult.error(), StatusCode::NOT_AVAILABLE);
 }
 
+TEST_F(FakeVehicleHardwareTest, testGetHvacPropNotAvailable) {
+    StatusCode status = setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_POWER_ON),
+                                                  .areaId = HVAC_ALL,
+                                                  .value.int32Values = {0}});
+
+    ASSERT_EQ(status, StatusCode::OK);
+
+    for (size_t i = 0; i < sizeof(HVAC_POWER_PROPERTIES) / sizeof(int32_t); i++) {
+        int powerPropId = HVAC_POWER_PROPERTIES[i];
+        auto getValueResult = getValue(VehiclePropValue{
+                .prop = powerPropId,
+                .areaId = HVAC_ALL,
+        });
+
+        EXPECT_FALSE(getValueResult.ok());
+        EXPECT_EQ(getValueResult.error(), StatusCode::NOT_AVAILABLE);
+    }
+}
+
+TEST_F(FakeVehicleHardwareTest, testSetHvacPropNotAvailable) {
+    StatusCode status = setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_POWER_ON),
+                                                  .areaId = HVAC_ALL,
+                                                  .value.int32Values = {0}});
+
+    ASSERT_EQ(status, StatusCode::OK);
+
+    for (size_t i = 0; i < sizeof(HVAC_POWER_PROPERTIES) / sizeof(int32_t); i++) {
+        int powerPropId = HVAC_POWER_PROPERTIES[i];
+        status = setValue(VehiclePropValue{
+                .prop = powerPropId,
+                .areaId = HVAC_ALL,
+        });
+
+        EXPECT_EQ(status, StatusCode::NOT_AVAILABLE);
+    }
+}
+
+TEST_F(FakeVehicleHardwareTest, testHvacPowerOnSendCurrentHvacPropValues) {
+    StatusCode status = setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_POWER_ON),
+                                                  .areaId = HVAC_ALL,
+                                                  .value.int32Values = {0}});
+
+    ASSERT_EQ(status, StatusCode::OK);
+
+    clearChangedProperties();
+
+    status = setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_POWER_ON),
+                                       .areaId = HVAC_ALL,
+                                       .value.int32Values = {1}});
+
+    auto events = getChangedProperties();
+    // If we turn HVAC power on, we expect to receive one property event for every HVAC prop areas
+    // plus one event for HVAC_POWER_ON.
+    std::vector<int32_t> changedPropIds;
+    for (size_t i = 0; i < sizeof(HVAC_POWER_PROPERTIES) / sizeof(int32_t); i++) {
+        changedPropIds.push_back(HVAC_POWER_PROPERTIES[i]);
+    }
+    changedPropIds.push_back(toInt(VehicleProperty::HVAC_POWER_ON));
+    ASSERT_EQ(events.size(), changedPropIds.size());
+    for (const auto& event : events) {
+        EXPECT_EQ(event.areaId, HVAC_ALL);
+        EXPECT_THAT(event.prop, AnyOfArray(changedPropIds));
+    }
+}
+
 TEST_F(FakeVehicleHardwareTest, testGetUserPropertySetOnly) {
     for (VehicleProperty prop : std::vector<VehicleProperty>({
                  VehicleProperty::INITIAL_USER_INFO,
@@ -1776,7 +1842,61 @@
              "failed to parse keyCode as int: \"0.1\""},
             {"genfakedata_keypress_invalid_display",
              {"--genfakedata", "--keypress", "1", "0.1"},
-             "failed to parse display as int: \"0.1\""}};
+             "failed to parse display as int: \"0.1\""},
+            {"genfakedata_keyinputv2_incorrect_arguments",
+             {"--genfakedata", "--keyinputv2", "1", "1"},
+             "incorrect argument count, need 7 arguments for --genfakedata --keyinputv2\n"},
+            {"genfakedata_keyinputv2_invalid_area",
+             {"--genfakedata", "--keyinputv2", "0.1", "1", "1", "1", "1"},
+             "failed to parse area as int: \"0.1\""},
+            {"genfakedata_keyinputv2_invalid_display",
+             {"--genfakedata", "--keyinputv2", "1", "0.1", "1", "1", "1"},
+             "failed to parse display as int: \"0.1\""},
+            {"genfakedata_keyinputv2_invalid_keycode",
+             {"--genfakedata", "--keyinputv2", "1", "1", "0.1", "1", "1"},
+             "failed to parse keyCode as int: \"0.1\""},
+            {"genfakedata_keyinputv2_invalid_action",
+             {"--genfakedata", "--keyinputv2", "1", "1", "1", "0.1", "1"},
+             "failed to parse action as int: \"0.1\""},
+            {"genfakedata_keyinputv2_invalid_repeatcount",
+             {"--genfakedata", "--keyinputv2", "1", "1", "1", "1", "0.1"},
+             "failed to parse repeatCount as int: \"0.1\""},
+            {"genfakedata_motioninput_invalid_argument_count",
+             {"--genfakedata", "--motioninput", "1", "1", "1", "1", "1"},
+             "incorrect argument count, need at least 14 arguments for --genfakedata "
+             "--motioninput including at least 1 --pointer\n"},
+            {"genfakedata_motioninput_pointer_invalid_argument_count",
+             {"--genfakedata", "--motioninput", "1", "1", "1", "1", "1", "--pointer", "1", "1", "1",
+              "1", "1", "1", "--pointer"},
+             "incorrect argument count, need 6 arguments for every --pointer\n"},
+            {"genfakedata_motioninput_invalid_area",
+             {"--genfakedata", "--motioninput", "0.1", "1", "1", "1", "1", "--pointer", "1", "1",
+              "1", "1", "1", "1"},
+             "failed to parse area as int: \"0.1\""},
+            {"genfakedata_motioninput_invalid_display",
+             {"--genfakedata", "--motioninput", "1", "0.1", "1", "1", "1", "--pointer", "1", "1",
+              "1", "1", "1", "1"},
+             "failed to parse display as int: \"0.1\""},
+            {"genfakedata_motioninput_invalid_inputtype",
+             {"--genfakedata", "--motioninput", "1", "1", "0.1", "1", "1", "--pointer", "1", "1",
+              "1", "1", "1", "1"},
+             "failed to parse inputType as int: \"0.1\""},
+            {"genfakedata_motioninput_invalid_action",
+             {"--genfakedata", "--motioninput", "1", "1", "1", "0.1", "1", "--pointer", "1", "1",
+              "1", "1", "1", "1"},
+             "failed to parse action as int: \"0.1\""},
+            {"genfakedata_motioninput_invalid_buttonstate",
+             {"--genfakedata", "--motioninput", "1", "1", "1", "1", "0.1", "--pointer", "1", "1",
+              "1.2", "1.2", "1.2", "1.2"},
+             "failed to parse buttonState as int: \"0.1\""},
+            {"genfakedata_motioninput_invalid_pointerid",
+             {"--genfakedata", "--motioninput", "1", "1", "1", "1", "1", "--pointer", "0.1", "1",
+              "1.2", "1", "1", "1"},
+             "failed to parse pointerId as int: \"0.1\""},
+            {"genfakedata_motioninput_invalid_tooltype",
+             {"--genfakedata", "--motioninput", "1", "1", "1", "1", "1", "--pointer", "1", "0.1",
+              "1.2", "1", "1", "1"},
+             "failed to parse toolType as int: \"0.1\""}};
 }
 
 TEST_P(FakeVehicleHardwareOptionsTest, testInvalidOptions) {
@@ -1965,6 +2085,78 @@
     EXPECT_EQ(2, events[1].value.int32Values[2]);
 }
 
+TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataKeyInputV2) {
+    std::vector<std::string> options = {"--genfakedata", "--keyinputv2", "1", "2", "3", "4", "5"};
+
+    DumpResult result = getHardware()->dump(options);
+
+    ASSERT_FALSE(result.callerShouldDumpState);
+    ASSERT_THAT(result.buffer, HasSubstr("successfully"));
+
+    auto events = getChangedProperties();
+    ASSERT_EQ(1u, events.size());
+    EXPECT_EQ(toInt(VehicleProperty::HW_KEY_INPUT_V2), events[0].prop);
+    ASSERT_EQ(4u, events[0].value.int32Values.size());
+    EXPECT_EQ(2, events[0].value.int32Values[0]);
+    EXPECT_EQ(3, events[0].value.int32Values[1]);
+    EXPECT_EQ(4, events[0].value.int32Values[2]);
+    EXPECT_EQ(5, events[0].value.int32Values[3]);
+    ASSERT_EQ(1u, events[0].value.int64Values.size());
+}
+
+TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataMotionInput) {
+    std::vector<std::string> options = {"--genfakedata",
+                                        "--motioninput",
+                                        "1",
+                                        "2",
+                                        "3",
+                                        "4",
+                                        "5",
+                                        "--pointer",
+                                        "11",
+                                        "22",
+                                        "33.3",
+                                        "44.4",
+                                        "55.5",
+                                        "66.6",
+                                        "--pointer",
+                                        "21",
+                                        "32",
+                                        "43.3",
+                                        "54.4",
+                                        "65.5",
+                                        "76.6"};
+
+    DumpResult result = getHardware()->dump(options);
+
+    ASSERT_FALSE(result.callerShouldDumpState);
+    ASSERT_THAT(result.buffer, HasSubstr("successfully"));
+
+    auto events = getChangedProperties();
+    ASSERT_EQ(1u, events.size());
+    EXPECT_EQ(toInt(VehicleProperty::HW_MOTION_INPUT), events[0].prop);
+    ASSERT_EQ(9u, events[0].value.int32Values.size());
+    EXPECT_EQ(2, events[0].value.int32Values[0]);
+    EXPECT_EQ(3, events[0].value.int32Values[1]);
+    EXPECT_EQ(4, events[0].value.int32Values[2]);
+    EXPECT_EQ(5, events[0].value.int32Values[3]);
+    EXPECT_EQ(2, events[0].value.int32Values[4]);
+    EXPECT_EQ(11, events[0].value.int32Values[5]);
+    EXPECT_EQ(21, events[0].value.int32Values[6]);
+    EXPECT_EQ(22, events[0].value.int32Values[7]);
+    EXPECT_EQ(32, events[0].value.int32Values[8]);
+    ASSERT_EQ(8u, events[0].value.floatValues.size());
+    EXPECT_FLOAT_EQ(33.3, events[0].value.floatValues[0]);
+    EXPECT_FLOAT_EQ(43.3, events[0].value.floatValues[1]);
+    EXPECT_FLOAT_EQ(44.4, events[0].value.floatValues[2]);
+    EXPECT_FLOAT_EQ(54.4, events[0].value.floatValues[3]);
+    EXPECT_FLOAT_EQ(55.5, events[0].value.floatValues[4]);
+    EXPECT_FLOAT_EQ(65.5, events[0].value.floatValues[5]);
+    EXPECT_FLOAT_EQ(66.6, events[0].value.floatValues[6]);
+    EXPECT_FLOAT_EQ(76.6, events[0].value.floatValues[7]);
+    ASSERT_EQ(1u, events[0].value.int64Values.size());
+}
+
 TEST_F(FakeVehicleHardwareTest, testGetEchoReverseBytes) {
     ASSERT_EQ(setValue(VehiclePropValue{
                       .prop = ECHO_REVERSE_BYTES,
diff --git a/automotive/vehicle/aidl/impl/vhal/src/VehicleService.cpp b/automotive/vehicle/aidl/impl/vhal/src/VehicleService.cpp
index c8b5c65..f8a4e7d 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/VehicleService.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/VehicleService.cpp
@@ -27,6 +27,13 @@
 using ::android::hardware::automotive::vehicle::fake::FakeVehicleHardware;
 
 int main(int /* argc */, char* /* argv */[]) {
+    ALOGI("Starting thread pool...");
+    if (!ABinderProcess_setThreadPoolMaxThreadCount(4)) {
+        ALOGE("%s", "failed to set thread pool max thread count");
+        return 1;
+    }
+    ABinderProcess_startThreadPool();
+
     std::unique_ptr<FakeVehicleHardware> hardware = std::make_unique<FakeVehicleHardware>();
     std::shared_ptr<DefaultVehicleHal> vhal =
             ::ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
@@ -39,12 +46,6 @@
         return 1;
     }
 
-    if (!ABinderProcess_setThreadPoolMaxThreadCount(4)) {
-        ALOGE("%s", "failed to set thread pool max thread count");
-        return 1;
-    }
-    ABinderProcess_startThreadPool();
-
     ALOGI("Vehicle Service Ready");
 
     ABinderProcess_joinThreadPool();
diff --git a/automotive/vehicle/vhal_static_cpp_lib.mk b/automotive/vehicle/vhal_static_cpp_lib.mk
new file mode 100644
index 0000000..b15b26c
--- /dev/null
+++ b/automotive/vehicle/vhal_static_cpp_lib.mk
@@ -0,0 +1,18 @@
+# Copyright (C) 2023 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 make file contains the latest version of static cpp libraries for VHAL
+# interface and VHAL properties.
+
+LOCAL_STATIC_LIBRARIES += android.hardware.automotive.vehicle-V2-ndk
diff --git a/camera/device/3.2/default/convert.cpp b/camera/device/3.2/default/convert.cpp
index 06ad7e9..2075607 100644
--- a/camera/device/3.2/default/convert.cpp
+++ b/camera/device/3.2/default/convert.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "android.hardware.camera.device@3.2-convert-impl"
 #include <log/log.h>
+#include <system/camera_metadata.h>
 
 #include "include/convert.h"
 
@@ -43,6 +44,13 @@
         ALOGE("%s: input CameraMetadata is corrupt!", __FUNCTION__);
         return false;
     }
+
+    if (validate_camera_metadata_structure((camera_metadata_t*)data, /*expected_size=*/NULL) !=
+        OK) {
+        ALOGE("%s: Failed to validate the metadata structure", __FUNCTION__);
+        return false;
+    }
+
     *dst = (camera_metadata_t*) data;
     return true;
 }
diff --git a/camera/provider/aidl/vts/camera_aidl_test.h b/camera/provider/aidl/vts/camera_aidl_test.h
index 8b8a4bc..f13d6b2 100644
--- a/camera/provider/aidl/vts/camera_aidl_test.h
+++ b/camera/provider/aidl/vts/camera_aidl_test.h
@@ -17,7 +17,10 @@
 #ifndef HARDWARE_INTERFACES_CAMERA_PROVIDER_AIDL_VTS_CAMERA_AIDL_TEST_H_
 #define HARDWARE_INTERFACES_CAMERA_PROVIDER_AIDL_VTS_CAMERA_AIDL_TEST_H_
 
+// TODO: LOG_TAG should not be in header
+#ifndef LOG_TAG
 #define LOG_TAG "camera_aidl_hal_test"
+#endif
 
 #include <string>
 #include <unordered_map>
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 9ccb6ec..1403ec4 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -1,12 +1,4 @@
 <compatibility-matrix version="1.0" type="framework" level="8">
-    <hal format="hidl" optional="true">
-        <name>android.hardware.atrace</name>
-        <version>1.0</version>
-        <interface>
-            <name>IAtraceDevice</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
     <hal format="hidl" optional="false">
         <name>android.hardware.audio</name>
         <version>6.0</version>
@@ -307,6 +299,7 @@
             <instance>default</instance>
         </interface>
     </hal>
+    <!-- Either the native or the HIDL mapper HAL must exist on the device -->
     <hal format="hidl" optional="true">
         <name>android.hardware.graphics.mapper</name>
         <!-- New, non-Go devices should use 4.0, tested in vts_treble_vintf_vendor_test -->
@@ -609,6 +602,14 @@
             <instance>default</instance>
         </interface>
     </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.soundtrigger</name>
+        <version>2.3</version>
+        <interface>
+            <name>ISoundTriggerHw</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
     <hal format="aidl" optional="true">
          <name>android.hardware.soundtrigger3</name>
          <version>1</version>
@@ -765,9 +766,10 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="native">
+    <!-- Either the native or the HIDL mapper HAL must exist on the device -->
+    <hal format="native" optional="true">
         <name>mapper</name>
-        <version>1.0</version>
+        <version>5.0</version>
         <interface>
             <name>I</name>
             <regex-instance>.*</regex-instance>
diff --git a/contexthub/1.0/default/OWNERS b/contexthub/1.0/default/OWNERS
deleted file mode 100644
index 90c2330..0000000
--- a/contexthub/1.0/default/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-arthuri@google.com
-bduddie@google.com
-stange@google.com
diff --git a/contexthub/1.1/default/OWNERS b/contexthub/1.1/default/OWNERS
deleted file mode 100644
index 90c2330..0000000
--- a/contexthub/1.1/default/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-arthuri@google.com
-bduddie@google.com
-stange@google.com
diff --git a/contexthub/1.1/vts/functional/OWNERS b/contexthub/1.1/vts/functional/OWNERS
deleted file mode 100644
index 2cf5bca..0000000
--- a/contexthub/1.1/vts/functional/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 156070
-include ../../../1.0/vts/functional/OWNERS
diff --git a/contexthub/1.2/default/OWNERS b/contexthub/1.2/default/OWNERS
deleted file mode 100644
index 90c2330..0000000
--- a/contexthub/1.2/default/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-arthuri@google.com
-bduddie@google.com
-stange@google.com
diff --git a/contexthub/1.2/vts/functional/OWNERS b/contexthub/1.2/vts/functional/OWNERS
deleted file mode 100644
index 2cf5bca..0000000
--- a/contexthub/1.2/vts/functional/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 156070
-include ../../../1.0/vts/functional/OWNERS
diff --git a/contexthub/1.0/vts/functional/OWNERS b/contexthub/OWNERS
similarity index 81%
rename from contexthub/1.0/vts/functional/OWNERS
rename to contexthub/OWNERS
index f254cd5..d5cfc2e 100644
--- a/contexthub/1.0/vts/functional/OWNERS
+++ b/contexthub/OWNERS
@@ -1,5 +1,4 @@
 # Bug component: 156070
-#Context Hub team
 arthuri@google.com
 bduddie@google.com
 stange@google.com
diff --git a/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IContextHub.aidl b/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IContextHub.aidl
index 272d768..c1f4df8 100644
--- a/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IContextHub.aidl
+++ b/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IContextHub.aidl
@@ -46,5 +46,6 @@
   void onHostEndpointConnected(in android.hardware.contexthub.HostEndpointInfo hostEndpointInfo);
   void onHostEndpointDisconnected(char hostEndpointId);
   long[] getPreloadedNanoappIds();
+  void onNanSessionStateChanged(in boolean state);
   const int EX_CONTEXT_HUB_UNSPECIFIED = -1;
 }
diff --git a/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IContextHubCallback.aidl b/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IContextHubCallback.aidl
index f81f7cf..e72ae73 100644
--- a/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IContextHubCallback.aidl
+++ b/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IContextHubCallback.aidl
@@ -38,4 +38,6 @@
   void handleContextHubMessage(in android.hardware.contexthub.ContextHubMessage msg, in String[] msgContentPerms);
   void handleContextHubAsyncEvent(in android.hardware.contexthub.AsyncEventType evt);
   void handleTransactionResult(in int transactionId, in boolean success);
+  void handleNanSessionRequest(in boolean enable);
+  const int CONTEXTHUB_NAN_TRANSACTION_TIMEOUT_MS = 10000;
 }
diff --git a/contexthub/aidl/android/hardware/contexthub/IContextHub.aidl b/contexthub/aidl/android/hardware/contexthub/IContextHub.aidl
index 9fa67a5..7f50730 100644
--- a/contexthub/aidl/android/hardware/contexthub/IContextHub.aidl
+++ b/contexthub/aidl/android/hardware/contexthub/IContextHub.aidl
@@ -204,6 +204,19 @@
     long[] getPreloadedNanoappIds();
 
     /**
+     * Invoked when the state of the NAN session requested through handleNanSessionRequest()
+     * changes. This function may be invoked without a corresponding handleNanSessionRequest to
+     * indicate if a NAN session was terminated without a request due to resource limitations.
+     *
+     * If the state becomes disabled without an explicit request from the HAL, the HAL MUST
+     * explicitly invoke handleNanSessionRequest() at a later point in time to attempt to
+     * re-enable NAN.
+     *
+     * @param state True if the NAN session is currently enabled.
+     */
+    void onNanSessionStateChanged(in boolean state);
+
+    /**
      * Error codes that are used as service specific errors with the AIDL return
      * value EX_SERVICE_SPECIFIC.
      */
diff --git a/contexthub/aidl/android/hardware/contexthub/IContextHubCallback.aidl b/contexthub/aidl/android/hardware/contexthub/IContextHubCallback.aidl
index e385d48..cff1893 100644
--- a/contexthub/aidl/android/hardware/contexthub/IContextHubCallback.aidl
+++ b/contexthub/aidl/android/hardware/contexthub/IContextHubCallback.aidl
@@ -74,4 +74,25 @@
      *
      */
     void handleTransactionResult(in int transactionId, in boolean success);
+
+    /**
+     * This callback is passed by the Contexthub service to the HAL implementation to allow the HAL
+     * to request a WiFi NAN session is created to allow the Contexthub to be able to utilize NAN
+     * functionality.
+     *
+     * onNanSessionStateChanged() will be invoked asynchronously after the NAN session request has
+     * been completed. This must be done within CONTEXTHUB_NAN_TRANSACTION_TIMEOUT_MS. If the
+     * request times out, onNanSessionStateChanged() will be invoked with the state that the session
+     * was previously in.
+     *
+     * @param enable Whether the NAN session should be enabled or disabled depending on whether the
+     *        Contexthub needs access to NAN.
+     */
+    void handleNanSessionRequest(in boolean enable);
+
+    /**
+     * Amount of time, in milliseconds, that a handleNanSessionRequest can be pending before the
+     * Contexthub service must respond.
+     */
+    const int CONTEXTHUB_NAN_TRANSACTION_TIMEOUT_MS = 10000;
 }
diff --git a/contexthub/aidl/default/ContextHub.cpp b/contexthub/aidl/default/ContextHub.cpp
index ac1dc46..615ac5c 100644
--- a/contexthub/aidl/default/ContextHub.cpp
+++ b/contexthub/aidl/default/ContextHub.cpp
@@ -87,6 +87,10 @@
     return ndk::ScopedAStatus::ok();
 }
 
+ScopedAStatus ContextHub::onNanSessionStateChanged(bool /*sin_state*/) {
+    return ndk::ScopedAStatus::ok();
+}
+
 ScopedAStatus ContextHub::registerCallback(int32_t in_contextHubId,
                                            const std::shared_ptr<IContextHubCallback>& in_cb) {
     if (in_contextHubId == kMockHubId) {
diff --git a/contexthub/aidl/default/include/contexthub-impl/ContextHub.h b/contexthub/aidl/default/include/contexthub-impl/ContextHub.h
index 4aeb948..b3998b9 100644
--- a/contexthub/aidl/default/include/contexthub-impl/ContextHub.h
+++ b/contexthub/aidl/default/include/contexthub-impl/ContextHub.h
@@ -47,6 +47,7 @@
     ::ndk::ScopedAStatus onHostEndpointConnected(const HostEndpointInfo& in_info) override;
 
     ::ndk::ScopedAStatus onHostEndpointDisconnected(char16_t in_hostEndpointId) override;
+    ::ndk::ScopedAStatus onNanSessionStateChanged(bool in_state) override;
 
   private:
     static constexpr uint32_t kMockHubId = 0;
diff --git a/contexthub/aidl/vts/OWNERS b/contexthub/aidl/vts/OWNERS
deleted file mode 100644
index 150818d..0000000
--- a/contexthub/aidl/vts/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file:/contexthub/common/vts/OWNERS
diff --git a/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp b/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp
index 8104f27..4731648 100644
--- a/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp
+++ b/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp
@@ -100,6 +100,8 @@
     Status handleTransactionResult(int32_t /* transactionId */, bool /* success */) override {
         return Status::ok();
     }
+
+    Status handleNanSessionRequest(bool /* enable */) override { return Status::ok(); }
 };
 
 TEST_P(ContextHubAidl, TestRegisterCallback) {
@@ -131,6 +133,8 @@
         return Status::ok();
     }
 
+    Status handleNanSessionRequest(bool /* enable */) override { return Status::ok(); }
+
     std::promise<std::vector<NanoappInfo>> promise;
 };
 
@@ -194,6 +198,8 @@
         return Status::ok();
     }
 
+    Status handleNanSessionRequest(bool /* enable */) override { return Status::ok(); }
+
     uint32_t expectedTransactionId = 0;
     std::promise<bool> promise;
 };
@@ -354,6 +360,11 @@
     ASSERT_TRUE(contextHub->onHostEndpointDisconnected(kHostEndpointId).isOk());
 }
 
+TEST_P(ContextHubAidl, TestNanSessionStateChange) {
+    ASSERT_TRUE(contextHub->onNanSessionStateChanged(true /*state*/).isOk());
+    ASSERT_TRUE(contextHub->onNanSessionStateChanged(false /*state*/).isOk());
+}
+
 std::string PrintGeneratedTest(const testing::TestParamInfo<ContextHubAidl::ParamType>& info) {
     return std::string("CONTEXT_HUB_ID_") + std::to_string(std::get<1>(info.param));
 }
diff --git a/contexthub/common/default/1.X/OWNERS b/contexthub/common/default/1.X/OWNERS
deleted file mode 100644
index 90c2330..0000000
--- a/contexthub/common/default/1.X/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-arthuri@google.com
-bduddie@google.com
-stange@google.com
diff --git a/contexthub/common/vts/OWNERS b/contexthub/common/vts/OWNERS
deleted file mode 100644
index 161b2f0..0000000
--- a/contexthub/common/vts/OWNERS
+++ /dev/null
@@ -1,8 +0,0 @@
-#Context Hub team
-arthuri@google.com
-bduddie@google.com
-stange@google.com
-
-#VTS team
-dshi@google.com
-trong@google.com
diff --git a/graphics/Android.bp b/graphics/Android.bp
index 4898dbe..2fbcb41 100644
--- a/graphics/Android.bp
+++ b/graphics/Android.bp
@@ -21,6 +21,9 @@
     static_libs: [
         "android.hardware.graphics.allocator-V2-ndk",
     ],
+    defaults: [
+        "android.hardware.graphics.common-ndk_static",
+    ],
 }
 
 cc_defaults {
@@ -28,6 +31,9 @@
     shared_libs: [
         "android.hardware.graphics.allocator-V2-ndk",
     ],
+    defaults: [
+        "android.hardware.graphics.common-ndk_shared",
+    ],
 }
 
 cc_defaults {
diff --git a/graphics/allocator/aidl/aidl_api/android.hardware.graphics.allocator/current/android/hardware/graphics/allocator/BufferDescriptorInfo.aidl b/graphics/allocator/aidl/aidl_api/android.hardware.graphics.allocator/current/android/hardware/graphics/allocator/BufferDescriptorInfo.aidl
index 980e246..49c2497 100644
--- a/graphics/allocator/aidl/aidl_api/android.hardware.graphics.allocator/current/android/hardware/graphics/allocator/BufferDescriptorInfo.aidl
+++ b/graphics/allocator/aidl/aidl_api/android.hardware.graphics.allocator/current/android/hardware/graphics/allocator/BufferDescriptorInfo.aidl
@@ -41,4 +41,5 @@
   android.hardware.graphics.common.PixelFormat format = android.hardware.graphics.common.PixelFormat.UNSPECIFIED;
   android.hardware.graphics.common.BufferUsage usage = android.hardware.graphics.common.BufferUsage.CPU_READ_NEVER;
   long reservedSize;
+  android.hardware.graphics.common.ExtendableType[] additionalOptions;
 }
diff --git a/graphics/allocator/aidl/android/hardware/graphics/allocator/AllocationResult.aidl b/graphics/allocator/aidl/android/hardware/graphics/allocator/AllocationResult.aidl
index 0774e25..76f1c95 100644
--- a/graphics/allocator/aidl/android/hardware/graphics/allocator/AllocationResult.aidl
+++ b/graphics/allocator/aidl/android/hardware/graphics/allocator/AllocationResult.aidl
@@ -18,13 +18,17 @@
 
 import android.hardware.common.NativeHandle;
 
- /**
+/**
  * Result of an IAllocator::allocate call.
  *
  * @sa +ndk libnativewindow#AHardwareBuffer_Desc
  */
 @VintfStability
 parcelable AllocationResult {
+    /**
+     * The number of pixels between two consecutive rows of an allocated buffer, when the concept
+     * of consecutive rows is defined. Otherwise, it has no meaning.
+     */
     int stride;
     NativeHandle[] buffers;
-}
\ No newline at end of file
+}
diff --git a/graphics/allocator/aidl/android/hardware/graphics/allocator/BufferDescriptorInfo.aidl b/graphics/allocator/aidl/android/hardware/graphics/allocator/BufferDescriptorInfo.aidl
index ffc50b8..50aa2b7 100644
--- a/graphics/allocator/aidl/android/hardware/graphics/allocator/BufferDescriptorInfo.aidl
+++ b/graphics/allocator/aidl/android/hardware/graphics/allocator/BufferDescriptorInfo.aidl
@@ -17,6 +17,7 @@
 package android.hardware.graphics.allocator;
 
 import android.hardware.graphics.common.BufferUsage;
+import android.hardware.graphics.common.ExtendableType;
 import android.hardware.graphics.common.PixelFormat;
 
 @VintfStability
@@ -54,6 +55,9 @@
     /**
      * Buffer usage mask; valid flags can be found in the definition of
      * BufferUsage.aidl in graphics/common
+     *
+     * The allocator must report isSupported() == false and reject any allocations
+     * with unrecognized buffer usages.
      */
     BufferUsage usage = BufferUsage.CPU_READ_NEVER;
 
@@ -62,4 +66,16 @@
      * See getReservedRegion for more information.
      */
     long reservedSize;
+
+    /**
+     * Extensible additional options that can be set.
+     *
+     * This is intended for options that do not change the overall usage, but which do impact
+     * how a buffer is allocated. An example of this is compression level, such as for
+     * the EGL_EXT_surface_compression extension.
+     *
+     * The allocator must report isSupported() == false and reject any allocations
+     * with unrecognized options.
+     */
+    ExtendableType[] additionalOptions;
 }
diff --git a/graphics/allocator/aidl/vts/VtsHalGraphicsAllocatorAidl_TargetTest.cpp b/graphics/allocator/aidl/vts/VtsHalGraphicsAllocatorAidl_TargetTest.cpp
index 09f1c15..4778020 100644
--- a/graphics/allocator/aidl/vts/VtsHalGraphicsAllocatorAidl_TargetTest.cpp
+++ b/graphics/allocator/aidl/vts/VtsHalGraphicsAllocatorAidl_TargetTest.cpp
@@ -217,6 +217,8 @@
         }
         return ret;
     }
+
+    int32_t allocatorVersion() const { return mIAllocatorVersion; }
 };
 
 BufferHandle::~BufferHandle() {
@@ -309,6 +311,62 @@
     EXPECT_GE(buffer->stride(), 64);
 }
 
+TEST_P(GraphicsAllocatorAidlTests, RejectsUnknownUsages) {
+    if (allocatorVersion() < 2) {
+        GTEST_SKIP() << "Must be version 2+";
+        return;
+    }
+
+    constexpr auto FirstInvalidV2Usage = static_cast<BufferUsage>(1LL << 33);
+
+    BufferUsage invalidUsage;
+    if (allocatorVersion() == 2) {
+        invalidUsage = FirstInvalidV2Usage;
+    } else {
+        GTEST_FAIL() << "Unknown version " << allocatorVersion();
+    }
+
+    BufferDescriptorInfo info{
+            .name = {"CPU_8888"},
+            .width = 64,
+            .height = 64,
+            .layerCount = 1,
+            .format = PixelFormat::RGBA_8888,
+            .usage = BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN,
+            .reservedSize = 0,
+    };
+
+    // First make sure we can allocate a known usage buffer as expected
+    EXPECT_TRUE(isSupported(info));
+    EXPECT_TRUE(allocate(info));
+
+    // Now add the unknown bit and verify it's rejected
+    info.usage |= invalidUsage;
+    EXPECT_FALSE(isSupported(info)) << "isSupported() returned true for unknown-to-HAL usage";
+    EXPECT_FALSE(allocate(info)) << "allocate succeeded for unknown-to-HAL usage";
+}
+
+TEST_P(GraphicsAllocatorAidlTests, RejectsUnknownOptions) {
+    if (allocatorVersion() < 2) {
+        GTEST_SKIP() << "Must be version 2+";
+        return;
+    }
+
+    BufferDescriptorInfo info{
+            .name = {"CPU_8888"},
+            .width = 64,
+            .height = 64,
+            .layerCount = 1,
+            .format = PixelFormat::RGBA_8888,
+            .usage = BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN,
+            .reservedSize = 0,
+    };
+    info.additionalOptions.push_back({"android.hardware.graphics.common.NotARealOption", 1});
+
+    EXPECT_FALSE(isSupported(info)) << "isSupported() returned true for unknown-to-HAL option";
+    EXPECT_FALSE(allocate(info)) << "allocate succeeded for unknown-to-HAL option";
+}
+
 TEST_P(GraphicsFrontBufferTests, FrontBufferGpuToCpu) {
     BufferDescriptorInfo info{
             .name = {"CPU_8888"},
diff --git a/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/StandardMetadataType.aidl b/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/StandardMetadataType.aidl
index 06b31a2..6e2e106 100644
--- a/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/StandardMetadataType.aidl
+++ b/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/StandardMetadataType.aidl
@@ -58,4 +58,5 @@
   CTA861_3 = 20,
   SMPTE2094_40 = 21,
   SMPTE2094_10 = 22,
+  STRIDE = 23,
 }
diff --git a/graphics/common/aidl/android/hardware/graphics/common/StandardMetadataType.aidl b/graphics/common/aidl/android/hardware/graphics/common/StandardMetadataType.aidl
index 4bca795..8cfdae6 100644
--- a/graphics/common/aidl/android/hardware/graphics/common/StandardMetadataType.aidl
+++ b/graphics/common/aidl/android/hardware/graphics/common/StandardMetadataType.aidl
@@ -368,4 +368,17 @@
      * If this is unset when encoded into a byte stream, the byte stream is empty.
      */
     SMPTE2094_10 = 22,
+
+    /**
+     * Can be used to get the stride in pixels of the buffer allocation. This is the number of
+     * pixels between two consecutive rows of an allocated buffer, when the concept of consecutive
+     * rows is defined. Otherwise, it has no meaning.
+     *
+     * Must match the value returned in android.hardware.graphics.allocator.AllocationResult#stride
+     *
+     * This is required metadata in mapper5 and should be read-only.
+     *
+     * The metadata type is a uint32_t.
+     */
+    STRIDE = 23,
 }
diff --git a/graphics/composer/aidl/vts/VtsComposerClient.cpp b/graphics/composer/aidl/vts/VtsComposerClient.cpp
index 00b578b..43f4b84 100644
--- a/graphics/composer/aidl/vts/VtsComposerClient.cpp
+++ b/graphics/composer/aidl/vts/VtsComposerClient.cpp
@@ -404,14 +404,31 @@
                 return {std::move(configs.first), vtsDisplays};
             }
             for (int config : configs.second) {
-                auto status = updateDisplayProperties(&vtsDisplay, config);
+                auto status = addDisplayConfig(&vtsDisplay, config);
                 if (!status.isOk()) {
-                    ALOGE("Unable to get the displays for test, failed to update the properties "
+                    ALOGE("Unable to get the displays for test, failed to add config "
                           "for display %" PRId64,
                           display);
                     return {std::move(status), vtsDisplays};
                 }
             }
+
+            auto config = getActiveConfig(display);
+            if (!config.first.isOk()) {
+                ALOGE("Unable to get the displays for test, failed to get active config "
+                      "for display %" PRId64, display);
+                return {std::move(config.first), vtsDisplays};
+            }
+
+            auto status = updateDisplayProperties(&vtsDisplay, config.second);
+            if (!status.isOk()) {
+                ALOGE("Unable to get the displays for test, "
+                      "failed to update the properties "
+                      "for display %" PRId64,
+                      display);
+                return {std::move(status), vtsDisplays};
+            }
+
             vtsDisplays.emplace_back(vtsDisplay);
             addDisplayToDisplayResources(display, /*isVirtual*/ false);
         }
@@ -420,7 +437,7 @@
     }
 }
 
-ScopedAStatus VtsComposerClient::updateDisplayProperties(VtsDisplay* vtsDisplay, int32_t config) {
+ScopedAStatus VtsComposerClient::addDisplayConfig(VtsDisplay* vtsDisplay, int32_t config) {
     const auto width =
             getDisplayAttribute(vtsDisplay->getDisplayId(), config, DisplayAttribute::WIDTH);
     const auto height =
@@ -431,7 +448,6 @@
             getDisplayAttribute(vtsDisplay->getDisplayId(), config, DisplayAttribute::CONFIG_GROUP);
     if (width.first.isOk() && height.first.isOk() && vsyncPeriod.first.isOk() &&
         configGroup.first.isOk()) {
-        vtsDisplay->setDimensions(width.second, height.second);
         vtsDisplay->addDisplayConfig(config, {vsyncPeriod.second, configGroup.second});
         return ScopedAStatus::ok();
     }
@@ -442,6 +458,21 @@
     return ScopedAStatus::fromServiceSpecificError(IComposerClient::EX_BAD_CONFIG);
 }
 
+ScopedAStatus VtsComposerClient::updateDisplayProperties(VtsDisplay* vtsDisplay, int32_t config) {
+    const auto width =
+            getDisplayAttribute(vtsDisplay->getDisplayId(), config, DisplayAttribute::WIDTH);
+    const auto height =
+            getDisplayAttribute(vtsDisplay->getDisplayId(), config, DisplayAttribute::HEIGHT);
+    if (width.first.isOk() && height.first.isOk()) {
+        vtsDisplay->setDimensions(width.second, height.second);
+        return ScopedAStatus::ok();
+    }
+
+    LOG(ERROR) << "Failed to update display property for width: " << width.first.isOk()
+               << ", height: " << height.first.isOk();
+    return ScopedAStatus::fromServiceSpecificError(IComposerClient::EX_BAD_CONFIG);
+}
+
 ScopedAStatus VtsComposerClient::addDisplayToDisplayResources(int64_t display, bool isVirtual) {
     if (mDisplayResources.insert({display, DisplayResource(isVirtual)}).second) {
         return ScopedAStatus::ok();
diff --git a/graphics/composer/aidl/vts/VtsComposerClient.h b/graphics/composer/aidl/vts/VtsComposerClient.h
index e61fde9..c4bc464 100644
--- a/graphics/composer/aidl/vts/VtsComposerClient.h
+++ b/graphics/composer/aidl/vts/VtsComposerClient.h
@@ -178,6 +178,7 @@
     std::pair<ScopedAStatus, OverlayProperties> getOverlaySupport();
 
   private:
+    ScopedAStatus addDisplayConfig(VtsDisplay* vtsDisplay, int32_t config);
     ScopedAStatus updateDisplayProperties(VtsDisplay* vtsDisplay, int32_t config);
 
     ScopedAStatus addDisplayToDisplayResources(int64_t display, bool isVirtual);
diff --git a/graphics/mapper/stable-c/README.md b/graphics/mapper/stable-c/README.md
new file mode 100644
index 0000000..30f3ccc
--- /dev/null
+++ b/graphics/mapper/stable-c/README.md
@@ -0,0 +1,110 @@
+# IMapper "stable-c" HAL
+
+Starting with gralloc version 5, IMapper is now exposed as a C API instead of through HIDL or AIDL.
+This is due to HIDL being deprecated, and AIDL not wanting to support a pass-through mode & pointers
+for just a couple of clients such as IMapper. So instead a stable C API is used to fill this gap.
+
+## Implementing
+
+To provide an implementation a library implementing the AIMapper API interface should be provided
+in `/vendor/lib[64]/hw/mapper.<imapper_suffix>.so`. The `<imapper_suffix>` should be specified
+as the `<instance>` in the VINTF manifest `<interface>` section. For example:
+```xml
+<manifest version="1.0" type="device">
+    <hal format="native">
+        <name>mapper</name>
+        <version>5.0</version>
+        <interface>
+            <name>I</name>
+            <instance>minigbm</instance>
+        </interface>
+    </hal>
+</manifest>
+```
+defines that the IMapper 5.0 library is provided by `/vendor/lib[64]/hw/mapper.minigbm.so`.
+
+This library must export the following `extern "C"` symbols:
+
+### `ANDROID_HAL_STABLEC_VERSION`
+
+This is a uint32_t that should simply be set to the exported AIMapper version. For example:
+```c++
+extern "C" uint32_t ANDROID_HAL_STABLEC_VERSION = AIMAPPER_VERSION_5;
+```
+
+### `AIMapper_loadIMapper`
+
+This is what should actually load the HAL interface. The full type signature is
+```c++
+extern "C" AIMapper_Error AIMapper_loadIMapper(AIMapper* _Nullable* _Nonnull outImplementation)
+```
+
+See `include/android/hardware/graphics/mapper/IMapper.h` for complete documentation on what
+this function must return.
+
+To make it easier to implement this C API, a header-only helper library is provided called
+`libimapper_providerutils`. This library handles mapping from the C API struct to a C++ class
+as well as provides helpers for encoding & decoding metadata, largely replacing the role that
+`libgralloctypes` filled with IMapper 4.
+
+To use this library, create a class that extends from `IMapperV5Impl` and use `IMapperProvider` to
+implement `AIMapper_loadIMapper`:
+
+```c++
+// The IMapper interface itself
+#include <android/hardware/graphics/mapper/IMapper.h>
+// Helpers for reading & writing metadata
+#include <android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h>
+// Helper for providing the implementation interface
+#include <android/hardware/graphics/mapper/utils/IMapperProvider.h>
+
+// Define an IMapperV5 implementation
+class CrosGrallocMapperV5 final : public vendor::mapper::IMapperV5Impl {
+    // Override all the methods of IMapperV5Impl
+      AIMapper_Error importBuffer(const native_handle_t* _Nonnull handle,
+                              buffer_handle_t _Nullable* _Nonnull outBufferHandle) override;
+      [etc...]
+};
+
+// Expose the required C symbols
+
+extern "C" uint32_t ANDROID_HAL_STABLEC_VERSION = AIMAPPER_VERSION_5;
+
+extern "C" AIMapper_Error AIMapper_loadIMapper(AIMapper* _Nullable* _Nonnull outImplementation) {
+    // Define an IMapperProvider for our V5 implementation
+    static vendor::mapper::IMapperProvider<CrosGrallocMapperV5> provider;
+    return provider.load(outImplementation);
+}
+```
+
+A complete example, including using IMapperMetadataTypes, can be found in the cuttlefish
+implementation in `//external/minigbm/cros_gralloc/mapper_stablec`
+
+### Testing
+
+As with HIDL & AIDL HALs, a VTS test is provided to validate the implementation. It is found in the
+`vts` folder and may be run using `$ atest VtsHalGraphicsMapperStableC_TargetTest`
+
+## Using
+
+It is strongly recommended that clients use either the `AHardwareBuffer` (preferred) or
+`GraphicBufferMapper` (from libui) APIs to use the mapper HAL rather than attempting to use
+`AIMapper` directly.
+
+## Version changes
+
+### Version 5
+
+* Initial introduction of this HAL interface
+* Largely feature-equivalent to IMapper4
+* Requires allocator-V2
+* Removes `BufferDescriptorInfo`;
+* IsSupported has moved to IAllocator
+* Removes `validateBufferSize`, validation is instead handled by clients using metadata queries
+* Getting the following StandardMetadataType is now mandatory:
+  * STRIDE
+* Setting the following StandardMetadataTypes is now mandatory:
+  * DATASPACE
+  * SMPTE2086
+  * CTA861_3
+  * BLEND_MODE
diff --git a/graphics/mapper/stable-c/implutils/include/android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h b/graphics/mapper/stable-c/implutils/include/android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h
index 102dada..0e6d3dc 100644
--- a/graphics/mapper/stable-c/implutils/include/android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h
+++ b/graphics/mapper/stable-c/implutils/include/android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h
@@ -563,6 +563,7 @@
 DEFINE_TYPE(CTA861_3, std::optional<Cta861_3>);
 DEFINE_TYPE(SMPTE2094_10, std::optional<std::vector<uint8_t>>);
 DEFINE_TYPE(SMPTE2094_40, std::optional<std::vector<uint8_t>>);
+DEFINE_TYPE(STRIDE, uint32_t);
 
 #undef DEFINE_TYPE
 
diff --git a/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp b/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp
index 85246ee..2c06353 100644
--- a/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp
+++ b/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp
@@ -154,6 +154,7 @@
     std::shared_ptr<IAllocator> mAllocator;
     AIMapper* mIMapper = nullptr;
     AIMapper_loadIMapperFn mIMapperLoader;
+    int32_t* mIMapperHALVersion = nullptr;
 
   protected:
     void Initialize(std::shared_ptr<IAllocator> allocator) {
@@ -171,10 +172,12 @@
         ASSERT_NE(nullptr, mIMapperLoader) << "AIMapper_locaIMapper missing from " << lib_name;
         ASSERT_EQ(AIMAPPER_ERROR_NONE, mIMapperLoader(&mIMapper));
         ASSERT_NE(mIMapper, nullptr);
+        mIMapperHALVersion = (int32_t*)dlsym(so, "ANDROID_HAL_MAPPER_VERSION");
     }
 
   public:
     AIMapper_loadIMapperFn getIMapperLoader() const { return mIMapperLoader; }
+    int32_t* getHalVersion() const { return mIMapperHALVersion; }
 
     std::unique_ptr<BufferAllocation> allocate(const BufferDescriptorInfo& descriptorInfo) {
         AllocationResult result;
@@ -557,6 +560,15 @@
     void TearDown() override {}
 };
 
+TEST_P(GraphicsMapperStableCTests, VersionChecks) {
+    ASSERT_NE(nullptr, getHalVersion()) << "Resolving ANDROID_HAL_MAPPER_VERSION symbol failed";
+    int32_t halVersion = *getHalVersion();
+    EXPECT_EQ(halVersion, AIMAPPER_VERSION_5) << "Unrecognized ANDROID_HAL_MAPPER_VERSION";
+    EXPECT_EQ(mapper()->version, AIMAPPER_VERSION_5) << "Unrecognized AIMapper::version";
+    EXPECT_EQ(halVersion, mapper()->version)
+            << "AIMapper version & ANDROID_HAL_MAPPER_VERSION don't agree";
+}
+
 TEST_P(GraphicsMapperStableCTests, AllV5CallbacksDefined) {
     ASSERT_GE(mapper()->version, AIMAPPER_VERSION_5);
 
@@ -1557,6 +1569,16 @@
     }
 }
 
+TEST_P(GraphicsMapperStableCTests, GetStride) {
+    auto buffer = allocateGeneric();
+    ASSERT_TRUE(buffer);
+    auto bufferHandle = buffer->import();
+    ASSERT_TRUE(bufferHandle);
+    auto value = getStandardMetadata<StandardMetadataType::STRIDE>(*bufferHandle);
+    ASSERT_TRUE(value.has_value());
+    EXPECT_EQ(buffer->stride(), *value);
+}
+
 TEST_P(GraphicsMapperStableCTests, SupportsRequiredGettersSetters) {
     auto buffer = allocateGeneric();
     ASSERT_TRUE(buffer);
@@ -1587,6 +1609,7 @@
             StandardMetadataType::BLEND_MODE,
             StandardMetadataType::SMPTE2086,
             StandardMetadataType::CTA861_3,
+            StandardMetadataType::STRIDE,
     };
 
     std::vector<StandardMetadataType> requiredSetters = {
diff --git a/radio/aidl/Android.bp b/radio/aidl/Android.bp
index 9caa97a..e8cebd7 100644
--- a/radio/aidl/Android.bp
+++ b/radio/aidl/Android.bp
@@ -163,7 +163,12 @@
             enabled: true,
         },
         java: {
+            apex_available: [
+                "//apex_available:platform",
+                "com.android.btservices",
+            ],
             sdk_version: "module_current",
+            min_sdk_version: "Tiramisu",
         },
     },
 
diff --git a/radio/aidl/vts/VtsHalRadioTargetTest.cpp b/radio/aidl/vts/VtsHalRadioTargetTest.cpp
index c25f930..33a0d7d 100644
--- a/radio/aidl/vts/VtsHalRadioTargetTest.cpp
+++ b/radio/aidl/vts/VtsHalRadioTargetTest.cpp
@@ -56,7 +56,7 @@
         testing::ValuesIn(android::getAidlHalInstanceNames(IRadioNetwork::descriptor)),
         android::PrintInstanceNameToString);
 
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(RadioSapTest);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SapTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, SapTest,
                          testing::ValuesIn(android::getAidlHalInstanceNames(ISap::descriptor)),
                          android::PrintInstanceNameToString);
diff --git a/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl b/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
index c506667..ff710f1 100644
--- a/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
+++ b/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
@@ -222,7 +222,7 @@
      *                2 : bstr             ; KID : EEK ID
      *                3 : -25,             ; Algorithm : ECDH-ES + HKDF-256
      *                -1 : 4,              ; Curve : X25519
-     *                -2 : bstr            ; X25519 public key
+     *                -2 : bstr            ; X25519 public key, little-endian
      *            }
      *
      *            EekP256 = {              ; COSE_Key
@@ -440,6 +440,9 @@
      *
      * ; The following section defines some types that are reused throughout the above
      * ; data structures.
+     * ; NOTE: Integer encoding is different for Ed25519 and P256 keys:
+     * ;       - Ed25519 is LE: https://www.rfc-editor.org/rfc/rfc8032#section-3.1
+     * ;       - P256 is BE: https://www.secg.org/sec1-v2.pdf#page=19 (section 2.3.7)
      * PubKeyEd25519 = {                ; COSE_Key
      *     1 : 1,                       ; Key type : octet key pair
      *     3 : AlgorithmEdDSA,          ; Algorithm : EdDSA
@@ -451,8 +454,8 @@
      *     1 : 2,                       ; Key type : EC2
      *     3 : AlgorithmES256,          ; Algorithm : ECDSA w/ SHA-256
      *     -1 : 1,                      ; Curve: P256
-     *     -2 : bstr,                   ; X coordinate
-     *     -3 : bstr                    ; Y coordinate
+     *     -2 : bstr,                   ; X coordinate, big-endian
+     *     -3 : bstr                    ; Y coordinate, big-endian
      * }
      *
      * AlgorithmES256 = -7
diff --git a/security/rkp/aidl/android/hardware/security/keymint/MacedPublicKey.aidl b/security/rkp/aidl/android/hardware/security/keymint/MacedPublicKey.aidl
index 275e322..1e41d1b 100644
--- a/security/rkp/aidl/android/hardware/security/keymint/MacedPublicKey.aidl
+++ b/security/rkp/aidl/android/hardware/security/keymint/MacedPublicKey.aidl
@@ -36,12 +36,15 @@
      *     ]
      *
      *     ; NOTE: -70000 is deprecated for v3 HAL implementations.
+     *     ; NOTE: Integer encoding is different for Ed25519 and P256 keys:
+     *     ;       - Ed25519 is LE: https://www.rfc-editor.org/rfc/rfc8032#section-3.1
+     *     ;       - P256 is BE: https://www.secg.org/sec1-v2.pdf#page=19 (section 2.3.7)
      *     PublicKey = {               ; COSE_Key
      *         1 : 2,                  ; Key type : EC2
      *         3 : -7,                 ; Algorithm : ES256
      *         -1 : 1,                 ; Curve : P256
-     *         -2 : bstr,              ; X coordinate, little-endian
-     *         -3 : bstr,              ; Y coordinate, little-endian
+     *         -2 : bstr,              ; X coordinate, big-endian
+     *         -3 : bstr,              ; Y coordinate, big-endian
      *         -70000 : nil            ; Presence indicates this is a test key. If set, K_mac is
      *                                 ; all zeros.
      *     },
diff --git a/security/rkp/aidl/android/hardware/security/keymint/ProtectedData.aidl b/security/rkp/aidl/android/hardware/security/keymint/ProtectedData.aidl
index d59508b..3f699bc 100644
--- a/security/rkp/aidl/android/hardware/security/keymint/ProtectedData.aidl
+++ b/security/rkp/aidl/android/hardware/security/keymint/ProtectedData.aidl
@@ -209,7 +209,7 @@
      *     PubKeyX25519 = {                 ; COSE_Key
      *          1 : 1,                      ; Key type : Octet Key Pair
      *         -1 : 4,                      ; Curve : X25519
-     *         -2 : bstr                    ; Sender X25519 public key
+     *         -2 : bstr                    ; Sender X25519 public key, little-endian
      *     }
      *
      *     PubKeyEd25519 = {                ; COSE_Key
@@ -222,16 +222,16 @@
      *     PubKeyEcdhP256 = {               ; COSE_Key
      *          1 : 2,                      ; Key type : EC2
      *          -1 : 1,                     ; Curve : P256
-     *          -2 : bstr                   ; Sender X coordinate
-     *          -3 : bstr                   ; Sender Y coordinate
+     *          -2 : bstr                   ; Sender X coordinate, big-endian
+     *          -3 : bstr                   ; Sender Y coordinate, big-endian
      *     }
      *
      *     PubKeyECDSA256 = {               ; COSE_Key
      *         1 : 2,                       ; Key type : EC2
      *         3 : AlgorithmES256,          ; Algorithm : ECDSA w/ SHA-256
      *         -1 : 1,                      ; Curve: P256
-     *         -2 : bstr,                   ; X coordinate
-     *         -3 : bstr                    ; Y coordinate
+     *         -2 : bstr,                   ; X coordinate, big-endian
+     *         -3 : bstr                    ; Y coordinate, big-endian
      *     }
      *
      *     AlgorithmES256 = -7
diff --git a/security/rkp/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/rkp/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
index aebcf67..bbda56d 100644
--- a/security/rkp/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
+++ b/security/rkp/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
@@ -223,6 +223,20 @@
     ASSERT_TRUE(provisionable_->getHardwareInfo(&hwInfo).isOk());
 
     const std::set<int> validCurves = {RpcHardwareInfo::CURVE_P256, RpcHardwareInfo::CURVE_25519};
+    // First check for the implementations that supports only IRPC V3+.
+    if (rpcHardwareInfo.versionNumber >= VERSION_WITHOUT_TEST_MODE) {
+        bytevec keysToSignMac;
+        DeviceInfo deviceInfo;
+        ProtectedData protectedData;
+        auto status = provisionable_->generateCertificateRequest(false, {}, {}, {}, &deviceInfo,
+                                                                 &protectedData, &keysToSignMac);
+        if (!status.isOk() &&
+            (status.getServiceSpecificError() == BnRemotelyProvisionedComponent::STATUS_REMOVED)) {
+            ASSERT_EQ(hwInfo.supportedEekCurve, RpcHardwareInfo::CURVE_NONE)
+                    << "Invalid curve: " << hwInfo.supportedEekCurve;
+            return;
+        }
+    }
     ASSERT_EQ(validCurves.count(hwInfo.supportedEekCurve), 1)
             << "Invalid curve: " << hwInfo.supportedEekCurve;
 }
diff --git a/usb/aidl/vts/VtsAidlUsbTargetTest.cpp b/usb/aidl/vts/VtsAidlUsbTargetTest.cpp
index d87f1f4..aec9954 100644
--- a/usb/aidl/vts/VtsAidlUsbTargetTest.cpp
+++ b/usb/aidl/vts/VtsAidlUsbTargetTest.cpp
@@ -307,7 +307,7 @@
       }
     }
   }
-  EXPECT_LE(1, disabledCount);
+  EXPECT_GE(1, disabledCount);
   ALOGI("UsbAidlTest DataStatusCheck end");
 }
 
diff --git a/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/IWifiChip.aidl b/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/IWifiChip.aidl
index f800e8f..2ebe145 100644
--- a/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/IWifiChip.aidl
+++ b/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/IWifiChip.aidl
@@ -80,24 +80,25 @@
   void startLoggingToDebugRingBuffer(in String ringName, in android.hardware.wifi.WifiDebugRingBufferVerboseLevel verboseLevel, in int maxIntervalInSec, in int minDataSizeInBytes);
   void stopLoggingToDebugRingBuffer();
   void triggerSubsystemRestart();
-  const int NO_POWER_CAP_CONSTANT = 2147483647;
+  void enableStaChannelForPeerNetwork(in android.hardware.wifi.IWifiChip.ChannelCategoryMask channelCategoryEnableFlag);
+  const int NO_POWER_CAP_CONSTANT = 0x7FFFFFFF;
   @Backing(type="int") @VintfStability
   enum ChipCapabilityMask {
-    DEBUG_MEMORY_FIRMWARE_DUMP = 1,
-    DEBUG_MEMORY_DRIVER_DUMP = 2,
-    DEBUG_RING_BUFFER_CONNECT_EVENT = 4,
-    DEBUG_RING_BUFFER_POWER_EVENT = 8,
-    DEBUG_RING_BUFFER_WAKELOCK_EVENT = 16,
-    DEBUG_RING_BUFFER_VENDOR_DATA = 32,
-    DEBUG_HOST_WAKE_REASON_STATS = 64,
-    DEBUG_ERROR_ALERTS = 128,
-    SET_TX_POWER_LIMIT = 256,
-    D2D_RTT = 512,
-    D2AP_RTT = 1024,
-    USE_BODY_HEAD_SAR = 2048,
-    SET_LATENCY_MODE = 4096,
-    P2P_RAND_MAC = 8192,
-    WIGIG = 16384,
+    DEBUG_MEMORY_FIRMWARE_DUMP = (1 << 0),
+    DEBUG_MEMORY_DRIVER_DUMP = (1 << 1),
+    DEBUG_RING_BUFFER_CONNECT_EVENT = (1 << 2),
+    DEBUG_RING_BUFFER_POWER_EVENT = (1 << 3),
+    DEBUG_RING_BUFFER_WAKELOCK_EVENT = (1 << 4),
+    DEBUG_RING_BUFFER_VENDOR_DATA = (1 << 5),
+    DEBUG_HOST_WAKE_REASON_STATS = (1 << 6),
+    DEBUG_ERROR_ALERTS = (1 << 7),
+    SET_TX_POWER_LIMIT = (1 << 8),
+    D2D_RTT = (1 << 9),
+    D2AP_RTT = (1 << 10),
+    USE_BODY_HEAD_SAR = (1 << 11),
+    SET_LATENCY_MODE = (1 << 12),
+    P2P_RAND_MAC = (1 << 13),
+    WIGIG = (1 << 14),
   }
   @VintfStability
   parcelable ChipConcurrencyCombinationLimit {
@@ -129,9 +130,9 @@
   }
   @Backing(type="int") @VintfStability
   enum CoexRestriction {
-    WIFI_DIRECT = 1,
-    SOFTAP = 2,
-    WIFI_AWARE = 4,
+    WIFI_DIRECT = (1 << 0),
+    SOFTAP = (1 << 1),
+    WIFI_AWARE = (1 << 2),
   }
   @VintfStability
   parcelable CoexUnsafeChannel {
@@ -159,8 +160,13 @@
   }
   @Backing(type="int") @VintfStability
   enum UsableChannelFilter {
-    CELLULAR_COEXISTENCE = 1,
-    CONCURRENCY = 2,
-    NAN_INSTANT_MODE = 4,
+    CELLULAR_COEXISTENCE = (1 << 0),
+    CONCURRENCY = (1 << 1),
+    NAN_INSTANT_MODE = (1 << 2),
+  }
+  @Backing(type="int") @VintfStability
+  enum ChannelCategoryMask {
+    INDOOR_CHANNEL = (1 << 0),
+    DFS_CHANNEL = (1 << 1),
   }
 }
diff --git a/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/NanCapabilities.aidl b/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/NanCapabilities.aidl
index 5b59945..cb5a95c 100644
--- a/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/NanCapabilities.aidl
+++ b/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/NanCapabilities.aidl
@@ -52,4 +52,5 @@
   boolean supports6g;
   boolean supportsHe;
   boolean supportsPairing;
+  boolean supportsSetClusterId;
 }
diff --git a/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/NanConfigRequestSupplemental.aidl b/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/NanConfigRequestSupplemental.aidl
index 682699e..99f2af7 100644
--- a/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/NanConfigRequestSupplemental.aidl
+++ b/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/NanConfigRequestSupplemental.aidl
@@ -40,4 +40,5 @@
   boolean enableRanging;
   boolean enableInstantCommunicationMode;
   int instantModeChannel;
+  int clusterId;
 }
diff --git a/wifi/aidl/android/hardware/wifi/IWifiChip.aidl b/wifi/aidl/android/hardware/wifi/IWifiChip.aidl
index 64692af..5ffea56 100644
--- a/wifi/aidl/android/hardware/wifi/IWifiChip.aidl
+++ b/wifi/aidl/android/hardware/wifi/IWifiChip.aidl
@@ -1106,4 +1106,29 @@
      *         |WifiStatusCode.ERROR_UNKNOWN|
      */
     void triggerSubsystemRestart();
+
+    /**
+     * Channel category mask.
+     */
+    @VintfStability
+    @Backing(type="int")
+    enum ChannelCategoryMask {
+        INDOOR_CHANNEL = 1 << 0,
+        DFS_CHANNEL = 1 << 1,
+    }
+
+    /**
+     * API to enable or disable the feature of allowing current STA-connected channel for WFA GO,
+     * SAP and Aware when the regulatory allows.
+     * If the channel category is enabled and allowed by the regulatory, the HAL method
+     * getUsableChannels() will contain the current STA-connected channel if that channel belongs
+     * to that category.
+     * @param channelCategoryEnableFlag bitmask of |ChannelCategoryMask|.
+     *        For each bit, 1 enables the channel category and 0 disables that channel category.
+     * @throws ServiceSpecificException with one of the following values:
+     *         |WifiStatusCode.ERROR_WIFI_CHIP_INVALID|,
+     *         |WifiStatusCode.ERROR_NOT_SUPPORTED|,
+     *         |WifiStatusCode.FAILURE_UNKNOWN|
+     */
+    void enableStaChannelForPeerNetwork(in ChannelCategoryMask channelCategoryEnableFlag);
 }
diff --git a/wifi/aidl/android/hardware/wifi/NanCapabilities.aidl b/wifi/aidl/android/hardware/wifi/NanCapabilities.aidl
index 0955b4c..4160176 100644
--- a/wifi/aidl/android/hardware/wifi/NanCapabilities.aidl
+++ b/wifi/aidl/android/hardware/wifi/NanCapabilities.aidl
@@ -97,4 +97,8 @@
      * Flag to indicate if NAN pairing is supported.
      */
     boolean supportsPairing;
+    /**
+     * Flag to indicate if NAN setting cluster ID is supported.
+     */
+    boolean supportsSetClusterId;
 }
diff --git a/wifi/aidl/android/hardware/wifi/NanConfigRequestSupplemental.aidl b/wifi/aidl/android/hardware/wifi/NanConfigRequestSupplemental.aidl
index eb01a9f..338a549 100644
--- a/wifi/aidl/android/hardware/wifi/NanConfigRequestSupplemental.aidl
+++ b/wifi/aidl/android/hardware/wifi/NanConfigRequestSupplemental.aidl
@@ -60,4 +60,8 @@
      * Controls which channel NAN instant communication mode operates on.
      */
     int instantModeChannel;
+    /**
+     * Controls which cluster to join.
+     */
+    int clusterId;
 }
diff --git a/wifi/aidl/default/aidl_struct_util.cpp b/wifi/aidl/default/aidl_struct_util.cpp
index 5e80ff9..ba7d2db 100644
--- a/wifi/aidl/default/aidl_struct_util.cpp
+++ b/wifi/aidl/default/aidl_struct_util.cpp
@@ -1706,6 +1706,8 @@
     legacy_request->enable_instant_mode = aidl_request2.enableInstantCommunicationMode;
     legacy_request->config_instant_mode_channel = 1;
     legacy_request->instant_mode_channel = aidl_request2.instantModeChannel;
+    legacy_request->config_cluster_id = 1;
+    legacy_request->cluster_id_val = aidl_request2.clusterId;
 
     return true;
 }
@@ -2245,6 +2247,7 @@
     aidl_response->supports6g = legacy_response.is_6g_supported;
     aidl_response->supportsHe = legacy_response.is_he_supported;
     aidl_response->supportsPairing = legacy_response.is_pairing_supported;
+    aidl_response->supportsSetClusterId = legacy_response.is_set_cluster_id_supported;
     return true;
 }
 
@@ -3311,6 +3314,19 @@
     return true;
 }
 
+uint32_t convertAidlChannelCategoryToLegacy(uint32_t aidl_channel_category_mask) {
+    uint32_t channel_category_mask = 0;
+    if (aidl_channel_category_mask &
+        static_cast<int32_t>(IWifiChip::ChannelCategoryMask::INDOOR_CHANNEL)) {
+        channel_category_mask |= legacy_hal::WIFI_INDOOR_CHANNEL;
+    }
+    if (aidl_channel_category_mask &
+        static_cast<int32_t>(IWifiChip::ChannelCategoryMask::DFS_CHANNEL)) {
+        channel_category_mask |= legacy_hal::WIFI_DFS_CHANNEL;
+    }
+    return channel_category_mask;
+}
+
 }  // namespace aidl_struct_util
 }  // namespace wifi
 }  // namespace hardware
diff --git a/wifi/aidl/default/aidl_struct_util.h b/wifi/aidl/default/aidl_struct_util.h
index e478fed..6407d32 100644
--- a/wifi/aidl/default/aidl_struct_util.h
+++ b/wifi/aidl/default/aidl_struct_util.h
@@ -200,6 +200,7 @@
 bool convertLegacyNanBootstrappingConfirmIndToAidl(
         const legacy_hal::NanBootstrappingConfirmInd& legacy_ind,
         NanBootstrappingConfirmInd* aidl_ind);
+uint32_t convertAidlChannelCategoryToLegacy(uint32_t aidl_channel_category_mask);
 }  // namespace aidl_struct_util
 }  // namespace wifi
 }  // namespace hardware
diff --git a/wifi/aidl/default/wifi_chip.cpp b/wifi/aidl/default/wifi_chip.cpp
index 4061699..5597001 100644
--- a/wifi/aidl/default/wifi_chip.cpp
+++ b/wifi/aidl/default/wifi_chip.cpp
@@ -34,6 +34,7 @@
 using aidl::android::hardware::wifi::IfaceType;
 using aidl::android::hardware::wifi::IWifiChip;
 using CoexRestriction = aidl::android::hardware::wifi::IWifiChip::CoexRestriction;
+using ChannelCategoryMask = aidl::android::hardware::wifi::IWifiChip::ChannelCategoryMask;
 using android::base::unique_fd;
 
 constexpr char kCpioMagic[] = "070701";
@@ -685,6 +686,13 @@
                            &WifiChip::getWifiChipCapabilitiesInternal, _aidl_return);
 }
 
+ndk::ScopedAStatus WifiChip::enableStaChannelForPeerNetwork(
+        ChannelCategoryMask in_channelCategoryEnableFlag) {
+    return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+                           &WifiChip::enableStaChannelForPeerNetworkInternal,
+                           in_channelCategoryEnableFlag);
+}
+
 void WifiChip::invalidateAndRemoveAllIfaces() {
     invalidateAndClearBridgedApAll();
     invalidateAndClearAll(ap_ifaces_);
@@ -1428,6 +1436,14 @@
     return {aidl_chip_capabilities, ndk::ScopedAStatus::ok()};
 }
 
+ndk::ScopedAStatus WifiChip::enableStaChannelForPeerNetworkInternal(
+        ChannelCategoryMask channelCategoryEnableFlag) {
+    auto legacy_status = legacy_hal_.lock()->enableStaChannelForPeerNetwork(
+            aidl_struct_util::convertAidlChannelCategoryToLegacy(
+                    static_cast<uint32_t>(channelCategoryEnableFlag)));
+    return createWifiStatusFromLegacyError(legacy_status);
+}
+
 ndk::ScopedAStatus WifiChip::triggerSubsystemRestartInternal() {
     auto legacy_status = legacy_hal_.lock()->triggerSubsystemRestart();
     return createWifiStatusFromLegacyError(legacy_status);
diff --git a/wifi/aidl/default/wifi_chip.h b/wifi/aidl/default/wifi_chip.h
index 7b04e85..c69c7fe 100644
--- a/wifi/aidl/default/wifi_chip.h
+++ b/wifi/aidl/default/wifi_chip.h
@@ -145,6 +145,8 @@
     ndk::ScopedAStatus getSupportedRadioCombinationsMatrix(
             WifiRadioCombinationMatrix* _aidl_return) override;
     ndk::ScopedAStatus getWifiChipCapabilities(WifiChipCapabilities* _aidl_return) override;
+    ndk::ScopedAStatus enableStaChannelForPeerNetwork(
+            ChannelCategoryMask in_channelCategoryEnableFlag) override;
     binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
 
   private:
@@ -214,6 +216,8 @@
     ndk::ScopedAStatus setCountryCodeInternal(const std::array<uint8_t, 2>& in_code);
     std::pair<std::vector<WifiUsableChannel>, ndk::ScopedAStatus> getUsableChannelsInternal(
             WifiBand band, WifiIfaceMode ifaceModeMask, UsableChannelFilter filterMask);
+    ndk::ScopedAStatus enableStaChannelForPeerNetworkInternal(
+            ChannelCategoryMask channelCategoryEnableFlag);
     ndk::ScopedAStatus handleChipConfiguration(std::unique_lock<std::recursive_mutex>* lock,
                                                int32_t mode_id);
     ndk::ScopedAStatus registerDebugRingBufferCallback();
diff --git a/wifi/aidl/default/wifi_legacy_hal.cpp b/wifi/aidl/default/wifi_legacy_hal.cpp
index 54e91d4..f03835f 100644
--- a/wifi/aidl/default/wifi_legacy_hal.cpp
+++ b/wifi/aidl/default/wifi_legacy_hal.cpp
@@ -1835,6 +1835,11 @@
     return {status, chip_capabilities};
 }
 
+wifi_error WifiLegacyHal::enableStaChannelForPeerNetwork(uint32_t channelCategoryEnableFlag) {
+    return global_func_table_.wifi_enable_sta_channel_for_peer_network(global_handle_,
+                                                                       channelCategoryEnableFlag);
+}
+
 void WifiLegacyHal::invalidate() {
     global_handle_ = nullptr;
     iface_name_to_handle_.clear();
diff --git a/wifi/aidl/default/wifi_legacy_hal.h b/wifi/aidl/default/wifi_legacy_hal.h
index 5620280..2300cb7 100644
--- a/wifi/aidl/default/wifi_legacy_hal.h
+++ b/wifi/aidl/default/wifi_legacy_hal.h
@@ -256,12 +256,14 @@
 using ::WIFI_CHAN_WIDTH_80;
 using ::WIFI_CHAN_WIDTH_80P80;
 using ::WIFI_CHAN_WIDTH_INVALID;
+using ::wifi_channel_category;
 using ::wifi_channel_info;
 using ::wifi_channel_stat;
 using ::wifi_channel_width;
 using ::wifi_chip_capabilities;
 using ::wifi_coex_restriction;
 using ::wifi_coex_unsafe_channel;
+using ::WIFI_DFS_CHANNEL;
 using ::WIFI_DUAL_STA_NON_TRANSIENT_UNBIASED;
 using ::WIFI_DUAL_STA_TRANSIENT_PREFER_PRIMARY;
 using ::wifi_error;
@@ -278,6 +280,7 @@
 using ::WIFI_ERROR_UNKNOWN;
 using ::wifi_gscan_capabilities;
 using ::wifi_hal_fn;
+using ::WIFI_INDOOR_CHANNEL;
 using ::wifi_information_element;
 using ::WIFI_INTERFACE_IBSS;
 using ::WIFI_INTERFACE_MESH;
@@ -755,6 +758,7 @@
     wifi_error getWifiCachedScanResults(const std::string& iface_name,
                                         const CachedScanResultsCallbackHandlers& handler);
     std::pair<wifi_error, wifi_chip_capabilities> getWifiChipCapabilities();
+    wifi_error enableStaChannelForPeerNetwork(uint32_t channelCategoryEnableFlag);
 
   private:
     // Retrieve interface handles for all the available interfaces.
diff --git a/wifi/aidl/default/wifi_legacy_hal_stubs.cpp b/wifi/aidl/default/wifi_legacy_hal_stubs.cpp
index a0cb2c0..11da373 100644
--- a/wifi/aidl/default/wifi_legacy_hal_stubs.cpp
+++ b/wifi/aidl/default/wifi_legacy_hal_stubs.cpp
@@ -172,6 +172,7 @@
     populateStubFor(&hal_fn->wifi_enable_tx_power_limits);
     populateStubFor(&hal_fn->wifi_get_cached_scan_results);
     populateStubFor(&hal_fn->wifi_get_chip_capabilities);
+    populateStubFor(&hal_fn->wifi_enable_sta_channel_for_peer_network);
     return true;
 }