Merge "Add more MLO link parameters"
diff --git a/audio/aidl/TEST_MAPPING b/audio/aidl/TEST_MAPPING
index 6a545c1..a166e61 100644
--- a/audio/aidl/TEST_MAPPING
+++ b/audio/aidl/TEST_MAPPING
@@ -13,9 +13,6 @@
       "name": "VtsHalDownmixTargetTest"
     },
     {
-      "name": "VtsHalDynamicsProcessingTargetTest"
-    },
-    {
       "name": "VtsHalEnvironmentalReverbTargetTest"
     },
     {
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/AcousticEchoCanceler.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/AcousticEchoCanceler.aidl
index 1d51ade..1ec7dad 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/AcousticEchoCanceler.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/AcousticEchoCanceler.aidl
@@ -42,10 +42,4 @@
     int vendorExtensionTag;
     android.hardware.audio.effect.AcousticEchoCanceler.Tag commonTag;
   }
-  @VintfStability
-  parcelable Capability {
-    ParcelableHolder extension;
-    int maxEchoDelayUs;
-    boolean supportMobileMode;
-  }
 }
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/AutomaticGainControl.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/AutomaticGainControl.aidl
index 39068d5..f3dd523 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/AutomaticGainControl.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/AutomaticGainControl.aidl
@@ -43,12 +43,6 @@
     int vendorExtensionTag;
     android.hardware.audio.effect.AutomaticGainControl.Tag commonTag;
   }
-  @VintfStability
-  parcelable Capability {
-    ParcelableHolder extension;
-    int maxFixedDigitalGainMb;
-    int maxSaturationMarginMb;
-  }
   @Backing(type="int") @VintfStability
   enum LevelEstimator {
     RMS = 0,
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/AutomaticGainControlV1.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/AutomaticGainControlV1.aidl
index ff010c6..57d4418 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/AutomaticGainControlV1.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/AutomaticGainControlV1.aidl
@@ -43,9 +43,4 @@
     int vendorExtensionTag;
     android.hardware.audio.effect.AutomaticGainControlV1.Tag commonTag;
   }
-  @VintfStability
-  parcelable Capability {
-    ParcelableHolder extension;
-    android.hardware.audio.effect.Range[] ranges;
-  }
 }
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/BassBoost.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/BassBoost.aidl
index f8baa2a..d09fe54 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/BassBoost.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/BassBoost.aidl
@@ -41,10 +41,4 @@
     int vendorExtensionTag;
     android.hardware.audio.effect.BassBoost.Tag commonTag;
   }
-  @VintfStability
-  parcelable Capability {
-    ParcelableHolder extension;
-    int maxStrengthPm;
-    boolean strengthSupported;
-  }
 }
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Capability.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Capability.aidl
index 28f77b3..c9df073 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Capability.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Capability.aidl
@@ -33,20 +33,7 @@
 
 package android.hardware.audio.effect;
 @VintfStability
-union Capability {
+parcelable Capability {
   android.hardware.audio.effect.VendorExtension vendorExtension;
-  android.hardware.audio.effect.AcousticEchoCanceler.Capability acousticEchoCanceler;
-  android.hardware.audio.effect.AutomaticGainControl.Capability automaticGainControl;
-  android.hardware.audio.effect.BassBoost.Capability bassBoost;
-  android.hardware.audio.effect.Downmix.Capability downmix;
-  android.hardware.audio.effect.DynamicsProcessing.Capability dynamicsProcessing;
-  android.hardware.audio.effect.EnvironmentalReverb.Capability environmentalReverb;
-  android.hardware.audio.effect.Equalizer.Capability equalizer;
-  android.hardware.audio.effect.HapticGenerator.Capability hapticGenerator;
-  android.hardware.audio.effect.LoudnessEnhancer.Capability loudnessEnhancer;
-  android.hardware.audio.effect.NoiseSuppression.Capability noiseSuppression;
-  android.hardware.audio.effect.PresetReverb.Capability presetReverb;
-  android.hardware.audio.effect.Virtualizer.Capability virtualizer;
-  android.hardware.audio.effect.Visualizer.Capability visualizer;
-  android.hardware.audio.effect.Volume.Capability volume;
+  android.hardware.audio.effect.Range range;
 }
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 402441d..45a1f28 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
@@ -42,10 +42,6 @@
     android.hardware.audio.effect.Downmix.Tag commonTag;
   }
   @VintfStability
-  parcelable Capability {
-    ParcelableHolder extension;
-  }
-  @VintfStability
   enum Type {
     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 8e5b719..3e20e33 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
@@ -49,12 +49,6 @@
     int vendorExtensionTag;
     android.hardware.audio.effect.DynamicsProcessing.Tag commonTag;
   }
-  @VintfStability
-  parcelable Capability {
-    ParcelableHolder extension;
-    float minCutOffFreq;
-    float maxCutOffFreq;
-  }
   enum ResolutionPreference {
     FAVOR_FREQUENCY_RESOLUTION,
     FAVOR_TIME_RESOLUTION,
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 9edad09..a193102 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
@@ -49,20 +49,4 @@
     int vendorExtensionTag;
     android.hardware.audio.effect.EnvironmentalReverb.Tag commonTag;
   }
-  @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/Equalizer.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Equalizer.aidl
index ea63de5..3e3539f 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Equalizer.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Equalizer.aidl
@@ -38,18 +38,14 @@
   android.hardware.audio.effect.Equalizer.BandLevel[] bandLevels;
   int preset;
   int[] centerFreqMh;
+  android.hardware.audio.effect.Equalizer.BandFrequency[] bandFrequencies;
+  android.hardware.audio.effect.Equalizer.Preset[] presets;
   @VintfStability
   union Id {
     int vendorExtensionTag;
     android.hardware.audio.effect.Equalizer.Tag commonTag;
   }
   @VintfStability
-  parcelable Capability {
-    ParcelableHolder extension;
-    android.hardware.audio.effect.Equalizer.BandFrequency[] bandFrequencies;
-    android.hardware.audio.effect.Equalizer.Preset[] presets;
-  }
-  @VintfStability
   parcelable BandLevel {
     int index;
     int levelMb;
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 35186c3..a7dc265 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
@@ -42,10 +42,6 @@
     int vendorExtensionTag;
     android.hardware.audio.effect.HapticGenerator.Tag commonTag;
   }
-  @VintfStability
-  parcelable Capability {
-    android.hardware.audio.effect.VendorExtension extension;
-  }
   @Backing(type="int") @VintfStability
   enum VibratorScale {
     MUTE = (-100) /* -100 */,
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/LoudnessEnhancer.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/LoudnessEnhancer.aidl
index 5c6ca16..774f45f 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/LoudnessEnhancer.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/LoudnessEnhancer.aidl
@@ -41,8 +41,4 @@
     int vendorExtensionTag;
     android.hardware.audio.effect.LoudnessEnhancer.Tag commonTag;
   }
-  @VintfStability
-  parcelable Capability {
-    android.hardware.audio.effect.VendorExtension extension;
-  }
 }
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 153e021..f1a3449 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
@@ -42,10 +42,6 @@
     int vendorExtensionTag;
     android.hardware.audio.effect.NoiseSuppression.Tag commonTag;
   }
-  @VintfStability
-  parcelable Capability {
-    ParcelableHolder extension;
-  }
   @Backing(type="int") @VintfStability
   enum Level {
     LOW,
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 4651742..148f79d 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
@@ -35,6 +35,7 @@
 @VintfStability
 union PresetReverb {
   android.hardware.audio.effect.VendorExtension vendor;
+  android.hardware.audio.effect.PresetReverb.Presets[] supportedPresets;
   android.hardware.audio.effect.PresetReverb.Presets preset;
   @Backing(type="int") @VintfStability
   enum Presets {
@@ -51,9 +52,4 @@
     int vendorExtensionTag;
     android.hardware.audio.effect.PresetReverb.Tag commonTag;
   }
-  @VintfStability
-  parcelable Capability {
-    android.hardware.audio.effect.VendorExtension extension;
-    android.hardware.audio.effect.PresetReverb.Presets[] supportedPresets;
-  }
 }
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Range.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Range.aidl
index 531d3a1..14e9cd8 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Range.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Range.aidl
@@ -33,34 +33,101 @@
 
 package android.hardware.audio.effect;
 @VintfStability
-parcelable Range {
-  int tag;
-  android.hardware.audio.effect.Range.Types types;
+union Range {
+  android.hardware.audio.effect.Range.VendorExtensionRange[] vendorExtension = {};
+  android.hardware.audio.effect.Range.AcousticEchoCancelerRange[] acousticEchoCanceler;
+  android.hardware.audio.effect.Range.AutomaticGainControlRange[] automaticGainControl;
+  android.hardware.audio.effect.Range.AutomaticGainControlV1Range[] automaticGainControlV1;
+  android.hardware.audio.effect.Range.BassBoostRange[] bassBoost;
+  android.hardware.audio.effect.Range.DownmixRange[] downmix;
+  android.hardware.audio.effect.Range.DynamicsProcessingRange[] dynamicsProcessing;
+  android.hardware.audio.effect.Range.EnvironmentalReverbRange[] environmentalReverb;
+  android.hardware.audio.effect.Range.EqualizerRange[] equalizer;
+  android.hardware.audio.effect.Range.HapticGeneratorRange[] hapticGenerator;
+  android.hardware.audio.effect.Range.LoudnessEnhancerRange[] loudnessEnhancer;
+  android.hardware.audio.effect.Range.NoiseSuppressionRange[] noiseSuppression;
+  android.hardware.audio.effect.Range.PresetReverbRange[] presetReverb;
+  android.hardware.audio.effect.Range.VirtualizerRange[] virtualizer;
+  android.hardware.audio.effect.Range.VisualizerRange[] visualizer;
+  android.hardware.audio.effect.Range.VolumeRange[] volume;
   @VintfStability
-  parcelable Int {
-    int min;
-    int max;
+  parcelable AcousticEchoCancelerRange {
+    android.hardware.audio.effect.AcousticEchoCanceler min;
+    android.hardware.audio.effect.AcousticEchoCanceler max;
   }
   @VintfStability
-  parcelable Float {
-    float min;
-    float max;
+  parcelable AutomaticGainControlRange {
+    android.hardware.audio.effect.AutomaticGainControl min;
+    android.hardware.audio.effect.AutomaticGainControl max;
   }
   @VintfStability
-  parcelable Long {
-    long min;
-    long max;
+  parcelable AutomaticGainControlV1Range {
+    android.hardware.audio.effect.AutomaticGainControlV1 min;
+    android.hardware.audio.effect.AutomaticGainControlV1 max;
   }
   @VintfStability
-  parcelable Byte {
-    byte min;
-    byte max;
+  parcelable BassBoostRange {
+    android.hardware.audio.effect.BassBoost min;
+    android.hardware.audio.effect.BassBoost max;
   }
   @VintfStability
-  union Types {
-    android.hardware.audio.effect.Range.Int rangeInt;
-    android.hardware.audio.effect.Range.Float rangeFloat;
-    android.hardware.audio.effect.Range.Long rangeLong;
-    android.hardware.audio.effect.Range.Byte rangeByte;
+  parcelable DownmixRange {
+    android.hardware.audio.effect.Downmix min;
+    android.hardware.audio.effect.Downmix max;
+  }
+  @VintfStability
+  parcelable DynamicsProcessingRange {
+    android.hardware.audio.effect.DynamicsProcessing min;
+    android.hardware.audio.effect.DynamicsProcessing max;
+  }
+  @VintfStability
+  parcelable EnvironmentalReverbRange {
+    android.hardware.audio.effect.EnvironmentalReverb min;
+    android.hardware.audio.effect.EnvironmentalReverb max;
+  }
+  @VintfStability
+  parcelable EqualizerRange {
+    android.hardware.audio.effect.Equalizer min;
+    android.hardware.audio.effect.Equalizer max;
+  }
+  @VintfStability
+  parcelable HapticGeneratorRange {
+    android.hardware.audio.effect.HapticGenerator min;
+    android.hardware.audio.effect.HapticGenerator max;
+  }
+  @VintfStability
+  parcelable LoudnessEnhancerRange {
+    android.hardware.audio.effect.LoudnessEnhancer min;
+    android.hardware.audio.effect.LoudnessEnhancer max;
+  }
+  @VintfStability
+  parcelable NoiseSuppressionRange {
+    android.hardware.audio.effect.NoiseSuppression min;
+    android.hardware.audio.effect.NoiseSuppression max;
+  }
+  @VintfStability
+  parcelable PresetReverbRange {
+    android.hardware.audio.effect.PresetReverb min;
+    android.hardware.audio.effect.PresetReverb max;
+  }
+  @VintfStability
+  parcelable VendorExtensionRange {
+    android.hardware.audio.effect.VendorExtension min;
+    android.hardware.audio.effect.VendorExtension max;
+  }
+  @VintfStability
+  parcelable VirtualizerRange {
+    android.hardware.audio.effect.Virtualizer min;
+    android.hardware.audio.effect.Virtualizer max;
+  }
+  @VintfStability
+  parcelable VisualizerRange {
+    android.hardware.audio.effect.Visualizer min;
+    android.hardware.audio.effect.Visualizer max;
+  }
+  @VintfStability
+  parcelable VolumeRange {
+    android.hardware.audio.effect.Volume min;
+    android.hardware.audio.effect.Volume max;
   }
 }
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Virtualizer.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Virtualizer.aidl
index 2f367d9..e9611e4 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Virtualizer.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Virtualizer.aidl
@@ -50,12 +50,6 @@
     android.media.audio.common.AudioDeviceDescription device;
   }
   @VintfStability
-  parcelable Capability {
-    android.hardware.audio.effect.VendorExtension extension;
-    int maxStrengthPm;
-    boolean strengthSupported;
-  }
-  @VintfStability
   parcelable ChannelAngle {
     int channel;
     int azimuthDegree;
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 c8cb551..d1b1b3e 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
@@ -36,30 +36,18 @@
 union Visualizer {
   android.hardware.audio.effect.Visualizer.Id id;
   android.hardware.audio.effect.VendorExtension vendor;
-  android.hardware.audio.effect.Visualizer.GetOnlyParameters getOnlyParameters;
-  android.hardware.audio.effect.Visualizer.SetOnlyParameters setOnlyParameters;
+  android.hardware.audio.effect.Visualizer.Measurement measurement;
+  byte[] captureSampleBuffer;
+  int latencyMs;
   int captureSamples;
   android.hardware.audio.effect.Visualizer.ScalingMode scalingMode;
   android.hardware.audio.effect.Visualizer.MeasurementMode measurementMode;
   @VintfStability
   union Id {
     int vendorExtensionTag;
-    android.hardware.audio.effect.Visualizer.GetOnlyParameters.Tag getOnlyParamTag;
-    android.hardware.audio.effect.Visualizer.SetOnlyParameters.Tag setOnlyParamTag;
     android.hardware.audio.effect.Visualizer.Tag commonTag;
   }
   @VintfStability
-  parcelable Capability {
-    android.hardware.audio.effect.VendorExtension extension;
-    int maxLatencyMs;
-    android.hardware.audio.effect.Visualizer.CaptureSamplesRange captureSampleRange;
-  }
-  @VintfStability
-  parcelable CaptureSamplesRange {
-    int min;
-    int max;
-  }
-  @VintfStability
   enum ScalingMode {
     NORMALIZED = 0,
     AS_PLAYED,
@@ -70,17 +58,8 @@
     PEAK_RMS,
   }
   @VintfStability
-  union GetOnlyParameters {
-    android.hardware.audio.effect.Visualizer.GetOnlyParameters.Measurement measurement;
-    byte[] captureSampleBuffer;
-    @VintfStability
-    parcelable Measurement {
-      int rms;
-      int peak;
-    }
-  }
-  @VintfStability
-  union SetOnlyParameters {
-    int latencyMs;
+  parcelable Measurement {
+    int rms;
+    int peak;
   }
 }
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Volume.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Volume.aidl
index 6259cfb..c2b2df7 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Volume.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Volume.aidl
@@ -42,10 +42,4 @@
     int vendorExtensionTag;
     android.hardware.audio.effect.Volume.Tag commonTag;
   }
-  @VintfStability
-  parcelable Capability {
-    android.hardware.audio.effect.VendorExtension extension;
-    int minLevelDb;
-    int maxLevelDb;
-  }
 }
diff --git a/audio/aidl/android/hardware/audio/effect/AcousticEchoCanceler.aidl b/audio/aidl/android/hardware/audio/effect/AcousticEchoCanceler.aidl
index 19d60b6..49377d6 100644
--- a/audio/aidl/android/hardware/audio/effect/AcousticEchoCanceler.aidl
+++ b/audio/aidl/android/hardware/audio/effect/AcousticEchoCanceler.aidl
@@ -22,9 +22,9 @@
  * Acoustic Echo Canceler (AEC) is an audio pre-processor which removes the contribution of the
  * signal received from the remote party from the captured audio signal.
  *
- * All parameters defined in union AcousticEchoCanceler must be gettable and settable. The
- * capabilities defined in AcousticEchoCanceler.Capability can only acquired with
- * IEffect.getDescriptor() and not settable.
+ * All parameter settings must be inside the range of Capability.Range.acousticEchoCanceler
+ * definition if the definition for the corresponding parameter tag exist. See more detals about
+ * Range in Range.aidl.
  */
 @VintfStability
 union AcousticEchoCanceler {
@@ -43,35 +43,20 @@
     VendorExtension vendor;
 
     /**
-     * Capability supported by AEC implementation.
-     */
-    @VintfStability
-    parcelable Capability {
-        /**
-         * AEC capability extension, vendor can use this extension in case existing capability
-         * definition not enough.
-         */
-        ParcelableHolder extension;
-
-        /**
-         * Maximum AEC echo delay in microseconds supported.
-         */
-        int maxEchoDelayUs;
-        /**
-         * If AEC mobile mode was supported by the AEC implementation.
-         */
-        boolean supportMobileMode;
-    }
-
-    /**
      * The AEC echo delay in microseconds.
-     * Must never be negative, and not larger than maxEchoDelayUs in capability.
      */
     int echoDelayUs;
     /**
-     * If AEC mobile mode enabled.
-     * Can only be false if AEC implementation indicate not support mobile mode by set
-     * supportMobileMode to false in capability.
+     * Indicate if the AEC mobile mode is enabled or not.
+     * If an effect implementation supports enabling and disabling mobileMode at runtime, it does
+     * not need to set the min and max range for mobileMode, or report the mobileMode min/max range
+     * as [false, true] in Range.AcousticEchoCancelerRange. If the effect implementation doesn't
+     * support mobileMode, it must report the mobileMode range as [false, false]. If the effect
+     * implementation supports mobileMode and cannot be disabled, it must report the mobileMode
+     * range as [true, true].
+     * If the effect implementation sets the range as invalid: [true, false], it indicates that
+     * mobileMode setParameter is not supported, and clients can only use getParameter to check if
+     * it's enabled or not.
      */
     boolean mobileMode;
 }
diff --git a/audio/aidl/android/hardware/audio/effect/AutomaticGainControl.aidl b/audio/aidl/android/hardware/audio/effect/AutomaticGainControl.aidl
index e82a564..0c984b5 100644
--- a/audio/aidl/android/hardware/audio/effect/AutomaticGainControl.aidl
+++ b/audio/aidl/android/hardware/audio/effect/AutomaticGainControl.aidl
@@ -24,9 +24,9 @@
  * so that the output signal level is virtually constant. AGC can be used by applications where the
  * input signal dynamic range is not important but where a constant strong capture level is desired.
  *
- * All parameters defined in union AutomaticGainControl must be gettable and settable. The
- * capabilities defined in AutomaticGainControl.Capability can only acquired with
- * IEffect.getDescriptor() and not settable.
+ * All parameter settings must be inside the range of Capability.Range.automaticGainControl
+ * definition if the definition for the corresponding parameter tag exist. See more detals about
+ * Range in Range.aidl.
  */
 @VintfStability
 union AutomaticGainControl {
@@ -44,26 +44,6 @@
      */
     VendorExtension vendor;
 
-    /**
-     * Capability supported by AutomaticGainControl implementation.
-     */
-    @VintfStability
-    parcelable Capability {
-        /**
-         * AutomaticGainControl capability extension, vendor can use this extension in case existing
-         * capability definition not enough.
-         */
-        ParcelableHolder extension;
-        /**
-         * Max fixed digital gain supported by AGC implementation in millibel.
-         */
-        int maxFixedDigitalGainMb;
-        /**
-         * Max fixed saturation margin supported by AGC implementation in millibel.
-         */
-        int maxSaturationMarginMb;
-    }
-
     @VintfStability
     @Backing(type="int")
     enum LevelEstimator {
@@ -75,7 +55,6 @@
 
     /**
      * The AGC fixed digital gain in millibel.
-     * Must never be negative, and not larger than maxFixedDigitalGainMb in capability.
      */
     int fixedDigitalGainMb;
     /*
@@ -84,7 +63,6 @@
     LevelEstimator levelEstimator;
     /**
      * The AGC saturation margin in millibel.
-     * Must never be negative, and not larger than maxSaturationMarginMb in capability.
      */
     int saturationMarginMb;
 }
diff --git a/audio/aidl/android/hardware/audio/effect/AutomaticGainControlV1.aidl b/audio/aidl/android/hardware/audio/effect/AutomaticGainControlV1.aidl
index d6e0648..9b2feff 100644
--- a/audio/aidl/android/hardware/audio/effect/AutomaticGainControlV1.aidl
+++ b/audio/aidl/android/hardware/audio/effect/AutomaticGainControlV1.aidl
@@ -16,7 +16,6 @@
 
 package android.hardware.audio.effect;
 
-import android.hardware.audio.effect.Range;
 import android.hardware.audio.effect.VendorExtension;
 
 /**
@@ -25,9 +24,9 @@
  * so that the output signal level is virtually constant. AGC can be used by applications where the
  * input signal dynamic range is not important but where a constant strong capture level is desired.
  *
- * All parameters defined in union AutomaticGainControlV1 must be gettable and settable. The
- * capabilities defined in AutomaticGainControlV1.Capability can only acquired with
- * IEffect.getDescriptor() and not settable.
+ * All parameter settings must be inside the range of Capability.Range.automaticGainControlV1
+ * definition if the definition for the corresponding parameter tag exist. See more detals about
+ * Range in Range.aidl.
  */
 @VintfStability
 union AutomaticGainControlV1 {
@@ -46,31 +45,13 @@
     VendorExtension vendor;
 
     /**
-     * Capability supported by AutomaticGainControlV1 implementation.
-     */
-    @VintfStability
-    parcelable Capability {
-        /**
-         * AutomaticGainControlV1 capability extension, vendor can use this extension in case
-         * existing capability definition not enough.
-         */
-        ParcelableHolder extension;
-        /**
-         * Supported range for parameters.
-         */
-        Range[] ranges;
-    }
-
-    /**
      * Target peak level (or envelope) of the AGC implementation in dBFs (dB relative to full
      * scale).
-     * Must be in range of AutomaticGainControlV1.Capability.ranges with targetPeakLevelDbFs tag.
      */
     int targetPeakLevelDbFs;
     /*
      * Sets the maximum gain the digital compression stage may apply, in dB. A higher number
      * corresponds to greater compression, while a value of 0 will leave the signal uncompressed.
-     * Must be in range of AutomaticGainControlV1.Capability.ranges with maxCompressionGainDb tag.
      */
     int maxCompressionGainDb;
     /**
diff --git a/audio/aidl/android/hardware/audio/effect/BassBoost.aidl b/audio/aidl/android/hardware/audio/effect/BassBoost.aidl
index 6a94242..d734825 100644
--- a/audio/aidl/android/hardware/audio/effect/BassBoost.aidl
+++ b/audio/aidl/android/hardware/audio/effect/BassBoost.aidl
@@ -22,8 +22,8 @@
  * Bass boost is an audio effect to boost or amplify low frequencies of the sound. It is comparable
  * to a simple equalizer but limited to one band amplification in the low frequency range.
  *
- * All parameters defined in union BassBoost must be gettable and settable. The capabilities defined
- * in BassBoost.Capability can only acquired with IEffect.getDescriptor() and not settable.
+ * All parameter settings must be inside the range of Capability.Range.bassBoost definition if the
+ * definition for the corresponding parameter tag exist. See more detals about Range in Range.aidl.
  */
 @VintfStability
 union BassBoost {
@@ -42,35 +42,11 @@
     VendorExtension vendor;
 
     /**
-     * Capability supported by BassBoost implementation.
-     */
-    @VintfStability
-    parcelable Capability {
-        /**
-         * BassBoost capability extension, vendor can use this extension in case existing capability
-         * definition not enough.
-         */
-        ParcelableHolder extension;
-        /**
-         * Maximum possible per mille strength.
-         */
-        int maxStrengthPm;
-        /**
-         * Indicates whether setting strength is supported. False value indicates only one strength
-         * is supported and setParameter() method will return EX_ILLEGAL_ARGUMENT.
-         */
-        boolean strengthSupported;
-    }
-
-    /**
      * The per mille strength of the bass boost effect.
      *
      * If the implementation does not support per mille accuracy for setting the strength, it is
      * allowed to round the given strength to the nearest supported value. In this case {@link
      * #IEffect.getParameter()} method should return the rounded value that was actually set.
-     *
-     * The value of the strength must be non-negative and not exceed the value specified by
-     * the 'maxStrengthPm' capability.
      */
     int strengthPm;
 }
diff --git a/audio/aidl/android/hardware/audio/effect/Capability.aidl b/audio/aidl/android/hardware/audio/effect/Capability.aidl
index 30780e6..06c2024 100644
--- a/audio/aidl/android/hardware/audio/effect/Capability.aidl
+++ b/audio/aidl/android/hardware/audio/effect/Capability.aidl
@@ -16,21 +16,8 @@
 
 package android.hardware.audio.effect;
 
-import android.hardware.audio.effect.AcousticEchoCanceler;
-import android.hardware.audio.effect.AutomaticGainControl;
-import android.hardware.audio.effect.BassBoost;
-import android.hardware.audio.effect.Downmix;
-import android.hardware.audio.effect.DynamicsProcessing;
-import android.hardware.audio.effect.EnvironmentalReverb;
-import android.hardware.audio.effect.Equalizer;
-import android.hardware.audio.effect.HapticGenerator;
-import android.hardware.audio.effect.LoudnessEnhancer;
-import android.hardware.audio.effect.NoiseSuppression;
-import android.hardware.audio.effect.PresetReverb;
+import android.hardware.audio.effect.Range;
 import android.hardware.audio.effect.VendorExtension;
-import android.hardware.audio.effect.Virtualizer;
-import android.hardware.audio.effect.Visualizer;
-import android.hardware.audio.effect.Volume;
 
 /**
  * Effect capability definitions.
@@ -38,32 +25,19 @@
  * not meant to change at runtime.
  */
 @VintfStability
-union Capability {
+parcelable Capability {
     /**
      * Vendor defined effect capability.
-     * This extension can be used when vendor have a new effect implementated and need
+     * This extension can be used when vendor has a new effect implementation and needs
      * capability definition for this new type of effect.
-     * If vendor want to extend existing effect capabilities, it is recommended to expose though
-     * the ParcelableHolder in each effect capability definition. For example:
-     * Equalizer.Capability.extension.
+     * If vendor want to extend existing effect capabilities, it is recommended to expose through
+     * the ParcelableHolder in each effect definition. For example: Equalizer.vendorExtension. And
+     * define an appropriate Range for the extension.
      */
     VendorExtension vendorExtension;
 
     /**
-     * Effect capabilities.
+     * Supported range for parameters. See Range.aidl.
      */
-    AcousticEchoCanceler.Capability acousticEchoCanceler;
-    AutomaticGainControl.Capability automaticGainControl;
-    BassBoost.Capability bassBoost;
-    Downmix.Capability downmix;
-    DynamicsProcessing.Capability dynamicsProcessing;
-    EnvironmentalReverb.Capability environmentalReverb;
-    Equalizer.Capability equalizer;
-    HapticGenerator.Capability hapticGenerator;
-    LoudnessEnhancer.Capability loudnessEnhancer;
-    NoiseSuppression.Capability noiseSuppression;
-    PresetReverb.Capability presetReverb;
-    Virtualizer.Capability virtualizer;
-    Visualizer.Capability visualizer;
-    Volume.Capability volume;
+    Range range;
 }
diff --git a/audio/aidl/android/hardware/audio/effect/Downmix.aidl b/audio/aidl/android/hardware/audio/effect/Downmix.aidl
index ee57baf..f90e339 100644
--- a/audio/aidl/android/hardware/audio/effect/Downmix.aidl
+++ b/audio/aidl/android/hardware/audio/effect/Downmix.aidl
@@ -21,8 +21,8 @@
 /**
  * Downmix specific definitions.
  *
- * All parameters defined in union Downmix must be gettable and settable. The capabilities defined
- * in Downmix.Capability can only acquired with IEffect.getDescriptor() and not settable.
+ * All parameter settings must be inside the range of Capability.Range.downmix definition if the
+ * definition for the corresponding parameter tag exist. See more detals about Range in Range.aidl.
  */
 @VintfStability
 union Downmix {
@@ -40,18 +40,6 @@
      */
     VendorExtension vendor;
 
-    /**
-     * Capability supported by Downmix implementation.
-     */
-    @VintfStability
-    parcelable Capability {
-        /**
-         * Downmix capability extension, vendor can use this extension in case existing capability
-         * definition not enough.
-         */
-        ParcelableHolder extension;
-    }
-
     @VintfStability
     enum Type {
         /**
diff --git a/audio/aidl/android/hardware/audio/effect/DynamicsProcessing.aidl b/audio/aidl/android/hardware/audio/effect/DynamicsProcessing.aidl
index 6db3338..486d4f4 100644
--- a/audio/aidl/android/hardware/audio/effect/DynamicsProcessing.aidl
+++ b/audio/aidl/android/hardware/audio/effect/DynamicsProcessing.aidl
@@ -21,9 +21,9 @@
 /**
  * DynamicsProcessing specific definitions.
  *
- * All parameters defined in union DynamicsProcessing must be gettable and settable. The
- * capabilities defined in DynamicsProcessing.Capability can only acquired with
- * IEffect.getDescriptor() and not settable.
+ * All parameter settings must be inside the range of Capability.Range.dynamicsProcessing definition
+ * if the definition for the corresponding parameter tag exist. See more detals about Range in
+ * Range.aidl.
  */
 @VintfStability
 union DynamicsProcessing {
@@ -42,26 +42,6 @@
     VendorExtension vendorExtension;
 
     /**
-     * Capability supported by DynamicsProcessing implementation.
-     */
-    @VintfStability
-    parcelable Capability {
-        /**
-         * DynamicsProcessing capability extension, vendor can use this extension in case existing
-         * 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;
-    }
-
-    /**
      * Resolution preference definition.
      */
     enum ResolutionPreference {
@@ -159,8 +139,7 @@
          */
         boolean enable;
         /**
-         * Topmost frequency number (in Hz) this band will process. Must be in the range of
-         * [minCutOffFreq, maxCutOffFreq] defined in Capability.
+         * Topmost frequency number (in Hz) this band will process.
          */
         float cutoffFrequencyHz;
         /**
@@ -190,8 +169,7 @@
          */
         boolean enable;
         /**
-         * Topmost frequency number (in Hz) this band will process. Must be in the range of
-         * [minCutOffFreq, maxCutOffFreq] defined in Capability.
+         * Topmost frequency number (in Hz) this band will process.
          */
         float cutoffFrequencyHz;
         /**
diff --git a/audio/aidl/android/hardware/audio/effect/EnvironmentalReverb.aidl b/audio/aidl/android/hardware/audio/effect/EnvironmentalReverb.aidl
index fc98fe6..a158dca 100644
--- a/audio/aidl/android/hardware/audio/effect/EnvironmentalReverb.aidl
+++ b/audio/aidl/android/hardware/audio/effect/EnvironmentalReverb.aidl
@@ -21,9 +21,9 @@
 /**
  * Environmental Reverb specific definitions.
  *
- * All parameters defined in union Environmental must be gettable and settable. The capabilities
- * * defined in EnvironmentalReverb.Capability can only acquired with IEffect.getDescriptor() and
- * not * settable.
+ * All parameter settings must be inside the range of Capability.Range.environmentalReverb
+ * definition if the definition for the corresponding parameter tag exist. See more detals about
+ * Range in Range.aidl.
  */
 
 @VintfStability
@@ -43,103 +43,35 @@
     VendorExtension vendor;
 
     /**
-     * Capability supported by effect implementation.
-     */
-    @VintfStability
-    parcelable Capability {
-        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;
-    }
-
-    /**
-     * 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.
+     * Room level apply to the reverb effect in millibels.
      */
     int roomLevelMb;
     /**
-     * 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.
+     * Room HF level apply to the reverb effect in millibels.
      */
     int roomHfLevelMb;
     /**
-     * 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.
+     * Delay time apply to the reverb effect in milliseconds.
      */
     int decayTimeMs;
     /**
-     * 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.
+     * HF decay ratio in permilles.
      */
     int decayHfRatioPm;
     /**
-     * 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.
+     * Reverb level in millibels.
      */
     int levelMb;
     /**
-     * Reverb delay in milliseconds. The value of the delayMs must be non-negative and not
-     * exceed the value specified by the 'maxDelayMs' capability.
+     * Reverb delay in milliseconds.
      */
     int delayMs;
     /**
-     * Diffusion in permilles. The value of the diffusionPm must be non-negative and not
-     * exceed the value specified by the 'maxDiffusionPm' capability.
+     * Diffusion in permilles.
      */
     int diffusionPm;
     /**
-     * Density in permilles. The value of the densityPm must be non-negative and not
-     * exceed the value specified by the 'maxDensityPm' capability.
+     * Density in permilles.
      */
     int densityPm;
 
diff --git a/audio/aidl/android/hardware/audio/effect/Equalizer.aidl b/audio/aidl/android/hardware/audio/effect/Equalizer.aidl
index 7903fde..2bce151 100644
--- a/audio/aidl/android/hardware/audio/effect/Equalizer.aidl
+++ b/audio/aidl/android/hardware/audio/effect/Equalizer.aidl
@@ -21,8 +21,8 @@
 /**
  * Equalizer specific definitions.
  *
- * All parameters defined in union Equalizer must be gettable and settable. The capabilities defined
- * in Equalizer.Capability can only acquired with IEffect.getDescriptor() and not settable.
+ * All parameter settings must be inside the range of Capability.Range.equalizer definition if the
+ * definition for the corresponding parameter tag exist. See more detals about Range in Range.aidl.
  */
 @VintfStability
 union Equalizer {
@@ -41,28 +41,6 @@
     VendorExtension vendorExtension;
 
     /**
-     * Capability MUST be supported by Equalizer implementation.
-     */
-    @VintfStability
-    parcelable Capability {
-        /**
-         * Equalizer capability extension, vendor can use this extension in case existing capability
-         * definition not enough.
-         */
-        ParcelableHolder extension;
-
-        /**
-         * Bands frequency ranges supported.
-         */
-        BandFrequency[] bandFrequencies;
-
-        /**
-         * Presets name and index.
-         */
-        Preset[] presets;
-    }
-
-    /**
      * Level setting for each band in millibels.
      */
     @VintfStability
@@ -107,4 +85,14 @@
      * Get only parameter, get the center frequency for all bands in milliHertz.
      */
     int[] centerFreqMh;
+
+    /**
+     * Get only parameter, indicating bands frequency ranges supported by implementation.
+     */
+    BandFrequency[] bandFrequencies;
+
+    /**
+     * Get only parameter, indicating presets name and index supported by implementation.
+     */
+    Preset[] presets;
 }
diff --git a/audio/aidl/android/hardware/audio/effect/HapticGenerator.aidl b/audio/aidl/android/hardware/audio/effect/HapticGenerator.aidl
index 3063ee3..a8e4564 100644
--- a/audio/aidl/android/hardware/audio/effect/HapticGenerator.aidl
+++ b/audio/aidl/android/hardware/audio/effect/HapticGenerator.aidl
@@ -22,9 +22,9 @@
  * HapticGenerator specific definitions. HapticGenerator effect provide HapticGenerator control and
  * mute/unmute functionality.
  *
- * All parameters defined in union HapticGenerator must be gettable and settable. The capabilities
- * defined in HapticGenerator.Capability can only acquired with IEffect.getDescriptor() and not
- * settable.
+ * All parameter settings must be inside the range of Capability.Range.hapticGenerator definition if
+ * the definition for the corresponding parameter tag exist. See more detals about Range in
+ * Range.aidl.
  */
 @VintfStability
 union HapticGenerator {
@@ -42,18 +42,6 @@
      */
     VendorExtension vendorExtension;
 
-    /**
-     * Capability supported by HapticGenerator implementation.
-     */
-    @VintfStability
-    parcelable Capability {
-        /**
-         * HapticGenerator capability extension, vendor can use this extension in case existing
-         * capability definition not enough.
-         */
-        VendorExtension extension;
-    }
-
     @VintfStability
     @Backing(type="int")
     enum VibratorScale {
diff --git a/audio/aidl/android/hardware/audio/effect/LoudnessEnhancer.aidl b/audio/aidl/android/hardware/audio/effect/LoudnessEnhancer.aidl
index 0441f10..a7cbe8d 100644
--- a/audio/aidl/android/hardware/audio/effect/LoudnessEnhancer.aidl
+++ b/audio/aidl/android/hardware/audio/effect/LoudnessEnhancer.aidl
@@ -21,9 +21,9 @@
 /**
  * LoudnessEnhancer specific definitions.
  *
- * All parameters defined in union LoudnessEnhancer must be gettable and settable. The capabilities
- * defined in LoudnessEnhancer.Capability can only acquired with IEffect.getDescriptor() and not
- * settable.
+ * All parameter settings must be inside the range of Capability.Range.loudnessEnhancer definition
+ * if the definition for the corresponding parameter tag exist. See more detals about Range in
+ * Range.aidl.
  */
 @VintfStability
 union LoudnessEnhancer {
@@ -42,18 +42,6 @@
     VendorExtension vendor;
 
     /**
-     * Capability supported by LoudnessEnhancer implementation.
-     */
-    @VintfStability
-    parcelable Capability {
-        /**
-         * LoudnessEnhancer capability extension, vendor can use this extension in case existing
-         * capability definition not enough.
-         */
-        VendorExtension extension;
-    }
-
-    /**
      * The maximum gain in millibels (mB) applied to the signal to process, default value is 0 which
      * corresponds to no amplification.
      */
diff --git a/audio/aidl/android/hardware/audio/effect/NoiseSuppression.aidl b/audio/aidl/android/hardware/audio/effect/NoiseSuppression.aidl
index c4baff8..6c2fb5f 100644
--- a/audio/aidl/android/hardware/audio/effect/NoiseSuppression.aidl
+++ b/audio/aidl/android/hardware/audio/effect/NoiseSuppression.aidl
@@ -24,9 +24,9 @@
  * engine, AC system) or non-stationary (other peoples conversations, car horn) for more advanced
  * implementations.
  *
- * All parameters defined in union NoiseSuppression must be gettable and settable. The capabilities
- * defined in NoiseSuppression.Capability can only acquired with IEffect.getDescriptor() and not
- * settable.
+ * All parameter settings must be inside the range of Capability.Range.noiseSuppression definition
+ * if the definition for the corresponding parameter tag exist. See more detals about Range in
+ * Range.aidl.
  */
 @VintfStability
 union NoiseSuppression {
@@ -45,18 +45,6 @@
     VendorExtension vendor;
 
     /**
-     * Capability supported by NoiseSuppression implementation.
-     */
-    @VintfStability
-    parcelable Capability {
-        /**
-         * NoiseSuppression capability extension, vendor can use this extension in case existing
-         * capability definition not enough.
-         */
-        ParcelableHolder extension;
-    }
-
-    /**
      * Different level of Noise Suppression to set.
      * As an example, webrtc have NsConfig::SuppressionLevel::k6dB applied for LOW level noise
      * suppression, NsConfig::SuppressionLevel::k12dB for MEDIUM, and
diff --git a/audio/aidl/android/hardware/audio/effect/Parameter.aidl b/audio/aidl/android/hardware/audio/effect/Parameter.aidl
index 473dfb5..be7144f 100644
--- a/audio/aidl/android/hardware/audio/effect/Parameter.aidl
+++ b/audio/aidl/android/hardware/audio/effect/Parameter.aidl
@@ -44,6 +44,14 @@
  * 2. Parameters defined for a specific effect type.
  * 3. Extension parameters ParcelableHolder can be used for vendor effect definition.
  *
+ * All parameter settings must be inside the range of Capability.Range.$EffectType$ definition. If
+ * an effect implementation doesn't have limitation for a parameter, then don't define any effect
+ * range.
+ *
+ * All parameters are get-able, if any parameter doesn't support set, effect implementation should
+ * report the supported range for this parameter as range.min > range.max. If no support range is
+ * defined for a parameter, it means this parameter doesn't have any limitation.
+ *
  */
 @VintfStability
 union Parameter {
diff --git a/audio/aidl/android/hardware/audio/effect/PresetReverb.aidl b/audio/aidl/android/hardware/audio/effect/PresetReverb.aidl
index 6048eea..87c78b0 100644
--- a/audio/aidl/android/hardware/audio/effect/PresetReverb.aidl
+++ b/audio/aidl/android/hardware/audio/effect/PresetReverb.aidl
@@ -21,9 +21,9 @@
 /**
  * PresetReverb specific definitions.
  *
- * All parameters defined in union PresetReverb must be gettable and settable. The capabilities
- * defined in PresetReverb.Capability can only acquired with IEffect.getDescriptor() and not
- * settable.
+ * All parameter settings must be inside the range of Capability.Range.presetReverb definition if
+ * the definition for the corresponding parameter tag exist. See more detals about Range in
+ * Range.aidl.
  */
 @VintfStability
 union PresetReverb {
@@ -78,21 +78,24 @@
     VendorExtension vendor;
 
     /**
-     * Capability supported by effect implementation.
+     * The list of presets supported by implementation, effect implementation must declare the
+     * support of Presets with Capability.Range.presetReverb definition. For example, if an effect
+     * implementation supports all Presets in PresetReverb.Presets, then the capability will be:
+     *  const std::vector<PresetReverb::Presets> kSupportedPresets{
+     *          ndk::enum_range<PresetReverb::Presets>().begin(),
+     *          ndk::enum_range<PresetReverb::Presets>().end()};
+     *  const std::vector<Range::PresetReverbRange> kRanges = {
+     *          MAKE_RANGE(PresetReverb, supportedPresets, kSupportedPresets, kSupportedPresets)};
      */
-    @VintfStability
-    parcelable Capability {
-        VendorExtension extension;
-
-        /**
-         * List of presets supported.
-         */
-        Presets[] supportedPresets;
-    }
+    Presets[] supportedPresets;
 
     /**
      * Get current reverb preset when used in getParameter.
      * Enable a preset reverb when used in setParameter.
+     * With the current Range definition, there is no good way to define enum capability, so the
+     * Presets vector supportedPresets is used to defined the capability. Client must check the
+     * capability in PresetReverb.supportedPresets, and make sure to get the list of supported
+     * presets before setting.
      */
     Presets preset;
 }
diff --git a/audio/aidl/android/hardware/audio/effect/Range.aidl b/audio/aidl/android/hardware/audio/effect/Range.aidl
index c052502..30e2359 100644
--- a/audio/aidl/android/hardware/audio/effect/Range.aidl
+++ b/audio/aidl/android/hardware/audio/effect/Range.aidl
@@ -16,50 +16,205 @@
 
 package android.hardware.audio.effect;
 
+import android.hardware.audio.effect.AcousticEchoCanceler;
+import android.hardware.audio.effect.AutomaticGainControl;
+import android.hardware.audio.effect.AutomaticGainControlV1;
+import android.hardware.audio.effect.BassBoost;
+import android.hardware.audio.effect.Downmix;
+import android.hardware.audio.effect.DynamicsProcessing;
+import android.hardware.audio.effect.EnvironmentalReverb;
+import android.hardware.audio.effect.Equalizer;
+import android.hardware.audio.effect.HapticGenerator;
+import android.hardware.audio.effect.LoudnessEnhancer;
+import android.hardware.audio.effect.NoiseSuppression;
+import android.hardware.audio.effect.PresetReverb;
+import android.hardware.audio.effect.VendorExtension;
+import android.hardware.audio.effect.Virtualizer;
+import android.hardware.audio.effect.Visualizer;
+import android.hardware.audio.effect.Volume;
+
 /**
- * Define the range (min and max) of a certain field, identified by tag.
- * Can be used by effect capabilities to define supported value ranges.
+ * Define the supported range of effect parameters.
+ * Effect implementation must report the supported range of parameter/capability if there is any
+ * limitation.
+ * Range of each effect type is defined with a vector, because each $EffectType$Range can only
+ * describe the range of one parameter. For example, Range::AcousticEchoCancelerRange can only
+ * describe one of vendor, echoDelayUs, and mobileMode.
+ *
+ * If an effect implementation needs to define the valid range for a certain parameter, it can
+ * write the minimum/maximum supported value to the corresponding effect range, and add the range
+ * to vector of this effect type.
+ * Say if an AcousticEchoCanceler implementation wants to define the supported range of echoDelayUs
+ * to [0, 500], then the Capability range should include an item in acousticEchoCanceler list:
+ * std::vector<Range::AcousticEchoCancelerRange> kRanges = {
+ *       MAKE_RANGE(AcousticEchoCanceler, echoDelayUs, 0, 500)};
+ *
+ * For a more complex example, if a DynamicsProcessing implementation wants to define the
+ * supported range of preEqBand channel to [0, 1], band index to [0, 5], and cutoffFrequencyHz to
+ * [220, 20000]:
+ * Range::DynamicsProcessingRange kRange = {
+ *   .min = DynamicsProcessing::make<DynamicsProcessing::preEqBand>(
+ *           {EqBandConfig({.channel = 0,
+ *                          .band = 0,
+ *                          .enable = false,
+ *                          .cutoffFrequencyHz = 220,
+ *                          .gainDb = std::numeric_limits<float>::min()})}),
+ *   .max = DynamicsProcessing::make<DynamicsProcessing::preEqBand>(
+ *           {EqBandConfig({.channel = 1,
+ *                          .band = 5,
+ *                          .enable = true,
+ *                          .cutoffFrequencyHz = 20000,
+ *                          .gainDb = std::numeric_limits<float>::max()})})};
+ *
+ * For get only parameters, the effect implementation must define an invalid range (min > max), to
+ * indicate no parameter from the effect client can be accepted for setting. The effect
+ * implementation must return EX_ILLEGAL_ARGUMENT if it receives any setParameter call for a get
+ * only parameter.
+ * As an example, the get-only parameter Virtualizer.speakerAngles can be defined with:
+ *   Range::VirtualizerRange kRanges = {
+ *       MAKE_RANGE(Virtualizer, speakerAngles,
+ *                  {Virtualizer::ChannelAngle({.channel = 1},
+ *                  {Virtualizer::ChannelAngle({.channel = 0})};
+ *
+ * For a capability definition (which is also get only), the effect implementation must define a
+ * range with min == max, to indicate this is a fixed capability which shouldn't set by client.
+ * As an example, the Equalizer presets capability can be defined with:
+ * std::vector<Equalizer::Preset> kPresets = {
+ *       {0, "Normal"},      {1, "Classical"}, {2, "Dance"}, {3, "Flat"}, {4, "Folk"},
+ *       {5, "Heavy Metal"}, {6, "Hip Hop"},   {7, "Jazz"},  {8, "Pop"},  {9, "Rock"}};
+ * Range::EqualizerRange kRanges =
+ *      MAKE_RANGE(Equalizer, presets, EqualizerSw::kPresets, EqualizerSw::kPresets);
+ *
+ * For enum capability, it's necessary to either list a range, or explicitly list out all enums in
+ * the Range definition, see PresetReverb.supportedPresets as example.
+ *
+ * The effect implementation must return EX_ILLEGAL_ARGUMENT if:
+ * 1. receive any setParameter call for get only parameter (min > max).
+ * 2. receive any setParameter call for capability parameter definition (min == max).
+ * 3. receive any setParameter call for parameters not in the range of [min, max].
+ *
  */
 @VintfStability
-parcelable Range {
+union Range {
+    @VintfStability
+    parcelable AcousticEchoCancelerRange {
+        AcousticEchoCanceler min;
+        AcousticEchoCanceler max;
+    }
+
+    @VintfStability
+    parcelable AutomaticGainControlRange {
+        AutomaticGainControl min;
+        AutomaticGainControl max;
+    }
+
+    @VintfStability
+    parcelable AutomaticGainControlV1Range {
+        AutomaticGainControlV1 min;
+        AutomaticGainControlV1 max;
+    }
+
+    @VintfStability
+    parcelable BassBoostRange {
+        BassBoost min;
+        BassBoost max;
+    }
+
+    @VintfStability
+    parcelable DownmixRange {
+        Downmix min;
+        Downmix max;
+    }
+
+    @VintfStability
+    parcelable DynamicsProcessingRange {
+        DynamicsProcessing min;
+        DynamicsProcessing max;
+    }
+
+    @VintfStability
+    parcelable EnvironmentalReverbRange {
+        EnvironmentalReverb min;
+        EnvironmentalReverb max;
+    }
+
+    @VintfStability
+    parcelable EqualizerRange {
+        Equalizer min;
+        Equalizer max;
+    }
+
+    @VintfStability
+    parcelable HapticGeneratorRange {
+        HapticGenerator min;
+        HapticGenerator max;
+    }
+
+    @VintfStability
+    parcelable LoudnessEnhancerRange {
+        LoudnessEnhancer min;
+        LoudnessEnhancer max;
+    }
+
+    @VintfStability
+    parcelable NoiseSuppressionRange {
+        NoiseSuppression min;
+        NoiseSuppression max;
+    }
+
+    @VintfStability
+    parcelable PresetReverbRange {
+        PresetReverb min;
+        PresetReverb max;
+    }
+
+    @VintfStability
+    parcelable VendorExtensionRange {
+        VendorExtension min;
+        VendorExtension max;
+    }
+
+    @VintfStability
+    parcelable VirtualizerRange {
+        Virtualizer min;
+        Virtualizer max;
+    }
+
+    @VintfStability
+    parcelable VisualizerRange {
+        Visualizer min;
+        Visualizer max;
+    }
+
+    @VintfStability
+    parcelable VolumeRange {
+        Volume min;
+        Volume max;
+    }
+
     /**
-     * The union tag name which the range is defined for.
-     * For example: if used in AutomaticGainControlV1.Capability, value of Range.tag could be
-     * targetLevelDbFs or compressionGainDb.
+     * The vector of range defined for parameters of each effect implementation.
+     * Each element of the vector represents the min and max range of a parameter, so the size of
+     * vector is the number of parameter ranges defined for this effect implementation.
+     *
+     * The effect implementation must not have duplicated parameter range definition in the vector.
+     * Client side must go though the vector and make sure the parameter setting to effect
+     * implementation is in valid range.
      */
-    int tag;
-
-    @VintfStability
-    parcelable Int {
-        int min;
-        int max;
-    }
-
-    @VintfStability
-    parcelable Float {
-        float min;
-        float max;
-    }
-
-    @VintfStability
-    parcelable Long {
-        long min;
-        long max;
-    }
-
-    @VintfStability
-    parcelable Byte {
-        byte min;
-        byte max;
-    }
-
-    @VintfStability
-    union Types {
-        Int rangeInt;
-        Float rangeFloat;
-        Long rangeLong;
-        Byte rangeByte;
-    }
-
-    Types types;
+    VendorExtensionRange[] vendorExtension = {};
+    AcousticEchoCancelerRange[] acousticEchoCanceler;
+    AutomaticGainControlRange[] automaticGainControl;
+    AutomaticGainControlV1Range[] automaticGainControlV1;
+    BassBoostRange[] bassBoost;
+    DownmixRange[] downmix;
+    DynamicsProcessingRange[] dynamicsProcessing;
+    EnvironmentalReverbRange[] environmentalReverb;
+    EqualizerRange[] equalizer;
+    HapticGeneratorRange[] hapticGenerator;
+    LoudnessEnhancerRange[] loudnessEnhancer;
+    NoiseSuppressionRange[] noiseSuppression;
+    PresetReverbRange[] presetReverb;
+    VirtualizerRange[] virtualizer;
+    VisualizerRange[] visualizer;
+    VolumeRange[] volume;
 }
diff --git a/audio/aidl/android/hardware/audio/effect/Virtualizer.aidl b/audio/aidl/android/hardware/audio/effect/Virtualizer.aidl
index fc453ad..37ea2a4 100644
--- a/audio/aidl/android/hardware/audio/effect/Virtualizer.aidl
+++ b/audio/aidl/android/hardware/audio/effect/Virtualizer.aidl
@@ -24,9 +24,8 @@
  * Virtualizer specific definitions. An audio virtualizer is a general name for an effect to
  * spatialize audio channels.
  *
- * All parameters defined in union Virtualizer must be gettable and settable. The capabilities
- * defined in Virtualizer.Capability can only acquired with IEffect.getDescriptor() and not
- * settable.
+ * All parameter settings must be inside the range of Capability.Range.virtualizer definition if the
+ * definition for the corresponding parameter tag exist. See more detals about Range in Range.aidl.
  */
 @VintfStability
 union Virtualizer {
@@ -65,35 +64,12 @@
     }
 
     /**
-     * Capability supported by Virtualizer implementation.
-     */
-    @VintfStability
-    parcelable Capability {
-        /**
-         * Virtualizer capability extension, vendor can use this extension in case existing
-         * capability definition not enough.
-         */
-        VendorExtension extension;
-        /**
-         * Maximum possible per mille strength.
-         */
-        int maxStrengthPm;
-        /**
-         * Indicates whether setting strength is supported. False value indicates only one strength
-         * is supported and setParameter() method will always return EX_ILLEGAL_ARGUMENT.
-         */
-        boolean strengthSupported;
-    }
-
-    /**
      * The per mille strength of the virtualizer effect.
      *
      * If the implementation does not support per mille accuracy for setting the strength, it is
      * allowed to round the given strength to the nearest supported value. In this case {@link
      * #IEffect.getParameter()} method should return the rounded value that was actually set.
      *
-     * The value of the strength must be non-negative and not exceed the value specified by
-     * the 'maxStrengthPm' capability.
      */
     int strengthPm;
 
@@ -128,6 +104,7 @@
     ChannelAngle[] speakerAngles;
 
     /**
+     * Get only parameter.
      * The audio device on which virtualzation mode is forced.
      */
     AudioDeviceDescription device;
diff --git a/audio/aidl/android/hardware/audio/effect/Visualizer.aidl b/audio/aidl/android/hardware/audio/effect/Visualizer.aidl
index dfe29c8..8f2faaa 100644
--- a/audio/aidl/android/hardware/audio/effect/Visualizer.aidl
+++ b/audio/aidl/android/hardware/audio/effect/Visualizer.aidl
@@ -22,9 +22,8 @@
  * Visualizer specific definitions. Visualizer enables application to retrieve part of the currently
  * playing audio for visualization purpose
  *
- * All parameters defined in union Visualizer other than these in GetOnlyParameters and
- * SetOnlyParameters must be gettable and settable. The capabilities defined in
- * Visualizer.Capability can only acquired with IEffect.getDescriptor() and not settable.
+ * All parameter settings must be inside the range of Capability.Range.visualizer definition if the
+ * definition for the corresponding parameter tag exist. See more detals about Range in Range.aidl.
  *
  */
 @VintfStability
@@ -35,8 +34,6 @@
     @VintfStability
     union Id {
         int vendorExtensionTag;
-        GetOnlyParameters.Tag getOnlyParamTag;
-        SetOnlyParameters.Tag setOnlyParamTag;
         Visualizer.Tag commonTag;
     }
     Id id;
@@ -47,35 +44,6 @@
     VendorExtension vendor;
 
     /**
-     * Capability supported by Visualizer implementation.
-     */
-    @VintfStability
-    parcelable Capability {
-        /**
-         * Visualizer capability extension, vendor can use this extension in case existing
-         * capability definition not enough.
-         */
-        VendorExtension extension;
-        /**
-         * Max latency supported in millseconds.
-         */
-        int maxLatencyMs;
-        /**
-         *  Capture size range.
-         */
-        CaptureSamplesRange captureSampleRange;
-    }
-
-    /**
-     * Supported capture size range in samples.
-     */
-    @VintfStability
-    parcelable CaptureSamplesRange {
-        int min;
-        int max;
-    }
-
-    /**
      * Type of scaling applied on the captured visualization data.
      */
     @VintfStability
@@ -115,51 +83,42 @@
     }
 
     /**
-     * Any parameter defined in this union must be gettable via getParameter(), but must not
-     * settable.
+     * Get only parameter to get the current measurements.
      */
     @VintfStability
-    union GetOnlyParameters {
-        /**
-         * Get the current measurements.
-         */
-        @VintfStability
-        parcelable Measurement {
-            int rms;
-            int peak;
-        }
-        Measurement measurement;
-
-        /**
-         * Get the latest captureSamples of PCM samples (8 bits per sample).
-         */
-        byte[] captureSampleBuffer;
+    parcelable Measurement {
+        int rms;
+        int peak;
     }
-    GetOnlyParameters getOnlyParameters;
+    Measurement measurement;
 
     /**
-     * Any parameter defined in this union must be settable via setParameter(), but must not
-     * gettable.
+     * Get only parameter to get the latest captured samples of PCM samples (8 bits per sample).
      */
-    @VintfStability
-    union SetOnlyParameters {
-        /**
-         * Used by framework to inform the visualizer about the downstream latency (audio hardware
-         * driver estimated latency in milliseconds).
-         */
-        int latencyMs;
-    }
-    SetOnlyParameters setOnlyParameters;
+    byte[] captureSampleBuffer;
 
     /**
-     * Current capture size in number of samples. The capture size must be inside
-     * Capability.captureSizeRange.
+     * Used by framework to inform the visualizer about the downstream latency (audio hardware
+     * driver estimated latency in milliseconds).
+     *
+     * Visualizer implementation must use Range.VisualizerRange to define the range of supported
+     * latency.
+     */
+    int latencyMs;
+
+    /**
+     * Current capture size in number of samples.
+     *
+     * Visualizer implementation must use Range.VisualizerRange to define the range of supported
+     * capture size.
      */
     int captureSamples;
+
     /**
      * Visualizer capture mode
      */
     ScalingMode scalingMode;
+
     /**
      * Visualizer measurement mode.
      */
diff --git a/audio/aidl/android/hardware/audio/effect/Volume.aidl b/audio/aidl/android/hardware/audio/effect/Volume.aidl
index 5033881..4a76703 100644
--- a/audio/aidl/android/hardware/audio/effect/Volume.aidl
+++ b/audio/aidl/android/hardware/audio/effect/Volume.aidl
@@ -21,8 +21,8 @@
 /**
  * Volume specific definitions. Volume effect provide volume control and mute/unmute functionality.
  *
- * All parameters defined in union Volume must be gettable and settable. The capabilities defined in
- * Volume.Capability can only acquired with IEffect.getDescriptor() and not settable.
+ * All parameter settings must be inside the range of Capability.Range.volume definition if the
+ * definition for the corresponding parameter tag exist. See more detals about Range in Range.aidl.
  */
 @VintfStability
 union Volume {
@@ -41,29 +41,7 @@
     VendorExtension vendor;
 
     /**
-     * Capability supported by Volume implementation.
-     */
-    @VintfStability
-    parcelable Capability {
-        /**
-         * Volume capability extension, vendor can use this extension in case existing capability
-         * definition not enough.
-         */
-        VendorExtension extension;
-
-        /**
-         * Minimum Volume level supported in dB.
-         */
-        int minLevelDb;
-
-        /**
-         * Maximum Volume level supported in dB.
-         */
-        int maxLevelDb;
-    }
-
-    /**
-     * Current level in dB with supported minimum and maximum level specified in capability.
+     * Current level in dB.
      */
     int levelDb;
     /**
diff --git a/audio/aidl/default/EffectImpl.cpp b/audio/aidl/default/EffectImpl.cpp
index 403a4b9..fa9eb95 100644
--- a/audio/aidl/default/EffectImpl.cpp
+++ b/audio/aidl/default/EffectImpl.cpp
@@ -78,7 +78,7 @@
 ndk::ScopedAStatus EffectImpl::setParameter(const Parameter& param) {
     LOG(DEBUG) << __func__ << " with: " << param.toString();
 
-    auto tag = param.getTag();
+    const auto tag = param.getTag();
     switch (tag) {
         case Parameter::common:
         case Parameter::deviceDescription:
diff --git a/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.cpp b/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.cpp
index 40b46e0..f5af81e 100644
--- a/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.cpp
+++ b/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.cpp
@@ -30,6 +30,7 @@
 using aidl::android::hardware::audio::effect::Descriptor;
 using aidl::android::hardware::audio::effect::IEffect;
 using aidl::android::hardware::audio::effect::kAcousticEchoCancelerSwImplUUID;
+using aidl::android::hardware::audio::effect::Range;
 using aidl::android::media::audio::common::AudioUuid;
 
 extern "C" binder_exception_t createEffect(const AudioUuid* in_impl_uuid,
@@ -60,8 +61,14 @@
 namespace aidl::android::hardware::audio::effect {
 
 const std::string AcousticEchoCancelerSw::kEffectName = "AcousticEchoCancelerSw";
-const AcousticEchoCanceler::Capability AcousticEchoCancelerSw::kCapability = {
-        .maxEchoDelayUs = 500, .supportMobileMode = false};
+
+const std::vector<Range::AcousticEchoCancelerRange> AcousticEchoCancelerSw::kRanges = {
+        MAKE_RANGE(AcousticEchoCanceler, echoDelayUs, 0, 500),
+        /* mobile mode not supported, and not settable */
+        MAKE_RANGE(AcousticEchoCanceler, mobileMode, false, false)};
+
+const Capability AcousticEchoCancelerSw::kCapability = {.range = AcousticEchoCancelerSw::kRanges};
+
 const Descriptor AcousticEchoCancelerSw::kDescriptor = {
         .common = {.id = {.type = kAcousticEchoCancelerTypeUUID,
                           .uuid = kAcousticEchoCancelerSwImplUUID,
@@ -71,8 +78,7 @@
                              .volume = Flags::Volume::CTRL},
                    .name = AcousticEchoCancelerSw::kEffectName,
                    .implementor = "The Android Open Source Project"},
-        .capability = Capability::make<Capability::acousticEchoCanceler>(
-                AcousticEchoCancelerSw::kCapability)};
+        .capability = AcousticEchoCancelerSw::kCapability};
 
 ndk::ScopedAStatus AcousticEchoCancelerSw::getDescriptor(Descriptor* _aidl_return) {
     LOG(DEBUG) << __func__ << kDescriptor.toString();
@@ -87,8 +93,9 @@
     RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
 
     auto& param = specific.get<Parameter::Specific::acousticEchoCanceler>();
-    auto tag = param.getTag();
+    RETURN_IF(!inRange(param, kRanges), EX_ILLEGAL_ARGUMENT, "outOfRange");
 
+    auto tag = param.getTag();
     switch (tag) {
         case AcousticEchoCanceler::echoDelayUs: {
             RETURN_IF(mContext->setEchoDelay(param.get<AcousticEchoCanceler::echoDelayUs>()) !=
@@ -182,10 +189,6 @@
 }
 
 RetCode AcousticEchoCancelerSwContext::setEchoDelay(int echoDelayUs) {
-    if (echoDelayUs < 0 || echoDelayUs > AcousticEchoCancelerSw::kCapability.maxEchoDelayUs) {
-        LOG(DEBUG) << __func__ << " illegal delay " << echoDelayUs;
-        return RetCode::ERROR_ILLEGAL_PARAMETER;
-    }
     mEchoDelayUs = echoDelayUs;
     return RetCode::SUCCESS;
 }
diff --git a/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.h b/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.h
index 5f1bc46..753207d 100644
--- a/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.h
+++ b/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <aidl/android/hardware/audio/effect/BnEffect.h>
+#include <aidl/android/hardware/audio/effect/Range.h>
 #include <fmq/AidlMessageQueue.h>
 #include <cstdlib>
 #include <memory>
@@ -43,8 +44,7 @@
 class AcousticEchoCancelerSw final : public EffectImpl {
   public:
     static const std::string kEffectName;
-    static const bool kStrengthSupported;
-    static const AcousticEchoCanceler::Capability kCapability;
+    static const Capability kCapability;
     static const Descriptor kDescriptor;
     AcousticEchoCancelerSw() { LOG(DEBUG) << __func__; }
     ~AcousticEchoCancelerSw() {
@@ -65,6 +65,7 @@
     IEffect::Status effectProcessImpl(float* in, float* out, int samples) override;
 
   private:
+    static const std::vector<Range::AcousticEchoCancelerRange> kRanges;
     std::shared_ptr<AcousticEchoCancelerSwContext> mContext;
     ndk::ScopedAStatus getParameterAcousticEchoCanceler(const AcousticEchoCanceler::Tag& tag,
                                                         Parameter::Specific* specific);
diff --git a/audio/aidl/default/automaticGainControl/AutomaticGainControlSw.cpp b/audio/aidl/default/automaticGainControl/AutomaticGainControlSw.cpp
index 8c706ef..30d7910 100644
--- a/audio/aidl/default/automaticGainControl/AutomaticGainControlSw.cpp
+++ b/audio/aidl/default/automaticGainControl/AutomaticGainControlSw.cpp
@@ -60,8 +60,13 @@
 namespace aidl::android::hardware::audio::effect {
 
 const std::string AutomaticGainControlSw::kEffectName = "AutomaticGainControlSw";
-const AutomaticGainControl::Capability AutomaticGainControlSw::kCapability = {
-        .maxFixedDigitalGainMb = 50000, .maxSaturationMarginMb = 10000};
+
+const std::vector<Range::AutomaticGainControlRange> AutomaticGainControlSw::kRanges = {
+        MAKE_RANGE(AutomaticGainControl, fixedDigitalGainMb, 0, 50000),
+        MAKE_RANGE(AutomaticGainControl, saturationMarginMb, 0, 10000)};
+
+const Capability AutomaticGainControlSw::kCapability = {.range = AutomaticGainControlSw::kRanges};
+
 const Descriptor AutomaticGainControlSw::kDescriptor = {
         .common = {.id = {.type = kAutomaticGainControlTypeUUID,
                           .uuid = kAutomaticGainControlSwImplUUID,
@@ -71,8 +76,7 @@
                              .volume = Flags::Volume::CTRL},
                    .name = AutomaticGainControlSw::kEffectName,
                    .implementor = "The Android Open Source Project"},
-        .capability = Capability::make<Capability::automaticGainControl>(
-                AutomaticGainControlSw::kCapability)};
+        .capability = AutomaticGainControlSw::kCapability};
 
 ndk::ScopedAStatus AutomaticGainControlSw::getDescriptor(Descriptor* _aidl_return) {
     LOG(DEBUG) << __func__ << kDescriptor.toString();
@@ -87,8 +91,8 @@
     RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
 
     auto& param = specific.get<Parameter::Specific::automaticGainControl>();
+    RETURN_IF(!inRange(param, kRanges), EX_ILLEGAL_ARGUMENT, "outOfRange");
     auto tag = param.getTag();
-
     switch (tag) {
         case AutomaticGainControl::fixedDigitalGainMb: {
             RETURN_IF(mContext->setDigitalGain(
@@ -196,10 +200,6 @@
 }
 
 RetCode AutomaticGainControlSwContext::setDigitalGain(int gain) {
-    if (gain < 0 || gain > AutomaticGainControlSw::kCapability.maxFixedDigitalGainMb) {
-        LOG(DEBUG) << __func__ << " illegal digital gain " << gain;
-        return RetCode::ERROR_ILLEGAL_PARAMETER;
-    }
     mDigitalGain = gain;
     return RetCode::SUCCESS;
 }
@@ -219,10 +219,6 @@
 }
 
 RetCode AutomaticGainControlSwContext::setSaturationMargin(int margin) {
-    if (margin < 0 || margin > AutomaticGainControlSw::kCapability.maxSaturationMarginMb) {
-        LOG(DEBUG) << __func__ << " illegal saturationMargin " << margin;
-        return RetCode::ERROR_ILLEGAL_PARAMETER;
-    }
     mSaturationMargin = margin;
     return RetCode::SUCCESS;
 }
diff --git a/audio/aidl/default/automaticGainControl/AutomaticGainControlSw.h b/audio/aidl/default/automaticGainControl/AutomaticGainControlSw.h
index 2724835..9327410 100644
--- a/audio/aidl/default/automaticGainControl/AutomaticGainControlSw.h
+++ b/audio/aidl/default/automaticGainControl/AutomaticGainControlSw.h
@@ -51,7 +51,7 @@
   public:
     static const std::string kEffectName;
     static const bool kStrengthSupported;
-    static const AutomaticGainControl::Capability kCapability;
+    static const Capability kCapability;
     static const Descriptor kDescriptor;
     AutomaticGainControlSw() { LOG(DEBUG) << __func__; }
     ~AutomaticGainControlSw() {
@@ -72,6 +72,7 @@
     IEffect::Status effectProcessImpl(float* in, float* out, int samples) override;
 
   private:
+    static const std::vector<Range::AutomaticGainControlRange> kRanges;
     std::shared_ptr<AutomaticGainControlSwContext> mContext;
     ndk::ScopedAStatus getParameterAutomaticGainControl(const AutomaticGainControl::Tag& tag,
                                                         Parameter::Specific* specific);
diff --git a/audio/aidl/default/bassboost/BassBoostSw.cpp b/audio/aidl/default/bassboost/BassBoostSw.cpp
index 0c7ebe1..e50f0a2 100644
--- a/audio/aidl/default/bassboost/BassBoostSw.cpp
+++ b/audio/aidl/default/bassboost/BassBoostSw.cpp
@@ -61,9 +61,10 @@
 namespace aidl::android::hardware::audio::effect {
 
 const std::string BassBoostSw::kEffectName = "BassBoostSw";
-const bool BassBoostSw::kStrengthSupported = true;
-const BassBoost::Capability BassBoostSw::kCapability = {.maxStrengthPm = 1000,
-                                                        .strengthSupported = kStrengthSupported};
+
+const std::vector<Range::BassBoostRange> BassBoostSw::kRanges = {
+        MAKE_RANGE(BassBoost, strengthPm, 0, 1000)};
+const Capability BassBoostSw::kCapability = {.range = {BassBoostSw::kRanges}};
 const Descriptor BassBoostSw::kDescriptor = {
         .common = {.id = {.type = kBassBoostTypeUUID,
                           .uuid = kBassBoostSwImplUUID,
@@ -73,7 +74,7 @@
                              .volume = Flags::Volume::CTRL},
                    .name = BassBoostSw::kEffectName,
                    .implementor = "The Android Open Source Project"},
-        .capability = Capability::make<Capability::bassBoost>(BassBoostSw::kCapability)};
+        .capability = BassBoostSw::kCapability};
 
 ndk::ScopedAStatus BassBoostSw::getDescriptor(Descriptor* _aidl_return) {
     LOG(DEBUG) << __func__ << kDescriptor.toString();
@@ -87,15 +88,14 @@
     RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
 
     auto& bbParam = specific.get<Parameter::Specific::bassBoost>();
+    RETURN_IF(!inRange(bbParam, kRanges), EX_ILLEGAL_ARGUMENT, "outOfRange");
     auto tag = bbParam.getTag();
 
     switch (tag) {
         case BassBoost::strengthPm: {
-            RETURN_IF(!kStrengthSupported, EX_ILLEGAL_ARGUMENT, "SettingStrengthNotSupported");
-
-            RETURN_IF(mContext->setBbStrengthPm(bbParam.get<BassBoost::strengthPm>()) !=
-                              RetCode::SUCCESS,
-                      EX_ILLEGAL_ARGUMENT, "strengthPmNotSupported");
+            const auto strength = bbParam.get<BassBoost::strengthPm>();
+            RETURN_IF(mContext->setBbStrengthPm(strength) != RetCode::SUCCESS, EX_ILLEGAL_ARGUMENT,
+                      "strengthPmNotSupported");
             return ndk::ScopedAStatus::ok();
         }
         default: {
@@ -173,11 +173,6 @@
 }
 
 RetCode BassBoostSwContext::setBbStrengthPm(int strength) {
-    if (strength < 0 || strength > BassBoostSw::kCapability.maxStrengthPm) {
-        LOG(ERROR) << __func__ << " invalid strength: " << strength;
-        return RetCode::ERROR_ILLEGAL_PARAMETER;
-    }
-    // TODO : Add implementation to apply new strength
     mStrength = strength;
     return RetCode::SUCCESS;
 }
diff --git a/audio/aidl/default/bassboost/BassBoostSw.h b/audio/aidl/default/bassboost/BassBoostSw.h
index 65c01c8..8d183dd 100644
--- a/audio/aidl/default/bassboost/BassBoostSw.h
+++ b/audio/aidl/default/bassboost/BassBoostSw.h
@@ -43,8 +43,7 @@
 class BassBoostSw final : public EffectImpl {
   public:
     static const std::string kEffectName;
-    static const bool kStrengthSupported;
-    static const BassBoost::Capability kCapability;
+    static const Capability kCapability;
     static const Descriptor kDescriptor;
     BassBoostSw() { LOG(DEBUG) << __func__; }
     ~BassBoostSw() {
@@ -65,6 +64,7 @@
     IEffect::Status effectProcessImpl(float* in, float* out, int samples) override;
 
   private:
+    static const std::vector<Range::BassBoostRange> kRanges;
     std::shared_ptr<BassBoostSwContext> mContext;
     ndk::ScopedAStatus getParameterBassBoost(const BassBoost::Tag& tag,
                                              Parameter::Specific* specific);
diff --git a/audio/aidl/default/downmix/DownmixSw.cpp b/audio/aidl/default/downmix/DownmixSw.cpp
index 7bb958d..0af95d0 100644
--- a/audio/aidl/default/downmix/DownmixSw.cpp
+++ b/audio/aidl/default/downmix/DownmixSw.cpp
@@ -60,17 +60,14 @@
 namespace aidl::android::hardware::audio::effect {
 
 const std::string DownmixSw::kEffectName = "DownmixSw";
-const Downmix::Capability DownmixSw::kCapability;
 const Descriptor DownmixSw::kDescriptor = {
-        .common = {.id = {.type = kDownmixTypeUUID,
-                          .uuid = kDownmixSwImplUUID,
-                          .proxy = std::nullopt},
-                   .flags = {.type = Flags::Type::INSERT,
-                             .insert = Flags::Insert::FIRST,
-                             .volume = Flags::Volume::CTRL},
-                   .name = kEffectName,
-                   .implementor = "The Android Open Source Project"},
-        .capability = Capability::make<Capability::downmix>(kCapability)};
+        .common = {
+                .id = {.type = kDownmixTypeUUID, .uuid = kDownmixSwImplUUID, .proxy = std::nullopt},
+                .flags = {.type = Flags::Type::INSERT,
+                          .insert = Flags::Insert::FIRST,
+                          .volume = Flags::Volume::CTRL},
+                .name = kEffectName,
+                .implementor = "The Android Open Source Project"}};
 
 ndk::ScopedAStatus DownmixSw::getDescriptor(Descriptor* _aidl_return) {
     LOG(DEBUG) << __func__ << kDescriptor.toString();
diff --git a/audio/aidl/default/downmix/DownmixSw.h b/audio/aidl/default/downmix/DownmixSw.h
index 51546c1..37c978b 100644
--- a/audio/aidl/default/downmix/DownmixSw.h
+++ b/audio/aidl/default/downmix/DownmixSw.h
@@ -47,7 +47,7 @@
 class DownmixSw final : public EffectImpl {
   public:
     static const std::string kEffectName;
-    static const Downmix::Capability kCapability;
+    static const Capability kCapability;
     static const Descriptor kDescriptor;
     DownmixSw() { LOG(DEBUG) << __func__; }
     ~DownmixSw() {
diff --git a/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp b/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp
index 0ffbaa1..da6d0c6 100644
--- a/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp
+++ b/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp
@@ -61,8 +61,33 @@
 namespace aidl::android::hardware::audio::effect {
 
 const std::string DynamicsProcessingSw::kEffectName = "DynamicsProcessingSw";
-const DynamicsProcessing::Capability DynamicsProcessingSw::kCapability = {.minCutOffFreq = 220,
-                                                                          .maxCutOffFreq = 20000};
+const DynamicsProcessing::EqBandConfig DynamicsProcessingSw::kEqBandConfigMin =
+        DynamicsProcessing::EqBandConfig({.channel = 0,
+                                          .band = 0,
+                                          .enable = false,
+                                          .cutoffFrequencyHz = 220,
+                                          .gainDb = std::numeric_limits<float>::min()});
+const DynamicsProcessing::EqBandConfig DynamicsProcessingSw::kEqBandConfigMax =
+        DynamicsProcessing::EqBandConfig({.channel = std::numeric_limits<int>::max(),
+                                          .band = std::numeric_limits<int>::max(),
+                                          .enable = true,
+                                          .cutoffFrequencyHz = 20000,
+                                          .gainDb = std::numeric_limits<float>::max()});
+const Range::DynamicsProcessingRange DynamicsProcessingSw::kPreEqBandRange = {
+        .min = DynamicsProcessing::make<DynamicsProcessing::preEqBand>(
+                {DynamicsProcessingSw::kEqBandConfigMin}),
+        .max = DynamicsProcessing::make<DynamicsProcessing::preEqBand>(
+                {DynamicsProcessingSw::kEqBandConfigMax})};
+const Range::DynamicsProcessingRange DynamicsProcessingSw::kPostEqBandRange = {
+        .min = DynamicsProcessing::make<DynamicsProcessing::postEqBand>(
+                {DynamicsProcessingSw::kEqBandConfigMin}),
+        .max = DynamicsProcessing::make<DynamicsProcessing::postEqBand>(
+                {DynamicsProcessingSw::kEqBandConfigMax})};
+
+const std::vector<Range::DynamicsProcessingRange> DynamicsProcessingSw::kRanges = {
+        DynamicsProcessingSw::kPreEqBandRange, DynamicsProcessingSw::kPostEqBandRange};
+const Capability DynamicsProcessingSw::kCapability = {.range = DynamicsProcessingSw::kRanges};
+
 const Descriptor DynamicsProcessingSw::kDescriptor = {
         .common = {.id = {.type = kDynamicsProcessingTypeUUID,
                           .uuid = kDynamicsProcessingSwImplUUID,
@@ -72,8 +97,7 @@
                              .volume = Flags::Volume::CTRL},
                    .name = DynamicsProcessingSw::kEffectName,
                    .implementor = "The Android Open Source Project"},
-        .capability = Capability::make<Capability::dynamicsProcessing>(
-                DynamicsProcessingSw::kCapability)};
+        .capability = DynamicsProcessingSw::kCapability};
 
 ndk::ScopedAStatus DynamicsProcessingSw::getDescriptor(Descriptor* _aidl_return) {
     LOG(DEBUG) << __func__ << kDescriptor.toString();
@@ -341,7 +365,6 @@
             LOG(WARNING) << __func__ << " skip invalid band " << cfg.toString();
             ret = RetCode::ERROR_ILLEGAL_PARAMETER;
             continue;
-            ;
         }
         targetCfgs[cfg.channel * stage.bandCount + cfg.band] = cfg;
     }
@@ -380,7 +403,6 @@
             LOG(WARNING) << __func__ << " skip invalid band " << it.toString();
             ret = RetCode::ERROR_ILLEGAL_PARAMETER;
             continue;
-            ;
         }
         mMbcChBands[it.channel * bandCount + it.band] = it;
     }
@@ -462,11 +484,6 @@
     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);
@@ -484,7 +501,7 @@
         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.band >= 0 && band.band < maxBand;
 }
 
 bool DynamicsProcessingSwContext::validateMbcBandConfig(
@@ -492,8 +509,7 @@
         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.band >= 0 && band.band < maxBand && band.attackTimeMs >= 0 &&
            band.releaseTimeMs >= 0 && band.ratio >= 0 && band.thresholdDb <= 0 &&
            band.kneeWidthDb <= 0 && band.noiseGateThresholdDb <= 0 && band.expanderRatio >= 0;
 }
diff --git a/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.h b/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.h
index e336df7..3e14cce 100644
--- a/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.h
+++ b/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.h
@@ -86,8 +86,6 @@
     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,
@@ -104,7 +102,7 @@
 class DynamicsProcessingSw final : public EffectImpl {
   public:
     static const std::string kEffectName;
-    static const DynamicsProcessing::Capability kCapability;
+    static const Capability kCapability;
     static const Descriptor kDescriptor;
     DynamicsProcessingSw() { LOG(DEBUG) << __func__; }
     ~DynamicsProcessingSw() {
@@ -125,6 +123,11 @@
     std::string getEffectName() override { return kEffectName; };
 
   private:
+    static const DynamicsProcessing::EqBandConfig kEqBandConfigMin;
+    static const DynamicsProcessing::EqBandConfig kEqBandConfigMax;
+    static const Range::DynamicsProcessingRange kPreEqBandRange;
+    static const Range::DynamicsProcessingRange kPostEqBandRange;
+    static const std::vector<Range::DynamicsProcessingRange> kRanges;
     std::shared_ptr<DynamicsProcessingSwContext> mContext;
     ndk::ScopedAStatus getParameterDynamicsProcessing(const DynamicsProcessing::Tag& tag,
                                                       Parameter::Specific* specific);
diff --git a/audio/aidl/default/envReverb/EnvReverbSw.cpp b/audio/aidl/default/envReverb/EnvReverbSw.cpp
index 905dba4..46d9016 100644
--- a/audio/aidl/default/envReverb/EnvReverbSw.cpp
+++ b/audio/aidl/default/envReverb/EnvReverbSw.cpp
@@ -60,18 +60,20 @@
 namespace aidl::android::hardware::audio::effect {
 
 const std::string EnvReverbSw::kEffectName = "EnvReverbSw";
-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 std::vector<Range::EnvironmentalReverbRange> EnvReverbSw::kRanges = {
+        MAKE_RANGE(EnvironmentalReverb, roomLevelMb, -6000, 0),
+        MAKE_RANGE(EnvironmentalReverb, roomHfLevelMb, -4000, 0),
+        MAKE_RANGE(EnvironmentalReverb, decayTimeMs, 0, 7000),
+        MAKE_RANGE(EnvironmentalReverb, decayHfRatioPm, 100, 2000),
+        MAKE_RANGE(EnvironmentalReverb, levelMb, -6000, 0),
+        MAKE_RANGE(EnvironmentalReverb, delayMs, 0, 65),
+        MAKE_RANGE(EnvironmentalReverb, diffusionPm, 0, 1000),
+        MAKE_RANGE(EnvironmentalReverb, densityPm, 0, 1000)};
+
+const Capability EnvReverbSw::kCapability = {
+        .range = Range::make<Range::environmentalReverb>(EnvReverbSw::kRanges)};
+
 const Descriptor EnvReverbSw::kDescriptor = {
         .common = {.id = {.type = kEnvReverbTypeUUID,
                           .uuid = kEnvReverbSwImplUUID,
@@ -81,7 +83,7 @@
                              .volume = Flags::Volume::CTRL},
                    .name = EnvReverbSw::kEffectName,
                    .implementor = "The Android Open Source Project"},
-        .capability = Capability::make<Capability::environmentalReverb>(EnvReverbSw::kCapability)};
+        .capability = EnvReverbSw::kCapability};
 
 ndk::ScopedAStatus EnvReverbSw::getDescriptor(Descriptor* _aidl_return) {
     LOG(DEBUG) << __func__ << kDescriptor.toString();
@@ -94,8 +96,8 @@
               "EffectNotSupported");
 
     auto& erParam = specific.get<Parameter::Specific::environmentalReverb>();
+    RETURN_IF(!inRange(erParam, kRanges), EX_ILLEGAL_ARGUMENT, "outOfRange");
     auto tag = erParam.getTag();
-
     switch (tag) {
         case EnvironmentalReverb::roomLevelMb: {
             RETURN_IF(mContext->setErRoomLevel(erParam.get<EnvironmentalReverb::roomLevelMb>()) !=
@@ -262,85 +264,41 @@
 }
 
 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;
 }
diff --git a/audio/aidl/default/envReverb/EnvReverbSw.h b/audio/aidl/default/envReverb/EnvReverbSw.h
index 77f384e..4f11a5c 100644
--- a/audio/aidl/default/envReverb/EnvReverbSw.h
+++ b/audio/aidl/default/envReverb/EnvReverbSw.h
@@ -79,7 +79,7 @@
 class EnvReverbSw final : public EffectImpl {
   public:
     static const std::string kEffectName;
-    static const EnvironmentalReverb::Capability kCapability;
+    static const Capability kCapability;
     static const Descriptor kDescriptor;
     EnvReverbSw() { LOG(DEBUG) << __func__; }
     ~EnvReverbSw() {
@@ -100,6 +100,7 @@
     std::string getEffectName() override { return kEffectName; }
 
   private:
+    static const std::vector<Range::EnvironmentalReverbRange> kRanges;
     std::shared_ptr<EnvReverbSwContext> mContext;
     ndk::ScopedAStatus getParameterEnvironmentalReverb(const EnvironmentalReverb::Tag& tag,
                                                        Parameter::Specific* specific);
diff --git a/audio/aidl/default/equalizer/EqualizerSw.cpp b/audio/aidl/default/equalizer/EqualizerSw.cpp
index 0a6ac34..2814322 100644
--- a/audio/aidl/default/equalizer/EqualizerSw.cpp
+++ b/audio/aidl/default/equalizer/EqualizerSw.cpp
@@ -60,6 +60,7 @@
 namespace aidl::android::hardware::audio::effect {
 
 const std::string EqualizerSw::kEffectName = "EqualizerSw";
+
 const std::vector<Equalizer::BandFrequency> EqualizerSw::kBandFrequency = {{0, 30000, 120000},
                                                                            {1, 120001, 460000},
                                                                            {2, 460001, 1800000},
@@ -69,19 +70,34 @@
         {0, "Normal"},      {1, "Classical"}, {2, "Dance"}, {3, "Flat"}, {4, "Folk"},
         {5, "Heavy Metal"}, {6, "Hip Hop"},   {7, "Jazz"},  {8, "Pop"},  {9, "Rock"}};
 
-const Equalizer::Capability EqualizerSw::kEqCap = {.bandFrequencies = kBandFrequency,
-                                                   .presets = kPresets};
+/**
+ * Use the same min and max to build a capability represented by Range.
+ */
+const std::vector<Range::EqualizerRange> EqualizerSw::kRanges = {
+        MAKE_RANGE(Equalizer, preset, 0, EqualizerSw::kPresets.size() - 1),
+        MAKE_RANGE(Equalizer, bandLevels,
+                   std::vector<Equalizer::BandLevel>{Equalizer::BandLevel(
+                           {.index = 0, .levelMb = std::numeric_limits<int>::min()})},
+                   std::vector<Equalizer::BandLevel>{
+                           Equalizer::BandLevel({.index = EqualizerSwContext::kMaxBandNumber - 1,
+                                                 .levelMb = std::numeric_limits<int>::max()})}),
+        /* capability definition */
+        MAKE_RANGE(Equalizer, bandFrequencies, EqualizerSw::kBandFrequency,
+                   EqualizerSw::kBandFrequency),
+        MAKE_RANGE(Equalizer, presets, EqualizerSw::kPresets, EqualizerSw::kPresets),
+        /* centerFreqMh is get only, set invalid range min > max */
+        MAKE_RANGE(Equalizer, centerFreqMh, std::vector<int>({1}), std::vector<int>({0}))};
 
-const Descriptor EqualizerSw::kDesc = {
-        .common = {.id = {.type = kEqualizerTypeUUID,
-                          .uuid = kEqualizerSwImplUUID,
-                          .proxy = kEqualizerProxyUUID},
-                   .flags = {.type = Flags::Type::INSERT,
-                             .insert = Flags::Insert::FIRST,
-                             .volume = Flags::Volume::CTRL},
-                   .name = EqualizerSw::kEffectName,
-                   .implementor = "The Android Open Source Project"},
-        .capability = Capability::make<Capability::equalizer>(EqualizerSw::kEqCap)};
+const Capability EqualizerSw::kEqCap = {.range = EqualizerSw::kRanges};
+const Descriptor EqualizerSw::kDesc = {.common = {.id = {.type = kEqualizerTypeUUID,
+                                                         .uuid = kEqualizerSwImplUUID,
+                                                         .proxy = kEqualizerProxyUUID},
+                                                  .flags = {.type = Flags::Type::INSERT,
+                                                            .insert = Flags::Insert::FIRST,
+                                                            .volume = Flags::Volume::CTRL},
+                                                  .name = EqualizerSw::kEffectName,
+                                                  .implementor = "The Android Open Source Project"},
+                                       .capability = EqualizerSw::kEqCap};
 
 ndk::ScopedAStatus EqualizerSw::getDescriptor(Descriptor* _aidl_return) {
     LOG(DEBUG) << __func__ << kDesc.toString();
@@ -95,6 +111,7 @@
     RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
 
     auto& eqParam = specific.get<Parameter::Specific::equalizer>();
+    RETURN_IF(!inRange(eqParam, kRanges), EX_ILLEGAL_ARGUMENT, "outOfRange");
     auto tag = eqParam.getTag();
     switch (tag) {
         case Equalizer::preset: {
diff --git a/audio/aidl/default/equalizer/EqualizerSw.h b/audio/aidl/default/equalizer/EqualizerSw.h
index fabcfeb..f8987c7 100644
--- a/audio/aidl/default/equalizer/EqualizerSw.h
+++ b/audio/aidl/default/equalizer/EqualizerSw.h
@@ -34,7 +34,7 @@
     }
 
     RetCode setEqPreset(const int& presetIdx) {
-        if (presetIdx < 0 || presetIdx >= NUM_OF_PRESETS) {
+        if (presetIdx < 0 || presetIdx >= kMaxPresetNumber) {
             return RetCode::ERROR_ILLEGAL_PARAMETER;
         }
         mPreset = presetIdx;
@@ -43,13 +43,13 @@
     int getEqPreset() { return mPreset; }
 
     RetCode setEqBandLevels(const std::vector<Equalizer::BandLevel>& bandLevels) {
-        if (bandLevels.size() > NUM_OF_BANDS) {
-            LOG(ERROR) << __func__ << " return because size exceed " << NUM_OF_BANDS;
+        if (bandLevels.size() > kMaxBandNumber) {
+            LOG(ERROR) << __func__ << " return because size exceed " << kMaxBandNumber;
             return RetCode::ERROR_ILLEGAL_PARAMETER;
         }
         RetCode ret = RetCode::SUCCESS;
         for (auto& it : bandLevels) {
-            if (it.index >= NUM_OF_BANDS || it.index < 0) {
+            if (it.index >= kMaxBandNumber || it.index < 0) {
                 LOG(ERROR) << __func__ << " index illegal, skip: " << it.index << " - "
                            << it.levelMb;
                 ret = RetCode::ERROR_ILLEGAL_PARAMETER;
@@ -62,7 +62,7 @@
 
     std::vector<Equalizer::BandLevel> getEqBandLevels() {
         std::vector<Equalizer::BandLevel> bandLevels;
-        for (int i = 0; i < NUM_OF_BANDS; i++) {
+        for (int i = 0; i < kMaxBandNumber; i++) {
             bandLevels.push_back({i, mBandLevels[i]});
         }
         return bandLevels;
@@ -71,16 +71,16 @@
     std::vector<int> getCenterFreqs() {
         return {std::begin(kPresetsFrequencies), std::end(kPresetsFrequencies)};
     }
+    static const int kMaxBandNumber = 5;
+    static const int kMaxPresetNumber = 10;
+    static const int kCustomPreset = -1;
 
   private:
-    static const int NUM_OF_BANDS = 5;
-    static const int NUM_OF_PRESETS = 10;
-    static const int PRESET_CUSTOM = -1;
-    static constexpr std::array<uint16_t, NUM_OF_BANDS> kPresetsFrequencies = {60, 230, 910, 3600,
-                                                                               14000};
+    static constexpr std::array<uint16_t, kMaxBandNumber> kPresetsFrequencies = {60, 230, 910, 3600,
+                                                                                 14000};
     // preset band level
-    int mPreset = PRESET_CUSTOM;
-    int32_t mBandLevels[NUM_OF_BANDS] = {3, 0, 0, 0, 3};
+    int mPreset = kCustomPreset;
+    int32_t mBandLevels[kMaxBandNumber] = {3, 0, 0, 0, 3};
 
     // Add equalizer specific context for processing here
 };
@@ -88,9 +88,7 @@
 class EqualizerSw final : public EffectImpl {
   public:
     static const std::string kEffectName;
-    static const std::vector<Equalizer::BandFrequency> kBandFrequency;
-    static const std::vector<Equalizer::Preset> kPresets;
-    static const Equalizer::Capability kEqCap;
+    static const Capability kEqCap;
     static const Descriptor kDesc;
 
     EqualizerSw() { LOG(DEBUG) << __func__; }
@@ -112,6 +110,9 @@
     std::string getEffectName() override { return kEffectName; }
 
   private:
+    static const std::vector<Equalizer::BandFrequency> kBandFrequency;
+    static const std::vector<Equalizer::Preset> kPresets;
+    static const std::vector<Range::EqualizerRange> kRanges;
     ndk::ScopedAStatus getParameterEqualizer(const Equalizer::Tag& tag,
                                              Parameter::Specific* specific);
     std::shared_ptr<EqualizerSwContext> mContext;
diff --git a/audio/aidl/default/hapticGenerator/HapticGeneratorSw.cpp b/audio/aidl/default/hapticGenerator/HapticGeneratorSw.cpp
index 3c3b66f..6037ad2 100644
--- a/audio/aidl/default/hapticGenerator/HapticGeneratorSw.cpp
+++ b/audio/aidl/default/hapticGenerator/HapticGeneratorSw.cpp
@@ -60,8 +60,6 @@
 namespace aidl::android::hardware::audio::effect {
 
 const std::string HapticGeneratorSw::kEffectName = "HapticGeneratorSw";
-/* capabilities */
-const HapticGenerator::Capability HapticGeneratorSw::kCapability;
 /* Effect descriptor */
 const Descriptor HapticGeneratorSw::kDescriptor = {
         .common = {.id = {.type = kHapticGeneratorTypeUUID,
@@ -71,9 +69,7 @@
                              .insert = Flags::Insert::FIRST,
                              .volume = Flags::Volume::CTRL},
                    .name = HapticGeneratorSw::kEffectName,
-                   .implementor = "The Android Open Source Project"},
-        .capability =
-                Capability::make<Capability::hapticGenerator>(HapticGeneratorSw::kCapability)};
+                   .implementor = "The Android Open Source Project"}};
 
 ndk::ScopedAStatus HapticGeneratorSw::getDescriptor(Descriptor* _aidl_return) {
     LOG(DEBUG) << __func__ << kDescriptor.toString();
diff --git a/audio/aidl/default/hapticGenerator/HapticGeneratorSw.h b/audio/aidl/default/hapticGenerator/HapticGeneratorSw.h
index 7159501..428f460 100644
--- a/audio/aidl/default/hapticGenerator/HapticGeneratorSw.h
+++ b/audio/aidl/default/hapticGenerator/HapticGeneratorSw.h
@@ -58,7 +58,6 @@
 class HapticGeneratorSw final : public EffectImpl {
   public:
     static const std::string kEffectName;
-    static const HapticGenerator::Capability kCapability;
     static const Descriptor kDescriptor;
     HapticGeneratorSw() { LOG(DEBUG) << __func__; }
     ~HapticGeneratorSw() {
diff --git a/audio/aidl/default/include/effect-impl/EffectTypes.h b/audio/aidl/default/include/effect-impl/EffectTypes.h
index b100a2e..fe534d7 100644
--- a/audio/aidl/default/include/effect-impl/EffectTypes.h
+++ b/audio/aidl/default/include/effect-impl/EffectTypes.h
@@ -19,16 +19,18 @@
 #include <string>
 
 #include <aidl/android/hardware/audio/effect/BnEffect.h>
+#include <aidl/android/hardware/audio/effect/Range.h>
 #include <android-base/logging.h>
+#include <system/audio_effects/aidl_effects_utils.h>
 
 typedef binder_exception_t (*EffectCreateFunctor)(
         const ::aidl::android::media::audio::common::AudioUuid*,
-        std::shared_ptr<aidl::android::hardware::audio::effect::IEffect>*);
+        std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>*);
 typedef binder_exception_t (*EffectDestroyFunctor)(
-        const std::shared_ptr<aidl::android::hardware::audio::effect::IEffect>&);
+        const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>&);
 typedef binder_exception_t (*EffectQueryFunctor)(
         const ::aidl::android::media::audio::common::AudioUuid*,
-        aidl::android::hardware::audio::effect::Descriptor*);
+        ::aidl::android::hardware::audio::effect::Descriptor*);
 
 struct effect_dl_interface_s {
     EffectCreateFunctor createEffectFunc;
@@ -116,6 +118,16 @@
         }                                                                   \
     }
 
+/**
+ * Make a Range::$EffectType$Range.
+ * T: The $EffectType$, Visualizer for example.
+ * Tag: The union tag name in $EffectType$ definition, latencyMs for example.
+ * l: The value of Range::$EffectType$Range.min.
+ * r: The value of Range::$EffectType$Range.max.
+ */
+#define MAKE_RANGE(T, Tag, l, r) \
+    { .min = T::make<T::Tag>(l), .max = T::make<T::Tag>(r) }
+
 static inline bool stringToUuid(const char* str,
                                 ::aidl::android::media::audio::common::AudioUuid* uuid) {
     RETURN_VALUE_IF(!uuid || !str, false, "nullPtr");
diff --git a/audio/aidl/default/loudnessEnhancer/LoudnessEnhancerSw.h b/audio/aidl/default/loudnessEnhancer/LoudnessEnhancerSw.h
index 5da70c7..e252f4a 100644
--- a/audio/aidl/default/loudnessEnhancer/LoudnessEnhancerSw.h
+++ b/audio/aidl/default/loudnessEnhancer/LoudnessEnhancerSw.h
@@ -47,7 +47,6 @@
 class LoudnessEnhancerSw final : public EffectImpl {
   public:
     static const std::string kEffectName;
-    static const LoudnessEnhancer::Capability kCapability;
     static const Descriptor kDescriptor;
     LoudnessEnhancerSw() { LOG(DEBUG) << __func__; }
     ~LoudnessEnhancerSw() {
diff --git a/audio/aidl/default/noiseSuppression/NoiseSuppressionSw.cpp b/audio/aidl/default/noiseSuppression/NoiseSuppressionSw.cpp
index 51fe4ea..0ea31ea 100644
--- a/audio/aidl/default/noiseSuppression/NoiseSuppressionSw.cpp
+++ b/audio/aidl/default/noiseSuppression/NoiseSuppressionSw.cpp
@@ -60,7 +60,6 @@
 namespace aidl::android::hardware::audio::effect {
 
 const std::string NoiseSuppressionSw::kEffectName = "NoiseSuppressionSw";
-const NoiseSuppression::Capability NoiseSuppressionSw::kCapability;
 const Descriptor NoiseSuppressionSw::kDescriptor = {
         .common = {.id = {.type = kNoiseSuppressionTypeUUID,
                           .uuid = kNoiseSuppressionSwImplUUID,
@@ -69,9 +68,7 @@
                              .insert = Flags::Insert::FIRST,
                              .volume = Flags::Volume::CTRL},
                    .name = NoiseSuppressionSw::kEffectName,
-                   .implementor = "The Android Open Source Project"},
-        .capability =
-                Capability::make<Capability::noiseSuppression>(NoiseSuppressionSw::kCapability)};
+                   .implementor = "The Android Open Source Project"}};
 
 ndk::ScopedAStatus NoiseSuppressionSw::getDescriptor(Descriptor* _aidl_return) {
     LOG(DEBUG) << __func__ << kDescriptor.toString();
diff --git a/audio/aidl/default/noiseSuppression/NoiseSuppressionSw.h b/audio/aidl/default/noiseSuppression/NoiseSuppressionSw.h
index a851e38..22bf066 100644
--- a/audio/aidl/default/noiseSuppression/NoiseSuppressionSw.h
+++ b/audio/aidl/default/noiseSuppression/NoiseSuppressionSw.h
@@ -47,7 +47,6 @@
   public:
     static const std::string kEffectName;
     static const bool kStrengthSupported;
-    static const NoiseSuppression::Capability kCapability;
     static const Descriptor kDescriptor;
     NoiseSuppressionSw() { LOG(DEBUG) << __func__; }
     ~NoiseSuppressionSw() {
diff --git a/audio/aidl/default/presetReverb/PresetReverbSw.cpp b/audio/aidl/default/presetReverb/PresetReverbSw.cpp
index d038596..2da3ff6 100644
--- a/audio/aidl/default/presetReverb/PresetReverbSw.cpp
+++ b/audio/aidl/default/presetReverb/PresetReverbSw.cpp
@@ -62,12 +62,17 @@
 
 const std::string PresetReverbSw::kEffectName = "PresetReverbSw";
 
-const std::vector<PresetReverb::Presets> kSupportedPresets{
+const std::vector<PresetReverb::Presets> PresetReverbSw::kSupportedPresets{
         ndk::enum_range<PresetReverb::Presets>().begin(),
         ndk::enum_range<PresetReverb::Presets>().end()};
 
-const PresetReverb::Capability PresetReverbSw::kCapability = {.supportedPresets =
-                                                                      kSupportedPresets};
+const std::vector<Range::PresetReverbRange> PresetReverbSw::kRanges = {
+        MAKE_RANGE(PresetReverb, supportedPresets, PresetReverbSw::kSupportedPresets,
+                   PresetReverbSw::kSupportedPresets)};
+
+const Capability PresetReverbSw::kCapability = {
+        .range = Range::make<Range::presetReverb>(PresetReverbSw::kRanges)};
+
 const Descriptor PresetReverbSw::kDescriptor = {
         .common = {.id = {.type = kPresetReverbTypeUUID,
                           .uuid = kPresetReverbSwImplUUID,
@@ -77,7 +82,7 @@
                              .volume = Flags::Volume::CTRL},
                    .name = PresetReverbSw::kEffectName,
                    .implementor = "The Android Open Source Project"},
-        .capability = Capability::make<Capability::presetReverb>(PresetReverbSw::kCapability)};
+        .capability = PresetReverbSw::kCapability};
 
 ndk::ScopedAStatus PresetReverbSw::getDescriptor(Descriptor* _aidl_return) {
     LOG(DEBUG) << __func__ << kDescriptor.toString();
@@ -92,6 +97,7 @@
     RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
 
     auto& prParam = specific.get<Parameter::Specific::presetReverb>();
+    RETURN_IF(!inRange(prParam, kRanges), EX_ILLEGAL_ARGUMENT, "outOfRange");
     auto tag = prParam.getTag();
 
     switch (tag) {
diff --git a/audio/aidl/default/presetReverb/PresetReverbSw.h b/audio/aidl/default/presetReverb/PresetReverbSw.h
index eb1d80a..5061475 100644
--- a/audio/aidl/default/presetReverb/PresetReverbSw.h
+++ b/audio/aidl/default/presetReverb/PresetReverbSw.h
@@ -46,7 +46,9 @@
 class PresetReverbSw final : public EffectImpl {
   public:
     static const std::string kEffectName;
-    static const PresetReverb::Capability kCapability;
+    static const std::vector<PresetReverb::Presets> kSupportedPresets;
+    static const std::vector<Range::PresetReverbRange> kRanges;
+    static const Capability kCapability;
     static const Descriptor kDescriptor;
     PresetReverbSw() { LOG(DEBUG) << __func__; }
     ~PresetReverbSw() {
diff --git a/audio/aidl/default/virtualizer/VirtualizerSw.cpp b/audio/aidl/default/virtualizer/VirtualizerSw.cpp
index 08535bd..5e99cba2 100644
--- a/audio/aidl/default/virtualizer/VirtualizerSw.cpp
+++ b/audio/aidl/default/virtualizer/VirtualizerSw.cpp
@@ -32,6 +32,7 @@
 using aidl::android::hardware::audio::effect::VirtualizerSw;
 using aidl::android::media::audio::common::AudioChannelLayout;
 using aidl::android::media::audio::common::AudioDeviceDescription;
+using aidl::android::media::audio::common::AudioDeviceType;
 using aidl::android::media::audio::common::AudioUuid;
 
 extern "C" binder_exception_t createEffect(const AudioUuid* in_impl_uuid,
@@ -62,9 +63,20 @@
 namespace aidl::android::hardware::audio::effect {
 
 const std::string VirtualizerSw::kEffectName = "VirtualizerSw";
-const bool VirtualizerSw::kStrengthSupported = true;
-const Virtualizer::Capability VirtualizerSw::kCapability = {
-        .maxStrengthPm = 1000, .strengthSupported = kStrengthSupported};
+
+const std::vector<Range::VirtualizerRange> VirtualizerSw::kRanges = {
+        MAKE_RANGE(Virtualizer, strengthPm, 0, 1000),
+        /* speakerAngle is get-only, set min > max */
+        MAKE_RANGE(Virtualizer, speakerAngles, {Virtualizer::ChannelAngle({.channel = 1})},
+                   {Virtualizer::ChannelAngle({.channel = 0})}),
+        /* device is get-only */
+        MAKE_RANGE(Virtualizer, device,
+                   AudioDeviceDescription({.type = AudioDeviceType::IN_DEFAULT}),
+                   AudioDeviceDescription({.type = AudioDeviceType::NONE}))};
+
+const Capability VirtualizerSw::kCapability = {
+        .range = Range::make<Range::virtualizer>(VirtualizerSw::kRanges)};
+
 const Descriptor VirtualizerSw::kDescriptor = {
         .common = {.id = {.type = kVirtualizerTypeUUID,
                           .uuid = kVirtualizerSwImplUUID,
@@ -74,7 +86,7 @@
                              .volume = Flags::Volume::CTRL},
                    .name = VirtualizerSw::kEffectName,
                    .implementor = "The Android Open Source Project"},
-        .capability = Capability::make<Capability::virtualizer>(VirtualizerSw::kCapability)};
+        .capability = VirtualizerSw::kCapability};
 
 ndk::ScopedAStatus VirtualizerSw::getDescriptor(Descriptor* _aidl_return) {
     LOG(DEBUG) << __func__ << kDescriptor.toString();
@@ -87,12 +99,11 @@
               "EffectNotSupported");
 
     auto& vrParam = specific.get<Parameter::Specific::virtualizer>();
+    RETURN_IF(!inRange(vrParam, kRanges), EX_ILLEGAL_ARGUMENT, "outOfRange");
     auto tag = vrParam.getTag();
 
     switch (tag) {
         case Virtualizer::strengthPm: {
-            RETURN_IF(!kStrengthSupported, EX_ILLEGAL_ARGUMENT, "SettingStrengthNotSupported");
-
             RETURN_IF(mContext->setVrStrength(vrParam.get<Virtualizer::strengthPm>()) !=
                               RetCode::SUCCESS,
                       EX_ILLEGAL_ARGUMENT, "setStrengthPmFailed");
@@ -213,11 +224,6 @@
 }
 
 RetCode VirtualizerSwContext::setVrStrength(int strength) {
-    if (strength < 0 || strength > VirtualizerSw::kCapability.maxStrengthPm) {
-        LOG(ERROR) << __func__ << " invalid strength: " << strength;
-        return RetCode::ERROR_ILLEGAL_PARAMETER;
-    }
-    // TODO : Add implementation to apply new strength
     mStrength = strength;
     return RetCode::SUCCESS;
 }
diff --git a/audio/aidl/default/virtualizer/VirtualizerSw.h b/audio/aidl/default/virtualizer/VirtualizerSw.h
index 1016ffc..5c5b616 100644
--- a/audio/aidl/default/virtualizer/VirtualizerSw.h
+++ b/audio/aidl/default/virtualizer/VirtualizerSw.h
@@ -51,8 +51,7 @@
 class VirtualizerSw final : public EffectImpl {
   public:
     static const std::string kEffectName;
-    static const bool kStrengthSupported;
-    static const Virtualizer::Capability kCapability;
+    static const Capability kCapability;
     static const Descriptor kDescriptor;
     VirtualizerSw() { LOG(DEBUG) << __func__; }
     ~VirtualizerSw() {
@@ -73,6 +72,7 @@
     std::string getEffectName() override { return kEffectName; }
 
   private:
+    static const std::vector<Range::VirtualizerRange> kRanges;
     std::shared_ptr<VirtualizerSwContext> mContext;
 
     ndk::ScopedAStatus getParameterVirtualizer(const Virtualizer::Tag& tag,
diff --git a/audio/aidl/default/visualizer/VisualizerSw.cpp b/audio/aidl/default/visualizer/VisualizerSw.cpp
index 614988c..deb3204 100644
--- a/audio/aidl/default/visualizer/VisualizerSw.cpp
+++ b/audio/aidl/default/visualizer/VisualizerSw.cpp
@@ -55,12 +55,15 @@
 namespace aidl::android::hardware::audio::effect {
 
 const std::string VisualizerSw::kEffectName = "VisualizerSw";
+
 /* capabilities */
-const Visualizer::CaptureSamplesRange VisualizerSwContext::kCaptureSamplesRange = {
-        VisualizerSwContext::kMinCaptureSize, VisualizerSwContext::kMaxCaptureSize};
-const Visualizer::Capability VisualizerSw::kCapability = {
-        .maxLatencyMs = VisualizerSwContext::kMaxLatencyMs,
-        .captureSampleRange = VisualizerSwContext::kCaptureSamplesRange};
+const std::vector<Range::VisualizerRange> VisualizerSw::kRanges = {
+        MAKE_RANGE(Visualizer, latencyMs, 0, VisualizerSwContext::kMaxLatencyMs),
+        MAKE_RANGE(Visualizer, captureSamples, VisualizerSwContext::kMinCaptureSize,
+                   VisualizerSwContext::kMaxCaptureSize)};
+
+const Capability VisualizerSw::kCapability = {
+        .range = Range::make<Range::visualizer>(VisualizerSw::kRanges)};
 
 const Descriptor VisualizerSw::kDescriptor = {
         .common = {.id = {.type = kVisualizerTypeUUID,
@@ -71,7 +74,7 @@
                              .volume = Flags::Volume::CTRL},
                    .name = VisualizerSw::kEffectName,
                    .implementor = "The Android Open Source Project"},
-        .capability = Capability::make<Capability::visualizer>(VisualizerSw::kCapability)};
+        .capability = VisualizerSw::kCapability};
 
 ndk::ScopedAStatus VisualizerSw::getDescriptor(Descriptor* _aidl_return) {
     LOG(DEBUG) << __func__ << kDescriptor.toString();
@@ -84,34 +87,33 @@
               "EffectNotSupported");
 
     auto& vsParam = specific.get<Parameter::Specific::visualizer>();
+    RETURN_IF(!inRange(vsParam, kRanges), EX_ILLEGAL_ARGUMENT, "outOfRange");
     auto tag = vsParam.getTag();
 
     switch (tag) {
         case Visualizer::captureSamples: {
             RETURN_IF(mContext->setVsCaptureSize(vsParam.get<Visualizer::captureSamples>()) !=
                               RetCode::SUCCESS,
-                      EX_ILLEGAL_ARGUMENT, "captureSizeNotSupported");
+                      EX_ILLEGAL_ARGUMENT, "setCaptureSizeFailed");
             return ndk::ScopedAStatus::ok();
         }
         case Visualizer::scalingMode: {
             RETURN_IF(mContext->setVsScalingMode(vsParam.get<Visualizer::scalingMode>()) !=
                               RetCode::SUCCESS,
-                      EX_ILLEGAL_ARGUMENT, "scalingModeNotSupported");
+                      EX_ILLEGAL_ARGUMENT, "setScalingModeFailed");
             return ndk::ScopedAStatus::ok();
         }
         case Visualizer::measurementMode: {
             RETURN_IF(mContext->setVsMeasurementMode(vsParam.get<Visualizer::measurementMode>()) !=
                               RetCode::SUCCESS,
-                      EX_ILLEGAL_ARGUMENT, "measurementModeNotSupported");
+                      EX_ILLEGAL_ARGUMENT, "setMeasurementModeFailed");
             return ndk::ScopedAStatus::ok();
         }
-        case Visualizer::setOnlyParameters: {
-            return setSetOnlyParameterVisualizer(vsParam.get<Visualizer::setOnlyParameters>());
-        }
-        case Visualizer::getOnlyParameters: {
-            LOG(ERROR) << __func__ << " unsupported settable getOnlyParam";
-            return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
-                    EX_ILLEGAL_ARGUMENT, "SetofGetOnlyParamsNotSupported");
+        case Visualizer::latencyMs: {
+            RETURN_IF(mContext->setVsLatency(vsParam.get<Visualizer::latencyMs>()) !=
+                              RetCode::SUCCESS,
+                      EX_ILLEGAL_ARGUMENT, "setLatencyFailed");
+            return ndk::ScopedAStatus::ok();
         }
         default: {
             LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag);
@@ -121,18 +123,6 @@
     }
 }
 
-ndk::ScopedAStatus VisualizerSw::setSetOnlyParameterVisualizer(
-        Visualizer::SetOnlyParameters setOnlyParam) {
-    auto tag = setOnlyParam.getTag();
-    RETURN_IF(Visualizer::SetOnlyParameters::latencyMs != tag, EX_ILLEGAL_ARGUMENT,
-              "SetOnlyParametersTagNotSupported");
-    RETURN_IF(
-            mContext->setVsLatency(setOnlyParam.get<Visualizer::SetOnlyParameters::latencyMs>()) !=
-                    RetCode::SUCCESS,
-            EX_ILLEGAL_ARGUMENT, "latencyNotSupported");
-    return ndk::ScopedAStatus::ok();
-}
-
 ndk::ScopedAStatus VisualizerSw::getParameterSpecific(const Parameter::Id& id,
                                                       Parameter::Specific* specific) {
     auto tag = id.getTag();
@@ -142,14 +132,6 @@
     switch (vsIdTag) {
         case Visualizer::Id::commonTag:
             return getParameterVisualizer(vsId.get<Visualizer::Id::commonTag>(), specific);
-        case Visualizer::Id::getOnlyParamTag:
-            return getGetOnlyParameterVisualizer(vsId.get<Visualizer::Id::getOnlyParamTag>(),
-                                                 specific);
-        case Visualizer::Id::setOnlyParamTag: {
-            LOG(ERROR) << __func__ << " unsupported gettable setOnlyParam";
-            return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
-                    EX_ILLEGAL_ARGUMENT, "GetofSetOnlyParamsNotSupported");
-        }
         default:
             LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag);
             return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
@@ -174,6 +156,18 @@
             vsParam.set<Visualizer::measurementMode>(mContext->getVsMeasurementMode());
             break;
         }
+        case Visualizer::measurement: {
+            vsParam.set<Visualizer::measurement>(mContext->getVsMeasurement());
+            break;
+        }
+        case Visualizer::captureSampleBuffer: {
+            vsParam.set<Visualizer::captureSampleBuffer>(mContext->getVsCaptureSampleBuffer());
+            break;
+        }
+        case Visualizer::latencyMs: {
+            vsParam.set<Visualizer::latencyMs>(mContext->getVsLatency());
+            break;
+        }
         default: {
             LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag);
             return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
@@ -184,32 +178,6 @@
     return ndk::ScopedAStatus::ok();
 }
 
-ndk::ScopedAStatus VisualizerSw::getGetOnlyParameterVisualizer(
-        const Visualizer::GetOnlyParameters::Tag& tag, Parameter::Specific* specific) {
-    Visualizer::GetOnlyParameters getOnlyParam;
-    switch (tag) {
-        case Visualizer::GetOnlyParameters::measurement: {
-            getOnlyParam.set<Visualizer::GetOnlyParameters::measurement>(
-                    mContext->getVsMeasurement());
-            break;
-        }
-        case Visualizer::GetOnlyParameters::captureSampleBuffer: {
-            getOnlyParam.set<Visualizer::GetOnlyParameters::captureSampleBuffer>(
-                    mContext->getVsCaptureSampleBuffer());
-            break;
-        }
-        default: {
-            LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag);
-            return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
-                    EX_ILLEGAL_ARGUMENT, "GetOnlyParameterTagNotSupported");
-        }
-    }
-    Visualizer vsParam;
-    vsParam.set<Visualizer::getOnlyParameters>(getOnlyParam);
-    specific->set<Parameter::Specific::visualizer>(vsParam);
-    return ndk::ScopedAStatus::ok();
-}
-
 std::shared_ptr<EffectContext> VisualizerSw::createContext(const Parameter::Common& common) {
     if (mContext) {
         LOG(DEBUG) << __func__ << " context already exist";
@@ -242,34 +210,21 @@
 }
 
 RetCode VisualizerSwContext::setVsCaptureSize(int captureSize) {
-    if (captureSize < VisualizerSw::kCapability.captureSampleRange.min ||
-        captureSize > VisualizerSw::kCapability.captureSampleRange.max) {
-        LOG(ERROR) << __func__ << " invalid captureSize " << captureSize;
-        return RetCode::ERROR_ILLEGAL_PARAMETER;
-    }
-    // TODO : Add implementation to apply new captureSize
     mCaptureSize = captureSize;
     return RetCode::SUCCESS;
 }
 
 RetCode VisualizerSwContext::setVsScalingMode(Visualizer::ScalingMode scalingMode) {
-    // TODO : Add implementation to apply new scalingMode
     mScalingMode = scalingMode;
     return RetCode::SUCCESS;
 }
 
 RetCode VisualizerSwContext::setVsMeasurementMode(Visualizer::MeasurementMode measurementMode) {
-    // TODO : Add implementation to apply new measurementMode
     mMeasurementMode = measurementMode;
     return RetCode::SUCCESS;
 }
 
 RetCode VisualizerSwContext::setVsLatency(int latency) {
-    if (latency < 0 || latency > VisualizerSw::kCapability.maxLatencyMs) {
-        LOG(ERROR) << __func__ << " invalid latency " << latency;
-        return RetCode::ERROR_ILLEGAL_PARAMETER;
-    }
-    // TODO : Add implementation to modify latency
     mLatency = latency;
     return RetCode::SUCCESS;
 }
diff --git a/audio/aidl/default/visualizer/VisualizerSw.h b/audio/aidl/default/visualizer/VisualizerSw.h
index e9d46d7..ee7276a 100644
--- a/audio/aidl/default/visualizer/VisualizerSw.h
+++ b/audio/aidl/default/visualizer/VisualizerSw.h
@@ -30,7 +30,6 @@
     static const int kMaxCaptureSize = 0x400;
     static const int kMaxLatencyMs = 3000;
     static const int kMaxCaptureBufSize = 0xffff;
-    static const Visualizer::CaptureSamplesRange kCaptureSamplesRange;
     VisualizerSwContext(int statusDepth, const Parameter::Common& common)
         : EffectContext(statusDepth, common) {
         LOG(DEBUG) << __func__;
@@ -48,8 +47,9 @@
     Visualizer::MeasurementMode getVsMeasurementMode() const { return mMeasurementMode; }
 
     RetCode setVsLatency(int latency);
+    int getVsLatency() const { return mLatency; }
 
-    Visualizer::GetOnlyParameters::Measurement getVsMeasurement() const { return mMeasurement; }
+    Visualizer::Measurement getVsMeasurement() const { return mMeasurement; }
     std::vector<uint8_t> getVsCaptureSampleBuffer() const { return mCaptureSampleBuffer; }
 
   private:
@@ -57,14 +57,14 @@
     Visualizer::ScalingMode mScalingMode = Visualizer::ScalingMode::NORMALIZED;
     Visualizer::MeasurementMode mMeasurementMode = Visualizer::MeasurementMode::NONE;
     int mLatency = 0;
-    const Visualizer::GetOnlyParameters::Measurement mMeasurement = {0, 0};
+    const Visualizer::Measurement mMeasurement = {0, 0};
     std::vector<uint8_t> mCaptureSampleBuffer;
 };
 
 class VisualizerSw final : public EffectImpl {
   public:
     static const std::string kEffectName;
-    static const Visualizer::Capability kCapability;
+    static const Capability kCapability;
     static const Descriptor kDescriptor;
     VisualizerSw() { LOG(DEBUG) << __func__; }
     ~VisualizerSw() {
@@ -85,12 +85,9 @@
     std::string getEffectName() override { return kEffectName; }
 
   private:
+    static const std::vector<Range::VisualizerRange> kRanges;
     std::shared_ptr<VisualizerSwContext> mContext;
-
-    ndk::ScopedAStatus setSetOnlyParameterVisualizer(Visualizer::SetOnlyParameters setOnlyParam);
     ndk::ScopedAStatus getParameterVisualizer(const Visualizer::Tag& tag,
                                               Parameter::Specific* specific);
-    ndk::ScopedAStatus getGetOnlyParameterVisualizer(const Visualizer::GetOnlyParameters::Tag& tag,
-                                                     Parameter::Specific* specific);
 };
 }  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/volume/VolumeSw.cpp b/audio/aidl/default/volume/VolumeSw.cpp
index 64301dc..796c332 100644
--- a/audio/aidl/default/volume/VolumeSw.cpp
+++ b/audio/aidl/default/volume/VolumeSw.cpp
@@ -60,7 +60,11 @@
 namespace aidl::android::hardware::audio::effect {
 
 const std::string VolumeSw::kEffectName = "VolumeSw";
-const Volume::Capability VolumeSw::kCapability = {.minLevelDb = -9600, .maxLevelDb = 0};
+
+const std::vector<Range::VolumeRange> VolumeSw::kRanges = {MAKE_RANGE(Volume, levelDb, -9600, 0)};
+
+const Capability VolumeSw::kCapability = {.range = Range::make<Range::volume>(VolumeSw::kRanges)};
+
 const Descriptor VolumeSw::kDescriptor = {
         .common = {.id = {.type = kVolumeTypeUUID,
                           .uuid = kVolumeSwImplUUID,
@@ -70,7 +74,7 @@
                              .volume = Flags::Volume::CTRL},
                    .name = VolumeSw::kEffectName,
                    .implementor = "The Android Open Source Project"},
-        .capability = Capability::make<Capability::volume>(VolumeSw::kCapability)};
+        .capability = VolumeSw::kCapability};
 
 ndk::ScopedAStatus VolumeSw::getDescriptor(Descriptor* _aidl_return) {
     LOG(DEBUG) << __func__ << kDescriptor.toString();
@@ -83,6 +87,7 @@
               "EffectNotSupported");
 
     auto& volParam = specific.get<Parameter::Specific::volume>();
+    RETURN_IF(!inRange(volParam, kRanges), EX_ILLEGAL_ARGUMENT, "outOfRange");
     auto tag = volParam.getTag();
 
     switch (tag) {
@@ -177,11 +182,6 @@
 }
 
 RetCode VolumeSwContext::setVolLevel(int level) {
-    if (level < VolumeSw::kCapability.minLevelDb || level > VolumeSw::kCapability.maxLevelDb) {
-        LOG(ERROR) << __func__ << " invalid level " << level;
-        return RetCode::ERROR_ILLEGAL_PARAMETER;
-    }
-    // TODO : Add implementation to apply new level
     mLevel = level;
     return RetCode::SUCCESS;
 }
diff --git a/audio/aidl/default/volume/VolumeSw.h b/audio/aidl/default/volume/VolumeSw.h
index b6f6077..2dd4324 100644
--- a/audio/aidl/default/volume/VolumeSw.h
+++ b/audio/aidl/default/volume/VolumeSw.h
@@ -49,7 +49,7 @@
 class VolumeSw final : public EffectImpl {
   public:
     static const std::string kEffectName;
-    static const Volume::Capability kCapability;
+    static const Capability kCapability;
     static const Descriptor kDescriptor;
     VolumeSw() { LOG(DEBUG) << __func__; }
     ~VolumeSw() {
@@ -70,6 +70,7 @@
     std::string getEffectName() override { return kEffectName; }
 
   private:
+    static const std::vector<Range::VolumeRange> kRanges;
     std::shared_ptr<VolumeSwContext> mContext;
 
     ndk::ScopedAStatus getParameterVolume(const Volume::Tag& tag, Parameter::Specific* specific);
diff --git a/audio/aidl/vts/EffectHelper.h b/audio/aidl/vts/EffectHelper.h
index 526a012..5ed8e1f 100644
--- a/audio/aidl/vts/EffectHelper.h
+++ b/audio/aidl/vts/EffectHelper.h
@@ -19,6 +19,7 @@
 #include <algorithm>
 #include <memory>
 #include <string>
+#include <type_traits>
 #include <unordered_map>
 #include <vector>
 
@@ -27,6 +28,7 @@
 #include <aidl/android/media/audio/common/AudioChannelLayout.h>
 #include <android/binder_auto_utils.h>
 #include <fmq/AidlMessageQueue.h>
+#include <system/audio_effects/aidl_effects_utils.h>
 
 #include "AudioHalBinderServiceUtil.h"
 #include "EffectFactoryHelper.h"
@@ -37,6 +39,7 @@
 using aidl::android::hardware::audio::effect::Descriptor;
 using aidl::android::hardware::audio::effect::IEffect;
 using aidl::android::hardware::audio::effect::Parameter;
+using aidl::android::hardware::audio::effect::Range;
 using aidl::android::hardware::audio::effect::State;
 using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
 using aidl::android::media::audio::common::AudioChannelLayout;
@@ -205,4 +208,58 @@
         std::unique_ptr<DataMQ> inputMQ;
         std::unique_ptr<DataMQ> outputMQ;
     };
+
+    template <typename T, Range::Tag tag>
+    static bool isParameterValid(const T& target, const Descriptor& desc) {
+        if (desc.capability.range.getTag() != tag) {
+            return true;
+        }
+        const auto& ranges = desc.capability.range.get<tag>();
+        return inRange(target, ranges);
+    }
+
+    /**
+     * Add to test value set: (min+max)/2, minimum/maximum numeric limits, and min-1/max+1 if
+     * result still in numeric limits after -1/+1.
+     * Only use this when the type of test value is basic type (std::is_arithmetic return true).
+     */
+    template <typename S, typename = std::enable_if_t<std::is_arithmetic_v<S>>>
+    static std::set<S> expandTestValueBasic(std::set<S>& s) {
+        const auto min = *s.begin(), max = *s.rbegin();
+        const auto minLimit = std::numeric_limits<S>::min(),
+                   maxLimit = std::numeric_limits<S>::max();
+        if (s.size()) {
+            s.insert(min + (max - min) / 2);
+            if (min != minLimit) {
+                s.insert(min - 1);
+            }
+            if (max != maxLimit) {
+                s.insert(max + 1);
+            }
+        }
+        s.insert(minLimit);
+        s.insert(maxLimit);
+        return s;
+    }
+
+    template <typename T, typename S, Range::Tag R, typename T::Tag tag, typename Functor>
+    static std::set<S> getTestValueSet(
+            std::vector<std::pair<std::shared_ptr<IFactory>, Descriptor>> kFactoryDescList,
+            Functor functor) {
+        std::set<S> result;
+        for (const auto& [_, desc] : kFactoryDescList) {
+            if (desc.capability.range.getTag() == R) {
+                const auto& ranges = desc.capability.range.get<R>();
+                for (const auto& range : ranges) {
+                    if (range.min.getTag() == tag) {
+                        result.insert(range.min.template get<tag>());
+                    }
+                    if (range.max.getTag() == tag) {
+                        result.insert(range.max.template get<tag>());
+                    }
+                }
+            }
+        }
+        return functor(result);
+    }
 };
diff --git a/audio/aidl/vts/VtsHalAECTargetTest.cpp b/audio/aidl/vts/VtsHalAECTargetTest.cpp
index c3427c8..39be191 100644
--- a/audio/aidl/vts/VtsHalAECTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAECTargetTest.cpp
@@ -23,16 +23,17 @@
 #define LOG_TAG "VtsHalAECParamTest"
 
 #include "EffectHelper.h"
+#include "effect-impl/EffectTypes.h"
 
 using namespace android;
 
 using aidl::android::hardware::audio::effect::AcousticEchoCanceler;
-using aidl::android::hardware::audio::effect::Capability;
 using aidl::android::hardware::audio::effect::Descriptor;
 using aidl::android::hardware::audio::effect::IEffect;
 using aidl::android::hardware::audio::effect::IFactory;
 using aidl::android::hardware::audio::effect::kAcousticEchoCancelerTypeUUID;
 using aidl::android::hardware::audio::effect::Parameter;
+using aidl::android::hardware::audio::effect::Range;
 
 enum ParamName { PARAM_INSTANCE_NAME, PARAM_ECHO_DELAY, PARAM_MOBILE_MODE };
 using AECParamTestParam = std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>,
@@ -87,7 +88,8 @@
             // validate parameter
             Descriptor desc;
             ASSERT_STATUS(EX_NONE, mEffect->getDescriptor(&desc));
-            const bool valid = isTagInRange(tag, aec, desc);
+            const bool valid =
+                    isParameterValid<AcousticEchoCanceler, Range::acousticEchoCanceler>(aec, desc);
             const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT;
 
             // set parameter
@@ -124,54 +126,6 @@
         mTags.push_back({AcousticEchoCanceler::mobileMode, aec});
     }
 
-    bool isTagInRange(const AcousticEchoCanceler::Tag& tag, const AcousticEchoCanceler& aec,
-                      const Descriptor& desc) const {
-        const AcousticEchoCanceler::Capability& aecCap =
-                desc.capability.get<Capability::acousticEchoCanceler>();
-        switch (tag) {
-            case AcousticEchoCanceler::echoDelayUs: {
-                return isEchoDelayInRange(aecCap, aec.get<AcousticEchoCanceler::echoDelayUs>());
-            }
-            case AcousticEchoCanceler::mobileMode: {
-                bool mode = aec.get<AcousticEchoCanceler::mobileMode>();
-                return isMobileModeValid(aecCap, mode);
-            }
-            default:
-                return false;
-        }
-    }
-
-    bool isEchoDelayInRange(const AcousticEchoCanceler::Capability& cap, int delay) const {
-        return (delay >= 0 && delay <= cap.maxEchoDelayUs);
-    }
-
-    bool isMobileModeValid(const AcousticEchoCanceler::Capability& cap, bool mode) const {
-        if (cap.supportMobileMode) {
-            return true;
-        } else {
-            return mode == false;
-        }
-    }
-
-    static std::unordered_set<int> getEchoDelayTestValues() {
-        auto descList = EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor,
-                                                                     kAcousticEchoCancelerTypeUUID);
-        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::acousticEchoCanceler>()
-                                   .maxEchoDelayUs <
-                           b.second.capability.get<Capability::acousticEchoCanceler>()
-                                   .maxEchoDelayUs;
-                });
-        if (max == descList.end()) {
-            return {0};
-        }
-        int maxDelay =
-                max->second.capability.get<Capability::acousticEchoCanceler>().maxEchoDelayUs;
-        return {-1, 0, maxDelay - 1, maxDelay, maxDelay + 1};
-    }
     static std::unordered_set<bool> getMobileModeValues() { return {true, false}; }
 
   private:
@@ -189,12 +143,20 @@
     SetAndGetParameters();
 }
 
+std::vector<std::pair<std::shared_ptr<IFactory>, Descriptor>> kDescPair;
 INSTANTIATE_TEST_SUITE_P(
         AECParamTest, AECParamTest,
-        ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
-                                   IFactory::descriptor, kAcousticEchoCancelerTypeUUID)),
-                           testing::ValuesIn(AECParamTest::getEchoDelayTestValues()),
-                           testing::ValuesIn(AECParamTest::getMobileModeValues())),
+        ::testing::Combine(
+                testing::ValuesIn(kDescPair = EffectFactoryHelper::getAllEffectDescriptors(
+                                          IFactory::descriptor, kAcousticEchoCancelerTypeUUID)),
+                testing::ValuesIn(EffectHelper::getTestValueSet<AcousticEchoCanceler, int,
+                                                                Range::acousticEchoCanceler,
+                                                                AcousticEchoCanceler::echoDelayUs>(
+                        kDescPair, EffectHelper::expandTestValueBasic<int>)),
+                testing::ValuesIn(EffectHelper::getTestValueSet<AcousticEchoCanceler, bool,
+                                                                Range::acousticEchoCanceler,
+                                                                AcousticEchoCanceler::mobileMode>(
+                        kDescPair, EffectHelper::expandTestValueBasic<bool>))),
         [](const testing::TestParamInfo<AECParamTest::ParamType>& info) {
             auto descriptor = std::get<PARAM_INSTANCE_NAME>(info.param).second;
             std::string echoDelay = std::to_string(std::get<PARAM_ECHO_DELAY>(info.param));
diff --git a/audio/aidl/vts/VtsHalAGCTargetTest.cpp b/audio/aidl/vts/VtsHalAGCTargetTest.cpp
index 3448ae2..974c2e7 100644
--- a/audio/aidl/vts/VtsHalAGCTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAGCTargetTest.cpp
@@ -26,7 +26,6 @@
 using namespace android;
 
 using aidl::android::hardware::audio::effect::AutomaticGainControl;
-using aidl::android::hardware::audio::effect::Capability;
 using aidl::android::hardware::audio::effect::Descriptor;
 using aidl::android::hardware::audio::effect::IEffect;
 using aidl::android::hardware::audio::effect::IFactory;
@@ -94,7 +93,8 @@
             // validate parameter
             Descriptor desc;
             ASSERT_STATUS(EX_NONE, mEffect->getDescriptor(&desc));
-            const bool valid = isTagInRange(tag, AGC, desc);
+            const bool valid =
+                    isParameterValid<AutomaticGainControl, Range::automaticGainControl>(AGC, desc);
             const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT;
 
             // set parameter
@@ -135,65 +135,7 @@
         mTags.push_back({AutomaticGainControl::levelEstimator, AGC});
     }
 
-    bool isTagInRange(const AutomaticGainControl::Tag& tag, const AutomaticGainControl& AGC,
-                      const Descriptor& desc) const {
-        const AutomaticGainControl::Capability& AGCCap =
-                desc.capability.get<Capability::automaticGainControl>();
-        switch (tag) {
-            case AutomaticGainControl::fixedDigitalGainMb: {
-                auto gain = AGC.get<AutomaticGainControl::fixedDigitalGainMb>();
-                return gain >= 0 && gain <= AGCCap.maxFixedDigitalGainMb;
-            }
-            case AutomaticGainControl::levelEstimator: {
-                return true;
-            }
-            case AutomaticGainControl::saturationMarginMb: {
-                auto margin = AGC.get<AutomaticGainControl::saturationMarginMb>();
-                return margin >= 0 && margin <= AGCCap.maxSaturationMarginMb;
-            }
-            default:
-                return false;
-        }
-    }
-    static std::unordered_set<int> getDigitalGainValues() {
-        auto descList = EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor,
-                                                                     kAutomaticGainControlTypeUUID);
-        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::automaticGainControl>()
-                                   .maxFixedDigitalGainMb <
-                           b.second.capability.get<Capability::automaticGainControl>()
-                                   .maxFixedDigitalGainMb;
-                });
-        if (max == descList.end()) {
-            return {0};
-        }
-        int maxGain = max->second.capability.get<Capability::automaticGainControl>()
-                              .maxFixedDigitalGainMb;
-        return {-1, 0, maxGain - 1, maxGain, maxGain + 1};
-    }
-    static std::unordered_set<int> getSaturationMarginValues() {
-        auto descList = EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor,
-                                                                     kAutomaticGainControlTypeUUID);
-        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::automaticGainControl>()
-                                   .maxSaturationMarginMb <
-                           b.second.capability.get<Capability::automaticGainControl>()
-                                   .maxSaturationMarginMb;
-                });
-        if (max == descList.end()) {
-            return {0};
-        }
-        int maxMargin = max->second.capability.get<Capability::automaticGainControl>()
-                                .maxSaturationMarginMb;
-        return {-1, 0, maxMargin - 1, maxMargin, maxMargin + 1};
-    }
-    static std::unordered_set<AutomaticGainControl::LevelEstimator> getLevelEstimatorValues() {
+    static std::set<AutomaticGainControl::LevelEstimator> getLevelEstimatorValues() {
         return {ndk::enum_range<AutomaticGainControl::LevelEstimator>().begin(),
                 ndk::enum_range<AutomaticGainControl::LevelEstimator>().end()};
     }
@@ -218,13 +160,21 @@
     SetAndGetParameters();
 }
 
+std::vector<std::pair<std::shared_ptr<IFactory>, Descriptor>> kDescPair;
 INSTANTIATE_TEST_SUITE_P(
         AGCParamTest, AGCParamTest,
-        ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
-                                   IFactory::descriptor, kAutomaticGainControlTypeUUID)),
-                           testing::ValuesIn(AGCParamTest::getDigitalGainValues()),
-                           testing::ValuesIn(AGCParamTest::getSaturationMarginValues()),
-                           testing::ValuesIn(AGCParamTest::getLevelEstimatorValues())),
+        ::testing::Combine(
+                testing::ValuesIn(kDescPair = EffectFactoryHelper::getAllEffectDescriptors(
+                                          IFactory::descriptor, kAutomaticGainControlTypeUUID)),
+                testing::ValuesIn(EffectHelper::getTestValueSet<
+                                  AutomaticGainControl, int, Range::automaticGainControl,
+                                  AutomaticGainControl::fixedDigitalGainMb>(
+                        kDescPair, EffectHelper::expandTestValueBasic<int>)),
+                testing::ValuesIn(EffectHelper::getTestValueSet<
+                                  AutomaticGainControl, int, Range::automaticGainControl,
+                                  AutomaticGainControl::saturationMarginMb>(
+                        kDescPair, EffectHelper::expandTestValueBasic<int>)),
+                testing::ValuesIn(AGCParamTest::getLevelEstimatorValues())),
         [](const testing::TestParamInfo<AGCParamTest::ParamType>& info) {
             auto descriptor = std::get<PARAM_INSTANCE_NAME>(info.param).second;
             std::string gain = std::to_string(std::get<PARAM_DIGITAL_GAIN>(info.param));
diff --git a/audio/aidl/vts/VtsHalBassBoostTargetTest.cpp b/audio/aidl/vts/VtsHalBassBoostTargetTest.cpp
index d49a865..a1862d2 100644
--- a/audio/aidl/vts/VtsHalBassBoostTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalBassBoostTargetTest.cpp
@@ -31,6 +31,7 @@
 using aidl::android::hardware::audio::effect::IFactory;
 using aidl::android::hardware::audio::effect::kBassBoostTypeUUID;
 using aidl::android::hardware::audio::effect::Parameter;
+using aidl::android::hardware::audio::effect::Range;
 
 /**
  * Here we focus on specific parameter checking, general IEffect interfaces testing performed in
@@ -92,7 +93,7 @@
             // validate parameter
             Descriptor desc;
             ASSERT_STATUS(EX_NONE, mEffect->getDescriptor(&desc));
-            const bool valid = isTagInRange(it.first, it.second, desc);
+            const bool valid = isParameterValid<BassBoost, Range::bassBoost>(it.second, desc);
             const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT;
 
             // set parameter
@@ -122,46 +123,6 @@
         mTags.push_back({BassBoost::strengthPm, bb});
     }
 
-    bool isTagInRange(const BassBoost::Tag& tag, const BassBoost& bb,
-                      const Descriptor& desc) const {
-        const BassBoost::Capability& bbCap = desc.capability.get<Capability::bassBoost>();
-        switch (tag) {
-            case BassBoost::strengthPm: {
-                int strength = bb.get<BassBoost::strengthPm>();
-                return isStrengthInRange(bbCap, strength);
-            }
-            default:
-                return false;
-        }
-        return false;
-    }
-
-    bool isStrengthInRange(const BassBoost::Capability& cap, int strength) const {
-        return cap.strengthSupported && strength >= 0 && strength <= cap.maxStrengthPm;
-    }
-
-    static std::vector<int> getStrengthTestValues(
-            std::vector<std::pair<std::shared_ptr<IFactory>, Descriptor>> kFactoryDescList) {
-        const auto max = std::max_element(
-                kFactoryDescList.begin(), kFactoryDescList.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::bassBoost>().maxStrengthPm <
-                           b.second.capability.get<Capability::bassBoost>().maxStrengthPm;
-                });
-        if (max == kFactoryDescList.end()) {
-            return {0};
-        }
-        int maxStrength = max->second.capability.get<Capability::bassBoost>().maxStrengthPm;
-        return {std::numeric_limits<int>::min(),
-                -1,
-                0,
-                maxStrength >> 1,
-                maxStrength,
-                maxStrength + 1,
-                std::numeric_limits<int>::max()};
-    }
-
   private:
     std::vector<std::pair<BassBoost::Tag, BassBoost>> mTags;
     void CleanUp() { mTags.clear(); }
@@ -172,14 +133,15 @@
     SetAndGetBassBoostParameters();
 }
 
+std::vector<std::pair<std::shared_ptr<IFactory>, Descriptor>> kDescPair;
 INSTANTIATE_TEST_SUITE_P(
         BassBoostTest, BassBoostParamTest,
         ::testing::Combine(
-                testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor,
-                                                                               kBassBoostTypeUUID)),
-                testing::ValuesIn(BassBoostParamTest::getStrengthTestValues(
-                        EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor,
-                                                                     kBassBoostTypeUUID)))),
+                testing::ValuesIn(kDescPair = EffectFactoryHelper::getAllEffectDescriptors(
+                                          IFactory::descriptor, kBassBoostTypeUUID)),
+                testing::ValuesIn(EffectHelper::getTestValueSet<BassBoost, int, Range::bassBoost,
+                                                                BassBoost::strengthPm>(
+                        kDescPair, EffectHelper::expandTestValueBasic<int>))),
         [](const testing::TestParamInfo<BassBoostParamTest::ParamType>& info) {
             auto descriptor = std::get<PARAM_INSTANCE_NAME>(info.param).second;
             std::string strength = std::to_string(std::get<PARAM_STRENGTH>(info.param));
diff --git a/audio/aidl/vts/VtsHalDownmixTargetTest.cpp b/audio/aidl/vts/VtsHalDownmixTargetTest.cpp
index 8612660..0601cc4 100644
--- a/audio/aidl/vts/VtsHalDownmixTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalDownmixTargetTest.cpp
@@ -22,7 +22,6 @@
 
 using namespace android;
 
-using aidl::android::hardware::audio::effect::Capability;
 using aidl::android::hardware::audio::effect::Descriptor;
 using aidl::android::hardware::audio::effect::Downmix;
 using aidl::android::hardware::audio::effect::IEffect;
diff --git a/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
index 9feff91..ece07f0 100644
--- a/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
+++ b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
@@ -28,7 +28,6 @@
 
 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;
@@ -82,31 +81,8 @@
     }
 
     // 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);
 
@@ -201,56 +177,6 @@
 
                 {{.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) {
@@ -304,117 +230,6 @@
     }
 }
 
-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) {
@@ -455,7 +270,8 @@
         // validate parameter
         Descriptor desc;
         ASSERT_STATUS(EX_NONE, mEffect->getDescriptor(&desc));
-        const bool valid = isParamValid(tag, dp, desc);
+        const bool valid =
+                isParameterValid<DynamicsProcessing, Range::dynamicsProcessing>(dp, desc);
         const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT;
 
         // set parameter
@@ -463,7 +279,10 @@
         Parameter::Specific specific;
         specific.set<Parameter::Specific::dynamicsProcessing>(dp);
         expectParam.set<Parameter::specific>(specific);
-        ASSERT_STATUS(expected, mEffect->setParameter(expectParam)) << expectParam.toString();
+        ASSERT_STATUS(expected, mEffect->setParameter(expectParam))
+                << "\n"
+                << expectParam.toString() << "\n"
+                << desc.toString();
 
         // only get if parameter in range and set success
         if (expected == EX_NONE) {
diff --git a/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp b/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp
index 82c8757..fea41cb 100644
--- a/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp
@@ -23,7 +23,6 @@
 
 using namespace android;
 
-using aidl::android::hardware::audio::effect::Capability;
 using aidl::android::hardware::audio::effect::Descriptor;
 using aidl::android::hardware::audio::effect::EnvironmentalReverb;
 using aidl::android::hardware::audio::effect::IEffect;
@@ -93,7 +92,8 @@
             // validate parameter
             Descriptor desc;
             ASSERT_STATUS(EX_NONE, mEffect->getDescriptor(&desc));
-            const bool valid = isTagInRange(it.first, it.second, desc);
+            const bool valid = isParameterValid<EnvironmentalReverb, Range::environmentalReverb>(
+                    it.second, desc);
             const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT;
 
             // set
@@ -171,239 +171,6 @@
         mTags.push_back({EnvironmentalReverb::bypass, er});
     }
 
-    bool isTagInRange(const EnvironmentalReverb::Tag& tag, const EnvironmentalReverb er,
-                      const Descriptor& desc) const {
-        const EnvironmentalReverb::Capability& erCap =
-                desc.capability.get<Capability::environmentalReverb>();
-        switch (tag) {
-            case EnvironmentalReverb::roomLevelMb: {
-                int roomLevel = er.get<EnvironmentalReverb::roomLevelMb>();
-                return isRoomLevelInRange(erCap, roomLevel);
-            }
-            case EnvironmentalReverb::roomHfLevelMb: {
-                int roomHfLevel = er.get<EnvironmentalReverb::roomHfLevelMb>();
-                return isRoomHfLevelInRange(erCap, roomHfLevel);
-            }
-            case EnvironmentalReverb::decayTimeMs: {
-                int decayTime = er.get<EnvironmentalReverb::decayTimeMs>();
-                return isDecayTimeInRange(erCap, decayTime);
-            }
-            case EnvironmentalReverb::decayHfRatioPm: {
-                int decayHfRatio = er.get<EnvironmentalReverb::decayHfRatioPm>();
-                return isDecayHfRatioInRange(erCap, decayHfRatio);
-            }
-            case EnvironmentalReverb::levelMb: {
-                int level = er.get<EnvironmentalReverb::levelMb>();
-                return isLevelInRange(erCap, level);
-            }
-            case EnvironmentalReverb::delayMs: {
-                int delay = er.get<EnvironmentalReverb::delayMs>();
-                return isDelayInRange(erCap, delay);
-            }
-            case EnvironmentalReverb::diffusionPm: {
-                int diffusion = er.get<EnvironmentalReverb::diffusionPm>();
-                return isDiffusionInRange(erCap, diffusion);
-            }
-            case EnvironmentalReverb::densityPm: {
-                int density = er.get<EnvironmentalReverb::densityPm>();
-                return isDensityInRange(erCap, density);
-            }
-            case EnvironmentalReverb::bypass: {
-                return true;
-            }
-            default:
-                return false;
-        }
-        return false;
-    }
-
-    bool isRoomLevelInRange(const EnvironmentalReverb::Capability& cap, int roomLevel) const {
-        return roomLevel >= cap.minRoomLevelMb && roomLevel <= cap.maxRoomLevelMb;
-    }
-
-    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 >= 0 && decayTime <= cap.maxDecayTimeMs;
-    }
-
-    bool isDecayHfRatioInRange(const EnvironmentalReverb::Capability& cap, int decayHfRatio) const {
-        return decayHfRatio >= cap.minDecayHfRatioPm && decayHfRatio <= cap.maxDecayHfRatioPm;
-    }
-
-    bool isLevelInRange(const EnvironmentalReverb::Capability& cap, int level) const {
-        return level >= cap.minLevelMb && level <= cap.maxLevelMb;
-    }
-
-    bool isDelayInRange(const EnvironmentalReverb::Capability& cap, int delay) const {
-        return delay >= 0 && delay <= cap.maxDelayMs;
-    }
-
-    bool isDiffusionInRange(const EnvironmentalReverb::Capability& cap, int diffusion) const {
-        return diffusion >= 0 && diffusion <= cap.maxDiffusionPm;
-    }
-
-    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:
     std::vector<std::pair<EnvironmentalReverb::Tag, EnvironmentalReverb>> mTags;
     void CleanUp() { mTags.clear(); }
@@ -428,11 +195,17 @@
     SetAndGetReverbParameters();
 }
 
+std::vector<std::pair<std::shared_ptr<IFactory>, Descriptor>> kDescPair;
+
 INSTANTIATE_TEST_SUITE_P(
         EnvironmentalReverbTest, EnvironmentalReverbRoomLevelTest,
-        ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
-                                   IFactory::descriptor, kEnvReverbTypeUUID)),
-                           testing::ValuesIn(EnvironmentalReverbHelper::getRoomLevelValues())),
+        ::testing::Combine(
+                testing::ValuesIn(kDescPair = EffectFactoryHelper::getAllEffectDescriptors(
+                                          IFactory::descriptor, kEnvReverbTypeUUID)),
+                testing::ValuesIn(EffectHelper::getTestValueSet<EnvironmentalReverb, int,
+                                                                Range::environmentalReverb,
+                                                                EnvironmentalReverb::roomLevelMb>(
+                        kDescPair, EffectHelper::expandTestValueBasic<int>))),
         [](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));
@@ -467,9 +240,13 @@
 
 INSTANTIATE_TEST_SUITE_P(
         EnvironmentalReverbTest, EnvironmentalReverbRoomHfLevelTest,
-        ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
-                                   IFactory::descriptor, kEnvReverbTypeUUID)),
-                           testing::ValuesIn(EnvironmentalReverbHelper::getRoomHfLevelValues())),
+        ::testing::Combine(
+                testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor,
+                                                                               kEnvReverbTypeUUID)),
+                testing::ValuesIn(EffectHelper::getTestValueSet<EnvironmentalReverb, int,
+                                                                Range::environmentalReverb,
+                                                                EnvironmentalReverb::roomHfLevelMb>(
+                        kDescPair, EffectHelper::expandTestValueBasic<int>))),
         [](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));
@@ -504,9 +281,13 @@
 
 INSTANTIATE_TEST_SUITE_P(
         EnvironmentalReverbTest, EnvironmentalReverbDecayTimeTest,
-        ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
-                                   IFactory::descriptor, kEnvReverbTypeUUID)),
-                           testing::ValuesIn(EnvironmentalReverbHelper::getDecayTimeValues())),
+        ::testing::Combine(
+                testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor,
+                                                                               kEnvReverbTypeUUID)),
+                testing::ValuesIn(EffectHelper::getTestValueSet<EnvironmentalReverb, int,
+                                                                Range::environmentalReverb,
+                                                                EnvironmentalReverb::decayTimeMs>(
+                        kDescPair, EffectHelper::expandTestValueBasic<int>))),
         [](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));
@@ -543,7 +324,10 @@
         EnvironmentalReverbTest, EnvironmentalReverbDecayHfRatioTest,
         ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
                                    IFactory::descriptor, kEnvReverbTypeUUID)),
-                           testing::ValuesIn(EnvironmentalReverbHelper::getDecayHfRatioValues())),
+                           testing::ValuesIn(EffectHelper::getTestValueSet<
+                                             EnvironmentalReverb, int, Range::environmentalReverb,
+                                             EnvironmentalReverb::decayHfRatioPm>(
+                                   kDescPair, EffectHelper::expandTestValueBasic<int>))),
         [](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));
@@ -579,9 +363,13 @@
 
 INSTANTIATE_TEST_SUITE_P(
         EnvironmentalReverbTest, EnvironmentalReverbLevelTest,
-        ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
-                                   IFactory::descriptor, kEnvReverbTypeUUID)),
-                           testing::ValuesIn(EnvironmentalReverbHelper::getLevelValues())),
+        ::testing::Combine(
+                testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor,
+                                                                               kEnvReverbTypeUUID)),
+                testing::ValuesIn(EffectHelper::getTestValueSet<EnvironmentalReverb, int,
+                                                                Range::environmentalReverb,
+                                                                EnvironmentalReverb::levelMb>(
+                        kDescPair, EffectHelper::expandTestValueBasic<int>))),
         [](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));
@@ -616,9 +404,13 @@
 
 INSTANTIATE_TEST_SUITE_P(
         EnvironmentalReverbTest, EnvironmentalReverbDelayTest,
-        ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
-                                   IFactory::descriptor, kEnvReverbTypeUUID)),
-                           testing::ValuesIn(EnvironmentalReverbHelper::getDelayValues())),
+        ::testing::Combine(
+                testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor,
+                                                                               kEnvReverbTypeUUID)),
+                testing::ValuesIn(EffectHelper::getTestValueSet<EnvironmentalReverb, int,
+                                                                Range::environmentalReverb,
+                                                                EnvironmentalReverb::delayMs>(
+                        kDescPair, EffectHelper::expandTestValueBasic<int>))),
         [](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));
@@ -653,9 +445,13 @@
 
 INSTANTIATE_TEST_SUITE_P(
         EnvironmentalReverbTest, EnvironmentalReverbDiffusionTest,
-        ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
-                                   IFactory::descriptor, kEnvReverbTypeUUID)),
-                           testing::ValuesIn(EnvironmentalReverbHelper::getDiffusionValues())),
+        ::testing::Combine(
+                testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor,
+                                                                               kEnvReverbTypeUUID)),
+                testing::ValuesIn(EffectHelper::getTestValueSet<EnvironmentalReverb, int,
+                                                                Range::environmentalReverb,
+                                                                EnvironmentalReverb::diffusionPm>(
+                        kDescPair, EffectHelper::expandTestValueBasic<int>))),
         [](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));
@@ -690,9 +486,13 @@
 
 INSTANTIATE_TEST_SUITE_P(
         EnvironmentalReverbTest, EnvironmentalReverbDensityTest,
-        ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
-                                   IFactory::descriptor, kEnvReverbTypeUUID)),
-                           testing::ValuesIn(EnvironmentalReverbHelper::getDensityValues())),
+        ::testing::Combine(
+                testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor,
+                                                                               kEnvReverbTypeUUID)),
+                testing::ValuesIn(EffectHelper::getTestValueSet<EnvironmentalReverb, int,
+                                                                Range::environmentalReverb,
+                                                                EnvironmentalReverb::densityPm>(
+                        kDescPair, EffectHelper::expandTestValueBasic<int>))),
         [](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/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp b/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp
index e11a936..54d00a7 100644
--- a/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp
@@ -18,6 +18,7 @@
 #include <limits>
 #include <map>
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -43,7 +44,6 @@
 
 using namespace android;
 
-using aidl::android::hardware::audio::effect::Capability;
 using aidl::android::hardware::audio::effect::Descriptor;
 using aidl::android::hardware::audio::effect::Equalizer;
 using aidl::android::hardware::audio::effect::IEffect;
@@ -56,8 +56,9 @@
  * testing performed in VtsAudioEfectTargetTest.
  */
 
-enum ParamName { PARAM_INSTANCE_NAME, PARAM_BAND_LEVEL };
-using EqualizerParamTestParam = std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int>;
+enum ParamName { PARAM_INSTANCE_NAME, PARAM_PRESET, PARAM_BAND_LEVEL };
+using EqualizerParamTestParam = std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int,
+                                           std::vector<Equalizer::BandLevel>>;
 
 /*
 Testing parameter range, assuming the parameter supported by effect is in this range.
@@ -69,7 +70,9 @@
 class EqualizerTest : public ::testing::TestWithParam<EqualizerParamTestParam>,
                       public EffectHelper {
   public:
-    EqualizerTest() : mBandLevel(std::get<PARAM_BAND_LEVEL>(GetParam())) {
+    EqualizerTest()
+        : mPresetIndex(std::get<PARAM_PRESET>(GetParam())),
+          mBandLevel(std::get<PARAM_BAND_LEVEL>(GetParam())) {
         std::tie(mFactory, mDescriptor) = std::get<PARAM_INSTANCE_NAME>(GetParam());
     }
 
@@ -77,48 +80,24 @@
         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 */,
                 kInputFrameCount /* iFrameCount */, kOutputFrameCount /* oFrameCount */);
         IEffect::OpenEffectReturn ret;
-        ASSERT_NO_FATAL_FAILURE(open(mEffect, common, specific, &ret, EX_NONE));
+        ASSERT_NO_FATAL_FAILURE(open(mEffect, common, std::nullopt, &ret, EX_NONE));
         ASSERT_NE(nullptr, mEffect);
-        ASSERT_NO_FATAL_FAILURE(setTagRange());
     }
     void TearDown() override {
         ASSERT_NO_FATAL_FAILURE(close(mEffect));
         ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
     }
 
-    std::pair<int, int> setPresetIndexRange(const Equalizer::Capability& cap) const {
-        const auto [min, max] =
-                std::minmax_element(cap.presets.begin(), cap.presets.end(),
-                                    [](const auto& a, const auto& b) { return a.index < b.index; });
-        return {min->index, max->index};
-    }
-    std::pair<int, int> setBandIndexRange(const Equalizer::Capability& cap) const {
-        const auto [min, max] =
-                std::minmax_element(cap.bandFrequencies.begin(), cap.bandFrequencies.end(),
-                                    [](const auto& a, const auto& b) { return a.index < b.index; });
-        return {min->index, max->index};
-    }
-    void setTagRange() {
-        Descriptor desc;
-        ASSERT_STATUS(EX_NONE, mEffect->getDescriptor(&desc));
-        Equalizer::Capability& eqCap = desc.capability.get<Capability::equalizer>();
-        mPresetIndex = setPresetIndexRange(eqCap);
-        mBandIndex = setBandIndexRange(eqCap);
-    }
-
     static const long kInputFrameCount = 0x100, kOutputFrameCount = 0x100;
     std::shared_ptr<IFactory> mFactory;
     std::shared_ptr<IEffect> mEffect;
     Descriptor mDescriptor;
-    std::pair<int, int> mPresetIndex;
-    std::pair<int, int> mBandIndex;
-    const int mBandLevel;
-    Descriptor mDesc;
+    int mPresetIndex;
+    std::vector<Equalizer::BandLevel> mBandLevel;
 
     void SetAndGetEqualizerParameters() {
         ASSERT_NE(nullptr, mEffect);
@@ -127,25 +106,22 @@
             auto& eq = it.second;
 
             // validate parameter
-            const bool valid = isTagInRange(it.first, it.second);
+            const bool valid = isParameterValid<Equalizer, Range::equalizer>(eq, mDescriptor);
             const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT;
 
             // set
-            Parameter expectParam;
-            Parameter::Specific specific;
-            specific.set<Parameter::Specific::equalizer>(eq);
-            expectParam.set<Parameter::specific>(specific);
+            Parameter::Specific specific =
+                    Parameter::Specific::make<Parameter::Specific::equalizer>(eq);
+            Parameter expectParam = Parameter::make<Parameter::specific>(specific);
             EXPECT_STATUS(expected, mEffect->setParameter(expectParam))
                     << expectParam.toString() << "\n"
-                    << mDesc.toString();
+                    << mDescriptor.toString();
 
             // only get if parameter in range and set success
             if (expected == EX_NONE) {
                 Parameter getParam;
-                Parameter::Id id;
-                Equalizer::Id eqId;
-                eqId.set<Equalizer::Id::commonTag>(tag);
-                id.set<Parameter::Id::equalizerTag>(eqId);
+                Equalizer::Id eqId = Equalizer::Id::make<Equalizer::Id::commonTag>(tag);
+                Parameter::Id id = Parameter::Id::make<Parameter::Id::equalizerTag>(eqId);
                 // if set success, then get should match
                 EXPECT_STATUS(expected, mEffect->getParameter(id, &getParam));
                 EXPECT_TRUE(isEqParameterExpected(expectParam, getParam))
@@ -197,140 +173,51 @@
     }
 
     void addPresetParam(int preset) {
-        Equalizer eq;
-        eq.set<Equalizer::preset>(preset);
-        mTags.push_back({Equalizer::preset, eq});
+        mTags.push_back({Equalizer::preset, Equalizer::make<Equalizer::preset>(preset)});
     }
 
     void addBandLevelsParam(std::vector<Equalizer::BandLevel>& bandLevels) {
-        Equalizer eq;
-        eq.set<Equalizer::bandLevels>(bandLevels);
-        mTags.push_back({Equalizer::bandLevels, eq});
-    }
-
-    bool isTagInRange(const Equalizer::Tag& tag, const Equalizer& eq) const {
-        switch (tag) {
-            case Equalizer::preset: {
-                int index = eq.get<Equalizer::preset>();
-                return index >= mPresetIndex.first && index <= mPresetIndex.second;
-            }
-            case Equalizer::bandLevels: {
-                auto& bandLevel = eq.get<Equalizer::bandLevels>();
-                return isBandInRange(bandLevel);
-            }
-            default:
-                return false;
-        }
-        return false;
-    }
-
-    bool isBandInRange(const std::vector<Equalizer::BandLevel>& bandLevel) const {
-        for (auto& it : bandLevel) {
-            if (it.index < mBandIndex.first || it.index > mBandIndex.second) return false;
-        }
-        return true;
-    }
-
-    Parameter::Specific getDefaultParamSpecific() {
-        Equalizer eq = Equalizer::make<Equalizer::preset>(0);
-        Parameter::Specific specific =
-                Parameter::Specific::make<Parameter::Specific::equalizer>(eq);
-        return specific;
+        mTags.push_back(
+                {Equalizer::bandLevels, Equalizer::make<Equalizer::bandLevels>(bandLevels)});
     }
 
   private:
     std::vector<std::pair<Equalizer::Tag, Equalizer>> mTags;
 
-    bool validCapabilityTag(Capability& cap) { return cap.getTag() == Capability::equalizer; }
-
     void CleanUp() { mTags.clear(); }
 };
 
-TEST_P(EqualizerTest, SetAndGetPresetOutOfLowerBound) {
-    addPresetParam(mPresetIndex.second - 1);
-    ASSERT_NO_FATAL_FAILURE(SetAndGetEqualizerParameters());
-}
-
-TEST_P(EqualizerTest, SetAndGetPresetOutOfUpperBound) {
-    addPresetParam(mPresetIndex.second + 1);
+TEST_P(EqualizerTest, SetAndGetParams) {
+    addBandLevelsParam(mBandLevel);
+    addPresetParam(mPresetIndex);
     EXPECT_NO_FATAL_FAILURE(SetAndGetEqualizerParameters());
 }
 
-TEST_P(EqualizerTest, SetAndGetPresetAtLowerBound) {
-    addPresetParam(mPresetIndex.first);
-    EXPECT_NO_FATAL_FAILURE(SetAndGetEqualizerParameters());
-}
-
-TEST_P(EqualizerTest, SetAndGetPresetAtHigherBound) {
-    addPresetParam(mPresetIndex.second);
-    EXPECT_NO_FATAL_FAILURE(SetAndGetEqualizerParameters());
-}
-
-TEST_P(EqualizerTest, SetAndGetPresetInBound) {
-    addPresetParam((mPresetIndex.first + mPresetIndex.second) >> 1);
-    EXPECT_NO_FATAL_FAILURE(SetAndGetEqualizerParameters());
-}
-
-TEST_P(EqualizerTest, SetAndGetBandOutOfLowerBound) {
-    std::vector<Equalizer::BandLevel> bandLevels{{mBandIndex.first - 1, mBandLevel}};
-    addBandLevelsParam(bandLevels);
-    EXPECT_NO_FATAL_FAILURE(SetAndGetEqualizerParameters());
-}
-
-TEST_P(EqualizerTest, SetAndGetBandOutOfUpperBound) {
-    std::vector<Equalizer::BandLevel> bandLevels{{mBandIndex.second + 1, mBandLevel}};
-    addBandLevelsParam(bandLevels);
-    EXPECT_NO_FATAL_FAILURE(SetAndGetEqualizerParameters());
-}
-
-TEST_P(EqualizerTest, SetAndGetBandAtLowerBound) {
-    std::vector<Equalizer::BandLevel> bandLevels{{mBandIndex.first, mBandLevel}};
-    addBandLevelsParam(bandLevels);
-    EXPECT_NO_FATAL_FAILURE(SetAndGetEqualizerParameters());
-}
-
-TEST_P(EqualizerTest, SetAndGetBandAtHigherBound) {
-    std::vector<Equalizer::BandLevel> bandLevels{{mBandIndex.second, mBandLevel}};
-    addBandLevelsParam(bandLevels);
-    EXPECT_NO_FATAL_FAILURE(SetAndGetEqualizerParameters());
-}
-
-TEST_P(EqualizerTest, SetAndGetBandInBound) {
-    std::vector<Equalizer::BandLevel> bandLevels{
-            {(mBandIndex.first + mBandIndex.second) >> 1, mBandLevel}};
-    addBandLevelsParam(bandLevels);
-    EXPECT_NO_FATAL_FAILURE(SetAndGetEqualizerParameters());
-}
-
-TEST_P(EqualizerTest, SetAndGetMultiBands) {
-    addPresetParam(mPresetIndex.first);
-    std::vector<Equalizer::BandLevel> bandLevels{
-            {mBandIndex.first, mBandLevel},
-            {mBandIndex.second, mBandLevel},
-            {(mBandIndex.first + mBandIndex.second) >> 1, mBandLevel}};
-    addBandLevelsParam(bandLevels);
-    EXPECT_NO_FATAL_FAILURE(SetAndGetEqualizerParameters());
-}
-
-TEST_P(EqualizerTest, SetAndGetMultipleParams) {
-    std::vector<Equalizer::BandLevel> bandLevels{
-            {(mBandIndex.first + mBandIndex.second) >> 1, mBandLevel}};
-    addBandLevelsParam(bandLevels);
-    addPresetParam((mPresetIndex.first + mPresetIndex.second) >> 1);
-    EXPECT_NO_FATAL_FAILURE(SetAndGetEqualizerParameters());
-}
-
+std::vector<std::pair<std::shared_ptr<IFactory>, Descriptor>> kDescPair;
 INSTANTIATE_TEST_SUITE_P(
         EqualizerTest, EqualizerTest,
-        ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
-                                   IFactory::descriptor, kEqualizerTypeUUID)),
-                           testing::ValuesIn(kBandLevels)),
+        ::testing::Combine(
+                testing::ValuesIn(kDescPair = EffectFactoryHelper::getAllEffectDescriptors(
+                                          IFactory::descriptor, kEqualizerTypeUUID)),
+                testing::ValuesIn(EffectHelper::getTestValueSet<Equalizer, int, Range::equalizer,
+                                                                Equalizer::preset>(
+                        kDescPair, EffectHelper::expandTestValueBasic<int>)),
+                testing::ValuesIn(
+                        EffectHelper::getTestValueSet<Equalizer, std::vector<Equalizer::BandLevel>,
+                                                      Range::equalizer, Equalizer::bandLevels>(
+                                kDescPair,
+                                [](std::set<std::vector<Equalizer::BandLevel>>& bandLevels) {
+                                    return bandLevels;
+                                }))),
         [](const testing::TestParamInfo<EqualizerTest::ParamType>& info) {
             auto descriptor = std::get<PARAM_INSTANCE_NAME>(info.param).second;
-            std::string bandLevel = std::to_string(std::get<PARAM_BAND_LEVEL>(info.param));
+            std::string bandLevel =
+                    ::android::internal::ToString(std::get<PARAM_BAND_LEVEL>(info.param));
             std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
                                descriptor.common.name + "_UUID_" +
-                               descriptor.common.id.uuid.toString() + "_bandLevel_" + bandLevel;
+                               descriptor.common.id.uuid.toString() + "_preset_" +
+                               std::to_string(std::get<PARAM_PRESET>(info.param)) + "_bandLevel_" +
+                               bandLevel;
             std::replace_if(
                     name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
             return name;
diff --git a/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp b/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp
index b8ea9c1..6c3016e 100644
--- a/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp
@@ -27,7 +27,6 @@
 
 using namespace android;
 
-using aidl::android::hardware::audio::effect::Capability;
 using aidl::android::hardware::audio::effect::Descriptor;
 using aidl::android::hardware::audio::effect::HapticGenerator;
 using aidl::android::hardware::audio::effect::IEffect;
diff --git a/audio/aidl/vts/VtsHalLoudnessEnhancerTargetTest.cpp b/audio/aidl/vts/VtsHalLoudnessEnhancerTargetTest.cpp
index 305c243..75941ff 100644
--- a/audio/aidl/vts/VtsHalLoudnessEnhancerTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalLoudnessEnhancerTargetTest.cpp
@@ -24,7 +24,6 @@
 
 using namespace android;
 
-using aidl::android::hardware::audio::effect::Capability;
 using aidl::android::hardware::audio::effect::Descriptor;
 using aidl::android::hardware::audio::effect::IEffect;
 using aidl::android::hardware::audio::effect::IFactory;
diff --git a/audio/aidl/vts/VtsHalNSTargetTest.cpp b/audio/aidl/vts/VtsHalNSTargetTest.cpp
index 0b92290..16c79e3 100644
--- a/audio/aidl/vts/VtsHalNSTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalNSTargetTest.cpp
@@ -26,7 +26,6 @@
 
 using namespace android;
 
-using aidl::android::hardware::audio::effect::Capability;
 using aidl::android::hardware::audio::effect::Descriptor;
 using aidl::android::hardware::audio::effect::IEffect;
 using aidl::android::hardware::audio::effect::IFactory;
diff --git a/audio/aidl/vts/VtsHalPresetReverbTargetTest.cpp b/audio/aidl/vts/VtsHalPresetReverbTargetTest.cpp
index 19d5747..c9c2a31 100644
--- a/audio/aidl/vts/VtsHalPresetReverbTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalPresetReverbTargetTest.cpp
@@ -23,7 +23,6 @@
 
 using namespace android;
 
-using aidl::android::hardware::audio::effect::Capability;
 using aidl::android::hardware::audio::effect::Descriptor;
 using aidl::android::hardware::audio::effect::IEffect;
 using aidl::android::hardware::audio::effect::IFactory;
@@ -84,7 +83,7 @@
             // validate parameter
             Descriptor desc;
             ASSERT_STATUS(EX_NONE, mEffect->getDescriptor(&desc));
-            const bool valid = isTagInRange(it.first, it.second, desc);
+            const bool valid = isParameterValid<PresetReverb, Range::presetReverb>(it.second, desc);
             const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT;
 
             // set parameter
@@ -113,27 +112,6 @@
         mTags.push_back({PresetReverb::preset, pr});
     }
 
-    bool isTagInRange(const PresetReverb::Tag& tag, const PresetReverb& pr,
-                      const Descriptor& desc) const {
-        const PresetReverb::Capability& prCap = desc.capability.get<Capability::presetReverb>();
-        switch (tag) {
-            case PresetReverb::preset: {
-                PresetReverb::Presets preset = pr.get<PresetReverb::preset>();
-                return isPresetInRange(prCap, preset);
-            }
-            default:
-                return false;
-        }
-        return false;
-    }
-
-    bool isPresetInRange(const PresetReverb::Capability& cap, PresetReverb::Presets preset) const {
-        for (auto i : cap.supportedPresets) {
-            if (preset == i) return true;
-        }
-        return false;
-    }
-
     Parameter::Specific getDefaultParamSpecific() {
         PresetReverb pr = PresetReverb::make<PresetReverb::preset>(PresetReverb::Presets::NONE);
         Parameter::Specific specific =
diff --git a/audio/aidl/vts/VtsHalVirtualizerTargetTest.cpp b/audio/aidl/vts/VtsHalVirtualizerTargetTest.cpp
index 090de17..8b0210c 100644
--- a/audio/aidl/vts/VtsHalVirtualizerTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalVirtualizerTargetTest.cpp
@@ -22,7 +22,6 @@
 
 using namespace android;
 
-using aidl::android::hardware::audio::effect::Capability;
 using aidl::android::hardware::audio::effect::Descriptor;
 using aidl::android::hardware::audio::effect::IEffect;
 using aidl::android::hardware::audio::effect::IFactory;
@@ -90,7 +89,7 @@
             // validate parameter
             Descriptor desc;
             ASSERT_STATUS(EX_NONE, mEffect->getDescriptor(&desc));
-            const bool valid = isTagInRange(it.first, it.second, desc);
+            const bool valid = isParameterValid<Virtualizer, Range::virtualizer>(it.second, desc);
             const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT;
 
             // set parameter
@@ -120,46 +119,6 @@
         mTags.push_back({Virtualizer::strengthPm, vr});
     }
 
-    bool isTagInRange(const Virtualizer::Tag& tag, const Virtualizer& vr,
-                      const Descriptor& desc) const {
-        const Virtualizer::Capability& vrCap = desc.capability.get<Capability::virtualizer>();
-        switch (tag) {
-            case Virtualizer::strengthPm: {
-                int strength = vr.get<Virtualizer::strengthPm>();
-                return isStrengthInRange(vrCap, strength);
-            }
-            default:
-                return false;
-        }
-        return false;
-    }
-
-    bool isStrengthInRange(const Virtualizer::Capability& cap, int strength) const {
-        return cap.strengthSupported && strength >= 0 && strength <= cap.maxStrengthPm;
-    }
-
-    static std::vector<int> getStrengthTestValues(
-            std::vector<std::pair<std::shared_ptr<IFactory>, Descriptor>> kFactoryDescList) {
-        const auto max = std::max_element(
-                kFactoryDescList.begin(), kFactoryDescList.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::virtualizer>().maxStrengthPm <
-                           b.second.capability.get<Capability::virtualizer>().maxStrengthPm;
-                });
-        if (max == kFactoryDescList.end()) {
-            return {0};
-        }
-        int maxStrength = max->second.capability.get<Capability::virtualizer>().maxStrengthPm;
-        return {std::numeric_limits<int>::min(),
-                -1,
-                0,
-                maxStrength >> 1,
-                maxStrength,
-                maxStrength + 1,
-                std::numeric_limits<int>::max()};
-    }
-
   private:
     std::vector<std::pair<Virtualizer::Tag, Virtualizer>> mTags;
     void CleanUp() { mTags.clear(); }
@@ -170,13 +129,15 @@
     SetAndGetVirtualizerParameters();
 }
 
+std::vector<std::pair<std::shared_ptr<IFactory>, Descriptor>> kDescPair;
 INSTANTIATE_TEST_SUITE_P(
         VirtualizerTest, VirtualizerParamTest,
-        ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
-                                   IFactory::descriptor, kVirtualizerTypeUUID)),
-                           testing::ValuesIn(VirtualizerParamTest::getStrengthTestValues(
-                                   EffectFactoryHelper::getAllEffectDescriptors(
-                                           IFactory::descriptor, kVirtualizerTypeUUID)))),
+        ::testing::Combine(
+                testing::ValuesIn(kDescPair = EffectFactoryHelper::getAllEffectDescriptors(
+                                          IFactory::descriptor, kVirtualizerTypeUUID)),
+                testing::ValuesIn(EffectHelper::getTestValueSet<
+                                  Virtualizer, int, Range::virtualizer, Virtualizer::strengthPm>(
+                        kDescPair, EffectHelper::expandTestValueBasic<int>))),
         [](const testing::TestParamInfo<VirtualizerParamTest::ParamType>& info) {
             auto descriptor = std::get<PARAM_INSTANCE_NAME>(info.param).second;
             std::string strength = std::to_string(std::get<PARAM_STRENGTH>(info.param));
diff --git a/audio/aidl/vts/VtsHalVisualizerTargetTest.cpp b/audio/aidl/vts/VtsHalVisualizerTargetTest.cpp
index 242be3f..e2625cb 100644
--- a/audio/aidl/vts/VtsHalVisualizerTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalVisualizerTargetTest.cpp
@@ -26,7 +26,6 @@
 
 using namespace android;
 
-using aidl::android::hardware::audio::effect::Capability;
 using aidl::android::hardware::audio::effect::Descriptor;
 using aidl::android::hardware::audio::effect::IEffect;
 using aidl::android::hardware::audio::effect::IFactory;
@@ -64,12 +63,11 @@
         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 */,
                 kInputFrameCount /* iFrameCount */, kOutputFrameCount /* oFrameCount */);
         IEffect::OpenEffectReturn ret;
-        ASSERT_NO_FATAL_FAILURE(open(mEffect, common, specific, &ret, EX_NONE));
+        ASSERT_NO_FATAL_FAILURE(open(mEffect, common, std::nullopt, &ret, EX_NONE));
         ASSERT_NE(nullptr, mEffect);
     }
 
@@ -77,13 +75,6 @@
         ASSERT_NO_FATAL_FAILURE(close(mEffect));
         ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
     }
-    Parameter::Specific getDefaultParamSpecific() {
-        Visualizer vs = Visualizer::make<Visualizer::captureSamples>(
-                mDescriptor.capability.get<Capability::visualizer>().captureSampleRange.max);
-        Parameter::Specific specific =
-                Parameter::Specific::make<Parameter::Specific::visualizer>(vs);
-        return specific;
-    }
 
     static const long kInputFrameCount = 0x100, kOutputFrameCount = 0x100;
     std::shared_ptr<IFactory> mFactory;
@@ -94,7 +85,7 @@
     Visualizer::MeasurementMode mMeasurementMode = Visualizer::MeasurementMode::NONE;
     int mLatency = 0;
 
-    void SetAndGetCommonParameters() {
+    void SetAndGetParameters() {
         for (auto& it : mCommonTags) {
             auto& tag = it.first;
             auto& vs = it.second;
@@ -102,7 +93,7 @@
             // validate parameter
             Descriptor desc;
             ASSERT_STATUS(EX_NONE, mEffect->getDescriptor(&desc));
-            const bool valid = isTagInRange(tag, vs, desc);
+            const bool valid = isParameterValid<Visualizer, Range::visualizer>(vs, desc);
             const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT;
 
             // set parameter
@@ -119,170 +110,39 @@
                 Visualizer::Id vsId;
                 vsId.set<Visualizer::Id::commonTag>(tag);
                 id.set<Parameter::Id::visualizerTag>(vsId);
-                EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &getParam));
-
+                EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &getParam))
+                        << " with: " << id.toString();
                 EXPECT_EQ(expectParam, getParam) << "\nexpect:" << expectParam.toString()
                                                  << "\ngetParam:" << getParam.toString();
             }
         }
     }
 
-    void SetAndGetSetOnlyParameters() {
-        for (auto& it : mSetOnlyParamTags) {
-            auto& tag = it.first;
-            auto& vs = it.second;
-
-            // validate parameter
-            Descriptor desc;
-            ASSERT_STATUS(EX_NONE, mEffect->getDescriptor(&desc));
-            const bool valid = isSetOnlyParamTagInRange(vs, desc);
-            const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT;
-
-            // set parameter
-            Parameter expectParam;
-            Parameter::Specific specific;
-            specific.set<Parameter::Specific::visualizer>(vs);
-            expectParam.set<Parameter::specific>(specific);
-            ASSERT_STATUS(expected, mEffect->setParameter(expectParam));
-
-            //  parameter defined in this setOnlyParameter union must be settable via
-            //  setParameter(), but must not be gettable
-            Parameter getParam;
-            Parameter::Id id;
-            Visualizer::Id vsId;
-            vsId.set<Visualizer::Id::setOnlyParamTag>(tag);
-            id.set<Parameter::Id::visualizerTag>(vsId);
-            EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, mEffect->getParameter(id, &getParam));
-        }
-    }
-
-    void GetandSetGetOnlyParameters() {
-        for (auto& tag : mGetOnlyParamTags) {
-            // get parameter
-            Parameter getParam;
-            Parameter::Id id;
-            Visualizer::Id vsId;
-            vsId.set<Visualizer::Id::getOnlyParamTag>(tag);
-            id.set<Parameter::Id::visualizerTag>(vsId);
-            ASSERT_STATUS(EX_NONE, mEffect->getParameter(id, &getParam));
-
-            //  parameter defined in this getOnlyParameter union must be gettable via
-            //  getParameter(), but must not be settable
-            // set parameter
-            ASSERT_STATUS(EX_ILLEGAL_ARGUMENT, mEffect->setParameter(getParam));
-        }
-    }
-
     void addCaptureSizeParam(int captureSize) {
-        Visualizer vs;
-        vs.set<Visualizer::captureSamples>(captureSize);
-        mCommonTags.push_back({Visualizer::captureSamples, vs});
+        mCommonTags.push_back({Visualizer::captureSamples,
+                               Visualizer::make<Visualizer::captureSamples>(captureSize)});
     }
 
     void addScalingModeParam(Visualizer::ScalingMode scalingMode) {
-        Visualizer vs;
-        vs.set<Visualizer::scalingMode>(scalingMode);
-        mCommonTags.push_back({Visualizer::scalingMode, vs});
+        mCommonTags.push_back(
+                {Visualizer::scalingMode, Visualizer::make<Visualizer::scalingMode>(scalingMode)});
     }
 
     void addMeasurementModeParam(Visualizer::MeasurementMode measurementMode) {
-        Visualizer vs;
-        vs.set<Visualizer::measurementMode>(measurementMode);
-        mCommonTags.push_back({Visualizer::measurementMode, vs});
+        mCommonTags.push_back({Visualizer::measurementMode,
+                               Visualizer::make<Visualizer::measurementMode>(measurementMode)});
     }
 
     void addLatencyParam(int latency) {
-        Visualizer vs;
-        Visualizer::SetOnlyParameters setOnlyParam;
-        setOnlyParam.set<Visualizer::SetOnlyParameters::latencyMs>(latency);
-        vs.set<Visualizer::setOnlyParameters>(setOnlyParam);
-        mSetOnlyParamTags.push_back({Visualizer::SetOnlyParameters::latencyMs, vs});
+        mCommonTags.push_back(
+                {Visualizer::latencyMs, Visualizer::make<Visualizer::latencyMs>(latency)});
     }
 
-    void addMeasurementTag() {
-        mGetOnlyParamTags.push_back(Visualizer::GetOnlyParameters::measurement);
-    }
-
-    void addCaptureBytesTag() {
-        mGetOnlyParamTags.push_back(Visualizer::GetOnlyParameters::captureSampleBuffer);
-    }
-
-    bool isTagInRange(const Visualizer::Tag& tag, const Visualizer& vs,
-                      const Descriptor& desc) const {
-        const Visualizer::Capability& vsCap = desc.capability.get<Capability::visualizer>();
-        switch (tag) {
-            case Visualizer::captureSamples: {
-                int captureSize = vs.get<Visualizer::captureSamples>();
-                return captureSize >= vsCap.captureSampleRange.min &&
-                       captureSize <= vsCap.captureSampleRange.max;
-            }
-            case Visualizer::scalingMode:
-            case Visualizer::measurementMode:
-                return true;
-            case Visualizer::setOnlyParameters: {
-                auto setOnly = vs.get<Visualizer::setOnlyParameters>();
-                if (setOnly.getTag() != Visualizer::SetOnlyParameters::latencyMs) {
-                    return false;
-                }
-                auto latencyMs = setOnly.get<Visualizer::SetOnlyParameters::latencyMs>();
-                return latencyMs >= 0 && latencyMs <= vsCap.maxLatencyMs;
-            }
-            default:
-                return false;
-        }
-    }
-
-    bool isSetOnlyParamTagInRange(const Visualizer& vs, const Descriptor& desc) const {
-        const Visualizer::Capability& vsCap = desc.capability.get<Capability::visualizer>();
-        if (vs.getTag() != Visualizer::setOnlyParameters) return false;
-        Visualizer::SetOnlyParameters setOnlyParam = vs.get<Visualizer::setOnlyParameters>();
-        if (setOnlyParam.getTag() != Visualizer::SetOnlyParameters::latencyMs) return false;
-        int latency = setOnlyParam.get<Visualizer::SetOnlyParameters::latencyMs>();
-        return isLatencyInRange(vsCap, latency);
-    }
-
-    bool isLatencyInRange(const Visualizer::Capability& cap, int latency) const {
-        return (latency >= 0 && latency <= cap.maxLatencyMs);
-    }
-
-    static std::unordered_set<int> getCaptureSizeValues() {
-        auto descList = EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor,
-                                                                     kVisualizerTypeUUID);
-        int minCaptureSize = std::numeric_limits<int>::max();
-        int maxCaptureSize = std::numeric_limits<int>::min();
-        for (const auto& it : descList) {
-            maxCaptureSize = std::max(
-                    it.second.capability.get<Capability::visualizer>().captureSampleRange.max,
-                    maxCaptureSize);
-            minCaptureSize = std::min(
-                    it.second.capability.get<Capability ::visualizer>().captureSampleRange.min,
-                    minCaptureSize);
-        }
-        return {std::numeric_limits<int>::min(),        minCaptureSize - 1, minCaptureSize,
-                (minCaptureSize + maxCaptureSize) >> 1, maxCaptureSize,     maxCaptureSize + 1,
-                std::numeric_limits<int>::max()};
-    }
-
-    static std::unordered_set<int> getLatencyValues() {
-        auto descList = EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor,
-                                                                     kVisualizerTypeUUID);
-        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::visualizer>().maxLatencyMs <
-                           b.second.capability.get<Capability::visualizer>().maxLatencyMs;
-                });
-        if (max == descList.end()) {
-            return {0};
-        }
-        int maxDelay = max->second.capability.get<Capability::visualizer>().maxLatencyMs;
-        return {-1, 0, maxDelay >> 1, maxDelay - 1, maxDelay, maxDelay + 1};
-    }
     static std::unordered_set<Visualizer::MeasurementMode> getMeasurementModeValues() {
         return {ndk::enum_range<Visualizer::MeasurementMode>().begin(),
                 ndk::enum_range<Visualizer::MeasurementMode>().end()};
     }
+
     static std::unordered_set<Visualizer::ScalingMode> getScalingModeValues() {
         return {ndk::enum_range<Visualizer::ScalingMode>().begin(),
                 ndk::enum_range<Visualizer::ScalingMode>().end()};
@@ -290,53 +150,43 @@
 
   private:
     std::vector<std::pair<Visualizer::Tag, Visualizer>> mCommonTags;
-    std::vector<std::pair<Visualizer::SetOnlyParameters::Tag, Visualizer>> mSetOnlyParamTags;
-    std::vector<Visualizer::GetOnlyParameters::Tag> mGetOnlyParamTags;
-    void CleanUp() {
-        mCommonTags.clear();
-        mSetOnlyParamTags.clear();
-        mGetOnlyParamTags.clear();
-    }
+    void CleanUp() { mCommonTags.clear(); }
 };
 
 TEST_P(VisualizerParamTest, SetAndGetCaptureSize) {
     EXPECT_NO_FATAL_FAILURE(addCaptureSizeParam(mCaptureSize));
-    SetAndGetCommonParameters();
+    SetAndGetParameters();
 }
 
 TEST_P(VisualizerParamTest, SetAndGetScalingMode) {
     EXPECT_NO_FATAL_FAILURE(addScalingModeParam(mScalingMode));
-    SetAndGetCommonParameters();
+    SetAndGetParameters();
 }
 
 TEST_P(VisualizerParamTest, SetAndGetMeasurementMode) {
     EXPECT_NO_FATAL_FAILURE(addMeasurementModeParam(mMeasurementMode));
-    SetAndGetCommonParameters();
+    SetAndGetParameters();
 }
 
 TEST_P(VisualizerParamTest, SetAndGetLatency) {
     EXPECT_NO_FATAL_FAILURE(addLatencyParam(mLatency));
-    SetAndGetSetOnlyParameters();
+    SetAndGetParameters();
 }
 
-TEST_P(VisualizerParamTest, GetAndSetMeasurement) {
-    EXPECT_NO_FATAL_FAILURE(addMeasurementTag());
-    GetandSetGetOnlyParameters();
-}
-
-TEST_P(VisualizerParamTest, GetAndSetCaptureBytes) {
-    EXPECT_NO_FATAL_FAILURE(addCaptureBytesTag());
-    GetandSetGetOnlyParameters();
-}
-
+std::vector<std::pair<std::shared_ptr<IFactory>, Descriptor>> kDescPair;
 INSTANTIATE_TEST_SUITE_P(
         VisualizerParamTest, VisualizerParamTest,
-        ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
-                                   IFactory::descriptor, kVisualizerTypeUUID)),
-                           testing::ValuesIn(VisualizerParamTest::getCaptureSizeValues()),
-                           testing::ValuesIn(VisualizerParamTest::getScalingModeValues()),
-                           testing::ValuesIn(VisualizerParamTest::getMeasurementModeValues()),
-                           testing::ValuesIn(VisualizerParamTest::getLatencyValues())),
+        ::testing::Combine(
+                testing::ValuesIn(kDescPair = EffectFactoryHelper::getAllEffectDescriptors(
+                                          IFactory::descriptor, kVisualizerTypeUUID)),
+                testing::ValuesIn(EffectHelper::getTestValueSet<Visualizer, int, Range::visualizer,
+                                                                Visualizer::captureSamples>(
+                        kDescPair, EffectHelper::expandTestValueBasic<int>)),
+                testing::ValuesIn(VisualizerParamTest::getScalingModeValues()),
+                testing::ValuesIn(VisualizerParamTest::getMeasurementModeValues()),
+                testing::ValuesIn(EffectHelper::getTestValueSet<Visualizer, int, Range::visualizer,
+                                                                Visualizer::latencyMs>(
+                        kDescPair, EffectHelper::expandTestValueBasic<int>))),
         [](const testing::TestParamInfo<VisualizerParamTest::ParamType>& info) {
             auto descriptor = std::get<PARAM_INSTANCE_NAME>(info.param).second;
             std::string captureSize = std::to_string(std::get<PARAM_CAPTURE_SIZE>(info.param));
diff --git a/audio/aidl/vts/VtsHalVolumeTargetTest.cpp b/audio/aidl/vts/VtsHalVolumeTargetTest.cpp
index 34625e7..44ce146 100644
--- a/audio/aidl/vts/VtsHalVolumeTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalVolumeTargetTest.cpp
@@ -22,7 +22,6 @@
 
 using namespace android;
 
-using aidl::android::hardware::audio::effect::Capability;
 using aidl::android::hardware::audio::effect::Descriptor;
 using aidl::android::hardware::audio::effect::IEffect;
 using aidl::android::hardware::audio::effect::IFactory;
@@ -84,9 +83,7 @@
             // validate parameter
             Descriptor desc;
             ASSERT_STATUS(EX_NONE, mEffect->getDescriptor(&desc));
-            // only set and get parameter if capability is valid
-            ASSERT_TRUE(isCapabilityValid(desc));
-            const bool valid = isTagInRange(it.first, it.second, desc);
+            const bool valid = isParameterValid<Volume, Range::volume>(it.second, desc);
             const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT;
 
             // set parameter
@@ -123,42 +120,6 @@
         mTags.push_back({Volume::mute, vol});
     }
 
-    bool isCapabilityValid(const Descriptor& desc) {
-        const Volume::Capability& volCap = desc.capability.get<Capability::volume>();
-        return (volCap.minLevelDb <= volCap.maxLevelDb);
-    }
-
-    bool isTagInRange(const Volume::Tag& tag, const Volume& vol, const Descriptor& desc) const {
-        const Volume::Capability& volCap = desc.capability.get<Capability::volume>();
-        switch (tag) {
-            case Volume::levelDb: {
-                int level = vol.get<Volume::levelDb>();
-                return isLevelInRange(volCap, level);
-            }
-            case Volume::mute:
-                return true;
-            default:
-                return false;
-        }
-    }
-
-    static std::vector<int> getLevelTestValues(
-            std::vector<std::pair<std::shared_ptr<IFactory>, Descriptor>> kFactoryDescList) {
-        int minLevelDb = std::numeric_limits<int>::max();
-        int maxLevelDb = std::numeric_limits<int>::min();
-        for (const auto& it : kFactoryDescList) {
-            maxLevelDb =
-                    std::max(it.second.capability.get<Capability::volume>().maxLevelDb, maxLevelDb);
-            minLevelDb = std::min(it.second.capability.get<Capability ::volume>().minLevelDb,
-                                  minLevelDb);
-        }
-        return {minLevelDb - 1, minLevelDb, -100, maxLevelDb, maxLevelDb + 1};
-    }
-
-    bool isLevelInRange(const Volume::Capability& volCap, int level) const {
-        return level >= volCap.minLevelDb && level <= volCap.maxLevelDb;
-    }
-
   private:
     std::vector<std::pair<Volume::Tag, Volume>> mTags;
     void CleanUp() { mTags.clear(); }
@@ -174,14 +135,15 @@
     SetAndGetParameters();
 }
 
+std::vector<std::pair<std::shared_ptr<IFactory>, Descriptor>> kDescPair;
 INSTANTIATE_TEST_SUITE_P(
         VolumeTest, VolumeParamTest,
         ::testing::Combine(
-                testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor,
-                                                                               kVolumeTypeUUID)),
-                testing::ValuesIn(VolumeParamTest::getLevelTestValues(
-                        EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor,
-                                                                     kVolumeTypeUUID))),
+                testing::ValuesIn(kDescPair = EffectFactoryHelper::getAllEffectDescriptors(
+                                          IFactory::descriptor, kVolumeTypeUUID)),
+                testing::ValuesIn(
+                        EffectHelper::getTestValueSet<Volume, int, Range::volume, Volume::levelDb>(
+                                kDescPair, EffectHelper::expandTestValueBasic<int>)),
                 testing::Bool() /* mute */),
         [](const testing::TestParamInfo<VolumeParamTest::ParamType>& info) {
             auto descriptor = std::get<PARAM_INSTANCE_NAME>(info.param).second;
diff --git a/automotive/audiocontrol/aidl/aidl_api/android.hardware.automotive.audiocontrol/current/android/hardware/automotive/audiocontrol/IAudioControl.aidl b/automotive/audiocontrol/aidl/aidl_api/android.hardware.automotive.audiocontrol/current/android/hardware/automotive/audiocontrol/IAudioControl.aidl
index 8dc5ffe..ffd575d 100644
--- a/automotive/audiocontrol/aidl/aidl_api/android.hardware.automotive.audiocontrol/current/android/hardware/automotive/audiocontrol/IAudioControl.aidl
+++ b/automotive/audiocontrol/aidl/aidl_api/android.hardware.automotive.audiocontrol/current/android/hardware/automotive/audiocontrol/IAudioControl.aidl
@@ -46,4 +46,6 @@
   oneway void onAudioFocusChangeWithMetaData(in android.hardware.audio.common.PlaybackTrackMetadata playbackMetaData, in int zoneId, in android.hardware.automotive.audiocontrol.AudioFocusChange focusChange);
   oneway void setAudioDeviceGainsChanged(in android.hardware.automotive.audiocontrol.Reasons[] reasons, in android.hardware.automotive.audiocontrol.AudioGainConfigInfo[] gains);
   oneway void registerGainCallback(in android.hardware.automotive.audiocontrol.IAudioGainCallback callback);
+  void setModuleChangeCallback(in android.hardware.automotive.audiocontrol.IModuleChangeCallback callback);
+  void clearModuleChangeCallback();
 }
diff --git a/automotive/audiocontrol/aidl/aidl_api/android.hardware.automotive.audiocontrol/current/android/hardware/automotive/audiocontrol/IModuleChangeCallback.aidl b/automotive/audiocontrol/aidl/aidl_api/android.hardware.automotive.audiocontrol/current/android/hardware/automotive/audiocontrol/IModuleChangeCallback.aidl
new file mode 100644
index 0000000..2bbb936
--- /dev/null
+++ b/automotive/audiocontrol/aidl/aidl_api/android.hardware.automotive.audiocontrol/current/android/hardware/automotive/audiocontrol/IModuleChangeCallback.aidl
@@ -0,0 +1,38 @@
+/*
+ * 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 FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.automotive.audiocontrol;
+@VintfStability
+interface IModuleChangeCallback {
+  oneway void onAudioPortsChanged(in android.media.audio.common.AudioPort[] audioPorts);
+}
diff --git a/automotive/audiocontrol/aidl/android/hardware/automotive/audiocontrol/IAudioControl.aidl b/automotive/audiocontrol/aidl/android/hardware/automotive/audiocontrol/IAudioControl.aidl
index 0ffcd5e..9564efc 100644
--- a/automotive/audiocontrol/aidl/android/hardware/automotive/audiocontrol/IAudioControl.aidl
+++ b/automotive/audiocontrol/aidl/android/hardware/automotive/audiocontrol/IAudioControl.aidl
@@ -51,6 +51,7 @@
 import android.hardware.automotive.audiocontrol.DuckingInfo;
 import android.hardware.automotive.audiocontrol.IAudioGainCallback;
 import android.hardware.automotive.audiocontrol.IFocusListener;
+import android.hardware.automotive.audiocontrol.IModuleChangeCallback;
 import android.hardware.automotive.audiocontrol.MutingInfo;
 import android.hardware.automotive.audiocontrol.Reasons;
 
@@ -183,4 +184,26 @@
      *                 interface.
      */
     oneway void registerGainCallback(in IAudioGainCallback callback);
+
+    /**
+     * Sets callback with HAL for notifying changes to hardware module (that is:
+     * {@link android.hardware.audio.core.IModule}) configurations.
+     *
+     * @param callback The {@link android.hardware.automotive.audiocontrol.IModuleChangeCallback}
+     *                 interface to use use when new updates are available for
+     *                 {@link android.hardware.audio.core.IModule} configs
+     * @throws EX_UNSUPPORTED_OPERATION if dynamic audio configs are not supported.
+     * @throws EX_ILLEGAL_STATE if a callback already exists
+     * @throws EX_ILLEGAL_ARGUMENT if the passed callback is (@code null}
+     */
+    void setModuleChangeCallback(in IModuleChangeCallback callback);
+
+    /**
+     * Clears module change callback
+     *
+     * If no callback is registered previously, then this call should be a no-op.
+     *
+     * @throws EX_UNSUPPORTED_OPERATION if dynamic audio configs are not supported.
+     */
+    void clearModuleChangeCallback();
 }
diff --git a/automotive/audiocontrol/aidl/android/hardware/automotive/audiocontrol/IModuleChangeCallback.aidl b/automotive/audiocontrol/aidl/android/hardware/automotive/audiocontrol/IModuleChangeCallback.aidl
new file mode 100644
index 0000000..4282483
--- /dev/null
+++ b/automotive/audiocontrol/aidl/android/hardware/automotive/audiocontrol/IModuleChangeCallback.aidl
@@ -0,0 +1,61 @@
+/*
+ * 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 android.hardware.automotive.audiocontrol;
+
+import android.media.audio.common.AudioPort;
+
+/**
+ * Interface definition for asynchronous changes to audio configs defined
+ * for a hardware {@link android.hardware.audio.core.IModule}.
+ */
+@VintfStability
+oneway interface IModuleChangeCallback {
+    /**
+     * Used to indicate that one or more {@link android.media.audio.common.AudioPort}
+     * configs have changed. Implementations MUST return at least one AudioPort.
+     *
+     * Notes for AudioPort:
+     * 1. For V3, the support will be limited to configurable AudioGain stages - per
+     *    car audio framework support.
+     * 2. For automotive 'bus' devices, the expected settings are
+     *     AudioDevice {
+     *        AudioDeviceDescription {type: IN/OUT_DEVICE, connection: CONNECTION_BUS}
+     *        AudioDeviceAddress {id: string}}
+     *
+     * Notes for AudioGain:
+     * 1. Car audio framework only supports AudioGainMode::JOINT. Any other mode
+     *    selection will be ignored.
+     *    See {@link android.media.audio.common.AudioGainMode}
+     * 2. Implementations MUST ensure that the gain stages are identical for buses
+     *    that map to the same volume group. Any inconsistencies will result in
+     *    inferior volume-change experience to the users.
+     * 3. Implementations MUST ensure that the new gain stages are subset (do not
+     *    exceed) of the gain stage definitions provided to audio policy. If they
+     *    exceed, it can result in failure when setting value over the range
+     *    allowed by the audio policy.
+     *
+     * Other notes:
+     * 1. In case of AudioControl  service restart or resume, the implementations MUST
+     *    issue an immediate callback following registration.
+     * 2. In case of client restart, the AudioControl service MUST clear all stale
+     *    callbacks.
+     *
+     * @param audioPorts list of {@link android.media.audio.common.AudioPort} that
+     *                   are updated
+     */
+    void onAudioPortsChanged(in AudioPort[] audioPorts);
+}
diff --git a/automotive/audiocontrol/aidl/default/Android.bp b/automotive/audiocontrol/aidl/default/Android.bp
index 6bf4b9d..dde05c3 100644
--- a/automotive/audiocontrol/aidl/default/Android.bp
+++ b/automotive/audiocontrol/aidl/default/Android.bp
@@ -31,7 +31,7 @@
         "android.hardware.audio.common@7.0-enums",
         "android.hardware.audio.common-V1-ndk",
         "android.frameworks.automotive.powerpolicy-V1-ndk",
-        "android.hardware.automotive.audiocontrol-V2-ndk",
+        "android.hardware.automotive.audiocontrol-V3-ndk",
         "libbase",
         "libbinder_ndk",
         "libcutils",
diff --git a/automotive/audiocontrol/aidl/default/AudioControl.cpp b/automotive/audiocontrol/aidl/default/AudioControl.cpp
index a121f8b..cf7307d 100644
--- a/automotive/audiocontrol/aidl/default/AudioControl.cpp
+++ b/automotive/audiocontrol/aidl/default/AudioControl.cpp
@@ -24,6 +24,7 @@
 #include <aidl/android/hardware/automotive/audiocontrol/IFocusListener.h>
 
 #include <android-base/logging.h>
+#include <android-base/parsebool.h>
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
 
@@ -37,6 +38,8 @@
 namespace aidl::android::hardware::automotive::audiocontrol {
 
 using ::android::base::EqualsIgnoreCase;
+using ::android::base::ParseBool;
+using ::android::base::ParseBoolResult;
 using ::android::base::ParseInt;
 using ::std::shared_ptr;
 using ::std::string;
@@ -70,6 +73,95 @@
 }
 }  // namespace
 
+namespace {
+using ::aidl::android::media::audio::common::AudioChannelLayout;
+using ::aidl::android::media::audio::common::AudioDeviceDescription;
+using ::aidl::android::media::audio::common::AudioDeviceType;
+using ::aidl::android::media::audio::common::AudioFormatDescription;
+using ::aidl::android::media::audio::common::AudioFormatType;
+using ::aidl::android::media::audio::common::AudioGain;
+using ::aidl::android::media::audio::common::AudioGainMode;
+using ::aidl::android::media::audio::common::AudioIoFlags;
+using ::aidl::android::media::audio::common::AudioOutputFlags;
+using ::aidl::android::media::audio::common::AudioPort;
+using ::aidl::android::media::audio::common::AudioPortDeviceExt;
+using ::aidl::android::media::audio::common::AudioPortExt;
+using ::aidl::android::media::audio::common::AudioPortMixExt;
+using ::aidl::android::media::audio::common::AudioProfile;
+using ::aidl::android::media::audio::common::PcmType;
+
+// reuse common code artifacts
+void fillProfile(const std::vector<int32_t>& channelLayouts,
+                 const std::vector<int32_t>& sampleRates, AudioProfile* profile) {
+    for (auto layout : channelLayouts) {
+        profile->channelMasks.push_back(
+                AudioChannelLayout::make<AudioChannelLayout::layoutMask>(layout));
+    }
+    profile->sampleRates.insert(profile->sampleRates.end(), sampleRates.begin(), sampleRates.end());
+}
+
+AudioProfile createProfile(PcmType pcmType, const std::vector<int32_t>& channelLayouts,
+                           const std::vector<int32_t>& sampleRates) {
+    AudioProfile profile;
+    profile.format.type = AudioFormatType::PCM;
+    profile.format.pcm = pcmType;
+    fillProfile(channelLayouts, sampleRates, &profile);
+    return profile;
+}
+
+AudioProfile createProfile(const std::string& encodingType,
+                           const std::vector<int32_t>& channelLayouts,
+                           const std::vector<int32_t>& sampleRates) {
+    AudioProfile profile;
+    profile.format.encoding = encodingType;
+    fillProfile(channelLayouts, sampleRates, &profile);
+    return profile;
+}
+
+AudioPortExt createDeviceExt(AudioDeviceType devType, int32_t flags,
+                             const std::string& connection = "", const std::string& address = "") {
+    AudioPortDeviceExt deviceExt;
+    deviceExt.device.type.type = devType;
+    if (devType == AudioDeviceType::IN_MICROPHONE && connection.empty()) {
+        deviceExt.device.address = "bottom";
+    } else if (devType == AudioDeviceType::IN_MICROPHONE_BACK && connection.empty()) {
+        deviceExt.device.address = "back";
+    } else {
+        deviceExt.device.address = std::move(address);
+    }
+    deviceExt.device.type.connection = std::move(connection);
+    deviceExt.flags = flags;
+    return AudioPortExt::make<AudioPortExt::Tag::device>(deviceExt);
+}
+
+AudioPort createPort(int32_t id, const std::string& name, int32_t flags, bool isInput,
+                     const AudioPortExt& ext) {
+    AudioPort port;
+    port.id = id;
+    port.name = name;
+    port.flags = isInput ? AudioIoFlags::make<AudioIoFlags::Tag::input>(flags)
+                         : AudioIoFlags::make<AudioIoFlags::Tag::output>(flags);
+    port.ext = ext;
+    return port;
+}
+
+AudioGain createGain(int32_t mode, AudioChannelLayout channelMask, int32_t minValue,
+                     int32_t maxValue, int32_t defaultValue, int32_t stepValue,
+                     int32_t minRampMs = 100, int32_t maxRampMs = 100, bool useForVolume = true) {
+    AudioGain gain;
+    gain.mode = mode;
+    gain.channelMask = channelMask;
+    gain.minValue = minValue;
+    gain.maxValue = maxValue;
+    gain.defaultValue = defaultValue;
+    gain.stepValue = stepValue;
+    gain.minRampMs = minRampMs;
+    gain.maxRampMs = maxRampMs;
+    gain.useForVolume = useForVolume;
+    return gain;
+}
+}  // namespace
+
 ndk::ScopedAStatus AudioControl::registerFocusListener(
         const shared_ptr<IFocusListener>& in_listener) {
     LOG(DEBUG) << "registering focus listener";
@@ -190,6 +282,33 @@
     return ndk::ScopedAStatus::ok();
 }
 
+ndk::ScopedAStatus AudioControl::setModuleChangeCallback(
+        const std::shared_ptr<IModuleChangeCallback>& in_callback) {
+    LOG(DEBUG) << ": " << __func__;
+
+    if (in_callback.get() == nullptr) {
+        LOG(ERROR) << __func__ << ": Callback is nullptr";
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+    if (mModuleChangeCallback != nullptr) {
+        LOG(ERROR) << __func__ << ": Module change callback was already registered";
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    std::atomic_store(&mModuleChangeCallback, in_callback);
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus AudioControl::clearModuleChangeCallback() {
+    if (mModuleChangeCallback != nullptr) {
+        shared_ptr<IModuleChangeCallback> nullCallback = nullptr;
+        std::atomic_store(&mModuleChangeCallback, nullCallback);
+        LOG(DEBUG) << ":" << __func__ << ": Unregistered successfully";
+    } else {
+        LOG(DEBUG) << ":" << __func__ << ": No callback registered, no-op";
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
 binder_status_t AudioControl::dump(int fd, const char** args, uint32_t numArgs) {
     if (numArgs == 0) {
         return dumpsys(fd);
@@ -208,6 +327,8 @@
         return cmdAbandonFocusWithMetaData(fd, args, numArgs);
     } else if (EqualsIgnoreCase(option, "--audioGainCallback")) {
         return cmdOnAudioDeviceGainsChanged(fd, args, numArgs);
+    } else if (EqualsIgnoreCase(option, "--audioPortsChangedCallback")) {
+        return cmdOnAudioPortsChanged(fd, args, numArgs);
     } else {
         dprintf(fd, "Invalid option: %s\n", option.c_str());
         return STATUS_BAD_VALUE;
@@ -262,7 +383,21 @@
     dprintf(fd,
             "Tags are optional. If provided, it must follow the <key>=<value> pattern, where the "
             "value is namespaced (for example com.google.strategy=VR).\n");
-
+    dprintf(fd,
+            "--audioPortsChangedCallback <ID_1> <NAME_1> <BUS_ADDRESS_1> <CONNECTION_TYPE_1> "
+            "<AUDIO_GAINS_1> [<ID_N> <NAME_N> <BUS_ADDRESS_N> <CONNECTION_TYPE_N> "
+            "<AUDIO_GAINS_N>]: fires audio ports changed callback. Carries list of modified "
+            "AudioPorts. "
+            "For simplicity, this command accepts limited information for each AudioPort: "
+            "id(int), name(string), port address(string), connection type (string), "
+            "audio gain (csv int)\n");
+    dprintf(fd, "Notes: \n");
+    dprintf(fd,
+            "1. AudioGain csv should match definition at "
+            "android/media/audio/common/AudioPort.aidl\n");
+    dprintf(fd,
+            "2. See android/media/audio/common/AudioDeviceDescription.aidl for valid "
+            "<CONNECTION_TYPE> values.\n");
     return STATUS_OK;
 }
 
@@ -514,4 +649,166 @@
             toEnumString(reasons).c_str(), toString(agcis).c_str());
     return STATUS_OK;
 }
+
+binder_status_t AudioControl::parseAudioGains(int fd, const std::string& stringGain,
+                                              std::vector<AudioGain>& gains) {
+    const int kAudioGainSize = 9;
+    std::stringstream csvGain(stringGain);
+    std::vector<std::string> vecGain;
+    std::string value;
+    while (getline(csvGain, value, ',')) {
+        vecGain.push_back(value);
+    }
+
+    if ((vecGain.size() == 0) || ((vecGain.size() % kAudioGainSize) != 0)) {
+        dprintf(fd, "Erroneous input to generate AudioGain: %s\n", stringGain.c_str());
+        return STATUS_BAD_VALUE;
+    }
+
+    // iterate over injected AudioGains
+    for (int index = 0; index < vecGain.size(); index += kAudioGainSize) {
+        int32_t mode;
+        if (!safelyParseInt(vecGain[index], &mode)) {
+            dprintf(fd, "Non-integer index provided with request: %s\n", vecGain[index].c_str());
+            return STATUS_BAD_VALUE;
+        }
+
+        // car audio framework only supports JOINT mode.
+        // skip injected AudioGains that are not compliant with this.
+        if (mode != static_cast<int>(AudioGainMode::JOINT)) {
+            LOG(WARNING) << ":" << __func__ << ": skipping gain since it is not JOINT mode!";
+            continue;
+        }
+
+        int32_t layout;
+        if (!safelyParseInt(vecGain[index + 1], &layout)) {
+            dprintf(fd, "Non-integer index provided with request: %s\n",
+                    vecGain[index + 1].c_str());
+            return STATUS_BAD_VALUE;
+        }
+        AudioChannelLayout channelMask =
+                AudioChannelLayout::make<AudioChannelLayout::layoutMask>(layout);
+
+        int32_t minValue;
+        if (!safelyParseInt(vecGain[index + 2], &minValue)) {
+            dprintf(fd, "Non-integer index provided with request: %s\n",
+                    vecGain[index + 2].c_str());
+            return STATUS_BAD_VALUE;
+        }
+
+        int32_t maxValue;
+        if (!safelyParseInt(vecGain[index + 3], &maxValue)) {
+            dprintf(fd, "Non-integer index provided with request: %s\n",
+                    vecGain[index + 3].c_str());
+            return STATUS_BAD_VALUE;
+        }
+
+        int32_t defaultValue;
+        if (!safelyParseInt(vecGain[index + 4], &defaultValue)) {
+            dprintf(fd, "Non-integer index provided with request: %s\n",
+                    vecGain[index + 4].c_str());
+            return STATUS_BAD_VALUE;
+        }
+
+        int32_t stepValue;
+        if (!safelyParseInt(vecGain[index + 5], &stepValue)) {
+            dprintf(fd, "Non-integer index provided with request: %s\n",
+                    vecGain[index + 5].c_str());
+            return STATUS_BAD_VALUE;
+        }
+
+        int32_t minRampMs;
+        if (!safelyParseInt(vecGain[index + 6], &minRampMs)) {
+            dprintf(fd, "Non-integer index provided with request: %s\n",
+                    vecGain[index + 6].c_str());
+            return STATUS_BAD_VALUE;
+        }
+
+        int32_t maxRampMs;
+        if (!safelyParseInt(vecGain[index + 7], &maxRampMs)) {
+            dprintf(fd, "Non-integer index provided with request: %s\n",
+                    vecGain[index + 7].c_str());
+            return STATUS_BAD_VALUE;
+        }
+
+        ParseBoolResult useForVolume = ParseBool(vecGain[index + 8]);
+        if (useForVolume == ParseBoolResult::kError) {
+            dprintf(fd, "Non-boolean index provided with request: %s\n",
+                    vecGain[index + 8].c_str());
+            return STATUS_BAD_VALUE;
+        } else if (useForVolume == ParseBoolResult::kFalse) {
+            // at this level we only care about gain stages that are relevant
+            // for volume control. skip the gain stage if its flagged as false.
+            LOG(WARNING) << ":" << __func__
+                         << ": skipping gain since it is not for volume control!";
+            continue;
+        }
+
+        AudioGain gain = createGain(mode, channelMask, minValue, maxValue, defaultValue, stepValue,
+                                    minRampMs, maxRampMs, true /*useForVolume*/);
+        gains.push_back(gain);
+    }
+    return STATUS_OK;
+}
+
+binder_status_t AudioControl::cmdOnAudioPortsChanged(int fd, const char** args, uint32_t numArgs) {
+    if (!checkCallerHasWritePermissions(fd)) {
+        return STATUS_PERMISSION_DENIED;
+    }
+
+    if ((numArgs < 6) || ((numArgs - 1) % 5 != 0)) {
+        dprintf(fd,
+                "Invalid number of arguments: please provide\n"
+                "--audioPortsChangedCallback <ID_1> <NAME_1> <BUS_ADDRESS_1> <CONNECTION_TYPE_1> "
+                "<AUDIO_GAINS_1> [<ID_N> <NAME_N> <BUS_ADDRESS_N> <CONNECTION_TYPE_N> "
+                "<AUDIO_GAINS_N>]: triggers audio ports changed callback. Carries list of "
+                "modified AudioPorts. "
+                "For simplicity, this command accepts limited information for each AudioPort: "
+                "id(int), name(string), port address(string), connection type (string), "
+                "audio gain (csv int)\n");
+        return STATUS_BAD_VALUE;
+    }
+
+    std::vector<AudioPort> ports;
+    for (uint32_t i = 1; i < numArgs; i += 5) {
+        binder_status_t status;
+        int32_t id;
+        if (!safelyParseInt(std::string(args[i]), &id)) {
+            dprintf(fd, "Non-integer index provided with request: %s\n",
+                    std::string(args[i]).c_str());
+            return STATUS_BAD_VALUE;
+        }
+
+        std::string name = std::string(args[i + 1]);
+        std::string address = std::string(args[i + 2]);
+        std::string connection = std::string(args[i + 3]);
+
+        std::string stringGains = std::string(args[i + 4]);
+        std::vector<AudioGain> gains;
+        status = parseAudioGains(fd, stringGains, gains);
+        if (status != STATUS_OK) {
+            return status;
+        }
+
+        AudioPort port = createPort(
+                id, name, 0 /*flags*/, false /*isInput*/,
+                createDeviceExt(AudioDeviceType::OUT_DEVICE, 0 /*flags*/, connection, address));
+        port.gains.insert(port.gains.begin(), gains.begin(), gains.end());
+
+        ports.push_back(port);
+    }
+
+    if (mModuleChangeCallback == nullptr) {
+        dprintf(fd,
+                "Unable to trigger audio port callback for ports: %s \n"
+                " - no module change callback registered\n",
+                toString(ports).c_str());
+        return STATUS_BAD_VALUE;
+    }
+
+    // TODO (b/269139706) fix atomic read issue
+    mModuleChangeCallback->onAudioPortsChanged(ports);
+    dprintf(fd, "SUCCESS audio port callback for ports: %s \n", toString(ports).c_str());
+    return STATUS_OK;
+}
 }  // namespace aidl::android::hardware::automotive::audiocontrol
diff --git a/automotive/audiocontrol/aidl/default/AudioControl.h b/automotive/audiocontrol/aidl/default/AudioControl.h
index 16b80e8..7eca446 100644
--- a/automotive/audiocontrol/aidl/default/AudioControl.h
+++ b/automotive/audiocontrol/aidl/default/AudioControl.h
@@ -21,11 +21,20 @@
 #include <aidl/android/hardware/automotive/audiocontrol/BnAudioControl.h>
 #include <aidl/android/hardware/automotive/audiocontrol/DuckingInfo.h>
 #include <aidl/android/hardware/automotive/audiocontrol/IAudioGainCallback.h>
+#include <aidl/android/hardware/automotive/audiocontrol/IModuleChangeCallback.h>
 #include <aidl/android/hardware/automotive/audiocontrol/MutingInfo.h>
 #include <aidl/android/hardware/automotive/audiocontrol/Reasons.h>
 
 #include <aidl/android/hardware/audio/common/PlaybackTrackMetadata.h>
 
+#include <aidl/android/media/audio/common/AudioChannelLayout.h>
+#include <aidl/android/media/audio/common/AudioDeviceType.h>
+#include <aidl/android/media/audio/common/AudioFormatDescription.h>
+#include <aidl/android/media/audio/common/AudioFormatType.h>
+#include <aidl/android/media/audio/common/AudioGainMode.h>
+#include <aidl/android/media/audio/common/AudioIoFlags.h>
+#include <aidl/android/media/audio/common/AudioOutputFlags.h>
+
 namespace aidl::android::hardware::automotive::audiocontrol {
 
 namespace audiohalcommon = ::aidl::android::hardware::audio::common;
@@ -51,6 +60,9 @@
             const std::vector<AudioGainConfigInfo>& in_gains) override;
     ndk::ScopedAStatus registerGainCallback(
             const std::shared_ptr<IAudioGainCallback>& in_callback) override;
+    ndk::ScopedAStatus setModuleChangeCallback(
+            const std::shared_ptr<IModuleChangeCallback>& in_callback) override;
+    ndk::ScopedAStatus clearModuleChangeCallback() override;
 
     binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
 
@@ -67,15 +79,23 @@
      */
     std::shared_ptr<IAudioGainCallback> mAudioGainCallback = nullptr;
 
+    std::shared_ptr<IModuleChangeCallback> mModuleChangeCallback = nullptr;
+
     binder_status_t cmdHelp(int fd) const;
     binder_status_t cmdRequestFocus(int fd, const char** args, uint32_t numArgs);
     binder_status_t cmdAbandonFocus(int fd, const char** args, uint32_t numArgs);
     binder_status_t cmdRequestFocusWithMetaData(int fd, const char** args, uint32_t numArgs);
     binder_status_t cmdAbandonFocusWithMetaData(int fd, const char** args, uint32_t numArgs);
     binder_status_t cmdOnAudioDeviceGainsChanged(int fd, const char** args, uint32_t numArgs);
+    binder_status_t cmdOnAudioPortsChanged(int fd, const char** args, uint32_t numArgs);
 
     binder_status_t parseMetaData(int fd, const std::string& metadataLiteral,
                                   audiohalcommon::PlaybackTrackMetadata& trackMetadata);
+    binder_status_t parseAudioGains(
+            int fd, const std::string& stringGain,
+            std::vector<::aidl::android::media::audio::common::AudioGain>& gains);
+    binder_status_t parseSampleRates(int fd, const std::string& sampleRates,
+                                     std::vector<int32_t>& vecSampleRates);
 
     binder_status_t dumpsys(int fd);
 };
diff --git a/automotive/audiocontrol/aidl/vts/Android.bp b/automotive/audiocontrol/aidl/vts/Android.bp
index edac160..a9122ce 100644
--- a/automotive/audiocontrol/aidl/vts/Android.bp
+++ b/automotive/audiocontrol/aidl/vts/Android.bp
@@ -39,7 +39,7 @@
         "libxml2",
     ],
     static_libs: [
-        "android.hardware.automotive.audiocontrol-V2-cpp",
+        "android.hardware.automotive.audiocontrol-V3-cpp",
         "libgmock",
     ],
     test_suites: [
diff --git a/automotive/audiocontrol/aidl/vts/VtsHalAudioControlTargetTest.cpp b/automotive/audiocontrol/aidl/vts/VtsHalAudioControlTargetTest.cpp
index f4f5eef..f4e3b5a 100644
--- a/automotive/audiocontrol/aidl/vts/VtsHalAudioControlTargetTest.cpp
+++ b/automotive/audiocontrol/aidl/vts/VtsHalAudioControlTargetTest.cpp
@@ -21,6 +21,7 @@
 
 #include <android/hardware/automotive/audiocontrol/BnAudioGainCallback.h>
 #include <android/hardware/automotive/audiocontrol/BnFocusListener.h>
+#include <android/hardware/automotive/audiocontrol/BnModuleChangeCallback.h>
 #include <android/hardware/automotive/audiocontrol/IAudioControl.h>
 #include <android/log.h>
 #include <binder/IServiceManager.h>
@@ -34,10 +35,14 @@
 using android::hardware::automotive::audiocontrol::AudioGainConfigInfo;
 using android::hardware::automotive::audiocontrol::BnAudioGainCallback;
 using android::hardware::automotive::audiocontrol::BnFocusListener;
+using android::hardware::automotive::audiocontrol::BnModuleChangeCallback;
 using android::hardware::automotive::audiocontrol::DuckingInfo;
 using android::hardware::automotive::audiocontrol::IAudioControl;
+using android::hardware::automotive::audiocontrol::IModuleChangeCallback;
 using android::hardware::automotive::audiocontrol::MutingInfo;
 using android::hardware::automotive::audiocontrol::Reasons;
+using ::testing::AnyOf;
+using ::testing::Eq;
 
 #include "android_audio_policy_configuration_V7_0.h"
 
@@ -55,6 +60,8 @@
         ASSERT_NE(audioControl, nullptr);
     }
 
+    void TearDown() override { audioControl = nullptr; }
+
     sp<IAudioControl> audioControl;
     int32_t capabilities;
 };
@@ -226,6 +233,47 @@
     ASSERT_TRUE(audioControl->registerGainCallback(gainCallback2).isOk());
 }
 
+/*
+ * Test Module change Callback registration.
+ *
+ * Verifies that:
+ * - setModuleChangeCallback succeeds
+ * - setting a double callback fails with exception
+ * - clearModuleChangeCallback succeeds
+ * - setting with nullptr callback fails with exception
+ * - closing handle does not crash
+ */
+struct ModuleChangeCallbackMock : BnModuleChangeCallback {
+    MOCK_METHOD(Status, onAudioPortsChanged,
+                (const std::vector<android::media::audio::common::AudioPort>& audioPorts));
+};
+
+TEST_P(AudioControlAidl, RegisterModuleChangeCallbackTwiceThrowsException) {
+    ALOGI("Register Module change callback test");
+    // make sure no stale callbacks.
+    audioControl->clearModuleChangeCallback();
+
+    sp<ModuleChangeCallbackMock> moduleChangeCallback = new ModuleChangeCallbackMock();
+    auto status = audioControl->setModuleChangeCallback(moduleChangeCallback);
+    EXPECT_THAT(status.exceptionCode(),
+                AnyOf(Eq(Status::EX_NONE), Eq(Status::EX_UNSUPPORTED_OPERATION)));
+    if (!status.isOk()) return;
+
+    sp<ModuleChangeCallbackMock> moduleChangeCallback2 = new ModuleChangeCallbackMock();
+    // no need to check for unsupported feature
+    EXPECT_EQ(Status::EX_ILLEGAL_STATE,
+              audioControl->setModuleChangeCallback(moduleChangeCallback2).exceptionCode());
+    ASSERT_TRUE(audioControl->clearModuleChangeCallback().isOk());
+    ASSERT_TRUE(audioControl->setModuleChangeCallback(moduleChangeCallback2).isOk());
+}
+
+TEST_P(AudioControlAidl, RegisterModuleChangeNullCallbackThrowsException) {
+    ALOGI("Register Module change callback with nullptr test");
+    auto status = audioControl->setModuleChangeCallback(nullptr);
+    EXPECT_THAT(status.exceptionCode(),
+                AnyOf(Eq(Status::EX_ILLEGAL_ARGUMENT), Eq(Status::EX_UNSUPPORTED_OPERATION)));
+}
+
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioControlAidl);
 INSTANTIATE_TEST_SUITE_P(
         Audiocontrol, AudioControlAidl,
diff --git a/automotive/ivn_android_device/aidl/Android.bp b/automotive/ivn_android_device/aidl/Android.bp
new file mode 100644
index 0000000..95a0c73
--- /dev/null
+++ b/automotive/ivn_android_device/aidl/Android.bp
@@ -0,0 +1,44 @@
+// 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 {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+    name: "android.hardware.automotive.ivn",
+    vendor_available: true,
+    srcs: [
+        "android/hardware/automotive/ivn/*.aidl",
+    ],
+    frozen: false,
+    stability: "vintf",
+    backend: {
+        cpp: {
+            enabled: false,
+        },
+        java: {
+            sdk_version: "module_current",
+            apex_available: [
+                "//apex_available:platform",
+                "com.android.car.framework",
+            ],
+        },
+    },
+}
diff --git a/automotive/ivn_android_device/aidl/aidl_api/android.hardware.automotive.ivn/current/android/hardware/automotive/ivn/ConnectProtocol.aidl b/automotive/ivn_android_device/aidl/aidl_api/android.hardware.automotive.ivn/current/android/hardware/automotive/ivn/ConnectProtocol.aidl
new file mode 100644
index 0000000..80d7a2a
--- /dev/null
+++ b/automotive/ivn_android_device/aidl/aidl_api/android.hardware.automotive.ivn/current/android/hardware/automotive/ivn/ConnectProtocol.aidl
@@ -0,0 +1,38 @@
+/*
+ * 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 FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.automotive.ivn;
+@Backing(type="int") @VintfStability
+enum ConnectProtocol {
+  TCP_IP = 0,
+}
diff --git a/automotive/ivn_android_device/aidl/aidl_api/android.hardware.automotive.ivn/current/android/hardware/automotive/ivn/EndpointInfo.aidl b/automotive/ivn_android_device/aidl/aidl_api/android.hardware.automotive.ivn/current/android/hardware/automotive/ivn/EndpointInfo.aidl
new file mode 100644
index 0000000..5693520
--- /dev/null
+++ b/automotive/ivn_android_device/aidl/aidl_api/android.hardware.automotive.ivn/current/android/hardware/automotive/ivn/EndpointInfo.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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 FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.automotive.ivn;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable EndpointInfo {
+  android.hardware.automotive.ivn.ConnectProtocol connectProtocol;
+  String ipAddress;
+  int portNumber;
+  android.hardware.automotive.ivn.HardwareIdentifiers hardwareId;
+}
diff --git a/automotive/ivn_android_device/aidl/aidl_api/android.hardware.automotive.ivn/current/android/hardware/automotive/ivn/HardwareIdentifiers.aidl b/automotive/ivn_android_device/aidl/aidl_api/android.hardware.automotive.ivn/current/android/hardware/automotive/ivn/HardwareIdentifiers.aidl
new file mode 100644
index 0000000..1a8b21d
--- /dev/null
+++ b/automotive/ivn_android_device/aidl/aidl_api/android.hardware.automotive.ivn/current/android/hardware/automotive/ivn/HardwareIdentifiers.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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 FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.automotive.ivn;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable HardwareIdentifiers {
+  @nullable String brandName;
+  @nullable String deviceName;
+  @nullable String productName;
+  @nullable String manufacturerName;
+  @nullable String modelName;
+  @nullable String serialNumber;
+}
diff --git a/automotive/ivn_android_device/aidl/aidl_api/android.hardware.automotive.ivn/current/android/hardware/automotive/ivn/IIvnAndroidDevice.aidl b/automotive/ivn_android_device/aidl/aidl_api/android.hardware.automotive.ivn/current/android/hardware/automotive/ivn/IIvnAndroidDevice.aidl
new file mode 100644
index 0000000..a04d829
--- /dev/null
+++ b/automotive/ivn_android_device/aidl/aidl_api/android.hardware.automotive.ivn/current/android/hardware/automotive/ivn/IIvnAndroidDevice.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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 FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.automotive.ivn;
+@VintfStability
+interface IIvnAndroidDevice {
+  int getMyDeviceId();
+  int[] getOtherDeviceIds();
+  int getDeviceIdForOccupantZone(int zoneId);
+  android.hardware.automotive.ivn.OccupantZoneInfo[] getOccupantZonesForDevice(int androidDeviceId);
+  android.hardware.automotive.ivn.EndpointInfo getMyEndpointInfo();
+  android.hardware.automotive.ivn.EndpointInfo getEndpointInfoForDevice(int androidDeviceId);
+}
diff --git a/automotive/ivn_android_device/aidl/aidl_api/android.hardware.automotive.ivn/current/android/hardware/automotive/ivn/OccupantType.aidl b/automotive/ivn_android_device/aidl/aidl_api/android.hardware.automotive.ivn/current/android/hardware/automotive/ivn/OccupantType.aidl
new file mode 100644
index 0000000..6dd0c07
--- /dev/null
+++ b/automotive/ivn_android_device/aidl/aidl_api/android.hardware.automotive.ivn/current/android/hardware/automotive/ivn/OccupantType.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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 FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.automotive.ivn;
+@Backing(type="int") @VintfStability
+enum OccupantType {
+  DRIVER = 1,
+  FRONT_PASSENGER = 2,
+  REAR_PASSENGER = 3,
+}
diff --git a/automotive/ivn_android_device/aidl/aidl_api/android.hardware.automotive.ivn/current/android/hardware/automotive/ivn/OccupantZoneInfo.aidl b/automotive/ivn_android_device/aidl/aidl_api/android.hardware.automotive.ivn/current/android/hardware/automotive/ivn/OccupantZoneInfo.aidl
new file mode 100644
index 0000000..41108e9
--- /dev/null
+++ b/automotive/ivn_android_device/aidl/aidl_api/android.hardware.automotive.ivn/current/android/hardware/automotive/ivn/OccupantZoneInfo.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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 FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.automotive.ivn;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable OccupantZoneInfo {
+  int zoneId;
+  android.hardware.automotive.ivn.OccupantType occupantType;
+  int seat;
+}
diff --git a/automotive/ivn_android_device/aidl/android/hardware/automotive/ivn/ConnectProtocol.aidl b/automotive/ivn_android_device/aidl/android/hardware/automotive/ivn/ConnectProtocol.aidl
new file mode 100644
index 0000000..9f621b3
--- /dev/null
+++ b/automotive/ivn_android_device/aidl/android/hardware/automotive/ivn/ConnectProtocol.aidl
@@ -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 android.hardware.automotive.ivn;
+
+/**
+ * Connect protocol for In-vehicle network Android device.
+ */
+@VintfStability
+@Backing(type="int")
+enum ConnectProtocol {
+    TCP_IP = 0,
+}
diff --git a/automotive/ivn_android_device/aidl/android/hardware/automotive/ivn/EndpointInfo.aidl b/automotive/ivn_android_device/aidl/android/hardware/automotive/ivn/EndpointInfo.aidl
new file mode 100644
index 0000000..32f6971
--- /dev/null
+++ b/automotive/ivn_android_device/aidl/android/hardware/automotive/ivn/EndpointInfo.aidl
@@ -0,0 +1,50 @@
+/*
+ * 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 android.hardware.automotive.ivn;
+
+import android.hardware.automotive.ivn.ConnectProtocol;
+import android.hardware.automotive.ivn.HardwareIdentifiers;
+
+/**
+ * Network endpoint information for an Android instance running on a difference device.
+ *
+ * The device is in the same vehicle as this device.
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable EndpointInfo {
+    /**
+     * The connection protocol. Only supports TCP/IP for now.
+     */
+    ConnectProtocol connectProtocol;
+    /**
+     * The IP address.
+     */
+    String ipAddress;
+    /**
+     * The port number exposed for connecting.
+     */
+    int portNumber;
+    /**
+     * Hardware identifiers.
+     *
+     * The hardware identifiers for the endpoint as defined in [Attestation Hardware Identifiers]
+     * {@link
+     * https://source.android.com/docs/security/features/keystore/attestation#hardware-identifiers}
+     */
+    HardwareIdentifiers hardwareId;
+}
diff --git a/automotive/ivn_android_device/aidl/android/hardware/automotive/ivn/HardwareIdentifiers.aidl b/automotive/ivn_android_device/aidl/android/hardware/automotive/ivn/HardwareIdentifiers.aidl
new file mode 100644
index 0000000..ffa09e6
--- /dev/null
+++ b/automotive/ivn_android_device/aidl/android/hardware/automotive/ivn/HardwareIdentifiers.aidl
@@ -0,0 +1,65 @@
+/*
+ * 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 android.hardware.automotive.ivn;
+
+/**
+ * Hardware Identifiers for an Android device.
+ *
+ * <p>These identifiers are embedded in the ID attestation certificate and are
+ * used to restrict what devices this device can connect to. All fields are
+ * optional but at least one of the fields must be specified.
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable HardwareIdentifiers {
+    /**
+     * Optional brand name, as returned by {@code Build.BRAND} in Android.
+     *
+     * If unspecified, we assume the other device has the same brand name as this device.
+     */
+    @nullable String brandName;
+    /**
+     * Optional brand name, as returned by {@code Build.DEVICE} in Android.
+     *
+     * If unspecified, we assume the other device has the same device name as this device.
+     */
+    @nullable String deviceName;
+    /**
+     * Optional model name, as returned by {@code Build.PRODUCT} in Android.
+     *
+     * If unspecified, we assume the other device has the same product name as this device.
+     */
+    @nullable String productName;
+    /**
+     * Optional manufacturer name, as returned by {@code Build.MANUFACTURER} in Android.
+     *
+     * If unspecified, we assume the other device has the same manufacturer name as this device.
+     */
+    @nullable String manufacturerName;
+    /**
+     * Optional model name, as returned by {@code Build.MODEL} in Android.
+     *
+     * If unspecified, we assume the other device has the same model name as this device.
+     */
+    @nullable String modelName;
+    /**
+     * Optional serial number.
+     *
+     * If unspecified, we allow the endpoint to have any serial number.
+     */
+    @nullable String serialNumber;
+}
diff --git a/automotive/ivn_android_device/aidl/android/hardware/automotive/ivn/IIvnAndroidDevice.aidl b/automotive/ivn_android_device/aidl/android/hardware/automotive/ivn/IIvnAndroidDevice.aidl
new file mode 100644
index 0000000..107b7a6
--- /dev/null
+++ b/automotive/ivn_android_device/aidl/android/hardware/automotive/ivn/IIvnAndroidDevice.aidl
@@ -0,0 +1,81 @@
+/*
+ * 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 android.hardware.automotive.ivn;
+
+import android.hardware.automotive.ivn.EndpointInfo;
+import android.hardware.automotive.ivn.OccupantZoneInfo;
+
+/**
+ * Interface for In-Vehicle Network(IVN) Android devices.
+ *
+ * This is used in a multi-zone multi-SoC environment where there are multiple
+ * SoCs running Android in a vehicle. Each support one or multiple occupant
+ * zones. E.g., one SoC for front passenger, one SoC for backseat left-zone
+ * and middle/right zone passengers.
+ */
+@VintfStability
+interface IIvnAndroidDevice {
+    /**
+     * Returns the unique ID for this Android device.
+     *
+     * <p>This ID has to be unique among all the android devices in the whole vehicle. It is usually
+     * a hard-coded value, e.g. serial number.
+     *
+     * @return an ID representing this device.
+     */
+    int getMyDeviceId();
+
+    /**
+     * Returns a list of unique IDs for other IVN Android devices.
+     *
+     * The returned list does not contain the current Android device ID. This list is usually
+     * pre-configured for this HAL, either hard-coded or read from configuration file.
+     *
+     * @return A list of IDs representing connected Android devices.
+     */
+    int[] getOtherDeviceIds();
+
+    /**
+     * Returns the Android device ID for a specified occupant zone.
+     *
+     * @pararm zoneID the occupant zone ID returned from {@link android.car.CarOccupantZoneManager}.
+     * @return an ID representing an Android device.
+     */
+    int getDeviceIdForOccupantZone(int zoneId);
+
+    /**
+     * Returns all the occupant zones supported for a specified IVN Android device.
+     *
+     * @param androidDeviceId the android device ID.
+     * @return A list of supported occupant zone info.
+     */
+    OccupantZoneInfo[] getOccupantZonesForDevice(int androidDeviceId);
+
+    /**
+     * Returns the connection endpoint info for this android device.
+     *
+     * @return The endpoint info.
+     */
+    EndpointInfo getMyEndpointInfo();
+
+    /**
+     * Returns the connection endpoint info for the specified IVN Android device.
+     *
+     * @return The endpoint info.
+     */
+    EndpointInfo getEndpointInfoForDevice(int androidDeviceId);
+}
diff --git a/automotive/ivn_android_device/aidl/android/hardware/automotive/ivn/OccupantType.aidl b/automotive/ivn_android_device/aidl/android/hardware/automotive/ivn/OccupantType.aidl
new file mode 100644
index 0000000..72542a7
--- /dev/null
+++ b/automotive/ivn_android_device/aidl/android/hardware/automotive/ivn/OccupantType.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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 android.hardware.automotive.ivn;
+
+/**
+ * Occupant type.
+ *
+ * This enum might be extended in the future.
+ */
+@VintfStability
+@Backing(type="int")
+enum OccupantType {
+    /**
+     * Represents the driver. There can be one or zero driver for the system. Zero driver situation
+     * can happen if the system is configured to support only passengers.
+     */
+    DRIVER = 1,
+    /**
+     * Represents front passengers who sit in front side of car. Most cars will have only
+     * one passenger of this type but this can be multiple.
+     */
+    FRONT_PASSENGER = 2,
+    /** Represents passengers in rear seats. There can be multiple passengers of this type. */
+    REAR_PASSENGER = 3,
+}
diff --git a/automotive/ivn_android_device/aidl/android/hardware/automotive/ivn/OccupantZoneInfo.aidl b/automotive/ivn_android_device/aidl/android/hardware/automotive/ivn/OccupantZoneInfo.aidl
new file mode 100644
index 0000000..af45444
--- /dev/null
+++ b/automotive/ivn_android_device/aidl/android/hardware/automotive/ivn/OccupantZoneInfo.aidl
@@ -0,0 +1,50 @@
+/*
+ * 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 android.hardware.automotive.ivn;
+
+import android.hardware.automotive.ivn.OccupantType;
+
+/**
+ * Represents an occupant zone in a car.
+ *
+ * <p>Each occupant does not necessarily represent single person but it is for mapping to one
+ * set of displays. For example, for display located in center rear seat, both left and right
+ * side passengers may use it but it is abstracted as a single occupant zone.</p>
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable OccupantZoneInfo {
+    /**
+     * This is an unique id to distinguish each occupant zone.
+     *
+     * <p>This can be helpful to distinguish different zones when {@link #occupantType} and
+     * {@link #seat} are the same for multiple occupant / passenger zones.</p>
+     *
+     * <p>This id will remain the same for the same zone across configuration changes like
+     * user switching or display changes</p>
+     */
+    int zoneId;
+    /** Represents type of passenger */
+    OccupantType occupantType;
+    /**
+     * Represents seat assigned for the occupant. In some system, this can have value of
+     * {@code VehicleAreaSeat#SEAT_UNKNOWN}.
+     *
+     * <p>This might be one of {@code VehicleAreaSeat} or a combination of {@code VehicleAreaSeat}.
+     */
+    int seat;
+}
diff --git a/automotive/vehicle/aidl_property/aidl_api/android.hardware.automotive.vehicle.property/current/android/hardware/automotive/vehicle/EvsServiceType.aidl b/automotive/vehicle/aidl_property/aidl_api/android.hardware.automotive.vehicle.property/current/android/hardware/automotive/vehicle/EvsServiceType.aidl
index 1363a64..285732c 100644
--- a/automotive/vehicle/aidl_property/aidl_api/android.hardware.automotive.vehicle.property/current/android/hardware/automotive/vehicle/EvsServiceType.aidl
+++ b/automotive/vehicle/aidl_property/aidl_api/android.hardware.automotive.vehicle.property/current/android/hardware/automotive/vehicle/EvsServiceType.aidl
@@ -36,4 +36,11 @@
 enum EvsServiceType {
   REARVIEW = 0,
   SURROUNDVIEW = 1,
+  FRONTVIEW = 2,
+  LEFTVIEW = 3,
+  RIGHTVIEW = 4,
+  DRIVERVIEW = 5,
+  FRONTPASSENGERSVIEW = 6,
+  REARPASSENGERSVIEW = 7,
+  USER_DEFINED = 1000,
 }
diff --git a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/EvsServiceType.aidl b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/EvsServiceType.aidl
index 6c621f7..20582dc 100644
--- a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/EvsServiceType.aidl
+++ b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/EvsServiceType.aidl
@@ -24,4 +24,11 @@
 enum EvsServiceType {
     REARVIEW = 0,
     SURROUNDVIEW = 1,
+    FRONTVIEW = 2,
+    LEFTVIEW = 3,
+    RIGHTVIEW = 4,
+    DRIVERVIEW = 5,
+    FRONTPASSENGERSVIEW = 6,
+    REARPASSENGERSVIEW = 7,
+    USER_DEFINED = 1000,
 }
diff --git a/compatibility_matrices/compatibility_matrix.4.xml b/compatibility_matrices/compatibility_matrix.4.xml
index 8ef0b3a..c898aab 100644
--- a/compatibility_matrices/compatibility_matrix.4.xml
+++ b/compatibility_matrices/compatibility_matrix.4.xml
@@ -171,7 +171,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.gatekeeper</name>
         <version>1.0</version>
         <interface>
diff --git a/compatibility_matrices/compatibility_matrix.5.xml b/compatibility_matrices/compatibility_matrix.5.xml
index 12b85c7..c5a1dc2 100644
--- a/compatibility_matrices/compatibility_matrix.5.xml
+++ b/compatibility_matrices/compatibility_matrix.5.xml
@@ -192,7 +192,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.gatekeeper</name>
         <version>1.0</version>
         <interface>
diff --git a/compatibility_matrices/compatibility_matrix.6.xml b/compatibility_matrices/compatibility_matrix.6.xml
index e19d2dd..1c76ee6 100644
--- a/compatibility_matrices/compatibility_matrix.6.xml
+++ b/compatibility_matrices/compatibility_matrix.6.xml
@@ -215,7 +215,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.gatekeeper</name>
         <version>1.0</version>
         <interface>
diff --git a/compatibility_matrices/compatibility_matrix.8.xml b/compatibility_matrices/compatibility_matrix.8.xml
index c9cf6ef..104a463 100644
--- a/compatibility_matrices/compatibility_matrix.8.xml
+++ b/compatibility_matrices/compatibility_matrix.8.xml
@@ -108,7 +108,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-     <hal format="aidl" optional="true">
+    <hal format="aidl" optional="true">
         <name>android.hardware.automotive.remoteaccess</name>
         <interface>
             <name>IRemoteAccess</name>
@@ -116,6 +116,13 @@
         </interface>
     </hal>
     <hal format="aidl" optional="true">
+        <name>android.hardware.automotive.ivn</name>
+        <interface>
+            <name>IIvnAndroidDevice</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
         <name>android.hardware.biometrics.face</name>
         <version>3</version>
         <interface>
diff --git a/compatibility_matrices/exclude/fcm_exclude.cpp b/compatibility_matrices/exclude/fcm_exclude.cpp
index b17c0e2..f3374c3 100644
--- a/compatibility_matrices/exclude/fcm_exclude.cpp
+++ b/compatibility_matrices/exclude/fcm_exclude.cpp
@@ -64,6 +64,7 @@
             "android.hardware.keymaster",
             "android.hardware.media.bufferpool2",
             "android.hardware.radio",
+            "android.hardware.threadnetwork",
             "android.hardware.uwb.fira_android",
 
             // Fastboot HAL is only used by recovery. Recovery is owned by OEM. Framework
diff --git a/staging/threadnetwork/aidl/Android.bp b/staging/threadnetwork/aidl/Android.bp
new file mode 100644
index 0000000..fcd3ab8
--- /dev/null
+++ b/staging/threadnetwork/aidl/Android.bp
@@ -0,0 +1,17 @@
+aidl_interface {
+    name: "android.hardware.threadnetwork",
+    host_supported: true,
+    vendor_available: true,
+
+    srcs: [
+        "android/hardware/threadnetwork/*.aidl",
+    ],
+
+    unstable: true,
+
+    backend: {
+        ndk: {
+            enabled: true,
+        },
+    },
+}
diff --git a/staging/threadnetwork/aidl/android/hardware/threadnetwork/IThreadChip.aidl b/staging/threadnetwork/aidl/android/hardware/threadnetwork/IThreadChip.aidl
new file mode 100644
index 0000000..3c57149
--- /dev/null
+++ b/staging/threadnetwork/aidl/android/hardware/threadnetwork/IThreadChip.aidl
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.threadnetwork;
+
+import android.hardware.threadnetwork.IThreadChipCallback;
+
+/**
+ * Controls a Thread radio chip on the device.
+ */
+
+interface IThreadChip {
+    /**
+     * The operation failed for the internal error.
+     */
+    const int ERROR_FAILED = 1;
+
+    /**
+     * Insufficient buffers available to send frames.
+     */
+    const int ERROR_NO_BUFS = 2;
+
+    /**
+     * Service is busy and could not service the operation.
+     */
+    const int ERROR_BUSY = 3;
+
+    /**
+     * This method initializes the Thread HAL instance. If open completes
+     * successfully, then the Thread HAL instance is ready to accept spinel
+     * messages through sendSpinelFrame() API.
+     *
+     * @param callback  A IThreadChipCallback callback instance. If multiple
+     *                  callbacks are passed in, the open() will return ERROR_BUSY.
+     *
+     * @throws EX_ILLEGAL_ARGUMENT  if the callback handle is invalid (for example, it is null).
+     * @throws ServiceSpecificException with one of the following values:
+     *     - ERROR_FAILED        The interface cannot be opened due to an internal error.
+     *     - ERROR_BUSY          This interface is in use.
+     */
+    void open(in IThreadChipCallback callback);
+
+    /**
+     * Close the Thread HAL instance. Must free all resources.
+     *
+     * @throws EX_ILLEGAL_STATE  if the Thread HAL instance is not opened.
+     *
+     */
+    void close();
+
+    /**
+     * This method resets the Thread HAL internal state. The callback registered by
+     * `open()` won’t be reset and the resource allocated by `open()` won’t be free.
+     *
+     */
+    void reset();
+
+    /**
+     * This method sends a spinel frame to the Thread HAL.
+     *
+     * This method should block until the frame is sent out successfully or
+     * the method throws errors immediately.
+     *
+     * Spinel Protocol:
+     *     https://github.com/openthread/openthread/blob/main/src/lib/spinel/spinel.h
+     *
+     * @param frame  The spinel frame to be sent.
+     *
+     * @throws ServiceSpecificException with one of the following values:
+     *         - ERROR_FAILED The Thread HAL failed to send the frame for an internal reason.
+     *         - ERROR_NO_BUFS Insufficient buffer space to send the frame.
+     *         - ERROR_BUSY The Thread HAL is busy.
+     */
+    void sendSpinelFrame(in byte[] frame);
+}
diff --git a/staging/threadnetwork/aidl/android/hardware/threadnetwork/IThreadChipCallback.aidl b/staging/threadnetwork/aidl/android/hardware/threadnetwork/IThreadChipCallback.aidl
new file mode 100644
index 0000000..a0fe88c
--- /dev/null
+++ b/staging/threadnetwork/aidl/android/hardware/threadnetwork/IThreadChipCallback.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.threadnetwork;
+
+interface IThreadChipCallback {
+    /**
+     * This method is called when a spinel frame is received. Thread network
+     * will process the received spinel frame.
+     *
+     * Spinel Protocol:
+     *     https://github.com/openthread/openthread/blob/main/src/lib/spinel/spinel.h
+     *
+     * @param frame  The received spinel frame.
+     */
+    oneway void onReceiveSpinelFrame(in byte[] frame);
+}
diff --git a/staging/threadnetwork/aidl/default/Android.bp b/staging/threadnetwork/aidl/default/Android.bp
new file mode 100644
index 0000000..c701295
--- /dev/null
+++ b/staging/threadnetwork/aidl/default/Android.bp
@@ -0,0 +1,54 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_defaults {
+    name: "threadnetwork_service_default",
+    vendor: true,
+    relative_install_path: "hw",
+
+    shared_libs: [
+        "android.hardware.threadnetwork-ndk",
+        "libbase",
+        "libbinder_ndk",
+        "libcutils",
+        "liblog",
+        "libutils",
+    ],
+
+    static_libs: [
+        "openthread-common",
+        "openthread-hdlc",
+        "openthread-platform",
+        "openthread-posix",
+        "openthread-url",
+    ],
+
+    srcs: [
+        "main.cpp",
+        "service.cpp",
+        "thread_chip.cpp",
+        "utils.cpp",
+    ],
+}
+
+cc_binary {
+    name: "android.hardware.threadnetwork-service.sim",
+    defaults: ["threadnetwork_service_default"],
+    init_rc: ["android.hardware.threadnetwork-service.sim.rc"],
+}
+
+cc_binary {
+    name: "android.hardware.threadnetwork-service",
+    defaults: ["threadnetwork_service_default"],
+}
diff --git a/staging/threadnetwork/aidl/default/android.hardware.threadnetwork-service.sim.rc b/staging/threadnetwork/aidl/default/android.hardware.threadnetwork-service.sim.rc
new file mode 100644
index 0000000..2fb409c
--- /dev/null
+++ b/staging/threadnetwork/aidl/default/android.hardware.threadnetwork-service.sim.rc
@@ -0,0 +1,3 @@
+service vendor.threadnetwork_hal /vendor/bin/hw/android.hardware.threadnetwork-service.sim spinel+hdlc+forkpty:///vendor/bin/ot-rcp?forkpty-arg=1
+    class hal
+    user thread_network
diff --git a/staging/threadnetwork/aidl/default/main.cpp b/staging/threadnetwork/aidl/default/main.cpp
new file mode 100644
index 0000000..b6c8bbb
--- /dev/null
+++ b/staging/threadnetwork/aidl/default/main.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <utils/Log.h>
+
+#include "service.hpp"
+
+int main(int argc, char* argv[]) {
+    CHECK_GT(argc, 1);
+    aidl::android::hardware::threadnetwork::Service service(&argv[1], argc - 1);
+
+    ALOGI("Thread Network HAL is running");
+
+    service.startLoop();
+    return EXIT_FAILURE;  // should not reach
+}
diff --git a/staging/threadnetwork/aidl/default/service.cpp b/staging/threadnetwork/aidl/default/service.cpp
new file mode 100644
index 0000000..8047214
--- /dev/null
+++ b/staging/threadnetwork/aidl/default/service.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "service.hpp"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <utils/Log.h>
+
+#include "thread_chip.hpp"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace threadnetwork {
+
+Service::Service(char* urls[], int numUrls) : mBinderFd(-1) {
+    int fd;
+
+    CHECK_NE(urls, nullptr);
+    CHECK_GT(numUrls, 0);
+
+    for (int i = 0; i < numUrls; i++) {
+        auto threadChip = ndk::SharedRefBase::make<ThreadChip>(i, urls[i]);
+        CHECK_NE(threadChip, nullptr);
+        mThreadChips.push_back(std::move(threadChip));
+    }
+
+    binder_status_t status = ABinderProcess_setupPolling(&fd);
+    CHECK_EQ(status, ::STATUS_OK);
+    CHECK_GE(fd, 0);
+    mBinderFd.reset(fd);
+}
+
+void Service::Update(otSysMainloopContext& context) {
+    FD_SET(mBinderFd.get(), &context.mReadFdSet);
+    context.mMaxFd = std::max(context.mMaxFd, mBinderFd.get());
+}
+
+void Service::Process(const otSysMainloopContext& context) {
+    if (FD_ISSET(mBinderFd.get(), &context.mReadFdSet)) {
+        ABinderProcess_handlePolledCommands();
+    }
+}
+
+void Service::startLoop(void) {
+    const struct timeval kPollTimeout = {1, 0};
+    otSysMainloopContext context;
+    int rval;
+
+    ot::Posix::Mainloop::Manager::Get().Add(*this);
+
+    while (true) {
+        context.mMaxFd = -1;
+        context.mTimeout = kPollTimeout;
+
+        FD_ZERO(&context.mReadFdSet);
+        FD_ZERO(&context.mWriteFdSet);
+        FD_ZERO(&context.mErrorFdSet);
+
+        ot::Posix::Mainloop::Manager::Get().Update(context);
+
+        rval = select(context.mMaxFd + 1, &context.mReadFdSet, &context.mWriteFdSet,
+                      &context.mErrorFdSet, &context.mTimeout);
+
+        if (rval >= 0) {
+            ot::Posix::Mainloop::Manager::Get().Process(context);
+        } else if (errno != EINTR) {
+            ALOGE("select() failed: %s", strerror(errno));
+            break;
+        }
+    }
+}
+}  // namespace threadnetwork
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/staging/threadnetwork/aidl/default/service.hpp b/staging/threadnetwork/aidl/default/service.hpp
new file mode 100644
index 0000000..6e6e868
--- /dev/null
+++ b/staging/threadnetwork/aidl/default/service.hpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/unique_fd.h>
+
+#include "mainloop.hpp"
+#include "thread_chip.hpp"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace threadnetwork {
+
+class Service : public ot::Posix::Mainloop::Source {
+  public:
+    Service(char* urls[], int numUrls);
+
+    void Update(otSysMainloopContext& context) override;
+    void Process(const otSysMainloopContext& context) override;
+    void startLoop(void);
+
+  private:
+    ::android::base::unique_fd mBinderFd;
+    std::vector<std::shared_ptr<ThreadChip>> mThreadChips;
+};
+}  // namespace threadnetwork
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/staging/threadnetwork/aidl/default/thread_chip.cpp b/staging/threadnetwork/aidl/default/thread_chip.cpp
new file mode 100644
index 0000000..38abad4
--- /dev/null
+++ b/staging/threadnetwork/aidl/default/thread_chip.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "thread_chip.hpp"
+
+#include <android-base/logging.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_ibinder.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <utils/Log.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace threadnetwork {
+
+static ndk::ScopedAStatus errorStatus(int32_t error, const char* message) {
+    return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(error, message));
+}
+
+ThreadChip::ThreadChip(uint8_t id, char* url)
+    : mUrl(),
+      mInterface(handleReceivedFrame, this, mRxFrameBuffer),
+      mRxFrameBuffer(),
+      mCallback(nullptr) {
+    const std::string name(std::string() + IThreadChip::descriptor + "/chip" + std::to_string(id));
+    binder_status_t status;
+
+    ALOGI("ServiceName: %s, Url: %s", name.c_str(), url);
+    CHECK_EQ(mUrl.Init(url), 0);
+    status = AServiceManager_addService(asBinder().get(), name.c_str());
+    CHECK_EQ(status, STATUS_OK);
+
+    mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient(
+            AIBinder_DeathRecipient_new(ThreadChip::onBinderDied));
+    AIBinder_DeathRecipient_setOnUnlinked(mDeathRecipient.get(), ThreadChip::onBinderUnlinked);
+}
+
+ThreadChip::~ThreadChip() {
+    AIBinder_DeathRecipient_delete(mDeathRecipient.get());
+}
+
+void ThreadChip::onBinderDied(void* context) {
+    reinterpret_cast<ThreadChip*>(context)->onBinderDied();
+}
+
+void ThreadChip::onBinderDied(void) {
+    ALOGW("Thread Network HAL client is dead.");
+}
+
+void ThreadChip::onBinderUnlinked(void* context) {
+    reinterpret_cast<ThreadChip*>(context)->onBinderUnlinked();
+}
+
+void ThreadChip::onBinderUnlinked(void) {
+    ALOGW("ThreadChip binder is unlinked.");
+    deinitChip();
+}
+
+void ThreadChip::handleReceivedFrame(void* context) {
+    reinterpret_cast<ThreadChip*>(context)->handleReceivedFrame();
+}
+
+void ThreadChip::handleReceivedFrame(void) {
+    if (mCallback != nullptr) {
+        mCallback->onReceiveSpinelFrame(std::vector<uint8_t>(
+                mRxFrameBuffer.GetFrame(), mRxFrameBuffer.GetFrame() + mRxFrameBuffer.GetLength()));
+    }
+
+    mRxFrameBuffer.DiscardFrame();
+}
+
+ndk::ScopedAStatus ThreadChip::open(const std::shared_ptr<IThreadChipCallback>& in_callback) {
+    ndk::ScopedAStatus status = initChip(in_callback);
+
+    if (status.isOk()) {
+        AIBinder_linkToDeath(in_callback->asBinder().get(), mDeathRecipient.get(), this);
+        ALOGI("Open IThreadChip successfully.");
+    } else {
+        ALOGW("Open IThreadChip failed, error: %s", status.getDescription().c_str());
+    }
+
+    return status;
+}
+
+ndk::ScopedAStatus ThreadChip::initChip(const std::shared_ptr<IThreadChipCallback>& in_callback) {
+    if (in_callback == nullptr) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    } else if (mCallback == nullptr) {
+        if (mInterface.Init(mUrl) != OT_ERROR_NONE) {
+            return errorStatus(ERROR_FAILED, "Failed to initialize the interface");
+        }
+
+        mCallback = in_callback;
+        ot::Posix::Mainloop::Manager::Get().Add(*this);
+        return ndk::ScopedAStatus::ok();
+    } else {
+        return errorStatus(ERROR_BUSY, "Interface is already opened");
+    }
+}
+
+ndk::ScopedAStatus ThreadChip::close() {
+    ndk::ScopedAStatus status;
+    std::shared_ptr<IThreadChipCallback> callback = mCallback;
+
+    status = deinitChip();
+    if (status.isOk()) {
+        if (callback != nullptr) {
+            AIBinder_unlinkToDeath(callback->asBinder().get(), mDeathRecipient.get(), this);
+        }
+
+        ALOGI("Close IThreadChip successfully");
+    } else {
+        ALOGW("Close IThreadChip failed, error: %s", status.getDescription().c_str());
+    }
+
+    return status;
+}
+
+ndk::ScopedAStatus ThreadChip::deinitChip() {
+    if (mCallback != nullptr) {
+        mInterface.Deinit();
+        ot::Posix::Mainloop::Manager::Get().Remove(*this);
+        mCallback = nullptr;
+        return ndk::ScopedAStatus::ok();
+    }
+
+    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+}
+
+ndk::ScopedAStatus ThreadChip::sendSpinelFrame(const std::vector<uint8_t>& in_frame) {
+    ndk::ScopedAStatus status;
+    otError error;
+
+    if (mCallback == nullptr) {
+        status = errorStatus(ERROR_FAILED, "The interface is not open");
+    } else {
+        error = mInterface.SendFrame(reinterpret_cast<const uint8_t*>(in_frame.data()),
+                                     in_frame.size());
+        if (error == OT_ERROR_NONE) {
+            status = ndk::ScopedAStatus::ok();
+        } else if (error == OT_ERROR_NO_BUFS) {
+            status = errorStatus(ERROR_NO_BUFS, "Insufficient buffer space to send");
+        } else if (error == OT_ERROR_BUSY) {
+            status = errorStatus(ERROR_BUSY, "The interface is busy");
+        } else {
+            status = errorStatus(ERROR_FAILED, "Failed to send the spinel frame");
+        }
+    }
+
+    if (!status.isOk()) {
+        ALOGW("Send spinel frame failed, error: %s", status.getDescription().c_str());
+    }
+
+    return status;
+}
+
+ndk::ScopedAStatus ThreadChip::reset() {
+    mInterface.OnRcpReset();
+    ALOGI("reset()");
+    return ndk::ScopedAStatus::ok();
+}
+
+void ThreadChip::Update(otSysMainloopContext& context) {
+    if (mCallback != nullptr) {
+        mInterface.UpdateFdSet(context.mReadFdSet, context.mWriteFdSet, context.mMaxFd,
+                               context.mTimeout);
+    }
+}
+
+void ThreadChip::Process(const otSysMainloopContext& context) {
+    struct RadioProcessContext radioContext;
+
+    if (mCallback != nullptr) {
+        radioContext.mReadFdSet = &context.mReadFdSet;
+        radioContext.mWriteFdSet = &context.mWriteFdSet;
+        mInterface.Process(radioContext);
+    }
+}
+}  // namespace threadnetwork
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/staging/threadnetwork/aidl/default/thread_chip.hpp b/staging/threadnetwork/aidl/default/thread_chip.hpp
new file mode 100644
index 0000000..da5cba7
--- /dev/null
+++ b/staging/threadnetwork/aidl/default/thread_chip.hpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/threadnetwork/BnThreadChip.h>
+#include <aidl/android/hardware/threadnetwork/IThreadChipCallback.h>
+
+#include "hdlc_interface.hpp"
+#include "lib/spinel/spinel_interface.hpp"
+#include "mainloop.hpp"
+
+#include <android/binder_auto_utils.h>
+#include <android/binder_ibinder.h>
+#include <utils/Mutex.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace threadnetwork {
+
+class ThreadChip : public BnThreadChip, ot::Posix::Mainloop::Source {
+  public:
+    ThreadChip(uint8_t id, char* url);
+    ~ThreadChip();
+
+    ndk::ScopedAStatus open(const std::shared_ptr<IThreadChipCallback>& in_callback) override;
+    ndk::ScopedAStatus close() override;
+    ndk::ScopedAStatus sendSpinelFrame(const std::vector<uint8_t>& in_frame) override;
+    ndk::ScopedAStatus reset() override;
+    void Update(otSysMainloopContext& context) override;
+    void Process(const otSysMainloopContext& context) override;
+
+  private:
+    static void onBinderDied(void* context);
+    void onBinderDied(void);
+    static void onBinderUnlinked(void* context);
+    void onBinderUnlinked(void);
+    static void handleReceivedFrame(void* context);
+    void handleReceivedFrame(void);
+
+    ndk::ScopedAStatus initChip(const std::shared_ptr<IThreadChipCallback>& in_callback);
+    ndk::ScopedAStatus deinitChip();
+
+    ot::Url::Url mUrl;
+    ot::Posix::HdlcInterface mInterface;
+    ot::Spinel::SpinelInterface::RxFrameBuffer mRxFrameBuffer;
+    std::shared_ptr<IThreadChipCallback> mCallback;
+    ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
+};
+
+}  // namespace threadnetwork
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/staging/threadnetwork/aidl/default/utils.cpp b/staging/threadnetwork/aidl/default/utils.cpp
new file mode 100644
index 0000000..d3b4062
--- /dev/null
+++ b/staging/threadnetwork/aidl/default/utils.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <openthread/logging.h>
+#include <utils/Log.h>
+
+void otLogCritPlat(const char* format, ...) {
+    va_list args;
+
+    va_start(args, format);
+    __android_log_vprint(ANDROID_LOG_FATAL, LOG_TAG, format, args);
+    va_end(args);
+}
+
+void otLogWarnPlat(const char* format, ...) {
+    va_list args;
+
+    va_start(args, format);
+    __android_log_vprint(ANDROID_LOG_WARN, LOG_TAG, format, args);
+    va_end(args);
+}
diff --git a/staging/threadnetwork/aidl/vts/Android.bp b/staging/threadnetwork/aidl/vts/Android.bp
new file mode 100644
index 0000000..70386d9
--- /dev/null
+++ b/staging/threadnetwork/aidl/vts/Android.bp
@@ -0,0 +1,38 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+    name: "VtsHalThreadNetworkTargetTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: [
+        "VtsHalThreadNetworkTargetTest.cpp",
+    ],
+
+    shared_libs: [
+        "libbinder",
+        "libbinder_ndk",
+    ],
+    static_libs: [
+        "android.hardware.threadnetwork-ndk",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
diff --git a/staging/threadnetwork/aidl/vts/VtsHalThreadNetworkTargetTest.cpp b/staging/threadnetwork/aidl/vts/VtsHalThreadNetworkTargetTest.cpp
new file mode 100644
index 0000000..3e43f9c
--- /dev/null
+++ b/staging/threadnetwork/aidl/vts/VtsHalThreadNetworkTargetTest.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ThreadNetworkHalTargetTest"
+
+#include <future>
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <android-base/logging.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_manager.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <log/log.h>
+
+#include <aidl/android/hardware/threadnetwork/BnThreadChipCallback.h>
+#include <aidl/android/hardware/threadnetwork/IThreadChip.h>
+
+using aidl::android::hardware::threadnetwork::BnThreadChipCallback;
+using aidl::android::hardware::threadnetwork::IThreadChip;
+using android::ProcessState;
+using ndk::ScopedAStatus;
+using ndk::SpAIBinder;
+
+namespace {
+constexpr static int kCallbackTimeoutMs = 5000;
+}  // namespace
+
+class ThreadChipCallback : public BnThreadChipCallback {
+  public:
+    ThreadChipCallback(const std::function<void(const std::vector<uint8_t>&)>& on_spinel_message_cb)
+        : on_spinel_message_cb_(on_spinel_message_cb) {}
+
+    ScopedAStatus onReceiveSpinelFrame(const std::vector<uint8_t>& in_aFrame) {
+        on_spinel_message_cb_(in_aFrame);
+        return ScopedAStatus::ok();
+    }
+
+  private:
+    std::function<void(const std::vector<uint8_t>&)> on_spinel_message_cb_;
+};
+
+class ThreadNetworkAidl : public testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        std::string serviceName = GetParam();
+
+        ALOGI("serviceName: %s", serviceName.c_str());
+
+        thread_chip = IThreadChip::fromBinder(
+                SpAIBinder(AServiceManager_waitForService(serviceName.c_str())));
+        ASSERT_NE(thread_chip, nullptr);
+    }
+
+    virtual void TearDown() override { thread_chip->close(); }
+
+    std::shared_ptr<IThreadChip> thread_chip;
+};
+
+TEST_P(ThreadNetworkAidl, Open) {
+    std::shared_ptr<ThreadChipCallback> callback =
+            ndk::SharedRefBase::make<ThreadChipCallback>([](auto /* data */) {});
+
+    EXPECT_TRUE(thread_chip->open(callback).isOk());
+    EXPECT_EQ(thread_chip->open(callback).getServiceSpecificError(), IThreadChip::ERROR_BUSY);
+}
+
+TEST_P(ThreadNetworkAidl, Close) {
+    std::shared_ptr<ThreadChipCallback> callback =
+            ndk::SharedRefBase::make<ThreadChipCallback>([](auto /* data */) {});
+
+    EXPECT_TRUE(thread_chip->open(callback).isOk());
+    EXPECT_TRUE(thread_chip->close().isOk());
+    EXPECT_EQ(thread_chip->close().getExceptionCode(), EX_ILLEGAL_STATE);
+}
+
+TEST_P(ThreadNetworkAidl, Reset) {
+    std::shared_ptr<ThreadChipCallback> callback =
+            ndk::SharedRefBase::make<ThreadChipCallback>([](auto /* data */) {});
+
+    EXPECT_TRUE(thread_chip->open(callback).isOk());
+    EXPECT_TRUE(thread_chip->reset().isOk());
+}
+
+TEST_P(ThreadNetworkAidl, SendSpinelFrame) {
+    const uint8_t kCmdOffset = 2;
+    const uint8_t kMajorVersionOffset = 3;
+    const uint8_t kMinorVersionOffset = 4;
+    const std::vector<uint8_t> kGetSpinelProtocolVersion({0x81, 0x02, 0x01});
+    const std::vector<uint8_t> kGetSpinelProtocolVersionResponse({0x81, 0x06, 0x01, 0x04, 0x03});
+    uint8_t min_major_version = kGetSpinelProtocolVersionResponse[kMajorVersionOffset];
+    uint8_t min_minor_version = kGetSpinelProtocolVersionResponse[kMinorVersionOffset];
+    uint8_t major_version;
+    uint8_t minor_version;
+    std::promise<void> open_cb_promise;
+    std::future<void> open_cb_future{open_cb_promise.get_future()};
+    std::shared_ptr<ThreadChipCallback> callback;
+    std::vector<uint8_t> received_frame;
+    std::chrono::milliseconds timeout{kCallbackTimeoutMs};
+
+    callback = ndk::SharedRefBase::make<ThreadChipCallback>(
+            [&](const std::vector<uint8_t>& in_aFrame) {
+                if (in_aFrame.size() == kGetSpinelProtocolVersionResponse.size() &&
+                    in_aFrame[kCmdOffset] == kGetSpinelProtocolVersionResponse[kCmdOffset]) {
+                    major_version = in_aFrame[kMajorVersionOffset];
+                    minor_version = in_aFrame[kMinorVersionOffset];
+                    open_cb_promise.set_value();
+                }
+            });
+
+    ASSERT_NE(callback, nullptr);
+
+    EXPECT_TRUE(thread_chip->open(callback).isOk());
+
+    EXPECT_TRUE(thread_chip->sendSpinelFrame(kGetSpinelProtocolVersion).isOk());
+    EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready);
+
+    EXPECT_GE(major_version, min_major_version);
+    if (major_version == min_major_version) {
+        EXPECT_GE(minor_version, min_minor_version);
+    }
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ThreadNetworkAidl);
+INSTANTIATE_TEST_SUITE_P(
+        Thread, ThreadNetworkAidl,
+        testing::ValuesIn(android::getAidlHalInstanceNames(IThreadChip::descriptor)),
+        android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ProcessState::self()->setThreadPoolMaxThreadCount(1);
+    ProcessState::self()->startThreadPool();
+    return RUN_ALL_TESTS();
+}
diff --git a/tv/hdmi/connection/aidl/default/HdmiConnectionMock.cpp b/tv/hdmi/connection/aidl/default/HdmiConnectionMock.cpp
index ca8c348..8f4411b 100644
--- a/tv/hdmi/connection/aidl/default/HdmiConnectionMock.cpp
+++ b/tv/hdmi/connection/aidl/default/HdmiConnectionMock.cpp
@@ -70,16 +70,21 @@
 }
 
 ScopedAStatus HdmiConnectionMock::setHpdSignal(HpdSignal signal, int32_t portId) {
-    if (mHdmiThreadRun) {
-        mHpdSignal.at(portId - 1) = signal;
-        return ScopedAStatus::ok();
-    } else {
+    if (portId > mTotalPorts || portId < 1) {
+        return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+    if (!mHdmiThreadRun) {
         return ScopedAStatus::fromServiceSpecificError(
                 static_cast<int32_t>(Result::FAILURE_INVALID_STATE));
     }
+    mHpdSignal.at(portId - 1) = signal;
+    return ScopedAStatus::ok();
 }
 
 ScopedAStatus HdmiConnectionMock::getHpdSignal(int32_t portId, HpdSignal* _aidl_return) {
+    if (portId > mTotalPorts || portId < 1) {
+        return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
     *_aidl_return = mHpdSignal.at(portId - 1);
     return ScopedAStatus::ok();
 }
@@ -123,7 +128,7 @@
     bool connected = ((msgBuf[3]) & 0xf) > 0;
     int32_t portId = static_cast<uint32_t>(msgBuf[0] & 0xf);
 
-    if (portId > static_cast<int32_t>(mPortInfos.size())) {
+    if (portId > static_cast<int32_t>(mPortInfos.size()) || portId < 1) {
         ALOGD("[halimp_aidl] ignore hot plug message, id %x does not exist", portId);
         return;
     }
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 fd59888..6d0ddaa 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
@@ -85,22 +85,14 @@
   const int NO_POWER_CAP_CONSTANT = 0x7FFFFFFF;
   @Backing(type="int") @VintfStability
   enum ChipCapabilityMask {
-    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),
-    SET_AFC_CHANNEL_ALLOWANCE = (1 << 15),
+    SET_TX_POWER_LIMIT = (1 << 0) /* 1 */,
+    D2D_RTT = (1 << 1) /* 2 */,
+    D2AP_RTT = (1 << 2) /* 4 */,
+    USE_BODY_HEAD_SAR = (1 << 3) /* 8 */,
+    SET_LATENCY_MODE = (1 << 4) /* 16 */,
+    P2P_RAND_MAC = (1 << 5) /* 32 */,
+    WIGIG = (1 << 6) /* 64 */,
+    SET_AFC_CHANNEL_ALLOWANCE = (1 << 7) /* 128 */,
   }
   @VintfStability
   parcelable ChipConcurrencyCombinationLimit {
@@ -132,9 +124,9 @@
   }
   @Backing(type="int") @VintfStability
   enum CoexRestriction {
-    WIFI_DIRECT = (1 << 0),
-    SOFTAP = (1 << 1),
-    WIFI_AWARE = (1 << 2),
+    WIFI_DIRECT = (1 << 0) /* 1 */,
+    SOFTAP = (1 << 1) /* 2 */,
+    WIFI_AWARE = (1 << 2) /* 4 */,
   }
   @VintfStability
   parcelable CoexUnsafeChannel {
@@ -162,13 +154,13 @@
   }
   @Backing(type="int") @VintfStability
   enum UsableChannelFilter {
-    CELLULAR_COEXISTENCE = (1 << 0),
-    CONCURRENCY = (1 << 1),
-    NAN_INSTANT_MODE = (1 << 2),
+    CELLULAR_COEXISTENCE = (1 << 0) /* 1 */,
+    CONCURRENCY = (1 << 1) /* 2 */,
+    NAN_INSTANT_MODE = (1 << 2) /* 4 */,
   }
   @Backing(type="int") @VintfStability
   enum ChannelCategoryMask {
-    INDOOR_CHANNEL = (1 << 0),
-    DFS_CHANNEL = (1 << 1),
+    INDOOR_CHANNEL = (1 << 0) /* 1 */,
+    DFS_CHANNEL = (1 << 1) /* 2 */,
   }
 }
diff --git a/wifi/aidl/android/hardware/wifi/IWifiChip.aidl b/wifi/aidl/android/hardware/wifi/IWifiChip.aidl
index 41ff7e6..8d415fd 100644
--- a/wifi/aidl/android/hardware/wifi/IWifiChip.aidl
+++ b/wifi/aidl/android/hardware/wifi/IWifiChip.aidl
@@ -46,71 +46,38 @@
     @Backing(type="int")
     enum ChipCapabilityMask {
         /**
-         * Memory dump of Firmware.
-         */
-        DEBUG_MEMORY_FIRMWARE_DUMP = 1 << 0,
-        /**
-         * Memory dump of Driver.
-         */
-        DEBUG_MEMORY_DRIVER_DUMP = 1 << 1,
-        /**
-         * Connectivity events reported via debug ring buffer.
-         */
-        DEBUG_RING_BUFFER_CONNECT_EVENT = 1 << 2,
-        /**
-         * Power events reported via debug ring buffer.
-         */
-        DEBUG_RING_BUFFER_POWER_EVENT = 1 << 3,
-        /**
-         * Wakelock events reported via debug ring buffer.
-         */
-        DEBUG_RING_BUFFER_WAKELOCK_EVENT = 1 << 4,
-        /**
-         * Vendor data reported via debug ring buffer.
-         * This mostly contains firmware event logs.
-         */
-        DEBUG_RING_BUFFER_VENDOR_DATA = 1 << 5,
-        /**
-         * Host wake reasons stats collection.
-         */
-        DEBUG_HOST_WAKE_REASON_STATS = 1 << 6,
-        /**
-         * Error alerts.
-         */
-        DEBUG_ERROR_ALERTS = 1 << 7,
-        /**
          * Set/Reset Tx Power limits.
          */
-        SET_TX_POWER_LIMIT = 1 << 8,
+        SET_TX_POWER_LIMIT = 1 << 0,
         /**
          * Device to Device RTT.
          */
-        D2D_RTT = 1 << 9,
+        D2D_RTT = 1 << 1,
         /**
          * Device to AP RTT.
          */
-        D2AP_RTT = 1 << 10,
+        D2AP_RTT = 1 << 2,
         /**
          * Set/Reset Tx Power limits.
          */
-        USE_BODY_HEAD_SAR = 1 << 11,
+        USE_BODY_HEAD_SAR = 1 << 3,
         /**
          * Set Latency Mode.
          */
-        SET_LATENCY_MODE = 1 << 12,
+        SET_LATENCY_MODE = 1 << 4,
         /**
          * Support P2P MAC randomization.
          */
-        P2P_RAND_MAC = 1 << 13,
+        P2P_RAND_MAC = 1 << 5,
         /**
          * Chip can operate in the 60GHz band (WiGig chip).
          */
-        WIGIG = 1 << 14,
+        WIGIG = 1 << 6,
         /**
          * Chip supports setting allowed channels along with PSD in 6GHz band
          * for AFC purposes.
          */
-        SET_AFC_CHANNEL_ALLOWANCE = 1 << 15,
+        SET_AFC_CHANNEL_ALLOWANCE = 1 << 7,
     }
 
     /**
@@ -540,7 +507,6 @@
      * API to enable/disable alert notifications from the chip.
      * These alerts must be used to notify the framework of any fatal error events
      * that the chip encounters via |IWifiChipEventCallback.onDebugErrorAlert| method.
-     * Must fail if |ChipCapabilityMask.DEBUG_ERROR_ALERTS| is not set.
      *
      * @param enable true to enable, false to disable.
      * @throws ServiceSpecificException with one of the following values:
diff --git a/wifi/aidl/default/aidl_struct_util.cpp b/wifi/aidl/default/aidl_struct_util.cpp
index 921b5dc..8463eac 100644
--- a/wifi/aidl/default/aidl_struct_util.cpp
+++ b/wifi/aidl/default/aidl_struct_util.cpp
@@ -41,23 +41,6 @@
     return std::vector<int32_t>(in.begin(), in.end());
 }
 
-IWifiChip::ChipCapabilityMask convertLegacyLoggerFeatureToAidlChipCapability(uint32_t feature) {
-    switch (feature) {
-        case legacy_hal::WIFI_LOGGER_MEMORY_DUMP_SUPPORTED:
-            return IWifiChip::ChipCapabilityMask::DEBUG_MEMORY_FIRMWARE_DUMP;
-        case legacy_hal::WIFI_LOGGER_DRIVER_DUMP_SUPPORTED:
-            return IWifiChip::ChipCapabilityMask::DEBUG_MEMORY_DRIVER_DUMP;
-        case legacy_hal::WIFI_LOGGER_CONNECT_EVENT_SUPPORTED:
-            return IWifiChip::ChipCapabilityMask::DEBUG_RING_BUFFER_CONNECT_EVENT;
-        case legacy_hal::WIFI_LOGGER_POWER_EVENT_SUPPORTED:
-            return IWifiChip::ChipCapabilityMask::DEBUG_RING_BUFFER_POWER_EVENT;
-        case legacy_hal::WIFI_LOGGER_WAKE_LOCK_SUPPORTED:
-            return IWifiChip::ChipCapabilityMask::DEBUG_RING_BUFFER_WAKELOCK_EVENT;
-    };
-    CHECK(false) << "Unknown legacy feature: " << feature;
-    return {};
-}
-
 IWifiStaIface::StaIfaceCapabilityMask convertLegacyLoggerFeatureToAidlStaIfaceCapability(
         uint32_t feature) {
     switch (feature) {
@@ -125,23 +108,11 @@
     return {};
 }
 
-bool convertLegacyFeaturesToAidlChipCapabilities(uint64_t legacy_feature_set,
-                                                 uint32_t legacy_logger_feature_set,
-                                                 uint32_t* aidl_caps) {
+bool convertLegacyFeaturesToAidlChipCapabilities(uint64_t legacy_feature_set, uint32_t* aidl_caps) {
     if (!aidl_caps) {
         return false;
     }
     *aidl_caps = {};
-    for (const auto feature : {legacy_hal::WIFI_LOGGER_MEMORY_DUMP_SUPPORTED,
-                               legacy_hal::WIFI_LOGGER_DRIVER_DUMP_SUPPORTED,
-                               legacy_hal::WIFI_LOGGER_CONNECT_EVENT_SUPPORTED,
-                               legacy_hal::WIFI_LOGGER_POWER_EVENT_SUPPORTED,
-                               legacy_hal::WIFI_LOGGER_WAKE_LOCK_SUPPORTED}) {
-        if (feature & legacy_logger_feature_set) {
-            *aidl_caps |=
-                    static_cast<uint32_t>(convertLegacyLoggerFeatureToAidlChipCapability(feature));
-        }
-    }
     std::vector<uint64_t> features = {WIFI_FEATURE_SET_TX_POWER_LIMIT,
                                       WIFI_FEATURE_USE_BODY_HEAD_SAR,
                                       WIFI_FEATURE_D2D_RTT,
@@ -156,13 +127,6 @@
         }
     }
 
-    // There are no flags for these 3 in the legacy feature set. Adding them to
-    // the set because all the current devices support it.
-    *aidl_caps |=
-            static_cast<uint32_t>(IWifiChip::ChipCapabilityMask::DEBUG_RING_BUFFER_VENDOR_DATA);
-    *aidl_caps |=
-            static_cast<uint32_t>(IWifiChip::ChipCapabilityMask::DEBUG_HOST_WAKE_REASON_STATS);
-    *aidl_caps |= static_cast<uint32_t>(IWifiChip::ChipCapabilityMask::DEBUG_ERROR_ALERTS);
     return true;
 }
 
diff --git a/wifi/aidl/default/aidl_struct_util.h b/wifi/aidl/default/aidl_struct_util.h
index 6407d32..df3785e 100644
--- a/wifi/aidl/default/aidl_struct_util.h
+++ b/wifi/aidl/default/aidl_struct_util.h
@@ -37,9 +37,7 @@
 namespace aidl_struct_util {
 
 // Chip conversion methods.
-bool convertLegacyFeaturesToAidlChipCapabilities(uint64_t legacy_feature_set,
-                                                 uint32_t legacy_logger_feature_set,
-                                                 uint32_t* aidl_caps);
+bool convertLegacyFeaturesToAidlChipCapabilities(uint64_t legacy_feature_set, uint32_t* aidl_caps);
 bool convertLegacyDebugRingBufferStatusToAidl(
         const legacy_hal::wifi_ring_buffer_status& legacy_status,
         WifiDebugRingBufferStatus* aidl_status);
diff --git a/wifi/aidl/default/tests/aidl_struct_util_unit_tests.cpp b/wifi/aidl/default/tests/aidl_struct_util_unit_tests.cpp
index f97c846..9997937 100644
--- a/wifi/aidl/default/tests/aidl_struct_util_unit_tests.cpp
+++ b/wifi/aidl/default/tests/aidl_struct_util_unit_tests.cpp
@@ -661,18 +661,12 @@
     using AidlChipCaps = IWifiChip::ChipCapabilityMask;
 
     uint32_t aidl_caps;
-
     uint32_t legacy_feature_set = WIFI_FEATURE_D2D_RTT | WIFI_FEATURE_SET_LATENCY_MODE;
-    uint32_t legacy_logger_feature_set = legacy_hal::WIFI_LOGGER_DRIVER_DUMP_SUPPORTED;
 
-    ASSERT_TRUE(aidl_struct_util::convertLegacyFeaturesToAidlChipCapabilities(
-            legacy_feature_set, legacy_logger_feature_set, &aidl_caps));
+    ASSERT_TRUE(aidl_struct_util::convertLegacyFeaturesToAidlChipCapabilities(legacy_feature_set,
+                                                                              &aidl_caps));
 
-    EXPECT_EQ((uint32_t)AidlChipCaps::DEBUG_RING_BUFFER_VENDOR_DATA |
-                      (uint32_t)AidlChipCaps::DEBUG_HOST_WAKE_REASON_STATS |
-                      (uint32_t)AidlChipCaps::DEBUG_ERROR_ALERTS | (uint32_t)AidlChipCaps::D2D_RTT |
-                      (uint32_t)AidlChipCaps::SET_LATENCY_MODE |
-                      (uint32_t)AidlChipCaps::DEBUG_MEMORY_DRIVER_DUMP,
+    EXPECT_EQ((uint32_t)AidlChipCaps::D2D_RTT | (uint32_t)AidlChipCaps::SET_LATENCY_MODE,
               aidl_caps);
 }
 
diff --git a/wifi/aidl/default/wifi_chip.cpp b/wifi/aidl/default/wifi_chip.cpp
index 541de16..41912b5 100644
--- a/wifi/aidl/default/wifi_chip.cpp
+++ b/wifi/aidl/default/wifi_chip.cpp
@@ -769,8 +769,8 @@
         legacy_logger_feature_set = 0;
     }
     uint32_t aidl_caps;
-    if (!aidl_struct_util::convertLegacyFeaturesToAidlChipCapabilities(
-                legacy_feature_set, legacy_logger_feature_set, &aidl_caps)) {
+    if (!aidl_struct_util::convertLegacyFeaturesToAidlChipCapabilities(legacy_feature_set,
+                                                                       &aidl_caps)) {
         return {IWifiChip::ChipCapabilityMask{}, createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
     }
     return {static_cast<IWifiChip::ChipCapabilityMask>(aidl_caps), ndk::ScopedAStatus::ok()};
diff --git a/wifi/aidl/vts/functional/wifi_chip_aidl_test.cpp b/wifi/aidl/vts/functional/wifi_chip_aidl_test.cpp
index fec9122..f908be6 100644
--- a/wifi/aidl/vts/functional/wifi_chip_aidl_test.cpp
+++ b/wifi/aidl/vts/functional/wifi_chip_aidl_test.cpp
@@ -131,16 +131,6 @@
         return {iface1, iface2};
     }
 
-    bool hasAnyRingBufferCapabilities(int32_t caps) {
-        return caps &
-               (static_cast<int32_t>(
-                        IWifiChip::ChipCapabilityMask::DEBUG_RING_BUFFER_CONNECT_EVENT) |
-                static_cast<int32_t>(IWifiChip::ChipCapabilityMask::DEBUG_RING_BUFFER_POWER_EVENT) |
-                static_cast<int32_t>(
-                        IWifiChip::ChipCapabilityMask::DEBUG_RING_BUFFER_WAKELOCK_EVENT) |
-                static_cast<int32_t>(IWifiChip::ChipCapabilityMask::DEBUG_RING_BUFFER_VENDOR_DATA));
-    }
-
     const char* getInstanceName() { return GetParam().c_str(); }
 
     std::shared_ptr<IWifiChip> wifi_chip_;
@@ -452,14 +442,9 @@
  */
 TEST_P(WifiChipAidlTest, RequestFirmwareDebugDump) {
     configureChipForConcurrencyType(IfaceConcurrencyType::STA);
-    int32_t caps = getChipCapabilities(wifi_chip_);
     std::vector<uint8_t> debug_dump;
     auto status = wifi_chip_->requestFirmwareDebugDump(&debug_dump);
-    if (caps & static_cast<int32_t>(IWifiChip::ChipCapabilityMask::DEBUG_MEMORY_FIRMWARE_DUMP)) {
-        EXPECT_TRUE(status.isOk());
-    } else {
-        EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
-    }
+    EXPECT_TRUE(status.isOk() || checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
 }
 
 /*
@@ -467,14 +452,9 @@
  */
 TEST_P(WifiChipAidlTest, RequestDriverDebugDump) {
     configureChipForConcurrencyType(IfaceConcurrencyType::STA);
-    int32_t caps = getChipCapabilities(wifi_chip_);
     std::vector<uint8_t> debug_dump;
     auto status = wifi_chip_->requestDriverDebugDump(&debug_dump);
-    if (caps & static_cast<int32_t>(IWifiChip::ChipCapabilityMask::DEBUG_MEMORY_DRIVER_DUMP)) {
-        EXPECT_TRUE(status.isOk());
-    } else {
-        EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
-    }
+    EXPECT_TRUE(status.isOk() || checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
 }
 
 /*
@@ -482,17 +462,14 @@
  */
 TEST_P(WifiChipAidlTest, GetDebugRingBuffersStatus) {
     configureChipForConcurrencyType(IfaceConcurrencyType::STA);
-    int32_t caps = getChipCapabilities(wifi_chip_);
     std::vector<WifiDebugRingBufferStatus> ring_buffer_status;
     auto status = wifi_chip_->getDebugRingBuffersStatus(&ring_buffer_status);
-    if (hasAnyRingBufferCapabilities(caps)) {
-        EXPECT_TRUE(status.isOk());
+    EXPECT_TRUE(status.isOk() || checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
+    if (status.isOk()) {
         ASSERT_NE(ring_buffer_status.size(), 0);
         for (const auto& ring_buffer : ring_buffer_status) {
             EXPECT_NE(ring_buffer.ringName.size(), 0);
         }
-    } else {
-        EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
     }
 }
 
@@ -501,14 +478,9 @@
  */
 TEST_P(WifiChipAidlTest, GetDebugHostWakeReasonStats) {
     configureChipForConcurrencyType(IfaceConcurrencyType::STA);
-    int32_t caps = getChipCapabilities(wifi_chip_);
     WifiDebugHostWakeReasonStats wake_reason_stats = {};
     auto status = wifi_chip_->getDebugHostWakeReasonStats(&wake_reason_stats);
-    if (caps & static_cast<int32_t>(IWifiChip::ChipCapabilityMask::DEBUG_HOST_WAKE_REASON_STATS)) {
-        EXPECT_TRUE(status.isOk());
-    } else {
-        EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
-    }
+    EXPECT_TRUE(status.isOk() || checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
 }
 
 /*
@@ -516,26 +488,19 @@
  */
 TEST_P(WifiChipAidlTest, StartLoggingToDebugRingBuffer) {
     configureChipForConcurrencyType(IfaceConcurrencyType::STA);
-    int32_t caps = getChipCapabilities(wifi_chip_);
     std::string ring_name;
     std::vector<WifiDebugRingBufferStatus> ring_buffer_status;
-    auto status = wifi_chip_->getDebugRingBuffersStatus(&ring_buffer_status);
 
-    if (hasAnyRingBufferCapabilities(caps)) {
-        EXPECT_TRUE(status.isOk());
+    auto status = wifi_chip_->getDebugRingBuffersStatus(&ring_buffer_status);
+    EXPECT_TRUE(status.isOk() || checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
+    if (status.isOk()) {
         ASSERT_NE(ring_buffer_status.size(), 0);
         ring_name = ring_buffer_status[0].ringName;
-    } else {
-        EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
     }
 
     status = wifi_chip_->startLoggingToDebugRingBuffer(
             ring_name, WifiDebugRingBufferVerboseLevel::VERBOSE, 5, 1024);
-    if (hasAnyRingBufferCapabilities(caps)) {
-        EXPECT_TRUE(status.isOk());
-    } else {
-        EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
-    }
+    EXPECT_TRUE(status.isOk() || checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
 }
 
 /*
@@ -543,25 +508,18 @@
  */
 TEST_P(WifiChipAidlTest, ForceDumpToDebugRingBuffer) {
     configureChipForConcurrencyType(IfaceConcurrencyType::STA);
-    int32_t caps = getChipCapabilities(wifi_chip_);
     std::string ring_name;
     std::vector<WifiDebugRingBufferStatus> ring_buffer_status;
-    auto status = wifi_chip_->getDebugRingBuffersStatus(&ring_buffer_status);
 
-    if (hasAnyRingBufferCapabilities(caps)) {
-        EXPECT_TRUE(status.isOk());
+    auto status = wifi_chip_->getDebugRingBuffersStatus(&ring_buffer_status);
+    EXPECT_TRUE(status.isOk() || checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
+    if (status.isOk()) {
         ASSERT_NE(ring_buffer_status.size(), 0);
         ring_name = ring_buffer_status[0].ringName;
-    } else {
-        EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
     }
 
     status = wifi_chip_->forceDumpToDebugRingBuffer(ring_name);
-    if (hasAnyRingBufferCapabilities(caps)) {
-        EXPECT_TRUE(status.isOk());
-    } else {
-        EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
-    }
+    EXPECT_TRUE(status.isOk() || checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
 }
 
 /*
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/INonStandardCertCallback.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/INonStandardCertCallback.aidl
index 28c1028..bcf0ea8 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/INonStandardCertCallback.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/INonStandardCertCallback.aidl
@@ -35,4 +35,5 @@
 @VintfStability
 interface INonStandardCertCallback {
   byte[] getBlob(in String alias);
+  String[] listAliases(in String prefix);
 }
diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/INonStandardCertCallback.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/INonStandardCertCallback.aidl
index 3259585..2f9e528 100644
--- a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/INonStandardCertCallback.aidl
+++ b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/INonStandardCertCallback.aidl
@@ -34,4 +34,13 @@
      *         |SupplicantStatusCode.FAILURE_UNKNOWN|
      */
     byte[] getBlob(in String alias);
+
+    /**
+     * List the aliases currently stored in the database.
+     *
+     * @param prefix Prefix to filter the aliases by.
+     * @return List of alias strings in the certificate store.
+               The resulting strings will each exclude the prefix.
+     */
+    String[] listAliases(in String prefix);
 }