Merge "Add BluetoothLeAudioCodecsProvider Unit Test"
diff --git a/audio/aidl/Android.bp b/audio/aidl/Android.bp
index 40aa229..e58ae6a 100644
--- a/audio/aidl/Android.bp
+++ b/audio/aidl/Android.bp
@@ -148,15 +148,26 @@
     name: "android.hardware.audio.effect",
     vendor_available: true,
     srcs: [
+        "android/hardware/audio/effect/BassBoost.aidl",
         "android/hardware/audio/effect/Capability.aidl",
         "android/hardware/audio/effect/CommandId.aidl",
         "android/hardware/audio/effect/Descriptor.aidl",
+        "android/hardware/audio/effect/Downmix.aidl",
+        "android/hardware/audio/effect/DynamicsProcessing.aidl",
         "android/hardware/audio/effect/Equalizer.aidl",
         "android/hardware/audio/effect/Flags.aidl",
+        "android/hardware/audio/effect/HapticGenerator.aidl",
         "android/hardware/audio/effect/IEffect.aidl",
         "android/hardware/audio/effect/IFactory.aidl",
+        "android/hardware/audio/effect/LoudnessEnhancer.aidl",
         "android/hardware/audio/effect/Parameter.aidl",
+        "android/hardware/audio/effect/Processing.aidl",
+        "android/hardware/audio/effect/Reverb.aidl",
         "android/hardware/audio/effect/State.aidl",
+        "android/hardware/audio/effect/VendorExtension.aidl",
+        "android/hardware/audio/effect/Virtualizer.aidl",
+        "android/hardware/audio/effect/Visualizer.aidl",
+        "android/hardware/audio/effect/Volume.aidl",
     ],
     imports: [
         "android.hardware.common-V2",
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/StreamDescriptor.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/StreamDescriptor.aidl
index db1ac22..da24a10 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/StreamDescriptor.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/StreamDescriptor.aidl
@@ -39,15 +39,34 @@
   int frameSizeBytes;
   long bufferSizeFrames;
   android.hardware.audio.core.StreamDescriptor.AudioBuffer audio;
-  const int COMMAND_BURST = 1;
+  const int LATENCY_UNKNOWN = -1;
   @FixedSize @VintfStability
   parcelable Position {
     long frames;
     long timeNs;
   }
+  @Backing(type="int") @VintfStability
+  enum State {
+    STANDBY = 1,
+    IDLE = 2,
+    ACTIVE = 3,
+    PAUSED = 4,
+    DRAINING = 5,
+    DRAIN_PAUSED = 6,
+    ERROR = 100,
+  }
+  @Backing(type="int") @VintfStability
+  enum CommandCode {
+    START = 1,
+    BURST = 2,
+    DRAIN = 3,
+    STANDBY = 4,
+    PAUSE = 5,
+    FLUSH = 6,
+  }
   @FixedSize @VintfStability
   parcelable Command {
-    int code;
+    android.hardware.audio.core.StreamDescriptor.CommandCode code = android.hardware.audio.core.StreamDescriptor.CommandCode.START;
     int fmqByteCount;
   }
   @FixedSize @VintfStability
@@ -57,6 +76,8 @@
     android.hardware.audio.core.StreamDescriptor.Position observable;
     android.hardware.audio.core.StreamDescriptor.Position hardware;
     int latencyMs;
+    int xrunFrames;
+    android.hardware.audio.core.StreamDescriptor.State state = android.hardware.audio.core.StreamDescriptor.State.STANDBY;
   }
   @VintfStability
   union AudioBuffer {
diff --git a/audio/aidl/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
new file mode 100644
index 0000000..979ebb8
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/BassBoost.aidl
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.audio.effect;
+@VintfStability
+union BassBoost {
+  android.hardware.audio.effect.VendorExtension vendor;
+  int strengthPm;
+  @VintfStability
+  union Id {
+    int vendorExtensionTag;
+    android.hardware.audio.effect.BassBoost.Tag commonTag;
+  }
+  @VintfStability
+  parcelable Capability {
+    ParcelableHolder extension;
+    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 11acf5e..06f2bfe 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
@@ -34,10 +34,15 @@
 package android.hardware.audio.effect;
 @VintfStability
 union Capability {
-  android.hardware.audio.effect.Capability.VendorEffectCapability vendor;
+  android.hardware.audio.effect.VendorExtension vendorExtension;
+  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.Equalizer.Capability equalizer;
-  @VintfStability
-  parcelable VendorEffectCapability {
-    ParcelableHolder extension;
-  }
+  android.hardware.audio.effect.HapticGenerator.Capability hapticGenerator;
+  android.hardware.audio.effect.LoudnessEnhancer.Capability loudnessEnhancer;
+  android.hardware.audio.effect.Reverb.Capability reverb;
+  android.hardware.audio.effect.Virtualizer.Capability virtualizer;
+  android.hardware.audio.effect.Visualizer.Capability visualizer;
+  android.hardware.audio.effect.Volume.Capability volume;
 }
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Descriptor.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Descriptor.aidl
index 1c86cf3..990d369 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Descriptor.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Descriptor.aidl
@@ -53,10 +53,15 @@
   parcelable Identity {
     android.media.audio.common.AudioUuid type;
     android.media.audio.common.AudioUuid uuid;
+    @nullable android.media.audio.common.AudioUuid proxy;
   }
   @VintfStability
   parcelable Common {
     android.hardware.audio.effect.Descriptor.Identity id;
     android.hardware.audio.effect.Flags flags;
+    int cpuLoad;
+    int memoryUsage;
+    @utf8InCpp String name;
+    @utf8InCpp String implementor;
   }
 }
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
new file mode 100644
index 0000000..76f8ce5
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Downmix.aidl
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.audio.effect;
+@VintfStability
+union Downmix {
+  android.hardware.audio.effect.VendorExtension vendor;
+  android.hardware.audio.effect.Downmix.Type type;
+  @VintfStability
+  union Id {
+    int vendorExtensionTag;
+    android.hardware.audio.effect.Downmix.Tag commonTag;
+  }
+  @VintfStability
+  parcelable Capability {
+    ParcelableHolder extension;
+  }
+  @VintfStability
+  enum Type {
+    STRIP = 0,
+    FOLD = 1,
+  }
+}
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
new file mode 100644
index 0000000..ed4dc80
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/DynamicsProcessing.aidl
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.audio.effect;
+@VintfStability
+union DynamicsProcessing {
+  android.hardware.audio.effect.VendorExtension vendorExtension;
+  android.hardware.audio.effect.DynamicsProcessing.EngineArchitecture engineArchitecture;
+  android.hardware.audio.effect.DynamicsProcessing.BandChannelConfig preEq;
+  android.hardware.audio.effect.DynamicsProcessing.BandChannelConfig postEq;
+  android.hardware.audio.effect.DynamicsProcessing.EqBandConfig preEqBand;
+  android.hardware.audio.effect.DynamicsProcessing.EqBandConfig postEqBand;
+  android.hardware.audio.effect.DynamicsProcessing.BandChannelConfig mbc;
+  android.hardware.audio.effect.DynamicsProcessing.MbcBandConfig mbcBand;
+  android.hardware.audio.effect.DynamicsProcessing.LimiterConfig limiter;
+  float inputGainDb;
+  @VintfStability
+  union Id {
+    int vendorExtensionTag;
+    android.hardware.audio.effect.DynamicsProcessing.Tag commonTag;
+  }
+  @VintfStability
+  parcelable Capability {
+    ParcelableHolder extension;
+  }
+  enum ResolutionPreference {
+    FAVOR_FREQUENCY_RESOLUTION = 0,
+    FAVOR_TIME_RESOLUTION = 1,
+  }
+  @VintfStability
+  parcelable BandEnablement {
+    boolean inUse;
+    int bandCount;
+  }
+  @VintfStability
+  parcelable EngineArchitecture {
+    android.hardware.audio.effect.DynamicsProcessing.ResolutionPreference resolutionPreference = android.hardware.audio.effect.DynamicsProcessing.ResolutionPreference.FAVOR_FREQUENCY_RESOLUTION;
+    float preferredFrameDurationMs;
+    android.hardware.audio.effect.DynamicsProcessing.BandEnablement preEqBand;
+    android.hardware.audio.effect.DynamicsProcessing.BandEnablement postEqBand;
+    android.hardware.audio.effect.DynamicsProcessing.BandEnablement mbcBand;
+    boolean limiterInUse;
+  }
+  @VintfStability
+  parcelable BandChannelConfig {
+    int channel;
+    android.hardware.audio.effect.DynamicsProcessing.BandEnablement enablement;
+  }
+  @VintfStability
+  parcelable EqBandConfig {
+    int channel;
+    int band;
+    boolean enable;
+    float cutoffFrequency;
+    float gain;
+  }
+  @VintfStability
+  parcelable MbcBandConfig {
+    int channel;
+    int band;
+    boolean enable;
+    float cutoffFrequencyHz;
+    float gainDb;
+    float attackTimeMs;
+    float releaseTimeMs;
+    float ratio;
+    float thresholdDb;
+    float kneeWidthDb;
+    float noiseGateThresholdDb;
+    float expanderRatio;
+    float preGainDb;
+    float postGainDb;
+  }
+  @VintfStability
+  parcelable LimiterConfig {
+    int channel;
+    boolean enable;
+    boolean inUse;
+    int linkGroup;
+    float attackTimeMs;
+    float releaseTimeMs;
+    float ratio;
+    float thresholdDb;
+    float postGainDb;
+  }
+}
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 31732d3..d825eac 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
@@ -34,13 +34,34 @@
 package android.hardware.audio.effect;
 @VintfStability
 union Equalizer {
-  android.hardware.audio.effect.Equalizer.VendorExtension vendor;
+  android.hardware.audio.effect.VendorExtension vendorExtension;
+  android.hardware.audio.effect.Equalizer.BandLevel[] bandLevels;
+  int preset;
+  @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 VendorExtension {
-    ParcelableHolder extension;
+  parcelable BandLevel {
+    int index;
+    int levelMb;
+  }
+  @VintfStability
+  parcelable BandFrequency {
+    int index;
+    int minMh;
+    int maxMh;
+  }
+  @VintfStability
+  parcelable Preset {
+    int index;
+    @utf8InCpp String name;
   }
 }
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Flags.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Flags.aidl
index af774e8..285ff18 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Flags.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Flags.aidl
@@ -34,4 +34,41 @@
 package android.hardware.audio.effect;
 @VintfStability
 parcelable Flags {
+  android.hardware.audio.effect.Flags.Type type = android.hardware.audio.effect.Flags.Type.INSERT;
+  android.hardware.audio.effect.Flags.Insert insert = android.hardware.audio.effect.Flags.Insert.ANY;
+  android.hardware.audio.effect.Flags.Volume volume = android.hardware.audio.effect.Flags.Volume.NONE;
+  android.hardware.audio.effect.Flags.HardwareAccelerator hwAcceleratorMode = android.hardware.audio.effect.Flags.HardwareAccelerator.NONE;
+  boolean offloadIndication;
+  boolean deviceIndication;
+  boolean audioModeIndication;
+  boolean audioSourceIndication;
+  boolean noProcessing;
+  @Backing(type="byte") @VintfStability
+  enum Type {
+    INSERT = 0,
+    AUXILIARY = 1,
+    REPLACE = 2,
+    PRE_PROC = 3,
+    POST_PROC = 4,
+  }
+  @Backing(type="byte") @VintfStability
+  enum Insert {
+    ANY = 0,
+    FIRST = 1,
+    LAST = 2,
+    EXCLUSIVE = 3,
+  }
+  @Backing(type="byte") @VintfStability
+  enum Volume {
+    NONE = 0,
+    CTRL = 1,
+    IND = 2,
+    MONITOR = 3,
+  }
+  @Backing(type="byte") @VintfStability
+  enum HardwareAccelerator {
+    NONE = 0,
+    SIMPLE = 1,
+    TUNNEL = 2,
+  }
 }
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
new file mode 100644
index 0000000..40a8d72
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/HapticGenerator.aidl
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.audio.effect;
+@VintfStability
+union HapticGenerator {
+  android.hardware.audio.effect.VendorExtension vendorExtension;
+  android.hardware.audio.effect.HapticGenerator.HapticScale hapticScale;
+  android.hardware.audio.effect.HapticGenerator.VibratorInformation vibratorInfo;
+  @VintfStability
+  union Id {
+    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,
+    VERY_LOW = -2,
+    LOW = -1,
+    NONE = 0,
+    HIGH = 1,
+    VERY_HIGH = 2,
+  }
+  @VintfStability
+  parcelable HapticScale {
+    int id;
+    android.hardware.audio.effect.HapticGenerator.VibratorScale scale = android.hardware.audio.effect.HapticGenerator.VibratorScale.MUTE;
+  }
+  @VintfStability
+  parcelable VibratorInformation {
+    float resonantFrequencyHz;
+    float qFactor;
+    float maxAmplitude;
+  }
+}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/IEffect.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/IEffect.aidl
index e5c96f5..8c196e7 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/IEffect.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/IEffect.aidl
@@ -34,7 +34,7 @@
 package android.hardware.audio.effect;
 @VintfStability
 interface IEffect {
-  android.hardware.audio.effect.IEffect.OpenEffectReturn open(in android.hardware.audio.effect.Parameter.Common common, in android.hardware.audio.effect.Parameter.Specific specific);
+  android.hardware.audio.effect.IEffect.OpenEffectReturn open(in android.hardware.audio.effect.Parameter.Common common, in @nullable android.hardware.audio.effect.Parameter.Specific specific);
   void close();
   android.hardware.audio.effect.Descriptor getDescriptor();
   void command(in android.hardware.audio.effect.CommandId commandId);
@@ -44,13 +44,13 @@
   @FixedSize @VintfStability
   parcelable Status {
     int status;
-    int fmqByteConsumed;
-    int fmqByteProduced;
+    int fmqConsumed;
+    int fmqProduced;
   }
   @VintfStability
   parcelable OpenEffectReturn {
     android.hardware.common.fmq.MQDescriptor<android.hardware.audio.effect.IEffect.Status,android.hardware.common.fmq.SynchronizedReadWrite> statusMQ;
-    android.hardware.common.fmq.MQDescriptor<byte,android.hardware.common.fmq.SynchronizedReadWrite> inputDataMQ;
-    android.hardware.common.fmq.MQDescriptor<byte,android.hardware.common.fmq.SynchronizedReadWrite> outputDataMQ;
+    android.hardware.common.fmq.MQDescriptor<float,android.hardware.common.fmq.SynchronizedReadWrite> inputDataMQ;
+    android.hardware.common.fmq.MQDescriptor<float,android.hardware.common.fmq.SynchronizedReadWrite> outputDataMQ;
   }
 }
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/IFactory.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/IFactory.aidl
index db06475..5b85d33 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/IFactory.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/IFactory.aidl
@@ -34,7 +34,8 @@
 package android.hardware.audio.effect;
 @VintfStability
 interface IFactory {
-  android.hardware.audio.effect.Descriptor.Identity[] queryEffects(in @nullable android.media.audio.common.AudioUuid type, in @nullable android.media.audio.common.AudioUuid implementation);
+  android.hardware.audio.effect.Descriptor.Identity[] queryEffects(in @nullable android.media.audio.common.AudioUuid type, in @nullable android.media.audio.common.AudioUuid implementation, in @nullable android.media.audio.common.AudioUuid proxy);
+  android.hardware.audio.effect.Processing[] queryProcessing(in @nullable android.hardware.audio.effect.Processing.Type type);
   android.hardware.audio.effect.IEffect createEffect(in android.media.audio.common.AudioUuid implUuid);
   void destroyEffect(in android.hardware.audio.effect.IEffect handle);
 }
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
new file mode 100644
index 0000000..5c6ca16
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/LoudnessEnhancer.aidl
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.audio.effect;
+@VintfStability
+union LoudnessEnhancer {
+  android.hardware.audio.effect.VendorExtension vendor;
+  int gainMb;
+  @VintfStability
+  union Id {
+    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/Parameter.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl
index 16bd3bb..321c286 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl
@@ -35,28 +35,50 @@
 @VintfStability
 union Parameter {
   android.hardware.audio.effect.Parameter.Common common;
-  android.hardware.audio.effect.Parameter.VendorEffectParameter vendorEffect;
+  android.media.audio.common.AudioDeviceDescription deviceDescription;
+  android.media.audio.common.AudioMode mode;
+  android.media.audio.common.AudioSource source;
+  android.hardware.audio.effect.Parameter.VolumeStereo volumeStereo;
   android.hardware.audio.effect.Parameter.Specific specific;
   @VintfStability
   union Id {
-    int commonTag;
-    int vendorTag;
-    android.hardware.audio.effect.Parameter.Specific.Tag specificTag;
+    int vendorEffectTag;
+    android.hardware.audio.effect.BassBoost.Id bassBoostTag;
+    android.hardware.audio.effect.Downmix.Id downmixTag;
+    android.hardware.audio.effect.DynamicsProcessing.Id dynamicsProcessingTag;
+    android.hardware.audio.effect.Equalizer.Id equalizerTag;
+    android.hardware.audio.effect.HapticGenerator.Id hapticGeneratorTag;
+    android.hardware.audio.effect.LoudnessEnhancer.Id loudnessEnhancerTag;
+    android.hardware.audio.effect.Reverb.Id reverbTag;
+    android.hardware.audio.effect.Virtualizer.Id virtualizerTag;
+    android.hardware.audio.effect.Visualizer.Id visualizerTag;
+    android.hardware.audio.effect.Volume.Id volumeTag;
+    android.hardware.audio.effect.Parameter.Tag commonTag;
   }
   @VintfStability
   parcelable Common {
     int session;
     int ioHandle;
-    android.media.audio.common.AudioDeviceDescription device;
     android.media.audio.common.AudioConfig input;
     android.media.audio.common.AudioConfig output;
   }
   @VintfStability
-  parcelable VendorEffectParameter {
-    ParcelableHolder extension;
+  parcelable VolumeStereo {
+    float left;
+    float right;
   }
   @VintfStability
   union Specific {
+    android.hardware.audio.effect.VendorExtension vendorEffect;
+    android.hardware.audio.effect.BassBoost bassBoost;
+    android.hardware.audio.effect.Downmix downmix;
+    android.hardware.audio.effect.DynamicsProcessing dynamicsProcessing;
     android.hardware.audio.effect.Equalizer equalizer;
+    android.hardware.audio.effect.LoudnessEnhancer loudnessEnhancer;
+    android.hardware.audio.effect.HapticGenerator hapticGenerator;
+    android.hardware.audio.effect.Reverb reverb;
+    android.hardware.audio.effect.Virtualizer virtualizer;
+    android.hardware.audio.effect.Visualizer visualizer;
+    android.hardware.audio.effect.Volume volume;
   }
 }
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Processing.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Processing.aidl
new file mode 100644
index 0000000..a779ae4
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Processing.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.audio.effect;
+@VintfStability
+parcelable Processing {
+  android.hardware.audio.effect.Processing.Type type;
+  android.hardware.audio.effect.Descriptor.Identity[] ids;
+  @VintfStability
+  union Type {
+    android.media.audio.common.AudioStreamType streamType = android.media.audio.common.AudioStreamType.INVALID;
+    android.media.audio.common.AudioSource source;
+  }
+}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Reverb.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Reverb.aidl
new file mode 100644
index 0000000..8ad4848
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Reverb.aidl
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.audio.effect;
+@VintfStability
+union Reverb {
+  android.hardware.audio.effect.VendorExtension vendor;
+  int roomLevelMb;
+  int roomHfLevelMb;
+  int decayTimeMs;
+  int decayHfRatioPm;
+  int levelMb;
+  int delayMs;
+  int diffusionPm;
+  int densityPm;
+  boolean bypass;
+  @VintfStability
+  union Id {
+    int vendorExtensionTag;
+    android.hardware.audio.effect.Reverb.Tag commonTag;
+  }
+  @VintfStability
+  parcelable Capability {
+    android.hardware.audio.effect.VendorExtension extension;
+    int maxDecayTimeMs;
+  }
+}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/VendorExtension.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/VendorExtension.aidl
new file mode 100644
index 0000000..b806334
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/VendorExtension.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.audio.effect;
+@VintfStability
+parcelable VendorExtension {
+  ParcelableHolder extension;
+}
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
new file mode 100644
index 0000000..d4fb9e0
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Virtualizer.aidl
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.audio.effect;
+@VintfStability
+union Virtualizer {
+  android.hardware.audio.effect.VendorExtension vendor;
+  int strengthPm;
+  @VintfStability
+  union Id {
+    int vendorExtensionTag;
+    android.hardware.audio.effect.Virtualizer.Tag commonTag;
+  }
+  @VintfStability
+  parcelable Capability {
+    android.hardware.audio.effect.VendorExtension extension;
+    boolean strengthSupported;
+  }
+}
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
new file mode 100644
index 0000000..9ee19f0
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Visualizer.aidl
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.audio.effect;
+@VintfStability
+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;
+  int captureSizeBytes;
+  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.CaptureSizeRange captureSizeRange;
+  }
+  @VintfStability
+  parcelable CaptureSizeRange {
+    int minBytes;
+    int maxBytes;
+  }
+  @VintfStability
+  enum ScalingMode {
+    NORMALIZED = 0,
+    AS_PLAYED = 1,
+  }
+  @VintfStability
+  enum MeasurementMode {
+    NONE = 0,
+    PEAK_RMS = 1,
+  }
+  @VintfStability
+  union GetOnlyParameters {
+    android.hardware.audio.effect.Visualizer.GetOnlyParameters.Measurement measurement;
+    byte[] captureBytes;
+    @VintfStability
+    parcelable Measurement {
+      int rms;
+      int peak;
+    }
+  }
+  @VintfStability
+  union SetOnlyParameters {
+    int latencyMs;
+  }
+}
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
new file mode 100644
index 0000000..8c836b0
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Volume.aidl
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.audio.effect;
+@VintfStability
+union Volume {
+  android.hardware.audio.effect.VendorExtension vendor;
+  int levelDb;
+  boolean mute;
+  @VintfStability
+  union Id {
+    int vendorExtensionTag;
+    android.hardware.audio.effect.Volume.Tag commonTag;
+  }
+  @VintfStability
+  parcelable Capability {
+    android.hardware.audio.effect.VendorExtension extension;
+    int maxLevel;
+  }
+}
diff --git a/audio/aidl/android/hardware/audio/core/IModule.aidl b/audio/aidl/android/hardware/audio/core/IModule.aidl
index 735f87f..0959840 100644
--- a/audio/aidl/android/hardware/audio/core/IModule.aidl
+++ b/audio/aidl/android/hardware/audio/core/IModule.aidl
@@ -263,6 +263,9 @@
      * be completing with an error, although data (zero filled) will still be
      * provided.
      *
+     * After the stream has been opened, it remains in the STANDBY state, see
+     * StreamDescriptor for more details.
+     *
      * @return An opened input stream and the associated descriptor.
      * @param args The pack of arguments, see 'OpenInputStreamArguments' parcelable.
      * @throws EX_ILLEGAL_ARGUMENT In the following cases:
@@ -325,6 +328,9 @@
      * StreamDescriptor will be completing with an error, although the data
      * will still be accepted and immediately discarded.
      *
+     * After the stream has been opened, it remains in the STANDBY state, see
+     * StreamDescriptor for more details.
+     *
      * @return An opened output stream and the associated descriptor.
      * @param args The pack of arguments, see 'OpenOutputStreamArguments' parcelable.
      * @throws EX_ILLEGAL_ARGUMENT In the following cases:
diff --git a/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl b/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl
index 2b1ed8c..e5e56fc 100644
--- a/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl
+++ b/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl
@@ -33,6 +33,72 @@
  * internal components of the stream while serving commands invoked via the
  * stream's AIDL interface and commands invoked via the command queue of the
  * descriptor.
+ *
+ * There is a state machine defined for the stream, which executes on the
+ * thread handling the commands from the queue. The states are defined based
+ * on the model of idealized producer and consumer connected via a ring buffer.
+ * For input streams, the "producer" is hardware, the "consumer" is software,
+ * for outputs streams it's the opposite. When the producer is active, but
+ * the buffer is full, the following actions are possible:
+ *  - if the consumer is active, the producer blocks until there is space,
+ *    this behavior is only possible for software producers;
+ *  - if the consumer is passive:
+ *    - the producer can preserve the buffer contents—a s/w producer can
+ *      keep the data on their side, while a h/w producer can only drop captured
+ *      data in this case;
+ *    - or the producer overwrites old data in the buffer.
+ * Similarly, when an active consumer faces an empty buffer, it can:
+ *  - block until there is data (producer must be active), only possible
+ *    for software consumers;
+ *  - walk away with no data; when the consumer is hardware, it must emit
+ *    silence in this case.
+ *
+ * The model is defined below, note the asymmetry regarding the 'IDLE' state
+ * between input and output streams:
+ *
+ *  Producer | Buffer state | Consumer | Applies | State
+ *  active?  |              | active?  | to      |
+ * ==========|==============|==========|=========|==============================
+ *  No       | Empty        | No       | Both    | STANDBY
+ * ----------|--------------|----------|---------|-----------------------------
+ *  Yes      | Filling up   | No       | Input   | IDLE, overwrite behavior
+ * ----------|--------------|----------|---------|-----------------------------
+ *  No       | Empty        | Yes†     | Output  | IDLE, h/w emits silence
+ * ----------|--------------|----------|---------|-----------------------------
+ *  Yes      | Not empty    | Yes      | Both    | ACTIVE, s/w x-runs counted
+ * ----------|--------------|----------|---------|-----------------------------
+ *  Yes      | Filling up   | No       | Input   | PAUSED, drop behavior
+ * ----------|--------------|----------|---------|-----------------------------
+ *  Yes      | Filling up   | No†      | Output  | PAUSED, s/w stops writing once
+ *           |              |          |         | the buffer is filled up;
+ *           |              |          |         | h/w emits silence.
+ * ----------|--------------|----------|---------|-----------------------------
+ *  No       | Not empty    | Yes      | Both    | DRAINING
+ * ----------|--------------|----------|---------|-----------------------------
+ *  No       | Not empty    | No†      | Output  | DRAIN_PAUSED,
+ *           |              |          |         | h/w emits silence.
+ *
+ * † - note that for output, "buffer empty, h/w consuming" has the same outcome
+ *     as "buffer not empty, h/w not consuming", but logically these conditions
+ *     are different.
+ *
+ * State machines of both input and output streams start from the 'STANDBY'
+ * state.  Transitions between states happen naturally with changes in the
+ * states of the model elements. For simplicity, we restrict the change to one
+ * element only, for example, in the 'STANDBY' state, either the producer or the
+ * consumer can become active, but not both at the same time. States 'STANDBY',
+ * 'IDLE', 'READY', and '*PAUSED' are "stable"—they require an external event,
+ * whereas a change from the 'DRAINING' state can happen with time as the buffer
+ * gets empty.
+ *
+ * The state machine for input streams is defined in the `stream-in-sm.gv` file,
+ * for output streams—in the `stream-out-sm.gv` file. State machines define how
+ * commands (from the enum 'CommandCode') trigger state changes. The full list
+ * of states and commands is defined by constants of the 'State' enum. Note that
+ * the 'CLOSED' state does not have a constant in the interface because the
+ * client can never observe a stream with a functioning command queue in this
+ * state. The 'ERROR' state is a special state which the state machine enters
+ * when an unrecoverable hardware error is detected by the HAL module.
  */
 @JavaDerive(equals=true, toString=true)
 @VintfStability
@@ -55,12 +121,110 @@
         long timeNs;
     }
 
-    /**
-     * The command used for audio I/O, see 'AudioBuffer'. For MMap No IRQ mode
-     * this command only provides updated positions and latency because actual
-     * audio I/O is done via the 'AudioBuffer.mmap' shared buffer.
-     */
-    const int COMMAND_BURST = 1;
+    @VintfStability
+    @Backing(type="int")
+    enum State {
+        /**
+         * 'STANDBY' is the initial state of the stream, entered after
+         * opening. Since both the producer and the consumer are inactive in
+         * this state, it allows the HAL module to put associated hardware into
+         * "standby" mode to save power.
+         */
+        STANDBY = 1,
+        /**
+         * In the 'IDLE' state the audio hardware is active. For input streams,
+         * the hardware is filling buffer with captured data, overwriting old
+         * contents on buffer wraparounds. For output streams, the buffer is
+         * still empty, and the hardware is outputting zeroes. The HAL module
+         * must not account for any under- or overruns as the client is not
+         * expected to perform audio I/O.
+         */
+        IDLE = 2,
+        /**
+         * The active state of the stream in which it handles audio I/O. The HAL
+         * module can assume that the audio I/O will be periodic, thus inability
+         * of the client to provide or consume audio data on time must be
+         * considered as an under- or overrun and indicated via the 'xrunFrames'
+         * field of the reply.
+         */
+        ACTIVE = 3,
+        /**
+         * In the 'PAUSED' state the consumer is inactive. For input streams,
+         * the hardware stops updating the buffer as soon as it fills up (this
+         * is the difference from the 'IDLE' state). For output streams,
+         * "inactivity" of hardware means that it does not consume audio data,
+         * but rather emits silence.
+         */
+        PAUSED = 4,
+        /**
+         * In the 'DRAINING' state the producer is inactive, the consumer is
+         * finishing up on the buffer contents, emptying it up. As soon as it
+         * gets empty, the stream transfers itself into the next state.
+         */
+        DRAINING = 5,
+        /**
+         * Used for output streams only, pauses draining. This state is similar
+         * to the 'PAUSED' state, except that the client is not adding any
+         * new data. If it emits a 'BURST' command, this brings the stream
+         * into the regular 'PAUSED' state.
+         */
+        DRAIN_PAUSED = 6,
+        /**
+         * The ERROR state is entered when the stream has encountered an
+         * irrecoverable error from the lower layer. After entering it, the
+         * stream can only be closed.
+         */
+        ERROR = 100,
+    }
+
+    @VintfStability
+    @Backing(type="int")
+    enum CommandCode {
+        /**
+         * See the state machines on the applicability of this command to
+         * different states. The 'fmqByteCount' field must always be set to 0.
+         */
+        START = 1,
+        /**
+         * The BURST command used for audio I/O, see 'AudioBuffer'. Differences
+         * for the MMap No IRQ mode:
+         *
+         *  - this command only provides updated positions and latency because
+         *    actual audio I/O is done via the 'AudioBuffer.mmap' shared buffer.
+         *    The client does not synchronize reads and writes into the buffer
+         *    with sending of this command.
+         *
+         *  - the 'fmqByteCount' must always be set to 0.
+         */
+        BURST = 2,
+        /**
+         * See the state machines on the applicability of this command to
+         * different states. The 'fmqByteCount' field must always be set to 0.
+         */
+        DRAIN = 3,
+        /**
+         * See the state machines on the applicability of this command to
+         * different states. The 'fmqByteCount' field must always be set to 0.
+         *
+         * Note that it's left on the discretion of the HAL implementation to
+         * assess all the necessary conditions that could prevent hardware from
+         * being suspended. Even if it can not be suspended, the state machine
+         * must still enter the 'STANDBY' state for consistency. Since the
+         * buffer must remain empty in this state, even if capturing hardware is
+         * still active, captured data must be discarded.
+         */
+        STANDBY = 4,
+        /**
+         * See the state machines on the applicability of this command to
+         * different states. The 'fmqByteCount' field must always be set to 0.
+         */
+        PAUSE = 5,
+        /**
+         * See the state machines on the applicability of this command to
+         * different states. The 'fmqByteCount' field must always be set to 0.
+         */
+        FLUSH = 6,
+    }
 
     /**
      * Used for sending commands to the HAL module. The client writes into
@@ -71,12 +235,16 @@
     @FixedSize
     parcelable Command {
         /**
-         * One of COMMAND_* codes.
+         * The code of the command.
          */
-        int code;
+        CommandCode code = CommandCode.START;
         /**
+         * This field is only used for the BURST command. For all other commands
+         * it must be set to 0. The following description applies to the use
+         * of this field for the BURST command.
+         *
          * For output streams: the amount of bytes that the client requests the
-         *   HAL module to read from the 'audio.fmq' queue.
+         *   HAL module to use out of the data contained in the 'audio.fmq' queue.
          * For input streams: the amount of bytes requested by the client to
          *   read from the hardware into the 'audio.fmq' queue.
          *
@@ -96,6 +264,12 @@
     MQDescriptor<Command, SynchronizedReadWrite> command;
 
     /**
+     * The value used for the 'Reply.latencyMs' field when the effective
+     * latency can not be reported by the HAL module.
+     */
+    const int LATENCY_UNKNOWN = -1;
+
+    /**
      * Used for providing replies to commands. The HAL module writes into
      * the queue, the client reads. The queue can only contain a single reply,
      * corresponding to the last command sent by the client.
@@ -107,17 +281,22 @@
          * One of Binder STATUS_* statuses:
          *  - STATUS_OK: the command has completed successfully;
          *  - STATUS_BAD_VALUE: invalid value in the 'Command' structure;
-         *  - STATUS_INVALID_OPERATION: the mix port is not connected
-         *                              to any producer or consumer, thus
-         *                              positions can not be reported;
+         *  - STATUS_INVALID_OPERATION: the command is not applicable in the
+         *                              current state of the stream, or to this
+         *                              type of the stream;
+         *  - STATUS_NO_INIT: positions can not be reported because the mix port
+         *                    is not connected to any producer or consumer, or
+         *                    because the HAL module does not support positions
+         *                    reporting for this AudioSource (on input streams).
          *  - STATUS_NOT_ENOUGH_DATA: a read or write error has
          *                            occurred for the 'audio.fmq' queue;
-         *
          */
         int status;
         /**
-         * For output streams: the amount of bytes actually consumed by the HAL
-         *   module from the 'audio.fmq' queue.
+         * Used with the BURST command only.
+         *
+         * For output streams: the amount of bytes of data actually consumed
+         *   by the HAL module.
          * For input streams: the amount of bytes actually provided by the HAL
          *   in the 'audio.fmq' queue.
          *
@@ -126,10 +305,18 @@
          */
         int fmqByteCount;
         /**
+         * It is recommended to report the current position for any command.
+         * If the position can not be reported, the 'status' field must be
+         * set to 'NO_INIT'.
+         *
          * For output streams: the moment when the specified stream position
          *   was presented to an external observer (i.e. presentation position).
          * For input streams: the moment when data at the specified stream position
          *   was acquired (i.e. capture position).
+         *
+         * The observable position must never be reset by the HAL module.
+         * The data type of the frame counter is large enough to support
+         * continuous counting for years of operation.
          */
         Position observable;
         /**
@@ -138,9 +325,22 @@
          */
         Position hardware;
         /**
-         * Current latency reported by the hardware.
+         * Current latency reported by the hardware. It is recommended to
+         * report the current latency for any command. If the value of latency
+         * can not be determined, this field must be set to 'LATENCY_UNKNOWN'.
          */
         int latencyMs;
+        /**
+         * Number of frames lost due to an underrun (for input streams),
+         * or not provided on time (for output streams) for the **previous**
+         * transfer operation.
+         */
+        int xrunFrames;
+        /**
+         * The state that the stream was in while the HAL module was sending the
+         * reply.
+         */
+        State state = State.STANDBY;
     }
     MQDescriptor<Reply, SynchronizedReadWrite> reply;
 
@@ -170,42 +370,59 @@
     @VintfStability
     union AudioBuffer {
         /**
-         * The fast message queue used for all modes except MMap No IRQ.  Both
-         * reads and writes into this queue are non-blocking because access to
-         * this queue is synchronized via the 'command' and 'reply' queues as
-         * described below. The queue nevertheless uses 'SynchronizedReadWrite'
-         * because there is only one reader, and the reading position must be
-         * shared.
+         * The fast message queue used for BURST commands in all modes except
+         * MMap No IRQ. Both reads and writes into this queue are non-blocking
+         * because access to this queue is synchronized via the 'command' and
+         * 'reply' queues as described below. The queue nevertheless uses
+         * 'SynchronizedReadWrite' because there is only one reader, and the
+         * reading position must be shared.
+         *
+         * Note that the fast message queue is a transient buffer, only used for
+         * data transfer. Neither of the sides can use it to store any data
+         * outside of the 'BURST' operation. The consumer must always retrieve
+         * all data available in the fast message queue, even if it can not use
+         * it. The producer must re-send any unconsumed data on the next
+         * transfer operation. This restriction is posed in order to make the
+         * fast message queue fully transparent from the latency perspective.
          *
          * For output streams the following sequence of operations is used:
          *  1. The client writes audio data into the 'audio.fmq' queue.
-         *  2. The client writes the 'BURST' command into the 'command' queue,
+         *  2. The client writes the BURST command into the 'command' queue,
          *     and hangs on waiting on a read from the 'reply' queue.
          *  3. The high priority thread in the HAL module wakes up due to 2.
-         *  4. The HAL module reads the command and audio data.
+         *  4. The HAL module reads the command and audio data. According
+         *     to the statement above, the HAL module must always read
+         *     from the FMQ all the data it contains. The amount of data that
+         *     the HAL module has actually consumed is indicated to the client
+         *     via the 'reply.fmqByteCount' field.
          *  5. The HAL module writes the command status and current positions
          *     into 'reply' queue, and hangs on waiting on a read from
          *     the 'command' queue.
          *  6. The client wakes up due to 5. and reads the reply.
          *
          * For input streams the following sequence of operations is used:
-         *  1. The client writes the 'BURST' command into the 'command' queue,
+         *  1. The client writes the BURST command into the 'command' queue,
          *     and hangs on waiting on a read from the 'reply' queue.
          *  2. The high priority thread in the HAL module wakes up due to 1.
          *  3. The HAL module writes audio data into the 'audio.fmq' queue.
+         *     The value of 'reply.fmqByteCount' must be the equal to the amount
+         *     of data in the queue.
          *  4. The HAL module writes the command status and current positions
          *     into 'reply' queue, and hangs on waiting on a read from
          *     the 'command' queue.
          *  5. The client wakes up due to 4.
-         *  6. The client reads the reply and audio data.
+         *  6. The client reads the reply and audio data. The client must
+         *     always read from the FMQ all the data it contains.
+         *
          */
         MQDescriptor<byte, SynchronizedReadWrite> fmq;
         /**
          * MMap buffers are shared directly with the DSP, which operates
-         * independently from the CPU. Writes and reads into these buffers
-         * are not synchronized with 'command' and 'reply' queues. However,
-         * the client still uses the 'BURST' command for obtaining current
-         * positions from the HAL module.
+         * independently from the CPU. Writes and reads into these buffers are
+         * not synchronized with 'command' and 'reply' queues. However, the
+         * client still uses the same commands for controlling the audio data
+         * exchange and for obtaining current positions and latency from the HAL
+         * module.
          */
         MmapBufferDescriptor mmap;
     }
diff --git a/audio/aidl/android/hardware/audio/core/stream-in-sm.gv b/audio/aidl/android/hardware/audio/core/stream-in-sm.gv
new file mode 100644
index 0000000..889a14b
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/core/stream-in-sm.gv
@@ -0,0 +1,42 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// To render: dot -Tpng stream-in-sm.gv -o stream-in-sm.png
+digraph stream_in_state_machine {
+    node [shape=doublecircle style=filled fillcolor=black width=0.5] I;
+    node [shape=point width=0.5] F;
+    node [shape=oval width=1];
+    node [fillcolor=lightgreen] STANDBY;  // buffer is empty
+    node [fillcolor=tomato] CLOSED;
+    node [fillcolor=tomato] ERROR;
+    node [style=dashed] ANY_STATE;
+    node [fillcolor=lightblue style=filled];
+    I -> STANDBY;
+    STANDBY -> IDLE [label="START"];    // producer -> active
+    IDLE -> STANDBY [label="STANDBY"];  // producer -> passive, buffer is cleared
+    IDLE -> ACTIVE [label="BURST"];     // consumer -> active
+    ACTIVE -> ACTIVE [label="BURST"];
+    ACTIVE -> PAUSED [label="PAUSE"];   // consumer -> passive
+    ACTIVE -> DRAINING [label="DRAIN"]; // producer -> passive
+    PAUSED -> ACTIVE [label="BURST"];   // consumer -> active
+    PAUSED -> STANDBY [label="FLUSH"];  // producer -> passive, buffer is cleared
+    DRAINING -> DRAINING [label="BURST"];
+    DRAINING -> ACTIVE [label="START"];  // producer -> active
+    DRAINING -> STANDBY [label="<empty buffer>"];  // consumer deactivates
+    IDLE -> ERROR [label="<hardware failure>"];
+    ACTIVE -> ERROR [label="<hardware failure>"];
+    PAUSED -> ERROR [label="<hardware failure>"];
+    ANY_STATE -> CLOSED [label="→IStream*.close"];
+    CLOSED -> F;
+}
diff --git a/audio/aidl/android/hardware/audio/core/stream-out-sm.gv b/audio/aidl/android/hardware/audio/core/stream-out-sm.gv
new file mode 100644
index 0000000..56dd5290
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/core/stream-out-sm.gv
@@ -0,0 +1,48 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// To render: dot -Tpng stream-out-sm.gv -o stream-out-sm.png
+digraph stream_out_state_machine {
+    node [shape=doublecircle style=filled fillcolor=black width=0.5] I;
+    node [shape=point width=0.5] F;
+    node [shape=oval width=1];
+    node [fillcolor=lightgreen] STANDBY;  // buffer is empty
+    node [fillcolor=lightgreen] IDLE;     // buffer is empty
+    node [fillcolor=tomato] CLOSED;
+    node [fillcolor=tomato] ERROR;
+    node [style=dashed] ANY_STATE;
+    node [fillcolor=lightblue style=filled];
+    I -> STANDBY;
+    STANDBY -> IDLE [label="START"];           // consumer -> active
+    STANDBY -> PAUSED [label="BURST"];         // producer -> active
+    IDLE -> STANDBY [label="STANDBY"];         // consumer -> passive
+    IDLE -> ACTIVE [label="BURST"];            // producer -> active
+    ACTIVE -> ACTIVE [label="BURST"];
+    ACTIVE -> PAUSED [label="PAUSE"];          // consumer -> passive (not consuming)
+    ACTIVE -> DRAINING [label="DRAIN"];        // producer -> passive
+    PAUSED -> PAUSED [label="BURST"];
+    PAUSED -> ACTIVE [label="START"];          // consumer -> active
+    PAUSED -> IDLE [label="FLUSH"];            // producer -> passive, buffer is cleared
+    DRAINING -> IDLE [label="<empty buffer>"];
+    DRAINING -> ACTIVE [label="BURST"];        // producer -> active
+    DRAINING -> DRAIN_PAUSED [label="PAUSE"];  // consumer -> passive (not consuming)
+    DRAIN_PAUSED -> DRAINING [label="START"];  // consumer -> active
+    DRAIN_PAUSED -> PAUSED [label="BURST"];    // producer -> active
+    DRAIN_PAUSED -> IDLE [label="FLUSH"];      // buffer is cleared
+    IDLE -> ERROR [label="<hardware failure>"];
+    ACTIVE -> ERROR [label="<hardware failure>"];
+    DRAINING -> ERROR [label="<hardware failure>"];
+    ANY_STATE -> CLOSED [label="→IStream*.close"];
+    CLOSED -> F;
+}
diff --git a/audio/aidl/android/hardware/audio/effect/BassBoost.aidl b/audio/aidl/android/hardware/audio/effect/BassBoost.aidl
new file mode 100644
index 0000000..810c188
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/effect/BassBoost.aidl
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.audio.effect;
+
+import android.hardware.audio.effect.VendorExtension;
+
+/**
+ * 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.
+ */
+@VintfStability
+union BassBoost {
+    /**
+     * Effect parameter tag to identify the parameters for getParameter().
+     */
+    @VintfStability
+    union Id {
+        int vendorExtensionTag;
+        BassBoost.Tag commonTag;
+    }
+
+    /**
+     * Vendor BassBoost implementation definition for additional parameters.
+     */
+    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;
+        /**
+         * 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 valid range for strength is [0, 1000].
+     */
+    int strengthPm;
+}
diff --git a/audio/aidl/android/hardware/audio/effect/Capability.aidl b/audio/aidl/android/hardware/audio/effect/Capability.aidl
index e792f86..f741f33 100644
--- a/audio/aidl/android/hardware/audio/effect/Capability.aidl
+++ b/audio/aidl/android/hardware/audio/effect/Capability.aidl
@@ -16,7 +16,17 @@
 
 package android.hardware.audio.effect;
 
+import android.hardware.audio.effect.BassBoost;
+import android.hardware.audio.effect.Downmix;
+import android.hardware.audio.effect.DynamicsProcessing;
 import android.hardware.audio.effect.Equalizer;
+import android.hardware.audio.effect.HapticGenerator;
+import android.hardware.audio.effect.LoudnessEnhancer;
+import android.hardware.audio.effect.Reverb;
+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.
@@ -33,14 +43,19 @@
      * the ParcelableHolder in each effect capability definition. For example:
      * Equalizer.Capability.extension.
      */
-    @VintfStability
-    parcelable VendorEffectCapability {
-        ParcelableHolder extension;
-    }
-    VendorEffectCapability vendor;
+    VendorExtension vendorExtension;
 
     /**
-     * Equalizer capability definition.
+     * Effect capabilities.
      */
+    BassBoost.Capability bassBoost;
+    Downmix.Capability downmix;
+    DynamicsProcessing.Capability dynamicsProcessing;
     Equalizer.Capability equalizer;
+    HapticGenerator.Capability hapticGenerator;
+    LoudnessEnhancer.Capability loudnessEnhancer;
+    Reverb.Capability reverb;
+    Virtualizer.Capability virtualizer;
+    Visualizer.Capability visualizer;
+    Volume.Capability volume;
 }
diff --git a/audio/aidl/android/hardware/audio/effect/CommandId.aidl b/audio/aidl/android/hardware/audio/effect/CommandId.aidl
index 208c163..d940b42 100644
--- a/audio/aidl/android/hardware/audio/effect/CommandId.aidl
+++ b/audio/aidl/android/hardware/audio/effect/CommandId.aidl
@@ -27,7 +27,9 @@
 @VintfStability
 @Backing(type="int")
 enum CommandId {
-    /// MUST be supported by all effects
+    /**
+     * Commands MUST be supported by all effects.
+     */
     /**
      * Start effect engine processing.
      * An effect instance must start processing data and transfer to PROCESSING state if it is in
@@ -53,10 +55,13 @@
      */
     RESET = 2,
 
-    /// MUST be supported by a specific type of effect.
-    // Commands must supported by Equalizer.
+    /**
+     * Commands MUST be supported by a specific type of effect.
+     */
 
-    /// Extension commands for vendor.
+    /**
+     * Extension commands for vendor.
+     */
     VENDOR_COMMAND_0 = 0x100,
     VENDOR_COMMAND_1,
     VENDOR_COMMAND_2,
diff --git a/audio/aidl/android/hardware/audio/effect/Descriptor.aidl b/audio/aidl/android/hardware/audio/effect/Descriptor.aidl
index aca9bbe..47c88dc 100644
--- a/audio/aidl/android/hardware/audio/effect/Descriptor.aidl
+++ b/audio/aidl/android/hardware/audio/effect/Descriptor.aidl
@@ -23,37 +23,70 @@
 /**
  * Descriptor contains all information (capabilities, attributes, etc) for an effect implementation.
  * The client uses this information to decide when and how to apply an effect implementation.
+ *
+ * Each type of effect can have more than one implementation (differentiated by implementation
+ * UUID), the effect proxy act as a combination of two implementations (usually one software and
+ * one offload implementation), so effect processing can be seamlessly switched between
+ * implementations in same proxy depending on the configuration and/or use case. If the optional
+ * proxy UUID is specified in Descriptor.Identity, then client must consider the effect instance as
+ * part of the effect proxy.
  */
 @VintfStability
 parcelable Descriptor {
     /**
      * UUID for effect types, these definitions are in sync with SDK, see @c AudioEffect.java.
      */
-    // UUID for environmental reverberation effect type.
+    /**
+     * UUID for environmental reverberation effect type.
+     */
     const String EFFECT_TYPE_UUID_ENV_REVERB = "c2e5d5f0-94bd-4763-9cac-4e234d06839e";
-    // UUID for preset reverberation effect type.
+    /**
+     * UUID for preset reverberation effect type.
+     */
     const String EFFECT_TYPE_UUID_PRESET_REVERB = "47382d60-ddd8-11db-bf3a-0002a5d5c51b";
-    // UUID for equalizer effect type.
+    /**
+     * UUID for equalizer effect type.
+     */
     const String EFFECT_TYPE_UUID_EQUALIZER = "0bed4300-ddd6-11db-8f34-0002a5d5c51b";
-    // UUID for bass boost effect type.
+    /**
+     * UUID for bass boost effect type.
+     */
     const String EFFECT_TYPE_UUID_BASS_BOOST = "0634f220-ddd4-11db-a0fc-0002a5d5c51b";
-    // UUID for virtualizer effect type.
+    /**
+     * UUID for virtualizer effect type.
+     */
     const String EFFECT_TYPE_UUID_VIRTUALIZER = "37cc2c00-dddd-11db-8577-0002a5d5c51b";
-    // UUID for Automatic Gain Control (AGC) type.
+    /**
+     * UUID for Automatic Gain Control (AGC) type.
+     */
     const String EFFECT_TYPE_UUID_AGC = "0a8abfe0-654c-11e0-ba26-0002a5d5c51b";
-    // UUID for Acoustic Echo Canceler (AEC) type.
+    /**
+     * UUID for Acoustic Echo Canceler (AEC) type.
+     */
     const String EFFECT_TYPE_UUID_AEC = "7b491460-8d4d-11e0-bd61-0002a5d5c51b";
-    // UUID for Noise Suppressor (NS) type.
+    /**
+     * UUID for Noise Suppressor (NS) type.
+     */
     const String EFFECT_TYPE_UUID_NS = "58b4b260-8e06-11e0-aa8e-0002a5d5c51b";
-    // UUID for Loudness Enhancer type.
+    /**
+     * UUID for Loudness Enhancer type.
+     */
     const String EFFECT_TYPE_UUID_LOUDNESS_ENHANCER = "fe3199be-aed0-413f-87bb-11260eb63cf1";
-    // UUID for Dynamics Processing type.
+    /**
+     * UUID for Dynamics Processing type.
+     */
     const String EFFECT_TYPE_UUID_DYNAMICS_PROCESSING = "7261676f-6d75-7369-6364-28e2fd3ac39e";
-    // UUID for Haptic Generator type.
+    /**
+     * UUID for Haptic Generator type.
+     */
     const String EFFECT_TYPE_UUID_HAPTIC_GENERATOR = "1411e6d6-aecd-4021-a1cf-a6aceb0d71e5";
-    // UUID for Spatializer type.
+    /**
+     * UUID for Spatializer type.
+     */
     const String EFFECT_TYPE_UUID_SPATIALIZER = "ccd4cf09-a79d-46c2-9aae-06a1698d6c8f";
-    // UUID for Volume type. The volume effect is used for automated tests only.
+    /**
+     * UUID for Volume type. The volume effect is used for automated tests only.
+     */
     const String EFFECT_TYPE_UUID_VOLUME = "09e8ede0-ddde-11db-b4f6-0002a5d5c51b";
 
     /**
@@ -69,9 +102,16 @@
          * UUID for this particular implementation.
          */
         AudioUuid uuid;
+        /**
+         * Optional proxy UUID. This field must be set to the proxy effect type UUID if the effect
+         * implementation is part of a proxy effect.
+         */
+        @nullable AudioUuid proxy;
     }
 
-    // Common attributes of all effect implementation.
+    /**
+     * Common attributes of all effect implementation.
+     */
     @VintfStability
     parcelable Common {
         /**
@@ -79,9 +119,26 @@
          */
         Identity id;
         /**
-         * Effect engine defined capabilities/requirements flags.
+         * Capability flags defined for the effect implementation.
          */
         Flags flags;
+        /**
+         * CPU load indication expressed in 0.1 MIPS units as estimated on an ARM9E core (ARMv5TE)
+         * with 0 WS.
+         */
+        int cpuLoad;
+        /**
+         * Data memory usage expressed in KB and includes only dynamically allocated memory.
+         */
+        int memoryUsage;
+        /**
+         * Human readable effect name, no intended to display on UI directly.
+         */
+        @utf8InCpp String name;
+        /**
+         * Human readable effect implementor name, no intended to display on UI directly.
+         */
+        @utf8InCpp String implementor;
     }
     Common common;
 
diff --git a/audio/aidl/android/hardware/audio/effect/Downmix.aidl b/audio/aidl/android/hardware/audio/effect/Downmix.aidl
new file mode 100644
index 0000000..ee57baf
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/effect/Downmix.aidl
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.audio.effect;
+
+import android.hardware.audio.effect.VendorExtension;
+
+/**
+ * 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.
+ */
+@VintfStability
+union Downmix {
+    /**
+     * Effect parameter tag to identify the parameters for getParameter().
+     */
+    @VintfStability
+    union Id {
+        int vendorExtensionTag;
+        Downmix.Tag commonTag;
+    }
+
+    /**
+     * Vendor Downmix implementation definition for additional parameters.
+     */
+    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 {
+        /**
+         * Throw away the extra channels.
+         */
+        STRIP,
+        /**
+         * Mix the extra channels with FL/FR.
+         */
+        FOLD,
+    }
+
+    /**
+     * Type of downmix.
+     */
+    Type type;
+}
diff --git a/audio/aidl/android/hardware/audio/effect/DynamicsProcessing.aidl b/audio/aidl/android/hardware/audio/effect/DynamicsProcessing.aidl
new file mode 100644
index 0000000..ee5dcad
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/effect/DynamicsProcessing.aidl
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.audio.effect;
+
+import android.hardware.audio.effect.VendorExtension;
+
+/**
+ * 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.
+ */
+@VintfStability
+union DynamicsProcessing {
+    /**
+     * Effect parameter tag to identify the parameters for getParameter().
+     */
+    @VintfStability
+    union Id {
+        int vendorExtensionTag;
+        DynamicsProcessing.Tag commonTag;
+    }
+
+    /**
+     * Vendor DynamicsProcessing implementation definition for additional parameters.
+     */
+    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;
+    }
+
+    /**
+     * Resolution preference definition.
+     */
+    enum ResolutionPreference {
+        /**
+         * Favors frequency domain based implementation.
+         */
+        FAVOR_FREQUENCY_RESOLUTION,
+        /**
+         * Favors tme domain based implementation.
+         */
+        FAVOR_TIME_RESOLUTION,
+    }
+
+    /**
+     * Band enablement configuration.
+     */
+    @VintfStability
+    parcelable BandEnablement {
+        /**
+         * True if multi-band stage is in use.
+         */
+        boolean inUse;
+        /**
+         * Number of bands configured for this stage.
+         */
+        int bandCount;
+    }
+
+    /**
+     * Effect engine configuration. Set the enablement of all stages.
+     */
+    @VintfStability
+    parcelable EngineArchitecture {
+        /**
+         * Resolution preference.
+         */
+        ResolutionPreference resolutionPreference = ResolutionPreference.FAVOR_FREQUENCY_RESOLUTION;
+        /**
+         * Preferred frame duration in milliseconds (ms).
+         */
+        float preferredFrameDurationMs;
+        /**
+         * PreEq stage (Multi-band Equalizer) configuration.
+         */
+        BandEnablement preEqBand;
+        /**
+         * PostEq stage (Multi-band Equalizer) configuration.
+         */
+        BandEnablement postEqBand;
+        /**
+         * MBC stage (Multi-band Compressor) configuration.
+         */
+        BandEnablement mbcBand;
+        /**
+         * True if Limiter stage is in use.
+         */
+        boolean limiterInUse;
+    }
+
+    /**
+     * Band enablement configuration for a specific channel.
+     */
+    @VintfStability
+    parcelable BandChannelConfig {
+        /**
+         * Channel index.
+         */
+        int channel;
+        /**
+         * Channel index.
+         */
+        BandEnablement enablement;
+    }
+
+    /**
+     * Equalizer band configuration for a specific channel and band.
+     */
+    @VintfStability
+    parcelable EqBandConfig {
+        /**
+         * Channel index.
+         */
+        int channel;
+        /**
+         * Band index, must in the range of [0, bandCount-1].
+         */
+        int band;
+        /**
+         * True if EQ stage is enabled.
+         */
+        boolean enable;
+        /**
+         * Topmost frequency number (in Hz) this band will process.
+         */
+        float cutoffFrequency;
+        /**
+         * Gain factor in decibels (dB).
+         */
+        float gain;
+    }
+
+    /**
+     * MBC configuration for a specific channel and band.
+     */
+    @VintfStability
+    parcelable MbcBandConfig {
+        /**
+         * Channel index.
+         */
+        int channel;
+        /**
+         * Band index, must in the range of [0, bandCount-1].
+         */
+        int band;
+        /**
+         * True if MBC stage is enabled.
+         */
+        boolean enable;
+        /**
+         * Topmost frequency number (in Hz) this band will process.
+         */
+        float cutoffFrequencyHz;
+        /**
+         * Gain factor in decibels (dB).
+         */
+        float gainDb;
+        /**
+         * Attack Time for compressor in milliseconds (ms).
+         */
+        float attackTimeMs;
+        /**
+         * Release Time for compressor in milliseconds (ms).
+         */
+        float releaseTimeMs;
+        /**
+         * Compressor ratio (N:1) (input:output).
+         */
+        float ratio;
+        /**
+         * Compressor threshold measured in decibels (dB) from 0 dB Full Scale (dBFS).
+         */
+        float thresholdDb;
+        /**
+         * Width in decibels (dB) around compressor threshold point.
+         */
+        float kneeWidthDb;
+        /**
+         * Noise gate threshold in decibels (dB) from 0 dB Full Scale (dBFS).
+         */
+        float noiseGateThresholdDb;
+        /**
+         * Expander ratio (1:N) (input:output) for signals below the Noise Gate Threshold.
+         */
+        float expanderRatio;
+        /**
+         * Gain applied to the signal BEFORE the compression in dB.
+         */
+        float preGainDb;
+        /**
+         * Gain applied to the signal AFTER compression in dB.
+         */
+        float postGainDb;
+    }
+
+    /**
+     * Limiter configuration for a specific channel.
+     */
+    @VintfStability
+    parcelable LimiterConfig {
+        /**
+         * Channel index.
+         */
+        int channel;
+        /**
+         * True if Limiter stage is enabled.
+         */
+        boolean enable;
+        /**
+         * True if Limiter stage is in use.
+         */
+        boolean inUse;
+        /**
+         * Index of group assigned to this Limiter. Only limiters that share the same linkGroup
+         * index will react together.
+         */
+        int linkGroup;
+        /**
+         * Attack Time for compressor in milliseconds (ms).
+         */
+        float attackTimeMs;
+        /**
+         * Release Time for compressor in milliseconds (ms).
+         */
+        float releaseTimeMs;
+        /**
+         * Compressor ratio (N:1) (input:output).
+         */
+        float ratio;
+        /**
+         * Compressor threshold measured in decibels (dB) from 0 dB Full Scale (dBFS).
+         */
+        float thresholdDb;
+        /**
+         * Gain applied to the signal AFTER compression in dB.
+         */
+        float postGainDb;
+    }
+
+    /**
+     * Effect engine architecture.
+     */
+    EngineArchitecture engineArchitecture;
+    /**
+     * PreEq stage per channel configuration.
+     */
+    BandChannelConfig preEq;
+    /**
+     * PostEq stage per channel configuration.
+     */
+    BandChannelConfig postEq;
+    /**
+     * PreEq stage per band configuration.
+     */
+    EqBandConfig preEqBand;
+    /**
+     * PostEq stage per band configuration.
+     */
+    EqBandConfig postEqBand;
+    /**
+     * MBC stage per channel configuration.
+     */
+    BandChannelConfig mbc;
+    /**
+     * PostEq stage per band configuration.
+     */
+    MbcBandConfig mbcBand;
+    /**
+     * Limiter stage configuration.
+     */
+    LimiterConfig limiter;
+    /**
+     * Input gain factor in decibels (dB). 0 dB means no change in level.
+     */
+    float inputGainDb;
+}
diff --git a/audio/aidl/android/hardware/audio/effect/Equalizer.aidl b/audio/aidl/android/hardware/audio/effect/Equalizer.aidl
index 309874d..79a1c4f 100644
--- a/audio/aidl/android/hardware/audio/effect/Equalizer.aidl
+++ b/audio/aidl/android/hardware/audio/effect/Equalizer.aidl
@@ -16,18 +16,32 @@
 
 package android.hardware.audio.effect;
 
-import android.media.audio.common.AudioProfile;
+import android.hardware.audio.effect.VendorExtension;
 
 /**
  * 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.
  */
 @VintfStability
 union Equalizer {
     /**
-     * Defines Equalizer implementation capabilities, it MUST be supported by all equalizer
-     * implementations.
-     *
-     * Equalizer.Capability definition is used by android.hardware.audio.effect.Capability.
+     * Effect parameter tag to identify the parameters for getParameter().
+     */
+    @VintfStability
+    union Id {
+        int vendorExtensionTag;
+        Equalizer.Tag commonTag;
+    }
+
+    /**
+     * Vendor Equalizer implementation definition for additional parameters.
+     */
+    VendorExtension vendorExtension;
+
+    /**
+     * Capability MUST be supported by Equalizer implementation.
      */
     @VintfStability
     parcelable Capability {
@@ -36,12 +50,55 @@
          * definition not enough.
          */
         ParcelableHolder extension;
+
+        /**
+         * Bands frequency ranges supported.
+         */
+        BandFrequency[] bandFrequencies;
+
+        /**
+         * Presets name and index.
+         */
+        Preset[] presets;
     }
 
-    // Vendor Equalizer implementation definition for additional parameters.
+    /**
+     * Level setting for each band in millibels.
+     */
     @VintfStability
-    parcelable VendorExtension {
-        ParcelableHolder extension;
+    parcelable BandLevel {
+        int index;
+        int levelMb;
     }
-    VendorExtension vendor;
+
+    /**
+     * Supported minimal and maximal frequency for each band in millihertz.
+     */
+    @VintfStability
+    parcelable BandFrequency {
+        int index;
+        int minMh;
+        int maxMh;
+    }
+
+    /**
+     * Factory presets supported.
+     */
+    @VintfStability
+    parcelable Preset {
+        int index;
+        /**
+         * Preset name, used to identify presets but no intended to display on UI directly.
+         */
+        @utf8InCpp String name;
+    }
+
+    /**
+     * Level for each band.
+     */
+    BandLevel[] bandLevels;
+    /**
+     * Index of current preset.
+     */
+    int preset;
 }
diff --git a/audio/aidl/android/hardware/audio/effect/Flags.aidl b/audio/aidl/android/hardware/audio/effect/Flags.aidl
index 8add975..f449c2d 100644
--- a/audio/aidl/android/hardware/audio/effect/Flags.aidl
+++ b/audio/aidl/android/hardware/audio/effect/Flags.aidl
@@ -17,10 +17,131 @@
 package android.hardware.audio.effect;
 
 /**
- * The common part of available capability/configuration for effects. For effect type specific
- * capability, see @c android.hardware.audio.effect.Capability.
+ * Some common capability for an effect instance.
  */
 @VintfStability
 parcelable Flags {
-    // TODO: add Effect engine defined capabilities/requirements flags.
+    /**
+     * Type of connection.
+     */
+    @VintfStability
+    @Backing(type="byte")
+    enum Type {
+        /**
+         * After track process.
+         */
+        INSERT = 0,
+        /**
+         * Connect to track auxiliary output and use send level.
+         */
+        AUXILIARY = 1,
+        /**
+         * Rreplaces track process function; must implement SRC, volume and mono to stereo.
+         */
+        REPLACE = 2,
+        /**
+         * Applied below audio HAL on in.
+         */
+        PRE_PROC = 3,
+        /**
+         * Applied below audio HAL on out.
+         */
+        POST_PROC = 4,
+    }
+    Type type = Type.INSERT;
+
+    /**
+     * Insertion preference.
+     */
+    @VintfStability
+    @Backing(type="byte")
+    enum Insert {
+        ANY = 0,
+        /**
+         * First of the chain.
+         */
+        FIRST = 1,
+        /**
+         * Last of the chain.
+         */
+        LAST = 2,
+        /**
+         * Exclusive (only effect in the insert chain.
+         */
+        EXCLUSIVE = 3,
+    }
+    Insert insert = Insert.ANY;
+
+    @VintfStability
+    @Backing(type="byte")
+    enum Volume {
+        NONE = 0,
+        /**
+         * Implements volume control.
+         */
+        CTRL = 1,
+        /**
+         * Requires volume indication.
+         */
+        IND = 2,
+        /**
+         * Monitors requested volume.
+         */
+        MONITOR = 3,
+    }
+    Volume volume = Volume.NONE;
+
+    @VintfStability
+    @Backing(type="byte")
+    enum HardwareAccelerator {
+        /**
+         * No hardware acceleration
+         */
+        NONE = 0,
+        /**
+         * Non tunneled hw acceleration: effect reads the samples, send them to HW accelerated
+         * effect processor, reads back the processed samples and returns them to the output buffer.
+         */
+        SIMPLE = 1,
+        /**
+         * The effect interface is only used to control the effect engine. This mode is relevant for
+         * global effects actually applied by the audio hardware on the output stream.
+         */
+        TUNNEL = 2,
+    }
+    HardwareAccelerator hwAcceleratorMode = HardwareAccelerator.NONE;
+
+    /**
+     * Effect instance set this flag to true if it requires update on if the playback thread the
+     * effect attached to is offloaded or not. In this case the framework must call
+     * IEffect.setParameter(Parameter.offload) to notify effect instance when playback thread
+     * offload changes.
+     */
+    boolean offloadIndication;
+
+    /**
+     * Effect instance set this flag to true if it requires device change update. In this case the
+     * framework must call IEffect.setParameter(Parameter.device) to notify effect instance when the
+     * device changes.
+     */
+    boolean deviceIndication;
+
+    /**
+     * Effect instance set this flag to true if it requires audio mode change update. In this case
+     * the framework must call IEffect.setParameter(Parameter.mode) to notify effect instance when
+     * the audio mode changes.
+     */
+    boolean audioModeIndication;
+
+    /**
+     * Effect instance set this flag to true if it requires audio source change update. In this case
+     * the framework must call IEffect.setParameter(Parameter.source) to notify effect instance when
+     * the audio source changes.
+     */
+    boolean audioSourceIndication;
+
+    /**
+     * Set to true if no processing done for this effect instance.
+     */
+    boolean noProcessing;
 }
diff --git a/audio/aidl/android/hardware/audio/effect/HapticGenerator.aidl b/audio/aidl/android/hardware/audio/effect/HapticGenerator.aidl
new file mode 100644
index 0000000..944155f
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/effect/HapticGenerator.aidl
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.audio.effect;
+
+import android.hardware.audio.effect.VendorExtension;
+
+/**
+ * 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.
+ */
+@VintfStability
+union HapticGenerator {
+    /**
+     * Effect parameter tag to identify the parameters for getParameter().
+     */
+    @VintfStability
+    union Id {
+        int vendorExtensionTag;
+        HapticGenerator.Tag commonTag;
+    }
+
+    /**
+     * Vendor HapticGenerator implementation definition for additional parameters.
+     */
+    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 {
+        MUTE = -100,
+        VERY_LOW = -2,
+        LOW = -1,
+        NONE = 0,
+        HIGH = 1,
+        VERY_HIGH = 2,
+    }
+
+    @VintfStability
+    parcelable HapticScale {
+        /**
+         * Audio track ID.
+         */
+        int id;
+        /**
+         * Haptic intensity.
+         */
+        VibratorScale scale = VibratorScale.MUTE;
+    }
+
+    /**
+     * Vibrator information including resonant frequency, Q factor.
+     */
+    @VintfStability
+    parcelable VibratorInformation {
+        /**
+         * Resonant frequency in Hz.
+         */
+        float resonantFrequencyHz;
+        float qFactor;
+        float maxAmplitude;
+    }
+
+    HapticScale hapticScale;
+    VibratorInformation vibratorInfo;
+}
diff --git a/audio/aidl/android/hardware/audio/effect/IEffect.aidl b/audio/aidl/android/hardware/audio/effect/IEffect.aidl
index 9ab8b02..3b957d7 100644
--- a/audio/aidl/android/hardware/audio/effect/IEffect.aidl
+++ b/audio/aidl/android/hardware/audio/effect/IEffect.aidl
@@ -44,24 +44,34 @@
          */
         int status;
         /**
-         * The amount of bytes consumed by the effect instance.
+         * The amount of audio data samples in the floating point format consumed by the effect
+         * instance.
          */
-        int fmqByteConsumed;
+        int fmqConsumed;
         /**
-         * The amount of bytes produced by the effect instance.
+         * The amount of audio data samples in the floating point format produced by the effect
+         * instance.
          */
-        int fmqByteProduced;
+        int fmqProduced;
     }
 
-    // Return data structure of IEffect.open() interface.
+    /**
+     * Return data structure of IEffect.open() interface.
+     */
     @VintfStability
     parcelable OpenEffectReturn {
-        // Message queue for effect processing status.
+        /**
+         * Message queue for effect processing status.
+         */
         MQDescriptor<Status, SynchronizedReadWrite> statusMQ;
-        // Message queue for input data buffer.
-        MQDescriptor<byte, SynchronizedReadWrite> inputDataMQ;
-        // Message queue for output data buffer.
-        MQDescriptor<byte, SynchronizedReadWrite> outputDataMQ;
+        /**
+         * Message queue for input data buffer.
+         */
+        MQDescriptor<float, SynchronizedReadWrite> inputDataMQ;
+        /**
+         * Message queue for output data buffer.
+         */
+        MQDescriptor<float, SynchronizedReadWrite> outputDataMQ;
     }
 
     /**
@@ -71,13 +81,15 @@
      * the effect instance must be able to handle all IEffect interface calls.
      *
      * @param common Parameters which MUST pass from client at open time.
+     * @param specific Effect specific parameters which can optional pass from client at open time.
      *
      * @throws EX_ILLEGAL_ARGUMENT if the effect instance receive unsupported command.
      * @throws a EX_UNSUPPORTED_OPERATION if device capability/resource is not enough or system
      *         failure happens.
      * @note Open an already-opened effect instance should do nothing and should not throw an error.
      */
-    OpenEffectReturn open(in Parameter.Common common, in Parameter.Specific specific);
+    OpenEffectReturn open(
+            in Parameter.Common common, in @nullable Parameter.Specific specific);
 
     /**
      * Called by the client to close the effect instance, processing thread should be destroyed and
diff --git a/audio/aidl/android/hardware/audio/effect/IFactory.aidl b/audio/aidl/android/hardware/audio/effect/IFactory.aidl
index 4873d3a..5943359 100644
--- a/audio/aidl/android/hardware/audio/effect/IFactory.aidl
+++ b/audio/aidl/android/hardware/audio/effect/IFactory.aidl
@@ -18,6 +18,7 @@
 
 import android.hardware.audio.effect.Descriptor;
 import android.hardware.audio.effect.IEffect;
+import android.hardware.audio.effect.Processing;
 import android.media.audio.common.AudioUuid;
 
 /**
@@ -38,11 +39,24 @@
      *        null, used as a filter for effect type UUIDs.
      * @param implementation Indicates the particular implementation of the effect in that type.
      *        This is an optional parameter, pass in null if this parameter is not necessary; if
-     *        non null, used as a filter for effect type UUIDs.
+     *        non null, used as a filter for effect implementation UUIDs.
+     * @param proxy Indicates the proxy UUID filter to query.
+     *        This is an optional parameter, pass in null if this parameter is not necessary; if
+     *        non null, used as a filter for effect proxy UUIDs.
      * @return List of effect identities supported and filtered by type/implementation UUID.
      */
-    Descriptor.Identity[] queryEffects(
-            in @nullable AudioUuid type, in @nullable AudioUuid implementation);
+    Descriptor.Identity[] queryEffects(in @nullable AudioUuid type,
+            in @nullable AudioUuid implementation, in @nullable AudioUuid proxy);
+
+    /**
+     * Return a list of defined processings, with the optional filter by Processing type.
+     * An effect can exist more than once in the returned list, which means this effect must be used
+     * in more than one processing type.
+     *
+     * @param type Type of processing to query, can be AudioStreamType, AudioSource, or null.
+     * @return list of processing defined with the optional filter by Processing.Type.
+     */
+    Processing[] queryProcessing(in @nullable Processing.Type type);
 
     /**
      * Called by the audio framework to create the effect (identified by the implementation UUID
diff --git a/audio/aidl/android/hardware/audio/effect/LoudnessEnhancer.aidl b/audio/aidl/android/hardware/audio/effect/LoudnessEnhancer.aidl
new file mode 100644
index 0000000..0441f10
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/effect/LoudnessEnhancer.aidl
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.audio.effect;
+
+import android.hardware.audio.effect.VendorExtension;
+
+/**
+ * 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.
+ */
+@VintfStability
+union LoudnessEnhancer {
+    /**
+     * Effect parameter tag to identify the parameters for getParameter().
+     */
+    @VintfStability
+    union Id {
+        int vendorExtensionTag;
+        LoudnessEnhancer.Tag commonTag;
+    }
+
+    /**
+     * Vendor LoudnessEnhancer implementation definition for additional parameters.
+     */
+    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.
+     */
+    int gainMb;
+}
diff --git a/audio/aidl/android/hardware/audio/effect/Parameter.aidl b/audio/aidl/android/hardware/audio/effect/Parameter.aidl
index 2951660..e7d3d5e 100644
--- a/audio/aidl/android/hardware/audio/effect/Parameter.aidl
+++ b/audio/aidl/android/hardware/audio/effect/Parameter.aidl
@@ -16,18 +16,30 @@
 
 package android.hardware.audio.effect;
 
+import android.hardware.audio.effect.BassBoost;
+import android.hardware.audio.effect.Downmix;
+import android.hardware.audio.effect.DynamicsProcessing;
 import android.hardware.audio.effect.Equalizer;
+import android.hardware.audio.effect.HapticGenerator;
+import android.hardware.audio.effect.LoudnessEnhancer;
+import android.hardware.audio.effect.Reverb;
+import android.hardware.audio.effect.VendorExtension;
+import android.hardware.audio.effect.Virtualizer;
+import android.hardware.audio.effect.Visualizer;
+import android.hardware.audio.effect.Volume;
 import android.media.audio.common.AudioConfig;
 import android.media.audio.common.AudioDeviceDescription;
+import android.media.audio.common.AudioMode;
+import android.media.audio.common.AudioSource;
+
 /**
  * Defines all parameters supported by the effect instance.
  *
  * There are three groups of parameters:
  * 1. Common parameters are essential parameters, MUST pass to effects at open() interface.
  * 2. Parameters defined for a specific effect type.
- * 3. Extension parameters for vendor.
+ * 3. Extension parameters ParcelableHolder can be used for vendor effect definition.
  *
- * For all supported parameter, implementation MUST support both set and get.
  */
 @VintfStability
 union Parameter {
@@ -40,12 +52,39 @@
      */
     @VintfStability
     union Id {
-        // Common parameter tag.
-        int commonTag;
-        // Vendor defined parameter tag.
-        int vendorTag;
-        // Specific effect parameter tag.
-        Specific.Tag specificTag;
+        /**
+         * Parameter tag defined for vendor effects. Use int here so there is flexibility for vendor
+         * to define different tag.
+         */
+        int vendorEffectTag;
+        /**
+         * Parameter tag defined for nested parameters. Can be used to get any parameter defined in
+         * nested Union structure.
+         *
+         * Example:
+         * To get BassBoost strength in param from effectInstance:
+         *  IEffect effectInstance;
+         *  Parameter param;
+         *  BassBoost::Id bassId = BassBoost::Id::make<BassBoost::Id::tag>(BassBoost::strengthPm);
+         *  Parameter::Id id = Parameter::Id::make<Parameter::Id::bassBoostTag>(bassId);
+         *  effectInstance.getParameter(id, &param);
+         *
+         */
+        BassBoost.Id bassBoostTag;
+        Downmix.Id downmixTag;
+        DynamicsProcessing.Id dynamicsProcessingTag;
+        Equalizer.Id equalizerTag;
+        HapticGenerator.Id hapticGeneratorTag;
+        LoudnessEnhancer.Id loudnessEnhancerTag;
+        Reverb.Id reverbTag;
+        Virtualizer.Id virtualizerTag;
+        Visualizer.Id visualizerTag;
+        Volume.Id volumeTag;
+        /**
+         * Non-nested parameter tag. Can be used to get any parameter defined in Union Parameter
+         * directly.
+         */
+        Parameter.Tag commonTag;
     }
 
     /**
@@ -53,35 +92,71 @@
      */
     @VintfStability
     parcelable Common {
-        // Type of Audio device.
+        /**
+         * Type of Audio device.
+         */
         int session;
-        // I/O Handle.
+        /**
+         * I/O Handle.
+         */
         int ioHandle;
-        // Type of Audio device.
-        AudioDeviceDescription device;
-        // Input config.
+        /**
+         * Input config.
+         */
         AudioConfig input;
-        // Output config.
+        /**
+         * Output config.
+         */
         AudioConfig output;
     }
     Common common;
 
     /**
-     * Parameters for vendor extension effect implementation usage.
+     * Used by audio framework to set the device type to effect engine.
+     * Effect must implement setParameter(device) if Flags.deviceIndication set to true.
+     */
+    AudioDeviceDescription deviceDescription;
+    /**
+     * Used by audio framework to set the audio mode to effect engine.
+     * Effect must implement setParameter(mode) if Flags.audioModeIndication set to true.
+     */
+    AudioMode mode;
+    /**
+     * Used by audio framework to set the audio source to effect engine.
+     * Effect must implement setParameter(source) if Flags.audioSourceIndication set to true.
+     */
+    AudioSource source;
+
+    /**
+     * The volume gain for left and right channel, left and right equals to same value if it's mono.
      */
     @VintfStability
-    parcelable VendorEffectParameter {
-        ParcelableHolder extension;
+    parcelable VolumeStereo {
+        float left;
+        float right;
     }
-    VendorEffectParameter vendorEffect;
+    /**
+     * Used by audio framework to delegate volume control to effect engine.
+     * Effect must implement setParameter(volume) if Flags.volume set to Volume.IND.
+     */
+    VolumeStereo volumeStereo;
 
     /**
      * Parameters MUST be supported by a Specific type of effect.
      */
     @VintfStability
     union Specific {
+        VendorExtension vendorEffect;
+        BassBoost bassBoost;
+        Downmix downmix;
+        DynamicsProcessing dynamicsProcessing;
         Equalizer equalizer;
-        // TODO: add other effect definitions here
+        LoudnessEnhancer loudnessEnhancer;
+        HapticGenerator hapticGenerator;
+        Reverb reverb;
+        Virtualizer virtualizer;
+        Visualizer visualizer;
+        Volume volume;
     }
     Specific specific;
 }
diff --git a/audio/aidl/android/hardware/audio/effect/Processing.aidl b/audio/aidl/android/hardware/audio/effect/Processing.aidl
new file mode 100644
index 0000000..ef32e8c
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/effect/Processing.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.audio.effect;
+
+import android.hardware.audio.effect.Descriptor;
+import android.media.audio.common.AudioSource;
+import android.media.audio.common.AudioStreamType;
+import android.media.audio.common.AudioUuid;
+
+/**
+ * List of effects which must be used for certain pre-processing or post-processing.
+ */
+@VintfStability
+parcelable Processing {
+    @VintfStability
+    union Type {
+        AudioStreamType streamType = AudioStreamType.INVALID;
+        AudioSource source;
+    }
+
+    /**
+     * Specifies the type of processing by referring to the output stream type (AudioStreamType) or
+     * the input stream source (AudioSource).
+     */
+    Type type;
+    /**
+     * List of effect identities for this processing.
+     */
+    Descriptor.Identity[] ids;
+}
diff --git a/audio/aidl/android/hardware/audio/effect/Reverb.aidl b/audio/aidl/android/hardware/audio/effect/Reverb.aidl
new file mode 100644
index 0000000..f60c2ea
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/effect/Reverb.aidl
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.audio.effect;
+
+import android.hardware.audio.effect.VendorExtension;
+
+/**
+ * Reverb specific definitions.
+ *
+ * All parameters defined in union Reverb must be gettable and settable. The capabilities defined in
+ * Reverb.Capability can only acquired with IEffect.getDescriptor() and not settable.
+ */
+@VintfStability
+union Reverb {
+    /**
+     * Effect parameter tag to identify the parameters for getParameter().
+     */
+    @VintfStability
+    union Id {
+        int vendorExtensionTag;
+        Reverb.Tag commonTag;
+    }
+
+    /**
+     * Vendor Reverb implementation definition for additional parameters.
+     */
+    VendorExtension vendor;
+
+    /**
+     * Capability supported by effect implementation.
+     */
+    @VintfStability
+    parcelable Capability {
+        VendorExtension extension;
+
+        /**
+         * Max decay time supported in millisecond.
+         */
+        int maxDecayTimeMs;
+    }
+
+    /**
+     * Room level apply to the reverb effect in millibels.
+     */
+    int roomLevelMb;
+    /**
+     * Room HF level apply to the reverb effect in millibels.
+     */
+    int roomHfLevelMb;
+    /**
+     * Delay time apply to the reverb effect in milliseconds.
+     */
+    int decayTimeMs;
+    /**
+     * HF decay ratio in permilles.
+     */
+    int decayHfRatioPm;
+    /**
+     * Reverb level in millibels.
+     */
+    int levelMb;
+    /**
+     * Reverb delay in milliseconds.
+     */
+    int delayMs;
+    /**
+     * Diffusion in permilles.
+     */
+    int diffusionPm;
+    /**
+     * Density in permilles.
+     */
+    int densityPm;
+    /**
+     * Bypass reverb and copy input to output if set to true.
+     */
+    boolean bypass;
+}
diff --git a/audio/aidl/android/hardware/audio/effect/VendorExtension.aidl b/audio/aidl/android/hardware/audio/effect/VendorExtension.aidl
new file mode 100644
index 0000000..c60f01a
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/effect/VendorExtension.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.audio.effect;
+
+/**
+ * Vendor exntension implementation definition, can be used for additional parameters.
+ */
+@VintfStability
+parcelable VendorExtension {
+    ParcelableHolder extension;
+}
diff --git a/audio/aidl/android/hardware/audio/effect/Virtualizer.aidl b/audio/aidl/android/hardware/audio/effect/Virtualizer.aidl
new file mode 100644
index 0000000..9d039bc
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/effect/Virtualizer.aidl
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.audio.effect;
+
+import android.hardware.audio.effect.VendorExtension;
+
+/**
+ * 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.
+ */
+@VintfStability
+union Virtualizer {
+    /**
+     * Effect parameter tag to identify the parameters for getParameter().
+     */
+    @VintfStability
+    union Id {
+        int vendorExtensionTag;
+        Virtualizer.Tag commonTag;
+    }
+
+    /**
+     * Vendor Virtualizer implementation definition for additional parameters.
+     */
+    VendorExtension vendor;
+
+    /**
+     * 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;
+        /**
+         * 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 valid range for strength is [0, 1000].
+     */
+    int strengthPm;
+}
diff --git a/audio/aidl/android/hardware/audio/effect/Visualizer.aidl b/audio/aidl/android/hardware/audio/effect/Visualizer.aidl
new file mode 100644
index 0000000..4c1b71a
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/effect/Visualizer.aidl
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.audio.effect;
+
+import android.hardware.audio.effect.VendorExtension;
+
+/**
+ * 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.
+ *
+ */
+@VintfStability
+union Visualizer {
+    /**
+     * Effect parameter tag to identify the parameters for getParameter().
+     */
+    @VintfStability
+    union Id {
+        int vendorExtensionTag;
+        GetOnlyParameters.Tag getOnlyParamTag;
+        SetOnlyParameters.Tag setOnlyParamTag;
+        Visualizer.Tag commonTag;
+    }
+    Id id;
+
+    /**
+     * Vendor Visualizer implementation definition for additional parameters.
+     */
+    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.
+         */
+        CaptureSizeRange captureSizeRange;
+    }
+
+    /**
+     * Supported capture size range in bytes.
+     */
+    @VintfStability
+    parcelable CaptureSizeRange {
+        int minBytes;
+        int maxBytes;
+    }
+
+    /**
+     * Type of scaling applied on the captured visualization data.
+     */
+    @VintfStability
+    enum ScalingMode {
+        /**
+         * Defines a capture mode where amplification is applied based on the content of the
+         * captured data. This is the default Visualizer mode, and is suitable for music
+         * visualization.
+         */
+        NORMALIZED = 0,
+        /**
+         * Defines a capture mode where the playback volume will affect (scale) the range of the
+         * captured data. A low playback volume will lead to low sample and fft values, and
+         * vice-versa.
+         */
+        AS_PLAYED,
+    }
+
+    /**
+     * Measurement modes to be performed.
+     */
+    @VintfStability
+    enum MeasurementMode {
+        /**
+         * No measurements are performed.
+         */
+        NONE = 0,
+        /**
+         * Defines a measurement mode which computes the peak and RMS value in mB below the "full
+         * scale", where 0mB is normally the maximum sample value (but see the note below). Minimum
+         * value depends on the resolution of audio samples used by the audio framework. The value
+         * of -9600mB is the minimum value for 16-bit audio systems and -14400mB or below for "high
+         * resolution" systems. Values for peak and RMS can be retrieved with {@link
+         * #getMeasurementPeakRms(MeasurementPeakRms)}.
+         */
+        PEAK_RMS,
+    }
+
+    /**
+     * Any parameter defined in this union must be gettable via getParameter(), but must not
+     * settable.
+     */
+    @VintfStability
+    union GetOnlyParameters {
+        /**
+         * Get the current measurements.
+         */
+        @VintfStability
+        parcelable Measurement {
+            int rms;
+            int peak;
+        }
+        Measurement measurement;
+
+        /**
+         * Gets the latest PCM capture, size of returned vector equals to @c captureSize.
+         */
+        byte[] captureBytes;
+    }
+    GetOnlyParameters getOnlyParameters;
+
+    /**
+     * Any parameter defined in this union must be settable via setParameter(), but must not
+     * gettable.
+     */
+    @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;
+
+    /**
+     * Current capture size in bytes. The capture size must be a power of 2 in the range
+     * Capability.captureSizeRange.
+     */
+    int captureSizeBytes;
+    /**
+     * Visualizer capture mode
+     */
+    ScalingMode scalingMode;
+    /**
+     * Visualizer measurement mode.
+     */
+    MeasurementMode measurementMode;
+}
diff --git a/audio/aidl/android/hardware/audio/effect/Volume.aidl b/audio/aidl/android/hardware/audio/effect/Volume.aidl
new file mode 100644
index 0000000..a3ce2f6
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/effect/Volume.aidl
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.audio.effect;
+
+import android.hardware.audio.effect.VendorExtension;
+
+/**
+ * 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.
+ */
+@VintfStability
+union Volume {
+    /**
+     * Effect parameter tag to identify the parameters for getParameter().
+     */
+    @VintfStability
+    union Id {
+        int vendorExtensionTag;
+        Volume.Tag commonTag;
+    }
+
+    /**
+     * Vendor Volume implementation definition for additional parameters.
+     */
+    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;
+
+        /**
+         * Volume strength supported in dB.
+         */
+        int maxLevel;
+    }
+
+    /**
+     * Current level in dB.
+     */
+    int levelDb;
+    /**
+     * Mute volume if true, when volume set to mute, the current level still saved and take effect
+     * when unmute.
+     */
+    boolean mute;
+}
diff --git a/audio/aidl/common/StreamWorker.cpp b/audio/aidl/common/StreamWorker.cpp
index 9bca760..dda0e4a 100644
--- a/audio/aidl/common/StreamWorker.cpp
+++ b/audio/aidl/common/StreamWorker.cpp
@@ -44,6 +44,10 @@
             mWorkerStateChangeRequest = true;
         }
     }
+    join();
+}
+
+void ThreadController::join() {
     if (mWorker.joinable()) {
         mWorker.join();
     }
diff --git a/audio/aidl/common/include/StreamWorker.h b/audio/aidl/common/include/StreamWorker.h
index 6260eca..ab2ec26 100644
--- a/audio/aidl/common/include/StreamWorker.h
+++ b/audio/aidl/common/include/StreamWorker.h
@@ -39,6 +39,9 @@
     ~ThreadController() { stop(); }
 
     bool start(const std::string& name, int priority);
+    // Note: 'pause' and 'resume' methods should only be used on the "driving" side.
+    // In the case of audio HAL I/O, the driving side is the client, because the HAL
+    // implementation always blocks on getting a command.
     void pause() { switchWorkerStateSync(WorkerState::RUNNING, WorkerState::PAUSE_REQUESTED); }
     void resume() { switchWorkerStateSync(WorkerState::PAUSED, WorkerState::RESUME_REQUESTED); }
     bool hasError() {
@@ -50,6 +53,10 @@
         return mError;
     }
     void stop();
+    // Direct use of 'join' assumes that the StreamLogic is not intended
+    // to run forever, and is guaranteed to exit by itself. This normally
+    // only happen in tests.
+    void join();
     bool waitForAtLeastOneCycle();
 
     // Only used by unit tests.
@@ -133,7 +140,8 @@
     void resume() { mThread.resume(); }
     bool hasError() { return mThread.hasError(); }
     std::string getError() { return mThread.getError(); }
-    void stop() { return mThread.stop(); }
+    void stop() { mThread.stop(); }
+    void join() { mThread.join(); }
     bool waitForAtLeastOneCycle() { return mThread.waitForAtLeastOneCycle(); }
 
     // Only used by unit tests.
diff --git a/audio/aidl/common/tests/streamworker_tests.cpp b/audio/aidl/common/tests/streamworker_tests.cpp
index e3e484d..8ea8424 100644
--- a/audio/aidl/common/tests/streamworker_tests.cpp
+++ b/audio/aidl/common/tests/streamworker_tests.cpp
@@ -160,6 +160,14 @@
     EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
 }
 
+TEST_P(StreamWorkerTest, WorkerJoin) {
+    ASSERT_TRUE(worker.start());
+    stream.setStopStatus();
+    worker.join();
+    EXPECT_FALSE(worker.hasError());
+    EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+}
+
 TEST_P(StreamWorkerTest, WorkerError) {
     ASSERT_TRUE(worker.start());
     stream.setErrorStatus();
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index 2baaad9..d34d68c 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -68,10 +68,20 @@
     ],
     vendor: true,
     shared_libs: [
+        "libaudioaidlcommon",
         "libbase",
         "libbinder_ndk",
+        "libcutils",
+        "libfmq",
+        "liblog",
+        "libutils",
+        "android.hardware.common-V2-ndk",
+        "android.hardware.common.fmq-V1-ndk",
         "android.hardware.audio.effect-V1-ndk",
-        "libequalizer",
+    ],
+    header_libs: [
+        "libaudioaidl_headers",
+        "libsystem_headers",
     ],
     cflags: [
         "-Wall",
@@ -81,24 +91,12 @@
     ],
 }
 
-cc_library_static {
-    name: "libaudioeffectserviceexampleimpl",
-    defaults: ["aidlaudioeffectservice_defaults"],
-    export_include_dirs: ["include"],
-    srcs: [
-        "EffectFactory.cpp",
-    ],
-    header_libs: [
-        "libsystem_headers",
-    ],
-    visibility: [
-        ":__subpackages__",
-    ],
-}
-
 filegroup {
     name: "effectCommonFile",
-    srcs: ["EffectThread.cpp"],
+    srcs: [
+        "EffectThread.cpp",
+        "EffectImpl.cpp",
+    ],
 }
 
 cc_binary {
@@ -107,15 +105,26 @@
     init_rc: ["android.hardware.audio.effect.service-aidl.example.rc"],
     vintf_fragments: ["android.hardware.audio.effect.service-aidl.xml"],
     defaults: ["aidlaudioeffectservice_defaults"],
-    static_libs: [
-        "libaudioeffectserviceexampleimpl",
+    shared_libs: [
+        "libbassboostsw",
+        "libdynamicsprocessingsw",
+        "libequalizersw",
+        "libhapticgeneratorsw",
+        "libloudnessenhancersw",
+        "libreverbsw",
+        "libvirtualizersw",
+        "libvisualizersw",
+        "libvolumesw",
     ],
-    srcs: ["EffectMain.cpp"],
+    srcs: [
+        "EffectMain.cpp",
+        "EffectFactory.cpp",
+    ],
 }
 
 cc_library_headers {
     name: "libaudioaidl_headers",
     export_include_dirs: ["include"],
-    vendor: true,
+    vendor_available: true,
     host_supported: true,
 }
diff --git a/audio/aidl/default/EffectFactory.cpp b/audio/aidl/default/EffectFactory.cpp
index 4877956..e03dda3 100644
--- a/audio/aidl/default/EffectFactory.cpp
+++ b/audio/aidl/default/EffectFactory.cpp
@@ -18,6 +18,7 @@
 #include <android-base/logging.h>
 #include <dlfcn.h>
 
+#include "effect-impl/EffectTypes.h"
 #include "effect-impl/EffectUUID.h"
 #include "effectFactory-impl/EffectFactory.h"
 
@@ -26,27 +27,21 @@
 namespace aidl::android::hardware::audio::effect {
 
 Factory::Factory() {
-    std::function<void(void*)> dlClose = [](void* handle) -> void {
-        if (handle && dlclose(handle)) {
-            LOG(ERROR) << "dlclose failed " << dlerror();
-        }
-    };
-    // TODO: implement this with audio_effect.xml.
-    auto libHandle =
-            std::unique_ptr<void, decltype(dlClose)>{dlopen("libequalizer.so", RTLD_LAZY), dlClose};
-    if (!libHandle) {
-        LOG(ERROR) << __func__ << ": dlopen failed, err: " << dlerror();
-        return;
-    }
-
-    LOG(DEBUG) << __func__ << " dlopen uuid: " << EqualizerSwImplUUID.toString() << " handle "
-               << libHandle;
-    mEffectLibMap.insert({EqualizerSwImplUUID, std::make_pair(std::move(libHandle), nullptr)});
-
-    Descriptor::Identity id;
-    id.type = EqualizerTypeUUID;
-    id.uuid = EqualizerSwImplUUID;
-    mIdentityList.push_back(id);
+    // TODO: get list of library UUID and name from audio_effect.xml.
+    openEffectLibrary(EqualizerTypeUUID, EqualizerSwImplUUID, std::nullopt, "libequalizersw.so");
+    openEffectLibrary(EqualizerTypeUUID, EqualizerBundleImplUUID, std::nullopt, "libbundleaidl.so");
+    openEffectLibrary(BassBoostTypeUUID, BassBoostSwImplUUID, std::nullopt, "libbassboostsw.so");
+    openEffectLibrary(DynamicsProcessingTypeUUID, DynamicsProcessingSwImplUUID, std::nullopt,
+                      "libdynamicsprocessingsw.so");
+    openEffectLibrary(HapticGeneratorTypeUUID, HapticGeneratorSwImplUUID, std::nullopt,
+                      "libhapticgeneratorsw.so");
+    openEffectLibrary(LoudnessEnhancerTypeUUID, LoudnessEnhancerSwImplUUID, std::nullopt,
+                      "libloudnessenhancersw.so");
+    openEffectLibrary(ReverbTypeUUID, ReverbSwImplUUID, std::nullopt, "libreverbsw.so");
+    openEffectLibrary(VirtualizerTypeUUID, VirtualizerSwImplUUID, std::nullopt,
+                      "libvirtualizersw.so");
+    openEffectLibrary(VisualizerTypeUUID, VisualizerSwImplUUID, std::nullopt, "libvisualizersw.so");
+    openEffectLibrary(VolumeTypeUUID, VolumeSwImplUUID, std::nullopt, "libvolumesw.so");
 }
 
 Factory::~Factory() {
@@ -64,12 +59,27 @@
 
 ndk::ScopedAStatus Factory::queryEffects(const std::optional<AudioUuid>& in_type_uuid,
                                          const std::optional<AudioUuid>& in_impl_uuid,
+                                         const std::optional<AudioUuid>& in_proxy_uuid,
                                          std::vector<Descriptor::Identity>* _aidl_return) {
-    std::copy_if(mIdentityList.begin(), mIdentityList.end(), std::back_inserter(*_aidl_return),
-                 [&](auto& desc) {
-                     return (!in_type_uuid.has_value() || in_type_uuid.value() == desc.type) &&
-                            (!in_impl_uuid.has_value() || in_impl_uuid.value() == desc.uuid);
-                 });
+    std::copy_if(
+            mIdentityList.begin(), mIdentityList.end(), std::back_inserter(*_aidl_return),
+            [&](auto& desc) {
+                return (!in_type_uuid.has_value() || in_type_uuid.value() == desc.type) &&
+                       (!in_impl_uuid.has_value() || in_impl_uuid.value() == desc.uuid) &&
+                       (!in_proxy_uuid.has_value() ||
+                        (desc.proxy.has_value() && in_proxy_uuid.value() == desc.proxy.value()));
+            });
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Factory::queryProcessing(const std::optional<Processing::Type>& in_type,
+                                            std::vector<Processing>* _aidl_return) {
+    // TODO: implement this with audio_effect.xml.
+    if (in_type.has_value()) {
+        // return all matching process filter
+        LOG(DEBUG) << __func__ << " process type: " << in_type.value().toString();
+    }
+    LOG(DEBUG) << __func__ << " return " << _aidl_return->size();
     return ndk::ScopedAStatus::ok();
 }
 
@@ -85,43 +95,37 @@
 ndk::ScopedAStatus Factory::createEffect(const AudioUuid& in_impl_uuid,
                                          std::shared_ptr<IEffect>* _aidl_return) {
     LOG(DEBUG) << __func__ << ": UUID " << in_impl_uuid.toString();
-    if (in_impl_uuid == EqualizerSwImplUUID) {
-        if (mEffectLibMap.count(in_impl_uuid)) {
-            auto& lib = mEffectLibMap[in_impl_uuid];
-            // didn't do dlsym yet
-            if (nullptr == lib.second) {
-                void* libHandle = lib.first.get();
-                struct effect_interface_s intf = {
-                        .createEffectFunc = (EffectCreateFunctor)dlsym(libHandle, "createEffect"),
-                        .destroyEffectFunc =
-                                (EffectDestroyFunctor)dlsym(libHandle, "destroyEffect")};
-                auto dlInterface = std::make_unique<struct effect_interface_s>(intf);
-                if (!dlInterface->createEffectFunc || !dlInterface->destroyEffectFunc) {
-                    LOG(ERROR) << __func__
-                               << ": create or destroy symbol not exist in library: " << libHandle
-                               << "!";
-                    return ndk::ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED);
-                }
-                lib.second = std::move(dlInterface);
-            }
-
-            auto& libInterface = lib.second;
-            std::shared_ptr<IEffect> effectSp;
-            RETURN_IF_BINDER_EXCEPTION(libInterface->createEffectFunc(&effectSp));
-            if (!effectSp) {
-                LOG(ERROR) << __func__ << ": library created null instance without return error!";
+    if (mEffectLibMap.count(in_impl_uuid)) {
+        auto& lib = mEffectLibMap[in_impl_uuid];
+        // didn't do dlsym yet
+        if (nullptr == lib.second) {
+            void* libHandle = lib.first.get();
+            auto dlInterface = std::make_unique<struct effect_dl_interface_s>();
+            dlInterface->createEffectFunc = (EffectCreateFunctor)dlsym(libHandle, "createEffect");
+            dlInterface->destroyEffectFunc =
+                    (EffectDestroyFunctor)dlsym(libHandle, "destroyEffect");
+            if (!dlInterface->createEffectFunc || !dlInterface->destroyEffectFunc) {
+                LOG(ERROR) << __func__
+                           << ": create or destroy symbol not exist in library: " << libHandle
+                           << " with dlerror: " << dlerror();
                 return ndk::ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED);
             }
-            *_aidl_return = effectSp;
-            mEffectUuidMap[std::weak_ptr<IEffect>(effectSp)] = in_impl_uuid;
-            LOG(DEBUG) << __func__ << ": instance " << effectSp.get() << " created successfully";
-            return ndk::ScopedAStatus::ok();
-        } else {
-            LOG(ERROR) << __func__ << ": library doesn't exist";
-            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+            lib.second = std::move(dlInterface);
         }
+
+        auto& libInterface = lib.second;
+        std::shared_ptr<IEffect> effectSp;
+        RETURN_IF_BINDER_EXCEPTION(libInterface->createEffectFunc(&in_impl_uuid, &effectSp));
+        if (!effectSp) {
+            LOG(ERROR) << __func__ << ": library created null instance without return error!";
+            return ndk::ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED);
+        }
+        *_aidl_return = effectSp;
+        mEffectUuidMap[std::weak_ptr<IEffect>(effectSp)] = in_impl_uuid;
+        LOG(DEBUG) << __func__ << ": instance " << effectSp.get() << " created successfully";
+        return ndk::ScopedAStatus::ok();
     } else {
-        LOG(ERROR) << __func__ << ": UUID not supported";
+        LOG(ERROR) << __func__ << ": library doesn't exist";
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
     return ndk::ScopedAStatus::ok();
@@ -168,4 +172,34 @@
     return status;
 }
 
+void Factory::openEffectLibrary(const AudioUuid& type, const AudioUuid& impl,
+                                const std::optional<AudioUuid>& proxy, const std::string& libName) {
+    std::function<void(void*)> dlClose = [](void* handle) -> void {
+        if (handle && dlclose(handle)) {
+            LOG(ERROR) << "dlclose failed " << dlerror();
+        }
+    };
+
+    auto libHandle =
+            std::unique_ptr<void, decltype(dlClose)>{dlopen(libName.c_str(), RTLD_LAZY), dlClose};
+    if (!libHandle) {
+        LOG(ERROR) << __func__ << ": dlopen failed, err: " << dlerror();
+        return;
+    }
+
+    LOG(DEBUG) << __func__ << " dlopen lib:" << libName << " for uuid:\ntype:" << type.toString()
+               << "\nimpl:" << impl.toString()
+               << "\nproxy:" << (proxy.has_value() ? proxy.value().toString() : "null")
+               << "\nhandle:" << libHandle;
+    mEffectLibMap.insert({impl, std::make_pair(std::move(libHandle), nullptr)});
+
+    Descriptor::Identity id;
+    id.type = type;
+    id.uuid = impl;
+    if (proxy.has_value()) {
+        id.proxy = proxy.value();
+    }
+    mIdentityList.push_back(id);
+}
+
 }  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/EffectImpl.cpp b/audio/aidl/default/EffectImpl.cpp
new file mode 100644
index 0000000..2754bb6
--- /dev/null
+++ b/audio/aidl/default/EffectImpl.cpp
@@ -0,0 +1,256 @@
+/*
+ * 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 "AHAL_EffectImpl"
+#include "effect-impl/EffectImpl.h"
+#include "effect-impl/EffectTypes.h"
+#include "include/effect-impl/EffectTypes.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+ndk::ScopedAStatus EffectImpl::open(const Parameter::Common& common,
+                                    const std::optional<Parameter::Specific>& specific,
+                                    OpenEffectReturn* ret) {
+    LOG(DEBUG) << __func__;
+    {
+        std::lock_guard lg(mMutex);
+        RETURN_OK_IF(mState != State::INIT);
+        mContext = createContext(common);
+        RETURN_IF(!mContext, EX_ILLEGAL_ARGUMENT, "createContextFailed");
+        setContext(mContext);
+    }
+
+    RETURN_IF_ASTATUS_NOT_OK(setParameterCommon(common), "setCommParamErr");
+    if (specific.has_value()) {
+        RETURN_IF_ASTATUS_NOT_OK(setParameterSpecific(specific.value()), "setSpecParamErr");
+    }
+
+    RETURN_IF(createThread(LOG_TAG) != RetCode::SUCCESS, EX_UNSUPPORTED_OPERATION,
+              "FailedToCreateWorker");
+
+    {
+        std::lock_guard lg(mMutex);
+        mContext->dupeFmq(ret);
+        mState = State::IDLE;
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus EffectImpl::close() {
+    std::lock_guard lg(mMutex);
+    RETURN_OK_IF(mState == State::INIT);
+    RETURN_IF(mState == State::PROCESSING, EX_ILLEGAL_STATE, "closeAtProcessing");
+
+    // stop the worker thread, ignore the return code
+    RETURN_IF(destroyThread() != RetCode::SUCCESS, EX_UNSUPPORTED_OPERATION,
+              "FailedToDestroyWorker");
+    RETURN_IF(releaseContext() != RetCode::SUCCESS, EX_UNSUPPORTED_OPERATION,
+              "FailedToCreateWorker");
+    mState = State::INIT;
+    LOG(DEBUG) << __func__;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus EffectImpl::setParameter(const Parameter& param) {
+    LOG(DEBUG) << __func__ << " with: " << param.toString();
+
+    auto tag = param.getTag();
+    switch (tag) {
+        case Parameter::common:
+        case Parameter::deviceDescription:
+        case Parameter::mode:
+        case Parameter::source:
+            FALLTHROUGH_INTENDED;
+        case Parameter::volumeStereo:
+            return setParameterCommon(param);
+        case Parameter::specific: {
+            return setParameterSpecific(param.get<Parameter::specific>());
+        }
+        default: {
+            LOG(ERROR) << __func__ << " unsupportedParameterTag " << toString(tag);
+            return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+                                                                    "ParameterNotSupported");
+        }
+    }
+}
+
+ndk::ScopedAStatus EffectImpl::getParameter(const Parameter::Id& id, Parameter* param) {
+    LOG(DEBUG) << __func__ << id.toString();
+    auto tag = id.getTag();
+    switch (tag) {
+        case Parameter::Id::commonTag: {
+            RETURN_IF_ASTATUS_NOT_OK(getParameterCommon(id.get<Parameter::Id::commonTag>(), param),
+                                     "CommonParamNotSupported");
+            break;
+        }
+        case Parameter::Id::vendorEffectTag: {
+            LOG(DEBUG) << __func__ << " noop for vendor tag";
+            return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+                                                                    "vendortagNotSupported");
+        }
+        default: {
+            Parameter::Specific specific;
+            RETURN_IF_ASTATUS_NOT_OK(getParameterSpecific(id, &specific), "SpecParamNotSupported");
+            param->set<Parameter::specific>(specific);
+            break;
+        }
+    }
+    LOG(DEBUG) << __func__ << param->toString();
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus EffectImpl::setParameterCommon(const Parameter& param) {
+    std::lock_guard lg(mMutex);
+    RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+    auto tag = param.getTag();
+    switch (tag) {
+        case Parameter::common:
+            RETURN_IF(mContext->setCommon(param.get<Parameter::common>()) != RetCode::SUCCESS,
+                      EX_ILLEGAL_ARGUMENT, "setCommFailed");
+            break;
+        case Parameter::deviceDescription:
+            RETURN_IF(mContext->setOutputDevice(param.get<Parameter::deviceDescription>()) !=
+                              RetCode::SUCCESS,
+                      EX_ILLEGAL_ARGUMENT, "setDeviceFailed");
+            break;
+        case Parameter::mode:
+            RETURN_IF(mContext->setAudioMode(param.get<Parameter::mode>()) != RetCode::SUCCESS,
+                      EX_ILLEGAL_ARGUMENT, "setModeFailed");
+            break;
+        case Parameter::source:
+            RETURN_IF(mContext->setAudioSource(param.get<Parameter::source>()) != RetCode::SUCCESS,
+                      EX_ILLEGAL_ARGUMENT, "setSourceFailed");
+            break;
+        case Parameter::volumeStereo:
+            RETURN_IF(mContext->setVolumeStereo(param.get<Parameter::volumeStereo>()) !=
+                              RetCode::SUCCESS,
+                      EX_ILLEGAL_ARGUMENT, "setVolumeStereoFailed");
+            break;
+        default: {
+            LOG(ERROR) << __func__ << " unsupportedParameterTag " << toString(tag);
+            return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+                                                                    "commonParamNotSupported");
+        }
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus EffectImpl::getParameterCommon(const Parameter::Tag& tag, Parameter* param) {
+    std::lock_guard lg(mMutex);
+    RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+    switch (tag) {
+        case Parameter::common: {
+            param->set<Parameter::common>(mContext->getCommon());
+            break;
+        }
+        case Parameter::deviceDescription: {
+            param->set<Parameter::deviceDescription>(mContext->getOutputDevice());
+            break;
+        }
+        case Parameter::mode: {
+            param->set<Parameter::mode>(mContext->getAudioMode());
+            break;
+        }
+        case Parameter::source: {
+            param->set<Parameter::source>(mContext->getAudioSource());
+            break;
+        }
+        case Parameter::volumeStereo: {
+            param->set<Parameter::volumeStereo>(mContext->getVolumeStereo());
+            break;
+        }
+        default: {
+            LOG(DEBUG) << __func__ << " unsupported tag " << toString(tag);
+            return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+                                                                    "tagNotSupported");
+        }
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus EffectImpl::getState(State* state) {
+    std::lock_guard lg(mMutex);
+    *state = mState;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus EffectImpl::command(CommandId command) {
+    std::lock_guard lg(mMutex);
+    LOG(DEBUG) << __func__ << ": receive command: " << toString(command) << " at state "
+               << toString(mState);
+    RETURN_IF(mState == State::INIT, EX_ILLEGAL_STATE, "CommandStateError");
+    RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+
+    switch (command) {
+        case CommandId::START:
+            RETURN_IF(mState == State::INIT, EX_ILLEGAL_STATE, "instanceNotOpen");
+            RETURN_OK_IF(mState == State::PROCESSING);
+            RETURN_IF_ASTATUS_NOT_OK(commandStart(), "commandStartFailed");
+            mState = State::PROCESSING;
+            startThread();
+            return ndk::ScopedAStatus::ok();
+        case CommandId::STOP:
+            RETURN_OK_IF(mState == State::IDLE);
+            mState = State::IDLE;
+            RETURN_IF_ASTATUS_NOT_OK(commandStop(), "commandStopFailed");
+            stopThread();
+            return ndk::ScopedAStatus::ok();
+        case CommandId::RESET:
+            RETURN_OK_IF(mState == State::IDLE);
+            mState = State::IDLE;
+            RETURN_IF_ASTATUS_NOT_OK(commandStop(), "commandStopFailed");
+            stopThread();
+            mContext->resetBuffer();
+            return ndk::ScopedAStatus::ok();
+        default:
+            LOG(ERROR) << __func__ << " instance still processing";
+            return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+                                                                    "CommandIdNotSupported");
+    }
+    LOG(DEBUG) << __func__ << " transfer to state: " << toString(mState);
+    return ndk::ScopedAStatus::ok();
+}
+
+void EffectImpl::cleanUp() {
+    command(CommandId::STOP);
+    close();
+}
+
+IEffect::Status EffectImpl::status(binder_status_t status, size_t consumed, size_t produced) {
+    IEffect::Status ret;
+    ret.status = status;
+    ret.fmqConsumed = consumed;
+    ret.fmqProduced = produced;
+    return ret;
+}
+
+// A placeholder processing implementation to copy samples from input to output
+IEffect::Status EffectImpl::effectProcessImpl(float* in, float* out, int processSamples) {
+    // lock before access context/parameters
+    std::lock_guard lg(mMutex);
+    IEffect::Status status = {EX_NULL_POINTER, 0, 0};
+    RETURN_VALUE_IF(!mContext, status, "nullContext");
+    auto frameSize = mContext->getInputFrameSize();
+    RETURN_VALUE_IF(0 == frameSize, status, "frameSizeIs0");
+    LOG(DEBUG) << __func__ << " in " << in << " out " << out << " samples " << processSamples
+               << " frames " << processSamples * sizeof(float) / frameSize;
+    for (int i = 0; i < processSamples; i++) {
+        *out++ = *in++;
+    }
+    LOG(DEBUG) << __func__ << " done processing " << processSamples << " samples";
+    return {STATUS_OK, processSamples, processSamples};
+}
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/EffectThread.cpp b/audio/aidl/default/EffectThread.cpp
index 0ad9a14..80f120b 100644
--- a/audio/aidl/default/EffectThread.cpp
+++ b/audio/aidl/default/EffectThread.cpp
@@ -28,11 +28,11 @@
 }
 
 EffectThread::~EffectThread() {
-    destroy();
+    destroyThread();
     LOG(DEBUG) << __func__ << " done";
 };
 
-RetCode EffectThread::create(const std::string& name, const int priority) {
+RetCode EffectThread::createThread(const std::string& name, const int priority) {
     if (mThread.joinable()) {
         LOG(WARNING) << __func__ << " thread already created, no-op";
         return RetCode::SUCCESS;
@@ -44,7 +44,7 @@
     return RetCode::SUCCESS;
 }
 
-RetCode EffectThread::destroy() {
+RetCode EffectThread::destroyThread() {
     {
         std::lock_guard lg(mMutex);
         mStop = mExit = true;
@@ -58,10 +58,10 @@
     return RetCode::SUCCESS;
 }
 
-RetCode EffectThread::start() {
+RetCode EffectThread::startThread() {
     if (!mThread.joinable()) {
         LOG(ERROR) << __func__ << " thread already destroyed";
-        return RetCode::ERROR;
+        return RetCode::ERROR_THREAD;
     }
 
     {
@@ -78,10 +78,10 @@
     return RetCode::SUCCESS;
 }
 
-RetCode EffectThread::stop() {
+RetCode EffectThread::stopThread() {
     if (!mThread.joinable()) {
         LOG(ERROR) << __func__ << " thread already destroyed";
-        return RetCode::ERROR;
+        return RetCode::ERROR_THREAD;
     }
 
     {
@@ -117,15 +117,4 @@
     }
 }
 
-std::string toString(RetCode& code) {
-    switch (code) {
-        case RetCode::SUCCESS:
-            return "SUCCESS";
-        case RetCode::ERROR:
-            return "ERROR";
-        default:
-            return "EnumError";
-    }
-}
-
 }  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index 312df72..7b544a1 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -85,126 +85,315 @@
     return "";
 }
 
+void StreamWorkerCommonLogic::populateReply(StreamDescriptor::Reply* reply,
+                                            bool isConnected) const {
+    if (isConnected) {
+        reply->status = STATUS_OK;
+        reply->observable.frames = mFrameCount;
+        reply->observable.timeNs = ::android::elapsedRealtimeNano();
+    } else {
+        reply->status = STATUS_NO_INIT;
+    }
+}
+
 const std::string StreamInWorkerLogic::kThreadName = "reader";
 
 StreamInWorkerLogic::Status StreamInWorkerLogic::cycle() {
     StreamDescriptor::Command command{};
     if (!mCommandMQ->readBlocking(&command, 1)) {
         LOG(ERROR) << __func__ << ": reading of command from MQ failed";
+        mState = StreamDescriptor::State::ERROR;
         return Status::ABORT;
     }
     StreamDescriptor::Reply reply{};
-    if (command.code == StreamContext::COMMAND_EXIT &&
+    if (static_cast<int32_t>(command.code) == StreamContext::COMMAND_EXIT &&
         command.fmqByteCount == mInternalCommandCookie) {
         LOG(DEBUG) << __func__ << ": received EXIT command";
+        setClosed();
         // This is an internal command, no need to reply.
         return Status::EXIT;
-    } else if (command.code == StreamDescriptor::COMMAND_BURST && command.fmqByteCount >= 0) {
+    } else if (command.code == StreamDescriptor::CommandCode::START && command.fmqByteCount >= 0) {
+        LOG(DEBUG) << __func__ << ": received START read command";
+        if (mState == StreamDescriptor::State::STANDBY ||
+            mState == StreamDescriptor::State::DRAINING) {
+            populateReply(&reply, mIsConnected);
+            mState = mState == StreamDescriptor::State::STANDBY ? StreamDescriptor::State::IDLE
+                                                                : StreamDescriptor::State::ACTIVE;
+        } else {
+            LOG(WARNING) << __func__ << ": START command can not be handled in the state "
+                         << toString(mState);
+            reply.status = STATUS_INVALID_OPERATION;
+        }
+    } else if (command.code == StreamDescriptor::CommandCode::BURST && command.fmqByteCount >= 0) {
         LOG(DEBUG) << __func__ << ": received BURST read command for " << command.fmqByteCount
                    << " bytes";
-        usleep(3000);  // Simulate a blocking call into the driver.
-        const size_t byteCount = std::min({static_cast<size_t>(command.fmqByteCount),
-                                           mDataMQ->availableToWrite(), mDataBufferSize});
-        const bool isConnected = mIsConnected;
-        // Simulate reading of data, or provide zeroes if the stream is not connected.
-        for (size_t i = 0; i < byteCount; ++i) {
-            using buffer_type = decltype(mDataBuffer)::element_type;
-            constexpr int kBufferValueRange = std::numeric_limits<buffer_type>::max() -
-                                              std::numeric_limits<buffer_type>::min() + 1;
-            mDataBuffer[i] = isConnected ? (std::rand() % kBufferValueRange) +
-                                                   std::numeric_limits<buffer_type>::min()
-                                         : 0;
-        }
-        bool success = byteCount > 0 ? mDataMQ->write(&mDataBuffer[0], byteCount) : true;
-        if (success) {
-            LOG(DEBUG) << __func__ << ": writing of " << byteCount << " bytes into data MQ"
-                       << " succeeded; connected? " << isConnected;
-            // Frames are provided and counted regardless of connection status.
-            reply.fmqByteCount = byteCount;
-            mFrameCount += byteCount / mFrameSize;
-            if (isConnected) {
-                reply.status = STATUS_OK;
-                reply.observable.frames = mFrameCount;
-                reply.observable.timeNs = ::android::elapsedRealtimeNano();
-            } else {
-                reply.status = STATUS_INVALID_OPERATION;
+        if (mState == StreamDescriptor::State::IDLE || mState == StreamDescriptor::State::ACTIVE ||
+            mState == StreamDescriptor::State::PAUSED ||
+            mState == StreamDescriptor::State::DRAINING) {
+            if (!read(command.fmqByteCount, &reply)) {
+                mState = StreamDescriptor::State::ERROR;
+            }
+            if (mState == StreamDescriptor::State::IDLE ||
+                mState == StreamDescriptor::State::PAUSED) {
+                mState = StreamDescriptor::State::ACTIVE;
+            } else if (mState == StreamDescriptor::State::DRAINING) {
+                // To simplify the reference code, we assume that the read operation
+                // has consumed all the data remaining in the hardware buffer.
+                // TODO: Provide parametrization on the duration of draining to test
+                //       handling of commands during the 'DRAINING' state.
+                mState = StreamDescriptor::State::STANDBY;
             }
         } else {
-            LOG(WARNING) << __func__ << ": writing of " << byteCount
-                         << " bytes of data to MQ failed";
-            reply.status = STATUS_NOT_ENOUGH_DATA;
+            LOG(WARNING) << __func__ << ": BURST command can not be handled in the state "
+                         << toString(mState);
+            reply.status = STATUS_INVALID_OPERATION;
         }
-        reply.latencyMs = Module::kLatencyMs;
+    } else if (command.code == StreamDescriptor::CommandCode::DRAIN && command.fmqByteCount == 0) {
+        LOG(DEBUG) << __func__ << ": received DRAIN read command";
+        if (mState == StreamDescriptor::State::ACTIVE) {
+            usleep(1000);  // Simulate a blocking call into the driver.
+            populateReply(&reply, mIsConnected);
+            // Can switch the state to ERROR if a driver error occurs.
+            mState = StreamDescriptor::State::DRAINING;
+        } else {
+            LOG(WARNING) << __func__ << ": DRAIN command can not be handled in the state "
+                         << toString(mState);
+            reply.status = STATUS_INVALID_OPERATION;
+        }
+    } else if (command.code == StreamDescriptor::CommandCode::PAUSE && command.fmqByteCount == 0) {
+        LOG(DEBUG) << __func__ << ": received PAUSE read command";
+        if (mState == StreamDescriptor::State::ACTIVE) {
+            usleep(1000);  // Simulate a blocking call into the driver.
+            populateReply(&reply, mIsConnected);
+            // Can switch the state to ERROR if a driver error occurs.
+            mState = StreamDescriptor::State::PAUSED;
+        } else {
+            LOG(WARNING) << __func__ << ": PAUSE command can not be handled in the state "
+                         << toString(mState);
+            reply.status = STATUS_INVALID_OPERATION;
+        }
+    } else if (command.code == StreamDescriptor::CommandCode::FLUSH && command.fmqByteCount == 0) {
+        LOG(DEBUG) << __func__ << ": received FLUSH read command";
+        if (mState == StreamDescriptor::State::PAUSED) {
+            usleep(1000);  // Simulate a blocking call into the driver.
+            populateReply(&reply, mIsConnected);
+            // Can switch the state to ERROR if a driver error occurs.
+            mState = StreamDescriptor::State::STANDBY;
+        } else {
+            LOG(WARNING) << __func__ << ": FLUSH command can not be handled in the state "
+                         << toString(mState);
+            reply.status = STATUS_INVALID_OPERATION;
+        }
+    } else if (command.code == StreamDescriptor::CommandCode::STANDBY &&
+               command.fmqByteCount == 0) {
+        LOG(DEBUG) << __func__ << ": received STANDBY read command";
+        if (mState == StreamDescriptor::State::IDLE) {
+            usleep(1000);  // Simulate a blocking call into the driver.
+            populateReply(&reply, mIsConnected);
+            // Can switch the state to ERROR if a driver error occurs.
+            mState = StreamDescriptor::State::STANDBY;
+        } else {
+            LOG(WARNING) << __func__ << ": FLUSH command can not be handled in the state "
+                         << toString(mState);
+            reply.status = STATUS_INVALID_OPERATION;
+        }
     } else {
         LOG(WARNING) << __func__ << ": invalid command (" << command.toString()
                      << ") or count: " << command.fmqByteCount;
         reply.status = STATUS_BAD_VALUE;
     }
+    reply.state = mState;
     LOG(DEBUG) << __func__ << ": writing reply " << reply.toString();
     if (!mReplyMQ->writeBlocking(&reply, 1)) {
         LOG(ERROR) << __func__ << ": writing of reply " << reply.toString() << " to MQ failed";
+        mState = StreamDescriptor::State::ERROR;
         return Status::ABORT;
     }
     return Status::CONTINUE;
 }
 
+bool StreamInWorkerLogic::read(size_t clientSize, StreamDescriptor::Reply* reply) {
+    // Can switch the state to ERROR if a driver error occurs.
+    const size_t byteCount = std::min({clientSize, mDataMQ->availableToWrite(), mDataBufferSize});
+    const bool isConnected = mIsConnected;
+    bool fatal = false;
+    // Simulate reading of data, or provide zeroes if the stream is not connected.
+    for (size_t i = 0; i < byteCount; ++i) {
+        using buffer_type = decltype(mDataBuffer)::element_type;
+        constexpr int kBufferValueRange = std::numeric_limits<buffer_type>::max() -
+                                          std::numeric_limits<buffer_type>::min() + 1;
+        mDataBuffer[i] = isConnected ? (std::rand() % kBufferValueRange) +
+                                               std::numeric_limits<buffer_type>::min()
+                                     : 0;
+    }
+    usleep(3000);  // Simulate a blocking call into the driver.
+    // Set 'fatal = true' if a driver error occurs.
+    if (bool success = byteCount > 0 ? mDataMQ->write(&mDataBuffer[0], byteCount) : true; success) {
+        LOG(DEBUG) << __func__ << ": writing of " << byteCount << " bytes into data MQ"
+                   << " succeeded; connected? " << isConnected;
+        // Frames are provided and counted regardless of connection status.
+        reply->fmqByteCount += byteCount;
+        mFrameCount += byteCount / mFrameSize;
+        populateReply(reply, isConnected);
+    } else {
+        LOG(WARNING) << __func__ << ": writing of " << byteCount << " bytes of data to MQ failed";
+        reply->status = STATUS_NOT_ENOUGH_DATA;
+    }
+    reply->latencyMs = Module::kLatencyMs;
+    return !fatal;
+}
+
 const std::string StreamOutWorkerLogic::kThreadName = "writer";
 
 StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() {
     StreamDescriptor::Command command{};
     if (!mCommandMQ->readBlocking(&command, 1)) {
         LOG(ERROR) << __func__ << ": reading of command from MQ failed";
+        mState = StreamDescriptor::State::ERROR;
         return Status::ABORT;
     }
     StreamDescriptor::Reply reply{};
-    if (command.code == StreamContext::COMMAND_EXIT &&
+    if (static_cast<int32_t>(command.code) == StreamContext::COMMAND_EXIT &&
         command.fmqByteCount == mInternalCommandCookie) {
         LOG(DEBUG) << __func__ << ": received EXIT command";
+        setClosed();
         // This is an internal command, no need to reply.
         return Status::EXIT;
-    } else if (command.code == StreamDescriptor::COMMAND_BURST && command.fmqByteCount >= 0) {
+    } else if (command.code == StreamDescriptor::CommandCode::START && command.fmqByteCount >= 0) {
+        LOG(DEBUG) << __func__ << ": received START read command";
+        switch (mState) {
+            case StreamDescriptor::State::STANDBY:
+                mState = StreamDescriptor::State::IDLE;
+                break;
+            case StreamDescriptor::State::PAUSED:
+                mState = StreamDescriptor::State::ACTIVE;
+                break;
+            case StreamDescriptor::State::DRAIN_PAUSED:
+                mState = StreamDescriptor::State::PAUSED;
+                break;
+            default:
+                LOG(WARNING) << __func__ << ": START command can not be handled in the state "
+                             << toString(mState);
+                reply.status = STATUS_INVALID_OPERATION;
+        }
+        if (reply.status != STATUS_INVALID_OPERATION) {
+            populateReply(&reply, mIsConnected);
+        }
+    } else if (command.code == StreamDescriptor::CommandCode::BURST && command.fmqByteCount >= 0) {
         LOG(DEBUG) << __func__ << ": received BURST write command for " << command.fmqByteCount
                    << " bytes";
-        const size_t byteCount = std::min({static_cast<size_t>(command.fmqByteCount),
-                                           mDataMQ->availableToRead(), mDataBufferSize});
-        bool success = byteCount > 0 ? mDataMQ->read(&mDataBuffer[0], byteCount) : true;
-        if (success) {
-            const bool isConnected = mIsConnected;
-            LOG(DEBUG) << __func__ << ": reading of " << byteCount << " bytes from data MQ"
-                       << " succeeded; connected? " << isConnected;
-            // Frames are consumed and counted regardless of connection status.
-            reply.fmqByteCount = byteCount;
-            mFrameCount += byteCount / mFrameSize;
-            if (isConnected) {
-                reply.status = STATUS_OK;
-                reply.observable.frames = mFrameCount;
-                reply.observable.timeNs = ::android::elapsedRealtimeNano();
-            } else {
-                reply.status = STATUS_INVALID_OPERATION;
+        if (mState != StreamDescriptor::State::ERROR) {  // BURST can be handled in all valid states
+            if (!write(command.fmqByteCount, &reply)) {
+                mState = StreamDescriptor::State::ERROR;
             }
-            usleep(3000);  // Simulate a blocking call into the driver.
+            if (mState == StreamDescriptor::State::STANDBY ||
+                mState == StreamDescriptor::State::DRAIN_PAUSED) {
+                mState = StreamDescriptor::State::PAUSED;
+            } else if (mState == StreamDescriptor::State::IDLE ||
+                       mState == StreamDescriptor::State::DRAINING) {
+                mState = StreamDescriptor::State::ACTIVE;
+            }  // When in 'ACTIVE' and 'PAUSED' do not need to change the state.
         } else {
-            LOG(WARNING) << __func__ << ": reading of " << byteCount
-                         << " bytes of data from MQ failed";
-            reply.status = STATUS_NOT_ENOUGH_DATA;
+            LOG(WARNING) << __func__ << ": BURST command can not be handled in the state "
+                         << toString(mState);
+            reply.status = STATUS_INVALID_OPERATION;
         }
-        reply.latencyMs = Module::kLatencyMs;
+    } else if (command.code == StreamDescriptor::CommandCode::DRAIN && command.fmqByteCount == 0) {
+        LOG(DEBUG) << __func__ << ": received DRAIN write command";
+        if (mState == StreamDescriptor::State::ACTIVE) {
+            usleep(1000);  // Simulate a blocking call into the driver.
+            populateReply(&reply, mIsConnected);
+            // Can switch the state to ERROR if a driver error occurs.
+            mState = StreamDescriptor::State::IDLE;
+            // Since there is no actual hardware that would be draining the buffer,
+            // in order to simplify the reference code, we assume that draining
+            // happens instantly, thus skipping the 'DRAINING' state.
+            // TODO: Provide parametrization on the duration of draining to test
+            //       handling of commands during the 'DRAINING' state.
+        } else {
+            LOG(WARNING) << __func__ << ": DRAIN command can not be handled in the state "
+                         << toString(mState);
+            reply.status = STATUS_INVALID_OPERATION;
+        }
+    } else if (command.code == StreamDescriptor::CommandCode::STANDBY &&
+               command.fmqByteCount == 0) {
+        LOG(DEBUG) << __func__ << ": received STANDBY write command";
+        if (mState == StreamDescriptor::State::IDLE) {
+            usleep(1000);  // Simulate a blocking call into the driver.
+            populateReply(&reply, mIsConnected);
+            // Can switch the state to ERROR if a driver error occurs.
+            mState = StreamDescriptor::State::STANDBY;
+        } else {
+            LOG(WARNING) << __func__ << ": STANDBY command can not be handled in the state "
+                         << toString(mState);
+            reply.status = STATUS_INVALID_OPERATION;
+        }
+    } else if (command.code == StreamDescriptor::CommandCode::PAUSE && command.fmqByteCount == 0) {
+        LOG(DEBUG) << __func__ << ": received PAUSE write command";
+        if (mState == StreamDescriptor::State::ACTIVE ||
+            mState == StreamDescriptor::State::DRAINING) {
+            populateReply(&reply, mIsConnected);
+            mState = mState == StreamDescriptor::State::ACTIVE
+                             ? StreamDescriptor::State::PAUSED
+                             : StreamDescriptor::State::DRAIN_PAUSED;
+        } else {
+            LOG(WARNING) << __func__ << ": PAUSE command can not be handled in the state "
+                         << toString(mState);
+            reply.status = STATUS_INVALID_OPERATION;
+        }
+    } else if (command.code == StreamDescriptor::CommandCode::FLUSH && command.fmqByteCount == 0) {
+        LOG(DEBUG) << __func__ << ": received FLUSH write command";
+        if (mState == StreamDescriptor::State::PAUSED ||
+            mState == StreamDescriptor::State::DRAIN_PAUSED) {
+            populateReply(&reply, mIsConnected);
+            mState = StreamDescriptor::State::IDLE;
+        } else {
+            LOG(WARNING) << __func__ << ": FLUSH command can not be handled in the state "
+                         << toString(mState);
+            reply.status = STATUS_INVALID_OPERATION;
+        }
     } else {
         LOG(WARNING) << __func__ << ": invalid command (" << command.toString()
                      << ") or count: " << command.fmqByteCount;
         reply.status = STATUS_BAD_VALUE;
     }
+    reply.state = mState;
     LOG(DEBUG) << __func__ << ": writing reply " << reply.toString();
     if (!mReplyMQ->writeBlocking(&reply, 1)) {
         LOG(ERROR) << __func__ << ": writing of reply " << reply.toString() << " to MQ failed";
+        mState = StreamDescriptor::State::ERROR;
         return Status::ABORT;
     }
     return Status::CONTINUE;
 }
 
+bool StreamOutWorkerLogic::write(size_t clientSize, StreamDescriptor::Reply* reply) {
+    const size_t readByteCount = mDataMQ->availableToRead();
+    // Amount of data that the HAL module is going to actually use.
+    const size_t byteCount = std::min({clientSize, readByteCount, mDataBufferSize});
+    bool fatal = false;
+    if (bool success = readByteCount > 0 ? mDataMQ->read(&mDataBuffer[0], readByteCount) : true) {
+        const bool isConnected = mIsConnected;
+        LOG(DEBUG) << __func__ << ": reading of " << readByteCount << " bytes from data MQ"
+                   << " succeeded; connected? " << isConnected;
+        // Frames are consumed and counted regardless of connection status.
+        reply->fmqByteCount += byteCount;
+        mFrameCount += byteCount / mFrameSize;
+        populateReply(reply, isConnected);
+        usleep(3000);  // Simulate a blocking call into the driver.
+        // Set 'fatal = true' if a driver error occurs.
+    } else {
+        LOG(WARNING) << __func__ << ": reading of " << readByteCount
+                     << " bytes of data from MQ failed";
+        reply->status = STATUS_NOT_ENOUGH_DATA;
+    }
+    reply->latencyMs = Module::kLatencyMs;
+    return !fatal;
+}
+
 template <class Metadata, class StreamWorker>
 StreamCommon<Metadata, StreamWorker>::~StreamCommon() {
-    if (!mIsClosed) {
+    if (!isClosed()) {
         LOG(ERROR) << __func__ << ": stream was not closed prior to destruction, resource leak";
         stopWorker();
         // The worker and the context should clean up by themselves via destructors.
@@ -214,13 +403,13 @@
 template <class Metadata, class StreamWorker>
 ndk::ScopedAStatus StreamCommon<Metadata, StreamWorker>::close() {
     LOG(DEBUG) << __func__;
-    if (!mIsClosed) {
+    if (!isClosed()) {
         stopWorker();
         LOG(DEBUG) << __func__ << ": joining the worker thread...";
         mWorker.stop();
         LOG(DEBUG) << __func__ << ": worker thread joined";
         mContext.reset();
-        mIsClosed = true;
+        mWorker.setClosed();
         return ndk::ScopedAStatus::ok();
     } else {
         LOG(ERROR) << __func__ << ": stream was already closed";
@@ -231,13 +420,14 @@
 template <class Metadata, class StreamWorker>
 void StreamCommon<Metadata, StreamWorker>::stopWorker() {
     if (auto commandMQ = mContext.getCommandMQ(); commandMQ != nullptr) {
-        LOG(DEBUG) << __func__ << ": asking the worker to stop...";
+        LOG(DEBUG) << __func__ << ": asking the worker to exit...";
         StreamDescriptor::Command cmd;
-        cmd.code = StreamContext::COMMAND_EXIT;
+        cmd.code = StreamDescriptor::CommandCode(StreamContext::COMMAND_EXIT);
         cmd.fmqByteCount = mContext.getInternalCommandCookie();
-        // FIXME: This can block in the case when the client wrote a command
-        // while the stream worker's cycle is not running. Need to revisit
-        // when implementing standby and pause/resume.
+        // Note: never call 'pause' and 'resume' methods of StreamWorker
+        // in the HAL implementation. These methods are to be used by
+        // the client side only. Preventing the worker loop from running
+        // on the HAL side can cause a deadlock.
         if (!commandMQ->writeBlocking(&cmd, 1)) {
             LOG(ERROR) << __func__ << ": failed to write exit command to the MQ";
         }
@@ -248,7 +438,7 @@
 template <class Metadata, class StreamWorker>
 ndk::ScopedAStatus StreamCommon<Metadata, StreamWorker>::updateMetadata(const Metadata& metadata) {
     LOG(DEBUG) << __func__;
-    if (!mIsClosed) {
+    if (!isClosed()) {
         mMetadata = metadata;
         return ndk::ScopedAStatus::ok();
     }
diff --git a/audio/aidl/default/bassboost/Android.bp b/audio/aidl/default/bassboost/Android.bp
new file mode 100644
index 0000000..f22eb95
--- /dev/null
+++ b/audio/aidl/default/bassboost/Android.bp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+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"],
+}
+
+cc_library_shared {
+    name: "libbassboostsw",
+    defaults: [
+        "aidlaudioeffectservice_defaults",
+        "latest_android_media_audio_common_types_ndk_shared",
+        "latest_android_hardware_audio_effect_ndk_shared",
+    ],
+    srcs: [
+        "BassBoostSw.cpp",
+        ":effectCommonFile",
+    ],
+    visibility: [
+        "//hardware/interfaces/audio/aidl/default",
+    ],
+}
diff --git a/audio/aidl/default/bassboost/BassBoostSw.cpp b/audio/aidl/default/bassboost/BassBoostSw.cpp
new file mode 100644
index 0000000..3c39824
--- /dev/null
+++ b/audio/aidl/default/bassboost/BassBoostSw.cpp
@@ -0,0 +1,118 @@
+/*
+ * 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 <cstddef>
+#define LOG_TAG "AHAL_BassBoostSw"
+#include <Utils.h>
+#include <algorithm>
+#include <unordered_set>
+
+#include <android-base/logging.h>
+#include <fmq/AidlMessageQueue.h>
+
+#include "BassBoostSw.h"
+
+using aidl::android::hardware::audio::effect::BassBoostSw;
+using aidl::android::hardware::audio::effect::BassBoostSwImplUUID;
+using aidl::android::hardware::audio::effect::IEffect;
+using aidl::android::hardware::audio::effect::State;
+using aidl::android::media::audio::common::AudioUuid;
+
+extern "C" binder_exception_t createEffect(const AudioUuid* in_impl_uuid,
+                                           std::shared_ptr<IEffect>* instanceSpp) {
+    if (!in_impl_uuid || *in_impl_uuid != BassBoostSwImplUUID) {
+        LOG(ERROR) << __func__ << "uuid not supported";
+        return EX_ILLEGAL_ARGUMENT;
+    }
+    if (instanceSpp) {
+        *instanceSpp = ndk::SharedRefBase::make<BassBoostSw>();
+        LOG(DEBUG) << __func__ << " instance " << instanceSpp->get() << " created";
+        return EX_NONE;
+    } else {
+        LOG(ERROR) << __func__ << " invalid input parameter!";
+        return EX_ILLEGAL_ARGUMENT;
+    }
+}
+
+extern "C" binder_exception_t destroyEffect(const std::shared_ptr<IEffect>& instanceSp) {
+    if (!instanceSp) {
+        return EX_NONE;
+    }
+    State state;
+    ndk::ScopedAStatus status = instanceSp->getState(&state);
+    if (!status.isOk() || State::INIT != state) {
+        LOG(ERROR) << __func__ << " instance " << instanceSp.get()
+                   << " in state: " << toString(state) << ", status: " << status.getDescription();
+        return EX_ILLEGAL_STATE;
+    }
+    LOG(DEBUG) << __func__ << " instance " << instanceSp.get() << " destroyed";
+    return EX_NONE;
+}
+
+namespace aidl::android::hardware::audio::effect {
+
+ndk::ScopedAStatus BassBoostSw::getDescriptor(Descriptor* _aidl_return) {
+    LOG(DEBUG) << __func__ << kDescriptor.toString();
+    *_aidl_return = kDescriptor;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus BassBoostSw::setParameterSpecific(const Parameter::Specific& specific) {
+    RETURN_IF(Parameter::Specific::bassBoost != specific.getTag(), EX_ILLEGAL_ARGUMENT,
+              "EffectNotSupported");
+    std::lock_guard lg(mMutex);
+    RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+
+    mSpecificParam = specific.get<Parameter::Specific::bassBoost>();
+    LOG(DEBUG) << __func__ << " success with: " << specific.toString();
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus BassBoostSw::getParameterSpecific(const Parameter::Id& id,
+                                                     Parameter::Specific* specific) {
+    auto tag = id.getTag();
+    RETURN_IF(Parameter::Id::bassBoostTag != tag, EX_ILLEGAL_ARGUMENT, "wrongIdTag");
+    specific->set<Parameter::Specific::bassBoost>(mSpecificParam);
+    return ndk::ScopedAStatus::ok();
+}
+
+std::shared_ptr<EffectContext> BassBoostSw::createContext(const Parameter::Common& common) {
+    if (mContext) {
+        LOG(DEBUG) << __func__ << " context already exist";
+        return mContext;
+    }
+    mContext = std::make_shared<BassBoostSwContext>(1 /* statusFmqDepth */, common);
+    return mContext;
+}
+
+RetCode BassBoostSw::releaseContext() {
+    if (mContext) {
+        mContext.reset();
+    }
+    return RetCode::SUCCESS;
+}
+
+// Processing method running in EffectWorker thread.
+IEffect::Status BassBoostSw::effectProcessImpl(float* in, float* out, int process) {
+    // TODO: get data buffer and process.
+    LOG(DEBUG) << __func__ << " in " << in << " out " << out << " process " << process;
+    for (int i = 0; i < process; i++) {
+        *out++ = *in++;
+    }
+    return {STATUS_OK, process, process};
+}
+
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/bassboost/BassBoostSw.h b/audio/aidl/default/bassboost/BassBoostSw.h
new file mode 100644
index 0000000..fe9c640
--- /dev/null
+++ b/audio/aidl/default/bassboost/BassBoostSw.h
@@ -0,0 +1,72 @@
+/*
+ * 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/audio/effect/BnEffect.h>
+#include <fmq/AidlMessageQueue.h>
+#include <cstdlib>
+#include <memory>
+
+#include "effect-impl/EffectImpl.h"
+#include "effect-impl/EffectUUID.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+class BassBoostSwContext final : public EffectContext {
+  public:
+    BassBoostSwContext(int statusDepth, const Parameter::Common& common)
+        : EffectContext(statusDepth, common) {
+        LOG(DEBUG) << __func__;
+    }
+    // TODO: add specific context here
+};
+
+class BassBoostSw final : public EffectImpl {
+  public:
+    BassBoostSw() { LOG(DEBUG) << __func__; }
+    ~BassBoostSw() {
+        LOG(DEBUG) << __func__;
+        releaseContext();
+    }
+
+    ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
+    ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific) override;
+    ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id,
+                                            Parameter::Specific* specific) override;
+    IEffect::Status effectProcessImpl(float* in, float* out, int process) override;
+    std::shared_ptr<EffectContext> createContext(const Parameter::Common& common) override;
+    RetCode releaseContext() override;
+
+  private:
+    std::shared_ptr<BassBoostSwContext> mContext;
+    /* capabilities */
+    const BassBoost::Capability kCapability;
+    /* Effect descriptor */
+    const Descriptor kDescriptor = {
+            .common = {.id = {.type = BassBoostTypeUUID,
+                              .uuid = BassBoostSwImplUUID,
+                              .proxy = std::nullopt},
+                       .flags = {.type = Flags::Type::INSERT,
+                                 .insert = Flags::Insert::FIRST,
+                                 .volume = Flags::Volume::CTRL},
+                       .name = "BassBoostSw"},
+            .capability = Capability::make<Capability::bassBoost>(kCapability)};
+
+    /* parameters */
+    BassBoost mSpecificParam;
+};
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/config/audio_policy_configuration.xsd b/audio/aidl/default/config/audio_policy_configuration.xsd
new file mode 100644
index 0000000..823b217
--- /dev/null
+++ b/audio/aidl/default/config/audio_policy_configuration.xsd
@@ -0,0 +1,827 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+         Licensed under the Apache License, Version 2.0 (the "License");
+         you may not use this file except in compliance with the License.
+         You may obtain a copy of the License at
+
+                    http://www.apache.org/licenses/LICENSE-2.0
+
+         Unless required by applicable law or agreed to in writing, software
+         distributed under the License is distributed on an "AS IS" BASIS,
+         WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+         See the License for the specific language governing permissions and
+         limitations under the License.
+-->
+<xs:schema version="2.0"
+           elementFormDefault="qualified"
+           attributeFormDefault="unqualified"
+           xmlns:xs="http://www.w3.org/2001/XMLSchema">
+    <!-- List the config versions supported by audio policy. -->
+    <xs:simpleType name="version">
+        <xs:restriction base="xs:decimal">
+            <xs:enumeration value="7.0"/>
+            <xs:enumeration value="7.1"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="halVersion">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+                Version of the interface the hal implements. Note that this
+                relates to legacy HAL API versions since HIDL APIs are versioned
+                using other mechanisms.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:decimal">
+            <!-- List of HAL versions supported by the framework. -->
+            <xs:enumeration value="2.0"/>
+            <xs:enumeration value="3.0"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:element name="audioPolicyConfiguration">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="globalConfiguration" type="globalConfiguration"/>
+                <xs:element name="modules" type="modules" maxOccurs="unbounded"/>
+                <xs:element name="volumes" type="volumes" maxOccurs="unbounded"/>
+                <xs:element name="surroundSound" type="surroundSound" minOccurs="0" />
+            </xs:sequence>
+            <xs:attribute name="version" type="version"/>
+        </xs:complexType>
+        <xs:key name="moduleNameKey">
+            <xs:selector xpath="modules/module"/>
+            <xs:field xpath="@name"/>
+        </xs:key>
+        <xs:unique name="volumeTargetUniqueness">
+            <xs:selector xpath="volumes/volume"/>
+            <xs:field xpath="@stream"/>
+            <xs:field xpath="@deviceCategory"/>
+        </xs:unique>
+        <xs:key name="volumeCurveNameKey">
+            <xs:selector xpath="volumes/reference"/>
+            <xs:field xpath="@name"/>
+        </xs:key>
+        <xs:keyref name="volumeCurveRef" refer="volumeCurveNameKey">
+            <xs:selector xpath="volumes/volume"/>
+            <xs:field xpath="@ref"/>
+        </xs:keyref>
+    </xs:element>
+    <xs:complexType name="globalConfiguration">
+        <xs:attribute name="speaker_drc_enabled" type="xs:boolean" use="required"/>
+        <xs:attribute name="call_screen_mode_supported" type="xs:boolean" use="optional"/>
+        <xs:attribute name="engine_library" type="engineSuffix" use="optional"/>
+    </xs:complexType>
+    <xs:complexType name="modules">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+                There should be one section per audio HW module present on the platform.
+                Each <module/> contains two mandatory tags: “halVersion” and “name”.
+                The module "name" is the same as in previous .conf file.
+                Each module must contain the following sections:
+                 - <devicePorts/>: a list of device descriptors for all
+                   input and output devices accessible via this module.
+                   This contains both permanently attached devices and removable devices.
+                 - <mixPorts/>: listing all output and input streams exposed by the audio HAL
+                 - <routes/>: list of possible connections between input
+                   and output devices or between stream and devices.
+                   A <route/> is defined by a set of 3 attributes:
+                        -"type": mux|mix means all sources are mutual exclusive (mux) or can be mixed (mix)
+                        -"sink": the sink involved in this route
+                        -"sources": all the sources than can be connected to the sink via this route
+                 - <attachedDevices/>: permanently attached devices.
+                   The attachedDevices section is a list of devices names.
+                   Their names correspond to device names defined in "devicePorts" section.
+                 - <defaultOutputDevice/> is the device to be used when no policy rule applies
+            </xs:documentation>
+        </xs:annotation>
+        <xs:sequence>
+            <xs:element name="module" maxOccurs="unbounded">
+                <xs:complexType>
+                    <xs:sequence>
+                        <xs:element name="attachedDevices" type="attachedDevices" minOccurs="0">
+                            <xs:unique name="attachedDevicesUniqueness">
+                                <xs:selector xpath="item"/>
+                                <xs:field xpath="."/>
+                            </xs:unique>
+                        </xs:element>
+                        <xs:element name="defaultOutputDevice" type="xs:token" minOccurs="0"/>
+                        <xs:element name="mixPorts" type="mixPorts" minOccurs="0"/>
+                        <xs:element name="devicePorts" type="devicePorts" minOccurs="0"/>
+                        <xs:element name="routes" type="routes" minOccurs="0"/>
+                    </xs:sequence>
+                    <xs:attribute name="name" type="xs:string" use="required"/>
+                    <xs:attribute name="halVersion" type="halVersion" use="required"/>
+                </xs:complexType>
+                <xs:unique name="mixPortNameUniqueness">
+                    <xs:selector xpath="mixPorts/mixPort"/>
+                    <xs:field xpath="@name"/>
+                </xs:unique>
+                <xs:key name="devicePortNameKey">
+                    <xs:selector xpath="devicePorts/devicePort"/>
+                    <xs:field xpath="@tagName"/>
+                </xs:key>
+                <xs:unique name="devicePortUniqueness">
+                    <xs:selector xpath="devicePorts/devicePort"/>
+                    <xs:field xpath="@type"/>
+                    <xs:field xpath="@address"/>
+                </xs:unique>
+                <xs:keyref name="defaultOutputDeviceRef" refer="devicePortNameKey">
+                    <xs:selector xpath="defaultOutputDevice"/>
+                    <xs:field xpath="."/>
+                </xs:keyref>
+                <xs:keyref name="attachedDeviceRef" refer="devicePortNameKey">
+                    <xs:selector xpath="attachedDevices/item"/>
+                    <xs:field xpath="."/>
+                </xs:keyref>
+                <!-- The following 3 constraints try to make sure each sink port
+                     is reference in one an only one route. -->
+                <xs:key name="routeSinkKey">
+                    <!-- predicate [@type='sink'] does not work in xsd 1.0 -->
+                    <xs:selector xpath="devicePorts/devicePort|mixPorts/mixPort"/>
+                    <xs:field xpath="@tagName|@name"/>
+                </xs:key>
+                <xs:keyref name="routeSinkRef" refer="routeSinkKey">
+                    <xs:selector xpath="routes/route"/>
+                    <xs:field xpath="@sink"/>
+                </xs:keyref>
+                <xs:unique name="routeUniqueness">
+                    <xs:selector xpath="routes/route"/>
+                    <xs:field xpath="@sink"/>
+                </xs:unique>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="attachedDevices">
+        <xs:sequence>
+            <xs:element name="item" type="xs:token" minOccurs="0" maxOccurs="unbounded"/>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:simpleType name="audioInOutFlag">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+              The flags indicate suggested stream attributes supported by the profile.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_DIRECT" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_PRIMARY" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_FAST" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_DEEP_BUFFER" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_NON_BLOCKING" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_HW_AV_SYNC" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_TTS" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_RAW" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_SYNC" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_DIRECT_PCM" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_MMAP_NOIRQ" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_VOIP_RX" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_INCALL_MUSIC" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_GAPLESS_OFFLOAD" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_SPATIALIZER" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_ULTRASOUND" />
+            <xs:enumeration value="AUDIO_INPUT_FLAG_FAST" />
+            <xs:enumeration value="AUDIO_INPUT_FLAG_HW_HOTWORD" />
+            <xs:enumeration value="AUDIO_INPUT_FLAG_RAW" />
+            <xs:enumeration value="AUDIO_INPUT_FLAG_SYNC" />
+            <xs:enumeration value="AUDIO_INPUT_FLAG_MMAP_NOIRQ" />
+            <xs:enumeration value="AUDIO_INPUT_FLAG_VOIP_TX" />
+            <xs:enumeration value="AUDIO_INPUT_FLAG_HW_AV_SYNC" />
+            <xs:enumeration value="AUDIO_INPUT_FLAG_DIRECT" />
+            <xs:enumeration value="AUDIO_INPUT_FLAG_ULTRASOUND" />
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="audioInOutFlags">
+        <xs:list itemType="audioInOutFlag" />
+    </xs:simpleType>
+    <xs:simpleType name="role">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="sink"/>
+            <xs:enumeration value="source"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:complexType name="mixPorts">
+        <xs:sequence>
+            <xs:element name="mixPort" minOccurs="0" maxOccurs="unbounded">
+                <xs:complexType>
+                    <xs:sequence>
+                        <xs:element name="profile" type="profile" minOccurs="0" maxOccurs="unbounded"/>
+                        <xs:element name="gains" type="gains" minOccurs="0"/>
+                    </xs:sequence>
+                    <xs:attribute name="name" type="xs:token" use="required"/>
+                    <xs:attribute name="role" type="role" use="required"/>
+                    <xs:attribute name="flags" type="audioInOutFlags"/>
+                    <xs:attribute name="maxOpenCount" type="xs:unsignedInt"/>
+                    <xs:attribute name="maxActiveCount" type="xs:unsignedInt"/>
+                    <xs:attribute name="preferredUsage" type="audioUsageList">
+                        <xs:annotation>
+                            <xs:documentation xml:lang="en">
+                                When choosing the mixPort of an audio track, the audioPolicy
+                                first considers the mixPorts with a preferredUsage including
+                                the track AudioUsage preferred .
+                                If non support the track format, the other mixPorts are considered.
+                                Eg: a <mixPort preferredUsage="AUDIO_USAGE_MEDIA" /> will receive
+                                    the audio of all apps playing with a MEDIA usage.
+                                    It may receive audio from ALARM if there are no audio compatible
+                                    <mixPort preferredUsage="AUDIO_USAGE_ALARM" />.
+                             </xs:documentation>
+                        </xs:annotation>
+                    </xs:attribute>
+                    <xs:attribute name="recommendedMuteDurationMs" type="xs:unsignedInt"/>
+                </xs:complexType>
+                <xs:unique name="mixPortProfileUniqueness">
+                    <xs:selector xpath="profile"/>
+                    <xs:field xpath="format"/>
+                    <xs:field xpath="samplingRate"/>
+                    <xs:field xpath="channelMasks"/>
+                </xs:unique>
+                <xs:unique name="mixPortGainUniqueness">
+                    <xs:selector xpath="gains/gain"/>
+                    <xs:field xpath="@name"/>
+                </xs:unique>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:simpleType name="audioDevice">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="AUDIO_DEVICE_NONE"/>
+
+            <xs:enumeration value="AUDIO_DEVICE_OUT_EARPIECE"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_SPEAKER"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_WIRED_HEADSET"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_WIRED_HEADPHONE"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_SCO"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_HDMI"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_HDMI_EARC"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_AUX_DIGITAL"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_USB_ACCESSORY"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_USB_DEVICE"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_REMOTE_SUBMIX"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_TELEPHONY_TX"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_LINE"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_HDMI_ARC"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_SPDIF"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_FM"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_AUX_LINE"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_SPEAKER_SAFE"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_IP"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_BUS"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_PROXY"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_USB_HEADSET"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_HEARING_AID"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_ECHO_CANCELLER"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_BLE_HEADSET"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_BLE_SPEAKER"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_BLE_BROADCAST"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_DEFAULT"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_STUB"/>
+
+            <xs:enumeration value="AUDIO_DEVICE_IN_COMMUNICATION"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_AMBIENT"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_BUILTIN_MIC"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_WIRED_HEADSET"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_HDMI"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_AUX_DIGITAL"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_VOICE_CALL"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_TELEPHONY_RX"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_BACK_MIC"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_REMOTE_SUBMIX"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_USB_ACCESSORY"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_USB_DEVICE"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_FM_TUNER"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_TV_TUNER"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_LINE"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_SPDIF"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_BLUETOOTH_A2DP"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_LOOPBACK"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_IP"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_BUS"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_PROXY"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_USB_HEADSET"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_BLUETOOTH_BLE"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_HDMI_ARC"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_HDMI_EARC"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_ECHO_REFERENCE"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_BLE_HEADSET"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_DEFAULT"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_STUB"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="vendorExtension">
+        <!-- Vendor extension names must be prefixed by "VX_" to distinguish them from
+             AOSP values. Vendors must namespace their names to avoid conflicts. The
+             namespace part must only use capital latin characters and decimal digits and
+             consist of at least 3 characters. The part of the extension name after the
+             namespace may in addition include underscores. Example for a hypothetical
+             Google virtual reality device:
+
+                 <devicePort tagName="VR" type="VX_GOOGLE_VR" role="sink" />
+        -->
+        <xs:restriction base="xs:string">
+            <xs:pattern value="VX_[A-Z0-9]{3,}_[_A-Z0-9]+"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="extendableAudioDevice">
+        <xs:union memberTypes="audioDevice vendorExtension"/>
+    </xs:simpleType>
+    <xs:simpleType name="audioFormat">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="AUDIO_FORMAT_DEFAULT" />
+            <xs:enumeration value="AUDIO_FORMAT_PCM_16_BIT" />
+            <xs:enumeration value="AUDIO_FORMAT_PCM_8_BIT"/>
+            <xs:enumeration value="AUDIO_FORMAT_PCM_32_BIT"/>
+            <xs:enumeration value="AUDIO_FORMAT_PCM_8_24_BIT"/>
+            <xs:enumeration value="AUDIO_FORMAT_PCM_FLOAT"/>
+            <xs:enumeration value="AUDIO_FORMAT_PCM_24_BIT_PACKED"/>
+            <xs:enumeration value="AUDIO_FORMAT_MP3"/>
+            <xs:enumeration value="AUDIO_FORMAT_AMR_NB"/>
+            <xs:enumeration value="AUDIO_FORMAT_AMR_WB"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_MAIN"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_LC"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_SSR"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_LTP"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_HE_V1"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_SCALABLE"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ERLC"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_LD"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_HE_V2"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ELD"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_XHE"/>
+            <xs:enumeration value="AUDIO_FORMAT_HE_AAC_V1"/>
+            <xs:enumeration value="AUDIO_FORMAT_HE_AAC_V2"/>
+            <xs:enumeration value="AUDIO_FORMAT_VORBIS"/>
+            <xs:enumeration value="AUDIO_FORMAT_OPUS"/>
+            <xs:enumeration value="AUDIO_FORMAT_AC3"/>
+            <xs:enumeration value="AUDIO_FORMAT_E_AC3"/>
+            <xs:enumeration value="AUDIO_FORMAT_E_AC3_JOC"/>
+            <xs:enumeration value="AUDIO_FORMAT_DTS"/>
+            <xs:enumeration value="AUDIO_FORMAT_DTS_HD"/>
+            <xs:enumeration value="AUDIO_FORMAT_IEC61937"/>
+            <xs:enumeration value="AUDIO_FORMAT_DOLBY_TRUEHD"/>
+            <xs:enumeration value="AUDIO_FORMAT_EVRC"/>
+            <xs:enumeration value="AUDIO_FORMAT_EVRCB"/>
+            <xs:enumeration value="AUDIO_FORMAT_EVRCWB"/>
+            <xs:enumeration value="AUDIO_FORMAT_EVRCNW"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADIF"/>
+            <xs:enumeration value="AUDIO_FORMAT_WMA"/>
+            <xs:enumeration value="AUDIO_FORMAT_WMA_PRO"/>
+            <xs:enumeration value="AUDIO_FORMAT_AMR_WB_PLUS"/>
+            <xs:enumeration value="AUDIO_FORMAT_MP2"/>
+            <xs:enumeration value="AUDIO_FORMAT_QCELP"/>
+            <xs:enumeration value="AUDIO_FORMAT_DSD"/>
+            <xs:enumeration value="AUDIO_FORMAT_FLAC"/>
+            <xs:enumeration value="AUDIO_FORMAT_ALAC"/>
+            <xs:enumeration value="AUDIO_FORMAT_APE"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_MAIN"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_LC"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_SSR"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_LTP"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_HE_V1"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_SCALABLE"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_ERLC"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_LD"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_HE_V2"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_ELD"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_XHE"/>
+            <xs:enumeration value="AUDIO_FORMAT_SBC"/>
+            <xs:enumeration value="AUDIO_FORMAT_APTX"/>
+            <xs:enumeration value="AUDIO_FORMAT_APTX_HD"/>
+            <xs:enumeration value="AUDIO_FORMAT_AC4"/>
+            <xs:enumeration value="AUDIO_FORMAT_LDAC"/>
+            <xs:enumeration value="AUDIO_FORMAT_MAT"/>
+            <xs:enumeration value="AUDIO_FORMAT_MAT_1_0"/>
+            <xs:enumeration value="AUDIO_FORMAT_MAT_2_0"/>
+            <xs:enumeration value="AUDIO_FORMAT_MAT_2_1"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_LATM"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_LATM_LC"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_LATM_HE_V1"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_LATM_HE_V2"/>
+            <xs:enumeration value="AUDIO_FORMAT_CELT"/>
+            <xs:enumeration value="AUDIO_FORMAT_APTX_ADAPTIVE"/>
+            <xs:enumeration value="AUDIO_FORMAT_LHDC"/>
+            <xs:enumeration value="AUDIO_FORMAT_LHDC_LL"/>
+            <xs:enumeration value="AUDIO_FORMAT_APTX_TWSP"/>
+            <xs:enumeration value="AUDIO_FORMAT_LC3"/>
+            <xs:enumeration value="AUDIO_FORMAT_MPEGH_BL_L3"/>
+            <xs:enumeration value="AUDIO_FORMAT_MPEGH_BL_L4"/>
+            <xs:enumeration value="AUDIO_FORMAT_MPEGH_LC_L3"/>
+            <xs:enumeration value="AUDIO_FORMAT_MPEGH_LC_L4"/>
+            <xs:enumeration value="AUDIO_FORMAT_IEC60958"/>
+            <xs:enumeration value="AUDIO_FORMAT_DTS_UHD"/>
+            <xs:enumeration value="AUDIO_FORMAT_DRA"/>
+            <xs:enumeration value="AUDIO_FORMAT_APTX_ADAPTIVE_QLEA"/>
+            <xs:enumeration value="AUDIO_FORMAT_APTX_ADAPTIVE_R4"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="extendableAudioFormat">
+        <xs:union memberTypes="audioFormat vendorExtension"/>
+    </xs:simpleType>
+    <xs:simpleType name="audioUsage">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+                Audio usage specifies the intended use case for the sound being played.
+                Please consult frameworks/base/media/java/android/media/AudioAttributes.java
+                for the description of each value.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="AUDIO_USAGE_UNKNOWN" />
+            <xs:enumeration value="AUDIO_USAGE_MEDIA" />
+            <xs:enumeration value="AUDIO_USAGE_VOICE_COMMUNICATION" />
+            <xs:enumeration value="AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING" />
+            <xs:enumeration value="AUDIO_USAGE_ALARM" />
+            <xs:enumeration value="AUDIO_USAGE_NOTIFICATION" />
+            <xs:enumeration value="AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE" />
+            <xs:enumeration value="AUDIO_USAGE_NOTIFICATION_EVENT" />
+            <xs:enumeration value="AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY" />
+            <xs:enumeration value="AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE" />
+            <xs:enumeration value="AUDIO_USAGE_ASSISTANCE_SONIFICATION" />
+            <xs:enumeration value="AUDIO_USAGE_GAME" />
+            <xs:enumeration value="AUDIO_USAGE_VIRTUAL_SOURCE" />
+            <xs:enumeration value="AUDIO_USAGE_ASSISTANT" />
+            <xs:enumeration value="AUDIO_USAGE_CALL_ASSISTANT" />
+            <xs:enumeration value="AUDIO_USAGE_EMERGENCY" />
+            <xs:enumeration value="AUDIO_USAGE_SAFETY" />
+            <xs:enumeration value="AUDIO_USAGE_VEHICLE_STATUS" />
+            <xs:enumeration value="AUDIO_USAGE_ANNOUNCEMENT" />
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="audioUsageList">
+        <xs:list itemType="audioUsage"/>
+    </xs:simpleType>
+    <xs:simpleType name="audioContentType">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+                Audio content type expresses the general category of the content.
+                Please consult frameworks/base/media/java/android/media/AudioAttributes.java
+                for the description of each value.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="AUDIO_CONTENT_TYPE_UNKNOWN"/>
+            <xs:enumeration value="AUDIO_CONTENT_TYPE_SPEECH"/>
+            <xs:enumeration value="AUDIO_CONTENT_TYPE_MUSIC"/>
+            <xs:enumeration value="AUDIO_CONTENT_TYPE_MOVIE"/>
+            <xs:enumeration value="AUDIO_CONTENT_TYPE_SONIFICATION"/>
+            <xs:enumeration value="AUDIO_CONTENT_TYPE_ULTRASOUND"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="samplingRates">
+        <xs:list itemType="xs:nonNegativeInteger" />
+    </xs:simpleType>
+    <xs:simpleType name="audioChannelMask">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+                Audio channel mask specifies presence of particular channels.
+                There are two representations:
+                - representation position (traditional discrete channel specification,
+                  e.g. "left", "right");
+                - indexed (this is similar to "tracks" in audio mixing, channels
+                  are represented using numbers).
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="AUDIO_CHANNEL_NONE"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_MONO"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_STEREO"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_2POINT1"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_TRI"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_TRI_BACK"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_3POINT1"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_2POINT0POINT2"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_2POINT1POINT2"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_3POINT0POINT2"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_3POINT1POINT2"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_QUAD"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_QUAD_BACK"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_QUAD_SIDE"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_SURROUND"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_PENTA"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_5POINT1"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_5POINT1_BACK"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_5POINT1POINT2"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_5POINT1POINT4"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_6POINT1"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_7POINT1"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_7POINT1POINT2"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_7POINT1POINT4"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_9POINT1POINT4"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_9POINT1POINT6"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_13POINT_360RA"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_22POINT2"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_MONO_HAPTIC_A"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_HAPTIC_AB"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+            <xs:enumeration value="AUDIO_CHANNEL_IN_MONO"/>
+            <xs:enumeration value="AUDIO_CHANNEL_IN_STEREO"/>
+            <xs:enumeration value="AUDIO_CHANNEL_IN_FRONT_BACK"/>
+            <xs:enumeration value="AUDIO_CHANNEL_IN_6"/>
+            <xs:enumeration value="AUDIO_CHANNEL_IN_2POINT0POINT2"/>
+            <xs:enumeration value="AUDIO_CHANNEL_IN_2POINT1POINT2"/>
+            <xs:enumeration value="AUDIO_CHANNEL_IN_3POINT0POINT2"/>
+            <xs:enumeration value="AUDIO_CHANNEL_IN_3POINT1POINT2"/>
+            <xs:enumeration value="AUDIO_CHANNEL_IN_5POINT1"/>
+            <xs:enumeration value="AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO"/>
+            <xs:enumeration value="AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO"/>
+            <xs:enumeration value="AUDIO_CHANNEL_IN_VOICE_CALL_MONO"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_1"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_2"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_3"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_4"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_5"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_6"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_7"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_8"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_9"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_10"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_11"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_12"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_13"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_14"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_15"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_16"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_17"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_18"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_19"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_20"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_21"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_22"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_23"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_24"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="channelMasks">
+        <xs:list itemType="audioChannelMask" />
+    </xs:simpleType>
+    <xs:simpleType name="audioEncapsulationType">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="AUDIO_ENCAPSULATION_TYPE_NONE"/>
+            <xs:enumeration value="AUDIO_ENCAPSULATION_TYPE_IEC61937"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:complexType name="profile">
+        <xs:attribute name="name" type="xs:token" use="optional"/>
+        <xs:attribute name="format" type="extendableAudioFormat" use="optional"/>
+        <xs:attribute name="samplingRates" type="samplingRates" use="optional"/>
+        <xs:attribute name="channelMasks" type="channelMasks" use="optional"/>
+        <xs:attribute name="encapsulationType" type="audioEncapsulationType" use="optional"/>
+    </xs:complexType>
+    <xs:simpleType name="audioGainMode">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="AUDIO_GAIN_MODE_JOINT"/>
+            <xs:enumeration value="AUDIO_GAIN_MODE_CHANNELS"/>
+            <xs:enumeration value="AUDIO_GAIN_MODE_RAMP"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="audioGainModeMaskUnrestricted">
+        <xs:list itemType="audioGainMode" />
+    </xs:simpleType>
+    <xs:simpleType name='audioGainModeMask'>
+        <xs:restriction base='audioGainModeMaskUnrestricted'>
+            <xs:minLength value='1' />
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:complexType name="gains">
+        <xs:sequence>
+            <xs:element name="gain" minOccurs="0" maxOccurs="unbounded">
+                <xs:complexType>
+                    <xs:attribute name="name" type="xs:token" use="required"/>
+                    <xs:attribute name="mode" type="audioGainModeMask" use="required"/>
+                    <xs:attribute name="channel_mask" type="audioChannelMask" use="optional"/>
+                    <xs:attribute name="minValueMB" type="xs:int" use="optional"/>
+                    <xs:attribute name="maxValueMB" type="xs:int" use="optional"/>
+                    <xs:attribute name="defaultValueMB" type="xs:int" use="optional"/>
+                    <xs:attribute name="stepValueMB" type="xs:int" use="optional"/>
+                    <xs:attribute name="minRampMs" type="xs:int" use="optional"/>
+                    <xs:attribute name="maxRampMs" type="xs:int" use="optional"/>
+                    <xs:attribute name="useForVolume" type="xs:boolean" use="optional"/>
+                </xs:complexType>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="devicePorts">
+        <xs:sequence>
+            <xs:element name="devicePort" minOccurs="0" maxOccurs="unbounded">
+                <xs:complexType>
+                    <xs:sequence>
+                        <xs:element name="profile" type="profile" minOccurs="0" maxOccurs="unbounded"/>
+                        <xs:element name="gains" type="gains" minOccurs="0"/>
+                    </xs:sequence>
+                    <xs:attribute name="tagName" type="xs:token" use="required"/>
+                    <xs:attribute name="type" type="extendableAudioDevice" use="required"/>
+                    <xs:attribute name="role" type="role" use="required"/>
+                    <xs:attribute name="address" type="xs:string" use="optional" default=""/>
+                    <!-- Note that XSD 1.0 can not check that a type only has one default. -->
+                    <xs:attribute name="default" type="xs:boolean" use="optional">
+                        <xs:annotation>
+                            <xs:documentation xml:lang="en">
+                                The default device will be used if multiple have the same type
+                                and no explicit route request exists for a specific device of
+                                that type.
+                            </xs:documentation>
+                        </xs:annotation>
+                    </xs:attribute>
+                    <xs:attribute name="encodedFormats" type="audioFormatsList" use="optional"
+                                  default="" />
+                </xs:complexType>
+                <xs:unique name="devicePortProfileUniqueness">
+                    <xs:selector xpath="profile"/>
+                    <xs:field xpath="format"/>
+                    <xs:field xpath="samplingRate"/>
+                    <xs:field xpath="channelMasks"/>
+                </xs:unique>
+                <xs:unique name="devicePortGainUniqueness">
+                    <xs:selector xpath="gains/gain"/>
+                    <xs:field xpath="@name"/>
+                </xs:unique>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:simpleType name="mixType">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="mix"/>
+            <xs:enumeration value="mux"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:complexType name="routes">
+        <xs:sequence>
+            <xs:element name="route" minOccurs="0" maxOccurs="unbounded">
+                <xs:annotation>
+                    <xs:documentation xml:lang="en">
+                        List all available sources for a given sink.
+                    </xs:documentation>
+                </xs:annotation>
+                <xs:complexType>
+                    <xs:attribute name="type" type="mixType" use="required"/>
+                    <xs:attribute name="sink" type="xs:string" use="required"/>
+                    <xs:attribute name="sources" type="xs:string" use="required"/>
+                </xs:complexType>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="volumes">
+        <xs:sequence>
+            <xs:element name="volume" type="volume" minOccurs="0" maxOccurs="unbounded"/>
+            <xs:element name="reference" type="reference" minOccurs="0" maxOccurs="unbounded">
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+    <!-- TODO: Always require a ref for better xsd validations.
+               Currently a volume could have no points nor ref
+               as it can not be forbidden by xsd 1.0.-->
+    <xs:simpleType name="volumePoint">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+                Comma separated pair of number.
+                The fist one is the framework level (between 0 and 100).
+                The second one is the volume to send to the HAL.
+                The framework will interpolate volumes not specified.
+                Their MUST be at least 2 points specified.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:pattern value="([0-9]{1,2}|100),-?[0-9]+"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="audioStreamType">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+                Audio stream type describing the intended use case of a stream.
+                Please consult frameworks/base/media/java/android/media/AudioSystem.java
+                for the description of each value.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="AUDIO_STREAM_VOICE_CALL"/>
+            <xs:enumeration value="AUDIO_STREAM_SYSTEM"/>
+            <xs:enumeration value="AUDIO_STREAM_RING"/>
+            <xs:enumeration value="AUDIO_STREAM_MUSIC"/>
+            <xs:enumeration value="AUDIO_STREAM_ALARM"/>
+            <xs:enumeration value="AUDIO_STREAM_NOTIFICATION"/>
+            <xs:enumeration value="AUDIO_STREAM_BLUETOOTH_SCO"/>
+            <xs:enumeration value="AUDIO_STREAM_ENFORCED_AUDIBLE"/>
+            <xs:enumeration value="AUDIO_STREAM_DTMF"/>
+            <xs:enumeration value="AUDIO_STREAM_TTS"/>
+            <xs:enumeration value="AUDIO_STREAM_ACCESSIBILITY"/>
+            <xs:enumeration value="AUDIO_STREAM_ASSISTANT"/>
+            <xs:enumeration value="AUDIO_STREAM_REROUTING"/>
+            <xs:enumeration value="AUDIO_STREAM_PATCH"/>
+            <xs:enumeration value="AUDIO_STREAM_CALL_ASSISTANT"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="audioSource">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+                An audio source defines the intended use case for the sound being recorded.
+                Please consult frameworks/base/media/java/android/media/MediaRecorder.java
+                for the description of each value.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="AUDIO_SOURCE_DEFAULT"/>
+            <xs:enumeration value="AUDIO_SOURCE_MIC"/>
+            <xs:enumeration value="AUDIO_SOURCE_VOICE_UPLINK"/>
+            <xs:enumeration value="AUDIO_SOURCE_VOICE_DOWNLINK"/>
+            <xs:enumeration value="AUDIO_SOURCE_VOICE_CALL"/>
+            <xs:enumeration value="AUDIO_SOURCE_CAMCORDER"/>
+            <xs:enumeration value="AUDIO_SOURCE_VOICE_RECOGNITION"/>
+            <xs:enumeration value="AUDIO_SOURCE_VOICE_COMMUNICATION"/>
+            <xs:enumeration value="AUDIO_SOURCE_REMOTE_SUBMIX"/>
+            <xs:enumeration value="AUDIO_SOURCE_UNPROCESSED"/>
+            <xs:enumeration value="AUDIO_SOURCE_VOICE_PERFORMANCE"/>
+            <xs:enumeration value="AUDIO_SOURCE_ECHO_REFERENCE"/>
+            <xs:enumeration value="AUDIO_SOURCE_FM_TUNER"/>
+            <xs:enumeration value="AUDIO_SOURCE_HOTWORD"/>
+            <xs:enumeration value="AUDIO_SOURCE_ULTRASOUND"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <!-- Enum values of device_category from Volume.h. -->
+    <xs:simpleType name="deviceCategory">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="DEVICE_CATEGORY_HEADSET"/>
+            <xs:enumeration value="DEVICE_CATEGORY_SPEAKER"/>
+            <xs:enumeration value="DEVICE_CATEGORY_EARPIECE"/>
+            <xs:enumeration value="DEVICE_CATEGORY_EXT_MEDIA"/>
+            <xs:enumeration value="DEVICE_CATEGORY_HEARING_AID"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:complexType name="volume">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+                Volume section defines a volume curve for a given use case and device category.
+                It contains a list of points of this curve expressing the attenuation in Millibels
+                for a given volume index from 0 to 100.
+                <volume stream="AUDIO_STREAM_MUSIC" deviceCategory="DEVICE_CATEGORY_SPEAKER">
+                    <point>0,-9600</point>
+                    <point>100,0</point>
+                </volume>
+
+                It may also reference a reference/@name to avoid duplicating curves.
+                <volume stream="AUDIO_STREAM_MUSIC" deviceCategory="DEVICE_CATEGORY_SPEAKER"
+                        ref="DEFAULT_MEDIA_VOLUME_CURVE"/>
+                <reference name="DEFAULT_MEDIA_VOLUME_CURVE">
+                    <point>0,-9600</point>
+                    <point>100,0</point>
+                </reference>
+            </xs:documentation>
+        </xs:annotation>
+        <xs:sequence>
+            <xs:element name="point" type="volumePoint" minOccurs="0" maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attribute name="stream" type="audioStreamType"/>
+        <xs:attribute name="deviceCategory" type="deviceCategory"/>
+        <xs:attribute name="ref" type="xs:token" use="optional"/>
+    </xs:complexType>
+    <xs:complexType name="reference">
+        <xs:sequence>
+            <xs:element name="point" type="volumePoint" minOccurs="2" maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attribute name="name" type="xs:token" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="surroundSound">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+                Surround Sound section provides configuration related to handling of
+                multi-channel formats.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:sequence>
+            <xs:element name="formats" type="surroundFormats"/>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:simpleType name="audioFormatsList">
+        <xs:list itemType="extendableAudioFormat" />
+    </xs:simpleType>
+    <xs:complexType name="surroundFormats">
+        <xs:sequence>
+            <xs:element name="format" minOccurs="0" maxOccurs="unbounded">
+                <xs:complexType>
+                    <xs:attribute name="name" type="extendableAudioFormat" use="required"/>
+                    <xs:attribute name="subformats" type="audioFormatsList" />
+                </xs:complexType>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:simpleType name="engineSuffix">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="default"/>
+            <xs:enumeration value="configurable"/>
+        </xs:restriction>
+    </xs:simpleType>
+</xs:schema>
diff --git a/audio/aidl/default/dynamicProcessing/Android.bp b/audio/aidl/default/dynamicProcessing/Android.bp
new file mode 100644
index 0000000..3697ba3
--- /dev/null
+++ b/audio/aidl/default/dynamicProcessing/Android.bp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+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"],
+}
+
+cc_library_shared {
+    name: "libdynamicsprocessingsw",
+    defaults: [
+        "aidlaudioeffectservice_defaults",
+        "latest_android_media_audio_common_types_ndk_shared",
+        "latest_android_hardware_audio_effect_ndk_shared",
+    ],
+    srcs: [
+        "DynamicsProcessingSw.cpp",
+        ":effectCommonFile",
+    ],
+    visibility: [
+        "//hardware/interfaces/audio/aidl/default",
+    ],
+}
diff --git a/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp b/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp
new file mode 100644
index 0000000..52403e1
--- /dev/null
+++ b/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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 <cstddef>
+#define LOG_TAG "AHAL_DynamicsProcessingSw"
+#include <Utils.h>
+#include <algorithm>
+#include <unordered_set>
+
+#include <android-base/logging.h>
+#include <fmq/AidlMessageQueue.h>
+
+#include "DynamicsProcessingSw.h"
+
+using aidl::android::hardware::audio::effect::DynamicsProcessingSw;
+using aidl::android::hardware::audio::effect::DynamicsProcessingSwImplUUID;
+using aidl::android::hardware::audio::effect::IEffect;
+using aidl::android::hardware::audio::effect::State;
+using aidl::android::media::audio::common::AudioUuid;
+
+extern "C" binder_exception_t createEffect(const AudioUuid* in_impl_uuid,
+                                           std::shared_ptr<IEffect>* instanceSpp) {
+    if (!in_impl_uuid || *in_impl_uuid != DynamicsProcessingSwImplUUID) {
+        LOG(ERROR) << __func__ << "uuid not supported";
+        return EX_ILLEGAL_ARGUMENT;
+    }
+    if (instanceSpp) {
+        *instanceSpp = ndk::SharedRefBase::make<DynamicsProcessingSw>();
+        LOG(DEBUG) << __func__ << " instance " << instanceSpp->get() << " created";
+        return EX_NONE;
+    } else {
+        LOG(ERROR) << __func__ << " invalid input parameter!";
+        return EX_ILLEGAL_ARGUMENT;
+    }
+}
+
+extern "C" binder_exception_t destroyEffect(const std::shared_ptr<IEffect>& instanceSp) {
+    if (!instanceSp) {
+        return EX_NONE;
+    }
+    State state;
+    ndk::ScopedAStatus status = instanceSp->getState(&state);
+    if (!status.isOk() || State::INIT != state) {
+        LOG(ERROR) << __func__ << " instance " << instanceSp.get()
+                   << " in state: " << toString(state) << ", status: " << status.getDescription();
+        return EX_ILLEGAL_STATE;
+    }
+    LOG(DEBUG) << __func__ << " instance " << instanceSp.get() << " destroyed";
+    return EX_NONE;
+}
+
+namespace aidl::android::hardware::audio::effect {
+
+ndk::ScopedAStatus DynamicsProcessingSw::getDescriptor(Descriptor* _aidl_return) {
+    LOG(DEBUG) << __func__ << kDescriptor.toString();
+    *_aidl_return = kDescriptor;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus DynamicsProcessingSw::setParameterSpecific(const Parameter::Specific& specific) {
+    RETURN_IF(Parameter::Specific::dynamicsProcessing != specific.getTag(), EX_ILLEGAL_ARGUMENT,
+              "EffectNotSupported");
+    std::lock_guard lg(mMutex);
+    RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+
+    mSpecificParam = specific.get<Parameter::Specific::dynamicsProcessing>();
+    LOG(DEBUG) << __func__ << " success with: " << specific.toString();
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus DynamicsProcessingSw::getParameterSpecific(const Parameter::Id& id,
+                                                              Parameter::Specific* specific) {
+    auto tag = id.getTag();
+    RETURN_IF(Parameter::Id::dynamicsProcessingTag != tag, EX_ILLEGAL_ARGUMENT, "wrongIdTag");
+    specific->set<Parameter::Specific::dynamicsProcessing>(mSpecificParam);
+    return ndk::ScopedAStatus::ok();
+}
+
+std::shared_ptr<EffectContext> DynamicsProcessingSw::createContext(
+        const Parameter::Common& common) {
+    if (mContext) {
+        LOG(DEBUG) << __func__ << " context already exist";
+        return mContext;
+    }
+    mContext = std::make_shared<DynamicsProcessingSwContext>(1 /* statusFmqDepth */, common);
+    return mContext;
+}
+
+RetCode DynamicsProcessingSw::releaseContext() {
+    if (mContext) {
+        mContext.reset();
+    }
+    return RetCode::SUCCESS;
+}
+
+// Processing method running in EffectWorker thread.
+IEffect::Status DynamicsProcessingSw::effectProcessImpl(float* in, float* out, int process) {
+    // TODO: get data buffer and process.
+    LOG(DEBUG) << __func__ << " in " << in << " out " << out << " process " << process;
+    for (int i = 0; i < process; i++) {
+        *out++ = *in++;
+    }
+    return {STATUS_OK, process, process};
+}
+
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.h b/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.h
new file mode 100644
index 0000000..fc26902
--- /dev/null
+++ b/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.h
@@ -0,0 +1,72 @@
+/*
+ * 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/audio/effect/BnEffect.h>
+#include <fmq/AidlMessageQueue.h>
+#include <cstdlib>
+#include <memory>
+
+#include "effect-impl/EffectImpl.h"
+#include "effect-impl/EffectUUID.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+class DynamicsProcessingSwContext final : public EffectContext {
+  public:
+    DynamicsProcessingSwContext(int statusDepth, const Parameter::Common& common)
+        : EffectContext(statusDepth, common) {
+        LOG(DEBUG) << __func__;
+    }
+    // TODO: add specific context here
+};
+
+class DynamicsProcessingSw final : public EffectImpl {
+  public:
+    DynamicsProcessingSw() { LOG(DEBUG) << __func__; }
+    ~DynamicsProcessingSw() {
+        LOG(DEBUG) << __func__;
+        releaseContext();
+    }
+
+    ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
+    ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific) override;
+    ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id,
+                                            Parameter::Specific* specific) override;
+    IEffect::Status effectProcessImpl(float* in, float* out, int process) override;
+    std::shared_ptr<EffectContext> createContext(const Parameter::Common& common) override;
+    RetCode releaseContext() override;
+
+  private:
+    std::shared_ptr<DynamicsProcessingSwContext> mContext;
+    /* capabilities */
+    const DynamicsProcessing::Capability kCapability;
+    /* Effect descriptor */
+    const Descriptor kDescriptor = {
+            .common = {.id = {.type = DynamicsProcessingTypeUUID,
+                              .uuid = DynamicsProcessingSwImplUUID,
+                              .proxy = std::nullopt},
+                       .flags = {.type = Flags::Type::INSERT,
+                                 .insert = Flags::Insert::FIRST,
+                                 .volume = Flags::Volume::CTRL},
+                       .name = "DynamicsProcessingSw"},
+            .capability = Capability::make<Capability::dynamicsProcessing>(kCapability)};
+
+    /* parameters */
+    DynamicsProcessing mSpecificParam;
+};
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/equalizer/Android.bp b/audio/aidl/default/equalizer/Android.bp
index 2a2ddbc..8de6b1a 100644
--- a/audio/aidl/default/equalizer/Android.bp
+++ b/audio/aidl/default/equalizer/Android.bp
@@ -24,28 +24,16 @@
 }
 
 cc_library_shared {
-    name: "libequalizer",
-    vendor: true,
-    shared_libs: [
-        "libaudioaidlcommon",
-        "libbase",
-        "android.hardware.common-V2-ndk",
-    ],
+    name: "libequalizersw",
     defaults: [
-        "aidlaudioservice_defaults",
+        "aidlaudioeffectservice_defaults",
         "latest_android_media_audio_common_types_ndk_shared",
         "latest_android_hardware_audio_effect_ndk_shared",
     ],
     srcs: [
-        "Equalizer.cpp",
+        "EqualizerSw.cpp",
         ":effectCommonFile",
     ],
-    cflags: [
-        "-Wall",
-        "-Wextra",
-        "-Werror",
-        "-Wthread-safety",
-    ],
     visibility: [
         "//hardware/interfaces/audio/aidl/default",
     ],
diff --git a/audio/aidl/default/equalizer/Equalizer.cpp b/audio/aidl/default/equalizer/Equalizer.cpp
deleted file mode 100644
index 2e4e538..0000000
--- a/audio/aidl/default/equalizer/Equalizer.cpp
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- * 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 "AHAL_EqualizerSw"
-#include <Utils.h>
-#include <android-base/logging.h>
-#include <unordered_set>
-
-#include "effect-impl/EffectUUID.h"
-#include "equalizer-impl/EqualizerSw.h"
-
-using android::hardware::audio::common::getFrameSizeInBytes;
-
-namespace aidl::android::hardware::audio::effect {
-
-extern "C" binder_exception_t createEffect(std::shared_ptr<IEffect>* instanceSpp) {
-    if (instanceSpp) {
-        *instanceSpp = ndk::SharedRefBase::make<EqualizerSw>();
-        LOG(DEBUG) << __func__ << " instance " << instanceSpp->get() << " created";
-        return EX_NONE;
-    } else {
-        LOG(ERROR) << __func__ << " invalid input parameter!";
-        return EX_ILLEGAL_ARGUMENT;
-    }
-}
-
-extern "C" binder_exception_t destroyEffect(const std::shared_ptr<IEffect>& instanceSp) {
-    State state;
-    ndk::ScopedAStatus status = instanceSp->getState(&state);
-    if (!status.isOk() || State::INIT != state) {
-        LOG(ERROR) << __func__ << " instance " << instanceSp.get()
-                   << " in state: " << toString(state) << ", status: " << status.getDescription();
-        return EX_ILLEGAL_STATE;
-    }
-    LOG(DEBUG) << __func__ << " instance " << instanceSp.get() << " destroyed";
-    return EX_NONE;
-}
-
-ndk::ScopedAStatus EqualizerSw::open(const Parameter::Common& common,
-                                     const Parameter::Specific& specific,
-                                     OpenEffectReturn* _aidl_return) {
-    LOG(DEBUG) << __func__;
-    if (mState != State::INIT) {
-        LOG(WARNING) << __func__ << " eq already open";
-        return ndk::ScopedAStatus::ok();
-    }
-
-    // Set essential parameters before create worker thread.
-    setCommonParameter(common);
-    setSpecificParameter(specific);
-
-    LOG(DEBUG) << " common: " << common.toString() << " specific " << specific.toString();
-
-    auto& input = common.input;
-    auto& output = common.output;
-    size_t inputFrameSize = getFrameSizeInBytes(input.base.format, input.base.channelMask);
-    size_t outputFrameSize = getFrameSizeInBytes(output.base.format, output.base.channelMask);
-    if (!createFmq(1, input.frameCount * inputFrameSize, output.frameCount * outputFrameSize,
-                   _aidl_return)) {
-        return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_UNSUPPORTED_OPERATION,
-                                                                "FailedToCreateFmq");
-    }
-
-    // create the worker thread
-    if (RetCode::SUCCESS != mWorker->create(LOG_TAG)) {
-        LOG(ERROR) << __func__ << " created worker thread failed";
-        destroyFmq();
-        return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_UNSUPPORTED_OPERATION,
-                                                                "FailedToCreateFmq");
-    }
-
-    mState = State::IDLE;
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus EqualizerSw::close() {
-    if (mState == State::INIT) {
-        LOG(WARNING) << __func__ << " instance already closed";
-        return ndk::ScopedAStatus::ok();
-    } else if (mState == State::PROCESSING) {
-        LOG(ERROR) << __func__ << " instance still processing";
-        return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE,
-                                                                "EqInstanceProcessing");
-    }
-
-    // stop the worker thread
-    mState = State::INIT;
-    mWorker->destroy();
-    destroyFmq();
-    LOG(DEBUG) << __func__;
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus EqualizerSw::getDescriptor(Descriptor* _aidl_return) {
-    LOG(DEBUG) << __func__ << mDesc.toString();
-    *_aidl_return = mDesc;
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus EqualizerSw::command(CommandId in_commandId) {
-    LOG(DEBUG) << __func__ << ": receive command:" << toString(in_commandId);
-    if (mState == State::INIT) {
-        LOG(ERROR) << __func__ << ": instance not open yet";
-        return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE,
-                                                                "CommandStateError");
-    }
-    switch (in_commandId) {
-        case CommandId::START:
-            // start processing.
-            mState = State::PROCESSING;
-            mWorker->start();
-            LOG(DEBUG) << __func__ << " state: " << toString(mState);
-            return ndk::ScopedAStatus::ok();
-        case CommandId::STOP:
-            // stop processing.
-            mState = State::IDLE;
-            mWorker->stop();
-            LOG(DEBUG) << __func__ << " state: " << toString(mState);
-            return ndk::ScopedAStatus::ok();
-        case CommandId::RESET:
-            // TODO: reset buffer status.
-            mState = State::IDLE;
-            mWorker->stop();
-            LOG(DEBUG) << __func__ << " state: " << toString(mState);
-            return ndk::ScopedAStatus::ok();
-        default:
-            return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
-                                                                    "CommandIdNotSupported");
-    }
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus EqualizerSw::setParameter(const Parameter& in_param) {
-    if (mState == State::INIT) {
-        LOG(ERROR) << __func__ << ": instance not open yet";
-        return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE, "StateError");
-    }
-    LOG(DEBUG) << __func__ << " with: " << in_param.toString();
-    auto tag = in_param.getTag();
-    switch (tag) {
-        case Parameter::common: {
-            return setCommonParameter(in_param.get<Parameter::common>());
-        }
-        case Parameter::specific: {
-            return setSpecificParameter(in_param.get<Parameter::specific>());
-        }
-        default:
-            return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
-                                                                    "ParameterNotSupported");
-    }
-}
-
-ndk::ScopedAStatus EqualizerSw::getParameter(const Parameter::Id& in_paramId,
-                                             Parameter* _aidl_return) {
-    LOG(DEBUG) << __func__ << in_paramId.toString();
-    auto tag = in_paramId.getTag();
-    switch (tag) {
-        case Parameter::Id::commonTag: {
-            _aidl_return->set<Parameter::common>(mCommonParam);
-            LOG(DEBUG) << __func__ << " get: " << _aidl_return->toString();
-            return ndk::ScopedAStatus::ok();
-        }
-        case Parameter::Id::specificTag: {
-            auto& id = in_paramId.get<Parameter::Id::specificTag>();
-            if (id != Parameter::Specific::equalizer) {
-                LOG(ERROR) << " unsupported parameter Id: " << in_paramId.toString();
-                return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
-                        EX_ILLEGAL_ARGUMENT, "Parameter::IdNotSupported");
-            }
-            Parameter::Specific specific;
-            specific.set<Parameter::Specific::equalizer>(mEqualizerParam);
-            _aidl_return->set<Parameter::specific>(specific);
-            LOG(DEBUG) << __func__ << _aidl_return->toString();
-            return ndk::ScopedAStatus::ok();
-        }
-        default:
-            return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
-                                                                    "Parameter::IdNotSupported");
-    }
-}
-
-ndk::ScopedAStatus EqualizerSw::getState(State* _aidl_return) {
-    *_aidl_return = mState;
-    return ndk::ScopedAStatus::ok();
-}
-
-/// Private methods.
-bool EqualizerSw::createFmq(int statusDepth, int inBufferSize, int outBufferSize,
-                            OpenEffectReturn* ret) {
-    mStatusMQ = std::make_unique<StatusMQ>(statusDepth, true /*configureEventFlagWord*/);
-    mInputMQ = std::make_unique<DataMQ>(inBufferSize);
-    mOutputMQ = std::make_unique<DataMQ>(outBufferSize);
-
-    if (!mStatusMQ->isValid() || !mInputMQ->isValid() || !mOutputMQ->isValid()) {
-        LOG(ERROR) << __func__ << " created invalid FMQ";
-        return false;
-    }
-    ret->statusMQ = mStatusMQ->dupeDesc();
-    ret->inputDataMQ = mInputMQ->dupeDesc();
-    ret->outputDataMQ = mOutputMQ->dupeDesc();
-    return true;
-}
-
-void EqualizerSw::destroyFmq() {
-    mStatusMQ.reset(nullptr);
-    mInputMQ.reset(nullptr);
-    mOutputMQ.reset(nullptr);
-}
-
-ndk::ScopedAStatus EqualizerSw::setCommonParameter(const Parameter::Common& common) {
-    mCommonParam = common;
-    LOG(DEBUG) << __func__ << " set: " << mCommonParam.toString();
-    return ndk::ScopedAStatus::ok();
-}
-
-// TODO: implementation need change to save all parameters.
-ndk::ScopedAStatus EqualizerSw::setSpecificParameter(const Parameter::Specific& specific) {
-    if (Parameter::Specific::equalizer != specific.getTag()) {
-        LOG(ERROR) << " unsupported effect: " << specific.toString();
-        return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
-                                                                "EffectNotSupported");
-    }
-
-    mEqualizerParam = specific.get<Parameter::Specific::equalizer>();
-    LOG(DEBUG) << __func__ << mEqualizerParam.toString();
-    return ndk::ScopedAStatus::ok();
-}
-
-void EqualizerSw::cleanUp() {
-    if (State::PROCESSING == mState) {
-        command(CommandId::STOP);
-    }
-    if (State::INIT != mState) {
-        close();
-    }
-}
-
-// Processing method running in worker thread.
-void EqualizerSwWorker::process() {
-    // TODO: add EQ processing with FMQ, should wait until data available before data processing.
-}
-
-}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/equalizer/EqualizerSw.cpp b/audio/aidl/default/equalizer/EqualizerSw.cpp
new file mode 100644
index 0000000..32c3969
--- /dev/null
+++ b/audio/aidl/default/equalizer/EqualizerSw.cpp
@@ -0,0 +1,170 @@
+/*
+ * 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 <cstddef>
+#define LOG_TAG "AHAL_EqualizerSw"
+#include <Utils.h>
+#include <algorithm>
+#include <unordered_set>
+
+#include <android-base/logging.h>
+#include <fmq/AidlMessageQueue.h>
+
+#include "EqualizerSw.h"
+
+using aidl::android::hardware::audio::effect::EqualizerSw;
+using aidl::android::hardware::audio::effect::EqualizerSwImplUUID;
+using aidl::android::hardware::audio::effect::IEffect;
+using aidl::android::hardware::audio::effect::State;
+using aidl::android::media::audio::common::AudioUuid;
+
+extern "C" binder_exception_t createEffect(const AudioUuid* in_impl_uuid,
+                                           std::shared_ptr<IEffect>* instanceSpp) {
+    if (!in_impl_uuid || *in_impl_uuid != EqualizerSwImplUUID) {
+        LOG(ERROR) << __func__ << "uuid not supported";
+        return EX_ILLEGAL_ARGUMENT;
+    }
+    if (instanceSpp) {
+        *instanceSpp = ndk::SharedRefBase::make<EqualizerSw>();
+        LOG(DEBUG) << __func__ << " instance " << instanceSpp->get() << " created";
+        return EX_NONE;
+    } else {
+        LOG(ERROR) << __func__ << " invalid input parameter!";
+        return EX_ILLEGAL_ARGUMENT;
+    }
+}
+
+extern "C" binder_exception_t destroyEffect(const std::shared_ptr<IEffect>& instanceSp) {
+    State state;
+    ndk::ScopedAStatus status = instanceSp->getState(&state);
+    if (!status.isOk() || State::INIT != state) {
+        LOG(ERROR) << __func__ << " instance " << instanceSp.get()
+                   << " in state: " << toString(state) << ", status: " << status.getDescription();
+        return EX_ILLEGAL_STATE;
+    }
+    LOG(DEBUG) << __func__ << " instance " << instanceSp.get() << " destroyed";
+    return EX_NONE;
+}
+
+namespace aidl::android::hardware::audio::effect {
+
+ndk::ScopedAStatus EqualizerSw::getDescriptor(Descriptor* _aidl_return) {
+    LOG(DEBUG) << __func__ << kDesc.toString();
+    *_aidl_return = kDesc;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus EqualizerSw::setParameterSpecific(const Parameter::Specific& specific) {
+    RETURN_IF(Parameter::Specific::equalizer != specific.getTag(), EX_ILLEGAL_ARGUMENT,
+              "EffectNotSupported");
+    std::lock_guard lg(mMutex);
+    RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+
+    auto& eqParam = specific.get<Parameter::Specific::equalizer>();
+    auto tag = eqParam.getTag();
+    switch (tag) {
+        case Equalizer::preset: {
+            RETURN_IF(mContext->setEqPreset(eqParam.get<Equalizer::preset>()) != RetCode::SUCCESS,
+                      EX_ILLEGAL_ARGUMENT, "setBandLevelsFailed");
+            return ndk::ScopedAStatus::ok();
+        }
+        case Equalizer::bandLevels: {
+            RETURN_IF(mContext->setEqBandLevels(eqParam.get<Equalizer::bandLevels>()) !=
+                              RetCode::SUCCESS,
+                      EX_ILLEGAL_ARGUMENT, "setBandLevelsFailed");
+            return ndk::ScopedAStatus::ok();
+        }
+        default: {
+            LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag);
+            return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+                                                                    "EqTagNotSupported");
+        }
+    }
+
+    LOG(ERROR) << __func__ << " unsupported eq param tag: " << toString(tag);
+    return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+                                                            "ParamNotSupported");
+}
+
+ndk::ScopedAStatus EqualizerSw::getParameterSpecific(const Parameter::Id& id,
+                                                     Parameter::Specific* specific) {
+    auto tag = id.getTag();
+    RETURN_IF(Parameter::Id::equalizerTag != tag, EX_ILLEGAL_ARGUMENT, "wrongIdTag");
+    auto eqId = id.get<Parameter::Id::equalizerTag>();
+    auto eqIdTag = eqId.getTag();
+    switch (eqIdTag) {
+        case Equalizer::Id::commonTag:
+            return getParameterEqualizer(eqId.get<Equalizer::Id::commonTag>(), specific);
+        default:
+            LOG(ERROR) << __func__ << " tag " << toString(eqIdTag) << " not supported";
+            return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+                                                                    "EqualizerTagNotSupported");
+    }
+}
+
+ndk::ScopedAStatus EqualizerSw::getParameterEqualizer(const Equalizer::Tag& tag,
+                                                      Parameter::Specific* specific) {
+    std::lock_guard lg(mMutex);
+    RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+
+    Equalizer eqParam;
+    switch (tag) {
+        case Equalizer::bandLevels: {
+            eqParam.set<Equalizer::bandLevels>(mContext->getEqBandLevels());
+            break;
+        }
+        case Equalizer::preset: {
+            eqParam.set<Equalizer::preset>(mContext->getEqPreset());
+            break;
+        }
+        default: {
+            LOG(ERROR) << __func__ << " not handled tag: " << toString(tag);
+            return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+                                                                    "unsupportedTag");
+        }
+    }
+
+    specific->set<Parameter::Specific::equalizer>(eqParam);
+    return ndk::ScopedAStatus::ok();
+}
+
+std::shared_ptr<EffectContext> EqualizerSw::createContext(const Parameter::Common& common) {
+    if (mContext) {
+        LOG(DEBUG) << __func__ << " context already exist";
+        return mContext;
+    }
+    mContext = std::make_shared<EqualizerSwContext>(1 /* statusFmqDepth */, common);
+    return mContext;
+}
+
+RetCode EqualizerSw::releaseContext() {
+    if (mContext) {
+        mContext.reset();
+    }
+    return RetCode::SUCCESS;
+}
+
+// Processing method running in EffectWorker thread.
+IEffect::Status EqualizerSw::effectProcessImpl(float* in, float* out, int process) {
+    // TODO: get data buffer and process.
+    LOG(DEBUG) << __func__ << " in " << in << " out " << out << " process " << process;
+    for (int i = 0; i < process; i++) {
+        *out++ = *in++;
+    }
+    return {STATUS_OK, process, process};
+}
+
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/equalizer/EqualizerSw.h b/audio/aidl/default/equalizer/EqualizerSw.h
new file mode 100644
index 0000000..8762f5d
--- /dev/null
+++ b/audio/aidl/default/equalizer/EqualizerSw.h
@@ -0,0 +1,124 @@
+/*
+ * 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/audio/effect/BnEffect.h>
+#include <fmq/AidlMessageQueue.h>
+#include <cstdlib>
+#include <memory>
+
+#include "effect-impl/EffectImpl.h"
+#include "effect-impl/EffectUUID.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+class EqualizerSwContext final : public EffectContext {
+  public:
+    EqualizerSwContext(int statusDepth, const Parameter::Common& common)
+        : EffectContext(statusDepth, common) {
+        LOG(DEBUG) << __func__;
+    }
+
+    RetCode setEqPreset(const int& presetIdx) {
+        if (presetIdx < 0 || presetIdx >= NUM_OF_PRESETS) {
+            return RetCode::ERROR_ILLEGAL_PARAMETER;
+        }
+        mPreset = presetIdx;
+        return RetCode::SUCCESS;
+    }
+    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;
+            return RetCode::ERROR_ILLEGAL_PARAMETER;
+        }
+        RetCode ret = RetCode::SUCCESS;
+        for (auto& it : bandLevels) {
+            if (it.index >= NUM_OF_BANDS || it.index < 0) {
+                LOG(ERROR) << __func__ << " index illegal, skip: " << it.index << " - "
+                           << it.levelMb;
+                ret = RetCode::ERROR_ILLEGAL_PARAMETER;
+            }
+            mBandLevels[it.index] = it.levelMb;
+        }
+        return ret;
+    }
+
+    std::vector<Equalizer::BandLevel> getEqBandLevels() {
+        std::vector<Equalizer::BandLevel> bandLevels;
+        for (int i = 0; i < NUM_OF_BANDS; i++) {
+            bandLevels.push_back({i, mBandLevels[i]});
+        }
+        return bandLevels;
+    }
+
+  private:
+    static const int NUM_OF_BANDS = 5;
+    static const int NUM_OF_PRESETS = 10;
+    static const int PRESET_CUSTOM = -1;
+    // preset band level
+    int mPreset = PRESET_CUSTOM;
+    int32_t mBandLevels[NUM_OF_BANDS] = {3, 0, 0, 0, 3};
+
+    // Add equalizer specific context for processing here
+};
+
+class EqualizerSw final : public EffectImpl {
+  public:
+    EqualizerSw() { LOG(DEBUG) << __func__; }
+    ~EqualizerSw() {
+        LOG(DEBUG) << __func__;
+        releaseContext();
+    }
+
+    ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
+    ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific) override;
+    ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id,
+                                            Parameter::Specific* specific) override;
+    IEffect::Status effectProcessImpl(float* in, float* out, int process) override;
+    std::shared_ptr<EffectContext> createContext(const Parameter::Common& common) override;
+    RetCode releaseContext() override;
+
+  private:
+    std::shared_ptr<EqualizerSwContext> mContext;
+    /* capabilities */
+    const std::vector<Equalizer::BandFrequency> mBandFrequency = {{0, 30000, 120000},
+                                                                  {1, 120001, 460000},
+                                                                  {2, 460001, 1800000},
+                                                                  {3, 1800001, 7000000},
+                                                                  {4, 7000001, 20000000}};
+    // presets supported by the device
+    const std::vector<Equalizer::Preset> mPresets = {
+            {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 kEqCap = {.bandFrequencies = mBandFrequency, .presets = mPresets};
+    // Effect descriptor.
+    const Descriptor kDesc = {.common = {.id = {.type = EqualizerTypeUUID,
+                                                .uuid = EqualizerSwImplUUID,
+                                                .proxy = std::nullopt},
+                                         .flags = {.type = Flags::Type::INSERT,
+                                                   .insert = Flags::Insert::FIRST,
+                                                   .volume = Flags::Volume::CTRL},
+                                         .name = "EqualizerSw"},
+                              .capability = Capability::make<Capability::equalizer>(kEqCap)};
+
+    ndk::ScopedAStatus getParameterEqualizer(const Equalizer::Tag& tag,
+                                             Parameter::Specific* specific);
+};
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/hapticGenerator/Android.bp b/audio/aidl/default/hapticGenerator/Android.bp
new file mode 100644
index 0000000..a632130
--- /dev/null
+++ b/audio/aidl/default/hapticGenerator/Android.bp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+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"],
+}
+
+cc_library_shared {
+    name: "libhapticgeneratorsw",
+    defaults: [
+        "aidlaudioeffectservice_defaults",
+        "latest_android_media_audio_common_types_ndk_shared",
+        "latest_android_hardware_audio_effect_ndk_shared",
+    ],
+    srcs: [
+        "HapticGeneratorSw.cpp",
+        ":effectCommonFile",
+    ],
+    visibility: [
+        "//hardware/interfaces/audio/aidl/default",
+    ],
+}
diff --git a/audio/aidl/default/hapticGenerator/HapticGeneratorSw.cpp b/audio/aidl/default/hapticGenerator/HapticGeneratorSw.cpp
new file mode 100644
index 0000000..90675c2
--- /dev/null
+++ b/audio/aidl/default/hapticGenerator/HapticGeneratorSw.cpp
@@ -0,0 +1,118 @@
+/*
+ * 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 <cstddef>
+#define LOG_TAG "AHAL_HapticGeneratorSw"
+#include <Utils.h>
+#include <algorithm>
+#include <unordered_set>
+
+#include <android-base/logging.h>
+#include <fmq/AidlMessageQueue.h>
+
+#include "HapticGeneratorSw.h"
+
+using aidl::android::hardware::audio::effect::HapticGeneratorSw;
+using aidl::android::hardware::audio::effect::HapticGeneratorSwImplUUID;
+using aidl::android::hardware::audio::effect::IEffect;
+using aidl::android::hardware::audio::effect::State;
+using aidl::android::media::audio::common::AudioUuid;
+
+extern "C" binder_exception_t createEffect(const AudioUuid* in_impl_uuid,
+                                           std::shared_ptr<IEffect>* instanceSpp) {
+    if (!in_impl_uuid || *in_impl_uuid != HapticGeneratorSwImplUUID) {
+        LOG(ERROR) << __func__ << "uuid not supported";
+        return EX_ILLEGAL_ARGUMENT;
+    }
+    if (instanceSpp) {
+        *instanceSpp = ndk::SharedRefBase::make<HapticGeneratorSw>();
+        LOG(DEBUG) << __func__ << " instance " << instanceSpp->get() << " created";
+        return EX_NONE;
+    } else {
+        LOG(ERROR) << __func__ << " invalid input parameter!";
+        return EX_ILLEGAL_ARGUMENT;
+    }
+}
+
+extern "C" binder_exception_t destroyEffect(const std::shared_ptr<IEffect>& instanceSp) {
+    if (!instanceSp) {
+        return EX_NONE;
+    }
+    State state;
+    ndk::ScopedAStatus status = instanceSp->getState(&state);
+    if (!status.isOk() || State::INIT != state) {
+        LOG(ERROR) << __func__ << " instance " << instanceSp.get()
+                   << " in state: " << toString(state) << ", status: " << status.getDescription();
+        return EX_ILLEGAL_STATE;
+    }
+    LOG(DEBUG) << __func__ << " instance " << instanceSp.get() << " destroyed";
+    return EX_NONE;
+}
+
+namespace aidl::android::hardware::audio::effect {
+
+ndk::ScopedAStatus HapticGeneratorSw::getDescriptor(Descriptor* _aidl_return) {
+    LOG(DEBUG) << __func__ << kDescriptor.toString();
+    *_aidl_return = kDescriptor;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus HapticGeneratorSw::setParameterSpecific(const Parameter::Specific& specific) {
+    RETURN_IF(Parameter::Specific::hapticGenerator != specific.getTag(), EX_ILLEGAL_ARGUMENT,
+              "EffectNotSupported");
+    std::lock_guard lg(mMutex);
+    RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+
+    mSpecificParam = specific.get<Parameter::Specific::hapticGenerator>();
+    LOG(DEBUG) << __func__ << " success with: " << specific.toString();
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus HapticGeneratorSw::getParameterSpecific(const Parameter::Id& id,
+                                                           Parameter::Specific* specific) {
+    auto tag = id.getTag();
+    RETURN_IF(Parameter::Id::hapticGeneratorTag != tag, EX_ILLEGAL_ARGUMENT, "wrongIdTag");
+    specific->set<Parameter::Specific::hapticGenerator>(mSpecificParam);
+    return ndk::ScopedAStatus::ok();
+}
+
+std::shared_ptr<EffectContext> HapticGeneratorSw::createContext(const Parameter::Common& common) {
+    if (mContext) {
+        LOG(DEBUG) << __func__ << " context already exist";
+        return mContext;
+    }
+    mContext = std::make_shared<HapticGeneratorSwContext>(1 /* statusFmqDepth */, common);
+    return mContext;
+}
+
+RetCode HapticGeneratorSw::releaseContext() {
+    if (mContext) {
+        mContext.reset();
+    }
+    return RetCode::SUCCESS;
+}
+
+// Processing method running in EffectWorker thread.
+IEffect::Status HapticGeneratorSw::effectProcessImpl(float* in, float* out, int process) {
+    // TODO: get data buffer and process.
+    LOG(DEBUG) << __func__ << " in " << in << " out " << out << " process " << process;
+    for (int i = 0; i < process; i++) {
+        *out++ = *in++;
+    }
+    return {STATUS_OK, process, process};
+}
+
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/hapticGenerator/HapticGeneratorSw.h b/audio/aidl/default/hapticGenerator/HapticGeneratorSw.h
new file mode 100644
index 0000000..b5a5036
--- /dev/null
+++ b/audio/aidl/default/hapticGenerator/HapticGeneratorSw.h
@@ -0,0 +1,72 @@
+/*
+ * 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/audio/effect/BnEffect.h>
+#include <fmq/AidlMessageQueue.h>
+#include <cstdlib>
+#include <memory>
+
+#include "effect-impl/EffectImpl.h"
+#include "effect-impl/EffectUUID.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+class HapticGeneratorSwContext final : public EffectContext {
+  public:
+    HapticGeneratorSwContext(int statusDepth, const Parameter::Common& common)
+        : EffectContext(statusDepth, common) {
+        LOG(DEBUG) << __func__;
+    }
+    // TODO: add specific context here
+};
+
+class HapticGeneratorSw final : public EffectImpl {
+  public:
+    HapticGeneratorSw() { LOG(DEBUG) << __func__; }
+    ~HapticGeneratorSw() {
+        LOG(DEBUG) << __func__;
+        releaseContext();
+    }
+
+    ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
+    ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific) override;
+    ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id,
+                                            Parameter::Specific* specific) override;
+    IEffect::Status effectProcessImpl(float* in, float* out, int process) override;
+    std::shared_ptr<EffectContext> createContext(const Parameter::Common& common) override;
+    RetCode releaseContext() override;
+
+  private:
+    std::shared_ptr<HapticGeneratorSwContext> mContext;
+    /* capabilities */
+    const HapticGenerator::Capability kCapability;
+    /* Effect descriptor */
+    const Descriptor kDescriptor = {
+            .common = {.id = {.type = HapticGeneratorTypeUUID,
+                              .uuid = HapticGeneratorSwImplUUID,
+                              .proxy = std::nullopt},
+                       .flags = {.type = Flags::Type::INSERT,
+                                 .insert = Flags::Insert::FIRST,
+                                 .volume = Flags::Volume::CTRL},
+                       .name = "HapticGeneratorSw"},
+            .capability = Capability::make<Capability::hapticGenerator>(kCapability)};
+
+    /* parameters */
+    HapticGenerator mSpecificParam;
+};
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h
index 488edf1..539fa8b 100644
--- a/audio/aidl/default/include/core-impl/Stream.h
+++ b/audio/aidl/default/include/core-impl/Stream.h
@@ -54,8 +54,10 @@
             int8_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
             DataMQ;
 
-    // Ensure that this value is not used by any of StreamDescriptor.COMMAND_*
-    static constexpr int COMMAND_EXIT = -1;
+    // Ensure that this value is not used by any of StreamDescriptor.CommandCode enums
+    static constexpr int32_t COMMAND_EXIT = -1;
+    // Ensure that this value is not used by any of StreamDescriptor.State enums
+    static constexpr int32_t STATE_CLOSED = -1;
 
     StreamContext() = default;
     StreamContext(std::unique_ptr<CommandMQ> commandMQ, std::unique_ptr<ReplyMQ> replyMQ,
@@ -99,6 +101,10 @@
 
 class StreamWorkerCommonLogic : public ::android::hardware::audio::common::StreamLogic {
   public:
+    bool isClosed() const {
+        return static_cast<int32_t>(mState.load()) == StreamContext::STATE_CLOSED;
+    }
+    void setClosed() { mState = static_cast<StreamDescriptor::State>(StreamContext::STATE_CLOSED); }
     void setIsConnected(bool connected) { mIsConnected = connected; }
 
   protected:
@@ -109,9 +115,12 @@
           mReplyMQ(context.getReplyMQ()),
           mDataMQ(context.getDataMQ()) {}
     std::string init() override;
+    void populateReply(StreamDescriptor::Reply* reply, bool isConnected) const;
 
-    // Used both by the main and worker threads.
+    // Atomic fields are used both by the main and worker threads.
     std::atomic<bool> mIsConnected = false;
+    static_assert(std::atomic<StreamDescriptor::State>::is_always_lock_free);
+    std::atomic<StreamDescriptor::State> mState = StreamDescriptor::State::STANDBY;
     // All fields are used on the worker thread only.
     const int mInternalCommandCookie;
     const size_t mFrameSize;
@@ -132,6 +141,9 @@
 
   protected:
     Status cycle() override;
+
+  private:
+    bool read(size_t clientSize, StreamDescriptor::Reply* reply);
 };
 using StreamInWorker = ::android::hardware::audio::common::StreamWorker<StreamInWorkerLogic>;
 
@@ -143,6 +155,9 @@
 
   protected:
     Status cycle() override;
+
+  private:
+    bool write(size_t clientSize, StreamDescriptor::Reply* reply);
 };
 using StreamOutWorker = ::android::hardware::audio::common::StreamWorker<StreamOutWorkerLogic>;
 
@@ -155,7 +170,7 @@
                        ? ndk::ScopedAStatus::ok()
                        : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
     }
-    bool isClosed() const { return mIsClosed; }
+    bool isClosed() const { return mWorker.isClosed(); }
     void setIsConnected(bool connected) { mWorker.setIsConnected(connected); }
     ndk::ScopedAStatus updateMetadata(const Metadata& metadata);
 
@@ -168,9 +183,6 @@
     Metadata mMetadata;
     StreamContext mContext;
     StreamWorker mWorker;
-    // This variable is checked in the destructor which can be called on an arbitrary Binder thread,
-    // thus we need to ensure that any changes made by other threads are sequentially consistent.
-    std::atomic<bool> mIsClosed = false;
 };
 
 class StreamIn
diff --git a/audio/aidl/default/include/effect-impl/EffectContext.h b/audio/aidl/default/include/effect-impl/EffectContext.h
new file mode 100644
index 0000000..f608e12
--- /dev/null
+++ b/audio/aidl/default/include/effect-impl/EffectContext.h
@@ -0,0 +1,157 @@
+/*
+ * 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 <Utils.h>
+#include <android-base/logging.h>
+#include <utils/Log.h>
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include <aidl/android/hardware/audio/effect/BnEffect.h>
+#include <fmq/AidlMessageQueue.h>
+#include "EffectTypes.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+class EffectContext {
+  public:
+    typedef ::android::AidlMessageQueue<
+            IEffect::Status, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
+            StatusMQ;
+    typedef ::android::AidlMessageQueue<
+            float, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
+            DataMQ;
+
+    EffectContext(size_t statusDepth, const Parameter::Common& common) {
+        mSessionId = common.session;
+        auto& input = common.input;
+        auto& output = common.output;
+
+        LOG_ALWAYS_FATAL_IF(
+                input.base.format.pcm != aidl::android::media::audio::common::PcmType::FLOAT_32_BIT,
+                "inputFormatNotFloat");
+        LOG_ALWAYS_FATAL_IF(output.base.format.pcm !=
+                                    aidl::android::media::audio::common::PcmType::FLOAT_32_BIT,
+                            "outputFormatNotFloat");
+        mInputFrameSize = ::android::hardware::audio::common::getFrameSizeInBytes(
+                input.base.format, input.base.channelMask);
+        mOutputFrameSize = ::android::hardware::audio::common::getFrameSizeInBytes(
+                output.base.format, output.base.channelMask);
+        // in/outBuffer size in float (FMQ data format defined for DataMQ)
+        size_t inBufferSizeInFloat = input.frameCount * mInputFrameSize / sizeof(float);
+        size_t outBufferSizeInFloat = output.frameCount * mOutputFrameSize / sizeof(float);
+
+        mStatusMQ = std::make_shared<StatusMQ>(statusDepth, true /*configureEventFlagWord*/);
+        mInputMQ = std::make_shared<DataMQ>(inBufferSizeInFloat);
+        mOutputMQ = std::make_shared<DataMQ>(outBufferSizeInFloat);
+
+        if (!mStatusMQ->isValid() || !mInputMQ->isValid() || !mOutputMQ->isValid()) {
+            LOG(ERROR) << __func__ << " created invalid FMQ";
+        }
+        mWorkBuffer.reserve(std::max(inBufferSizeInFloat, outBufferSizeInFloat));
+    }
+    virtual ~EffectContext() {}
+
+    std::shared_ptr<StatusMQ> getStatusFmq() { return mStatusMQ; }
+    std::shared_ptr<DataMQ> getInputDataFmq() { return mInputMQ; }
+    std::shared_ptr<DataMQ> getOutputDataFmq() { return mOutputMQ; }
+
+    float* getWorkBuffer() { return static_cast<float*>(mWorkBuffer.data()); }
+    // TODO: update with actual available size
+    size_t availableToRead() { return mWorkBuffer.capacity(); }
+    size_t availableToWrite() { return mWorkBuffer.capacity(); }
+
+    // reset buffer status by abandon all data and status in FMQ
+    void resetBuffer() {
+        auto buffer = getWorkBuffer();
+        std::vector<IEffect::Status> status(mStatusMQ->availableToRead());
+        mInputMQ->read(buffer, mInputMQ->availableToRead());
+        mOutputMQ->read(buffer, mOutputMQ->availableToRead());
+        mStatusMQ->read(status.data(), mStatusMQ->availableToRead());
+    }
+
+    void dupeFmq(IEffect::OpenEffectReturn* effectRet) {
+        if (effectRet) {
+            effectRet->statusMQ = getStatusFmq()->dupeDesc();
+            effectRet->inputDataMQ = getInputDataFmq()->dupeDesc();
+            effectRet->outputDataMQ = getOutputDataFmq()->dupeDesc();
+        }
+    }
+    size_t getInputFrameSize() { return mInputFrameSize; }
+    size_t getOutputFrameSize() { return mOutputFrameSize; }
+    int getSessionId() { return mSessionId; }
+
+    virtual RetCode setOutputDevice(
+            const aidl::android::media::audio::common::AudioDeviceDescription& device) {
+        mOutputDevice = device;
+        return RetCode::SUCCESS;
+    }
+    virtual aidl::android::media::audio::common::AudioDeviceDescription getOutputDevice() {
+        return mOutputDevice;
+    }
+
+    virtual RetCode setAudioMode(const aidl::android::media::audio::common::AudioMode& mode) {
+        mMode = mode;
+        return RetCode::SUCCESS;
+    }
+    virtual aidl::android::media::audio::common::AudioMode getAudioMode() { return mMode; }
+
+    virtual RetCode setAudioSource(const aidl::android::media::audio::common::AudioSource& source) {
+        mSource = source;
+        return RetCode::SUCCESS;
+    }
+    virtual aidl::android::media::audio::common::AudioSource getAudioSource() { return mSource; }
+
+    virtual RetCode setVolumeStereo(const Parameter::VolumeStereo& volumeStereo) {
+        mVolumeStereo = volumeStereo;
+        return RetCode::SUCCESS;
+    }
+    virtual Parameter::VolumeStereo getVolumeStereo() { return mVolumeStereo; }
+
+    virtual RetCode setCommon(const Parameter::Common& common) {
+        mCommon = common;
+        LOG(ERROR) << __func__ << mCommon.toString();
+        return RetCode::SUCCESS;
+    }
+    virtual Parameter::Common getCommon() {
+        LOG(ERROR) << __func__ << mCommon.toString();
+        return mCommon;
+    }
+
+  protected:
+    // common parameters
+    int mSessionId = INVALID_AUDIO_SESSION_ID;
+    size_t mInputFrameSize, mOutputFrameSize;
+    Parameter::Common mCommon;
+    aidl::android::media::audio::common::AudioDeviceDescription mOutputDevice;
+    aidl::android::media::audio::common::AudioMode mMode;
+    aidl::android::media::audio::common::AudioSource mSource;
+    Parameter::VolumeStereo mVolumeStereo;
+
+  private:
+    // fmq and buffers
+    std::shared_ptr<StatusMQ> mStatusMQ;
+    std::shared_ptr<DataMQ> mInputMQ;
+    std::shared_ptr<DataMQ> mOutputMQ;
+    // TODO handle effect process input and output
+    // work buffer set by effect instances, the access and update are in same thread
+    std::vector<float> mWorkBuffer;
+};
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/effect-impl/EffectImpl.h b/audio/aidl/default/include/effect-impl/EffectImpl.h
new file mode 100644
index 0000000..cb395b7
--- /dev/null
+++ b/audio/aidl/default/include/effect-impl/EffectImpl.h
@@ -0,0 +1,86 @@
+/*
+ * 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/audio/effect/BnEffect.h>
+#include <fmq/AidlMessageQueue.h>
+#include <cstdlib>
+#include <memory>
+#include <mutex>
+
+#include "EffectTypes.h"
+#include "effect-impl/EffectContext.h"
+#include "effect-impl/EffectTypes.h"
+#include "effect-impl/EffectWorker.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+class EffectImpl : public BnEffect, public EffectWorker {
+  public:
+    EffectImpl() { LOG(DEBUG) << __func__; }
+    ~EffectImpl() {
+        cleanUp();
+        LOG(DEBUG) << __func__;
+    }
+
+    /**
+     * Each effect implementation CAN override these methods if necessary
+     * If you would like implement IEffect::open completely, override EffectImpl::open(), if you
+     * want to keep most of EffectImpl logic but have a little customize, try override openImpl().
+     * openImpl() will be called at the beginning of EffectImpl::open() without lock protection.
+     *
+     * Same for closeImpl().
+     */
+    virtual ndk::ScopedAStatus open(const Parameter::Common& common,
+                                    const std::optional<Parameter::Specific>& specific,
+                                    OpenEffectReturn* ret) override;
+    virtual ndk::ScopedAStatus close() override;
+    virtual ndk::ScopedAStatus command(CommandId id) override;
+    virtual ndk::ScopedAStatus commandStart() { return ndk::ScopedAStatus::ok(); }
+    virtual ndk::ScopedAStatus commandStop() { return ndk::ScopedAStatus::ok(); }
+    virtual ndk::ScopedAStatus commandReset() { return ndk::ScopedAStatus::ok(); }
+
+    virtual ndk::ScopedAStatus getState(State* state) override;
+    virtual ndk::ScopedAStatus setParameter(const Parameter& param) override;
+    virtual ndk::ScopedAStatus getParameter(const Parameter::Id& id, Parameter* param) override;
+    virtual IEffect::Status effectProcessImpl(float* in, float* out, int process) override;
+
+    virtual ndk::ScopedAStatus setParameterCommon(const Parameter& param);
+    virtual ndk::ScopedAStatus getParameterCommon(const Parameter::Tag& tag, Parameter* param);
+
+    /* Methods MUST be implemented by each effect instances */
+    virtual ndk::ScopedAStatus getDescriptor(Descriptor* desc) = 0;
+    virtual ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific) = 0;
+    virtual ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id,
+                                                    Parameter::Specific* specific) = 0;
+    virtual std::shared_ptr<EffectContext> createContext(const Parameter::Common& common) = 0;
+    virtual RetCode releaseContext() = 0;
+
+  protected:
+    /*
+     * Lock is required if effectProcessImpl (which is running in an independent thread) needs to
+     * access state and parameters.
+     */
+    std::mutex mMutex;
+    State mState GUARDED_BY(mMutex) = State::INIT;
+
+    IEffect::Status status(binder_status_t status, size_t consumed, size_t produced);
+
+  private:
+    void cleanUp();
+    std::shared_ptr<EffectContext> mContext GUARDED_BY(mMutex);
+};
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/effect-impl/EffectThread.h b/audio/aidl/default/include/effect-impl/EffectThread.h
index e831cea..09a0000 100644
--- a/audio/aidl/default/include/effect-impl/EffectThread.h
+++ b/audio/aidl/default/include/effect-impl/EffectThread.h
@@ -22,12 +22,10 @@
 #include <android-base/thread_annotations.h>
 #include <system/thread_defs.h>
 
+#include "effect-impl/EffectTypes.h"
+
 namespace aidl::android::hardware::audio::effect {
 
-enum class RetCode { SUCCESS, ERROR };
-
-std::string toString(RetCode& code);
-
 class EffectThread {
   public:
     // default priority is same as HIDL: ANDROID_PRIORITY_URGENT_AUDIO
@@ -35,10 +33,11 @@
     virtual ~EffectThread();
 
     // called by effect implementation.
-    RetCode create(const std::string& name, const int priority = ANDROID_PRIORITY_URGENT_AUDIO);
-    RetCode destroy();
-    RetCode start();
-    RetCode stop();
+    RetCode createThread(const std::string& name,
+                         const int priority = ANDROID_PRIORITY_URGENT_AUDIO);
+    RetCode destroyThread();
+    RetCode startThread();
+    RetCode stopThread();
 
     // Will call process() in a loop if the thread is running.
     void threadLoop();
diff --git a/audio/aidl/default/include/effect-impl/EffectTypes.h b/audio/aidl/default/include/effect-impl/EffectTypes.h
new file mode 100644
index 0000000..edce26b
--- /dev/null
+++ b/audio/aidl/default/include/effect-impl/EffectTypes.h
@@ -0,0 +1,104 @@
+/*
+ * 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 <ostream>
+#include <string>
+
+#include <aidl/android/hardware/audio/effect/BnEffect.h>
+
+typedef binder_exception_t (*EffectCreateFunctor)(
+        const ::aidl::android::media::audio::common::AudioUuid*,
+        std::shared_ptr<aidl::android::hardware::audio::effect::IEffect>*);
+typedef binder_exception_t (*EffectDestroyFunctor)(
+        const std::shared_ptr<aidl::android::hardware::audio::effect::IEffect>&);
+
+struct effect_dl_interface_s {
+    EffectCreateFunctor createEffectFunc;
+    EffectDestroyFunctor destroyEffectFunc;
+};
+
+namespace aidl::android::hardware::audio::effect {
+
+enum class RetCode {
+    SUCCESS,
+    ERROR_ILLEGAL_PARAMETER, /* Illegal parameter */
+    ERROR_THREAD,            /* Effect thread error */
+    ERROR_NULL_POINTER,      /* NULL pointer */
+    ERROR_ALIGNMENT_ERROR,   /* Memory alignment error */
+    ERROR_BLOCK_SIZE_EXCEED, /* Maximum block size exceeded */
+    ERROR_EFFECT_LIB_ERROR
+};
+
+static const int INVALID_AUDIO_SESSION_ID = -1;
+
+inline std::ostream& operator<<(std::ostream& out, const RetCode& code) {
+    switch (code) {
+        case RetCode::SUCCESS:
+            return out << "SUCCESS";
+        case RetCode::ERROR_ILLEGAL_PARAMETER:
+            return out << "ERROR_ILLEGAL_PARAMETER";
+        case RetCode::ERROR_THREAD:
+            return out << "ERROR_THREAD";
+        case RetCode::ERROR_NULL_POINTER:
+            return out << "ERROR_NULL_POINTER";
+        case RetCode::ERROR_ALIGNMENT_ERROR:
+            return out << "ERROR_ALIGNMENT_ERROR";
+        case RetCode::ERROR_BLOCK_SIZE_EXCEED:
+            return out << "ERROR_BLOCK_SIZE_EXCEED";
+        case RetCode::ERROR_EFFECT_LIB_ERROR:
+            return out << "ERROR_EFFECT_LIB_ERROR";
+    }
+
+    return out << "EnumError: " << code;
+}
+
+#define RETURN_IF_ASTATUS_NOT_OK(status, message)                                              \
+    do {                                                                                       \
+        const ::ndk::ScopedAStatus curr_status = (status);                                     \
+        if (!curr_status.isOk()) {                                                             \
+            LOG(ERROR) << __func__ << ":" << __LINE__                                          \
+                       << "return with status: " << curr_status.getDescription() << (message); \
+            return ndk::ScopedAStatus::fromExceptionCodeWithMessage(                           \
+                    curr_status.getExceptionCode(), (message));                                \
+        }                                                                                      \
+    } while (0)
+
+#define RETURN_IF(expr, exception, message)                                                  \
+    do {                                                                                     \
+        if (expr) {                                                                          \
+            LOG(ERROR) << __func__ << ":" << __LINE__ << " return with expr " << #expr;      \
+            return ndk::ScopedAStatus::fromExceptionCodeWithMessage((exception), (message)); \
+        }                                                                                    \
+    } while (0)
+
+#define RETURN_OK_IF(expr)                                                             \
+    do {                                                                               \
+        if (expr) {                                                                    \
+            LOG(INFO) << __func__ << ":" << __LINE__ << " return with expr " << #expr; \
+            return ndk::ScopedAStatus::ok();                                           \
+        }                                                                              \
+    } while (0)
+
+#define RETURN_VALUE_IF(expr, ret, log)                                                          \
+    do {                                                                                         \
+        if (expr) {                                                                              \
+            LOG(ERROR) << __func__ << ":" << __LINE__ << " return with expr " << #expr << (log); \
+            return ret;                                                                          \
+        }                                                                                        \
+    } while (0)
+
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/effect-impl/EffectUUID.h b/audio/aidl/default/include/effect-impl/EffectUUID.h
index 99f6c24..767cf6c 100644
--- a/audio/aidl/default/include/effect-impl/EffectUUID.h
+++ b/audio/aidl/default/include/effect-impl/EffectUUID.h
@@ -21,6 +21,17 @@
 
 using ::aidl::android::media::audio::common::AudioUuid;
 
+// Null UUID
+static const AudioUuid EffectNullUuid = {static_cast<int32_t>(0xec7178ec),
+                                         0xe5e1,
+                                         0x4432,
+                                         0xa3f4,
+                                         {0x46, 0x57, 0xe6, 0x79, 0x52, 0x10}};
+
+// Zero UUID
+static const AudioUuid EffectZeroUuid = {
+        static_cast<int32_t>(0x0), 0x0, 0x0, 0x0, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
+
 // Equalizer type UUID.
 static const AudioUuid EqualizerTypeUUID = {static_cast<int32_t>(0x0bed4300),
                                             0xddd6,
@@ -35,11 +46,124 @@
                                               0xbb17,
                                               {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
 
-// Visualizer type UUID.
-static const AudioUuid VisualizerTypeUUID = {static_cast<int32_t>(0x1d4033c0),
-                                             0x8557,
-                                             0x11df,
-                                             0x9f2d,
-                                             {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
+// Equalizer bundle implementation UUID.
+static const AudioUuid EqualizerBundleImplUUID = {static_cast<int32_t>(0xce772f20),
+                                                  0x847d,
+                                                  0x11df,
+                                                  0xbb17,
+                                                  {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
+
+// fa8184a4-588b-11ed-9b6a-0242ac120002
+static const AudioUuid BassBoostTypeUUID = {static_cast<int32_t>(0xfa8184a4),
+                                            0x588b,
+                                            0x11ed,
+                                            0x9b6a,
+                                            {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
+// fa8181f2-588b-11ed-9b6a-0242ac120002
+static const AudioUuid BassBoostSwImplUUID = {static_cast<int32_t>(0xfa8181f2),
+                                              0x588b,
+                                              0x11ed,
+                                              0x9b6a,
+                                              {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
+// fa81862a-588b-11ed-9b6a-0242ac120002
+static const AudioUuid DownmixTypeUUID = {static_cast<int32_t>(0xfa81862a),
+                                          0x588b,
+                                          0x11ed,
+                                          0x9b6a,
+                                          {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
+// fa8187ba-588b-11ed-9b6a-0242ac120002
+static const AudioUuid DownmixSwImplUUID = {static_cast<int32_t>(0xfa8187ba),
+                                            0x588b,
+                                            0x11ed,
+                                            0x9b6a,
+                                            {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
+// fa818954-588b-11ed-9b6a-0242ac120002
+static const AudioUuid DynamicsProcessingTypeUUID = {static_cast<int32_t>(0xfa818954),
+                                                     0x588b,
+                                                     0x11ed,
+                                                     0x9b6a,
+                                                     {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
+// fa818d78-588b-11ed-9b6a-0242ac120002
+static const AudioUuid DynamicsProcessingSwImplUUID = {static_cast<int32_t>(0xfa818d78),
+                                                       0x588b,
+                                                       0x11ed,
+                                                       0x9b6a,
+                                                       {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
+// fa818f62-588b-11ed-9b6a-0242ac120002
+static const AudioUuid HapticGeneratorTypeUUID = {static_cast<int32_t>(0xfa818f62),
+                                                  0x588b,
+                                                  0x11ed,
+                                                  0x9b6a,
+                                                  {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
+// fa819110-588b-11ed-9b6a-0242ac120002
+static const AudioUuid HapticGeneratorSwImplUUID = {static_cast<int32_t>(0xfa819110),
+                                                    0x588b,
+                                                    0x11ed,
+                                                    0x9b6a,
+                                                    {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
+
+// fa8194a8-588b-11ed-9b6a-0242ac120002
+static const AudioUuid LoudnessEnhancerTypeUUID = {static_cast<int32_t>(0xfa8194a8),
+                                                   0x588b,
+                                                   0x11ed,
+                                                   0x9b6a,
+                                                   {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
+// fa819610-588b-11ed-9b6a-0242ac120002
+static const AudioUuid LoudnessEnhancerSwImplUUID = {static_cast<int32_t>(0xfa819610),
+                                                     0x588b,
+                                                     0x11ed,
+                                                     0x9b6a,
+                                                     {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
+// fa819886-588b-11ed-9b6a-0242ac120002
+static const AudioUuid ReverbTypeUUID = {static_cast<int32_t>(0xfa819886),
+                                         0x588b,
+                                         0x11ed,
+                                         0x9b6a,
+                                         {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
+// fa8199c6-588b-11ed-9b6a-0242ac120002
+static const AudioUuid ReverbSwImplUUID = {static_cast<int32_t>(0xfa8199c6),
+                                           0x588b,
+                                           0x11ed,
+                                           0x9b6a,
+                                           {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
+
+// fa819af2-588b-11ed-9b6a-0242ac120002
+static const AudioUuid VirtualizerTypeUUID = {static_cast<int32_t>(0xfa819af2),
+                                              0x588b,
+                                              0x11ed,
+                                              0x9b6a,
+                                              {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
+// fa819d86-588b-11ed-9b6a-0242ac120002
+static const AudioUuid VirtualizerSwImplUUID = {static_cast<int32_t>(0xfa819d86),
+                                                0x588b,
+                                                0x11ed,
+                                                0x9b6a,
+                                                {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
+
+// fa819f3e-588b-11ed-9b6a-0242ac120002
+static const AudioUuid VisualizerTypeUUID = {static_cast<int32_t>(0xfa819f3e),
+                                             0x588b,
+                                             0x11ed,
+                                             0x9b6a,
+                                             {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
+// fa81a0f6-588b-11ed-9b6a-0242ac120002
+static const AudioUuid VisualizerSwImplUUID = {static_cast<int32_t>(0xfa81a0f6),
+                                               0x588b,
+                                               0x11ed,
+                                               0x9b6a,
+                                               {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
+
+// fa81a2b8-588b-11ed-9b6a-0242ac120002
+static const AudioUuid VolumeTypeUUID = {static_cast<int32_t>(0xfa81a2b8),
+                                         0x588b,
+                                         0x11ed,
+                                         0x9b6a,
+                                         {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
+// fa81a718-588b-11ed-9b6a-0242ac120002
+static const AudioUuid VolumeSwImplUUID = {static_cast<int32_t>(0xfa81a718),
+                                           0x588b,
+                                           0x11ed,
+                                           0x9b6a,
+                                           {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
 
 }  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/effect-impl/EffectWorker.h b/audio/aidl/default/include/effect-impl/EffectWorker.h
new file mode 100644
index 0000000..a297937
--- /dev/null
+++ b/audio/aidl/default/include/effect-impl/EffectWorker.h
@@ -0,0 +1,73 @@
+/*
+ * 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 <algorithm>
+#include <memory>
+#include <mutex>
+#include <string>
+
+#include "EffectContext.h"
+#include "EffectThread.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+std::string toString(RetCode& code);
+
+class EffectWorker : public EffectThread {
+  public:
+    // set effect context for worker, suppose to only happen once here
+    void setContext(std::shared_ptr<EffectContext> context) {
+        std::call_once(mOnceFlag, [&]() { mContext = context; });
+    };
+
+    // handle FMQ and call effect implemented virtual function
+    void process() override {
+        RETURN_VALUE_IF(!mContext, void(), "nullContext");
+        std::shared_ptr<EffectContext::StatusMQ> statusMQ = mContext->getStatusFmq();
+        std::shared_ptr<EffectContext::DataMQ> inputMQ = mContext->getInputDataFmq();
+        std::shared_ptr<EffectContext::DataMQ> outputMQ = mContext->getOutputDataFmq();
+
+        // Only this worker will read from input data MQ and write to output data MQ.
+        auto readSamples = inputMQ->availableToRead(), writeSamples = outputMQ->availableToWrite();
+        if (readSamples && writeSamples) {
+            auto processSamples = std::min(readSamples, writeSamples);
+            LOG(DEBUG) << __func__ << " available to read " << readSamples << " available to write "
+                       << writeSamples << " process " << processSamples;
+
+            auto buffer = mContext->getWorkBuffer();
+            inputMQ->read(buffer, processSamples);
+
+            IEffect::Status status = effectProcessImpl(buffer, buffer, processSamples);
+            outputMQ->write(buffer, status.fmqProduced);
+            statusMQ->writeBlocking(&status, 1);
+            LOG(DEBUG) << __func__ << " done processing, effect consumed " << status.fmqConsumed
+                       << " produced " << status.fmqProduced;
+        } else {
+            // TODO: maybe add some sleep here to avoid busy waiting
+        }
+    }
+
+    // must implement by each effect implementation
+    virtual IEffect::Status effectProcessImpl(float* in, float* out, int processSamples) = 0;
+
+  private:
+    // make sure the context only set once.
+    std::once_flag mOnceFlag;
+    std::shared_ptr<EffectContext> mContext;
+};
+
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/effectFactory-impl/EffectFactory.h b/audio/aidl/default/include/effectFactory-impl/EffectFactory.h
index d373048..d50bd63 100644
--- a/audio/aidl/default/include/effectFactory-impl/EffectFactory.h
+++ b/audio/aidl/default/include/effectFactory-impl/EffectFactory.h
@@ -34,15 +34,27 @@
      *
      * @param in_type Type UUID.
      * @param in_instance Instance UUID.
+     * @param in_proxy Proxy UUID.
      * @param out_descriptor List of identities .
      * @return ndk::ScopedAStatus
      */
     ndk::ScopedAStatus queryEffects(
             const std::optional<::aidl::android::media::audio::common::AudioUuid>& in_type,
             const std::optional<::aidl::android::media::audio::common::AudioUuid>& in_instance,
+            const std::optional<::aidl::android::media::audio::common::AudioUuid>& in_proxy,
             std::vector<Descriptor::Identity>* out_descriptor) override;
 
     /**
+     * @brief Query list of defined processing, with the optional filter by AudioStreamType
+     *
+     * @param in_type Type of processing, could be AudioStreamType or AudioSource. Optional.
+     * @param _aidl_return List of processing filtered by in_type.
+     * @return ndk::ScopedAStatus
+     */
+    ndk::ScopedAStatus queryProcessing(const std::optional<Processing::Type>& in_type,
+                                       std::vector<Processing>* _aidl_return) override;
+
+    /**
      * @brief Create an effect instance for a certain implementation (identified by UUID).
      *
      * @param in_impl_uuid Effect implementation UUID.
@@ -69,16 +81,9 @@
     // List of effect descriptors supported by the devices.
     std::vector<Descriptor::Identity> mIdentityList;
 
-    typedef binder_exception_t (*EffectCreateFunctor)(std::shared_ptr<IEffect>*);
-    typedef binder_exception_t (*EffectDestroyFunctor)(const std::shared_ptr<IEffect>&);
-    struct effect_interface_s {
-        EffectCreateFunctor createEffectFunc;
-        EffectDestroyFunctor destroyEffectFunc;
-    };
-
     std::map<aidl::android::media::audio::common::AudioUuid /* implementationUUID */,
              std::pair<std::unique_ptr<void, std::function<void(void*)>> /* dlHandle */,
-                       std::unique_ptr<struct effect_interface_s>>>
+                       std::unique_ptr<struct effect_dl_interface_s>>>
             mEffectLibMap;
     std::map<std::weak_ptr<IEffect>, aidl::android::media::audio::common::AudioUuid,
              std::owner_less<>>
@@ -86,5 +91,10 @@
 
     ndk::ScopedAStatus destroyEffectImpl(const std::shared_ptr<IEffect>& in_handle);
     void cleanupEffectMap();
+    void openEffectLibrary(
+            const ::aidl::android::media::audio::common::AudioUuid& type,
+            const ::aidl::android::media::audio::common::AudioUuid& impl,
+            const std::optional<::aidl::android::media::audio::common::AudioUuid>& proxy,
+            const std::string& libName);
 };
 }  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/equalizer-impl/EqualizerSw.h b/audio/aidl/default/include/equalizer-impl/EqualizerSw.h
deleted file mode 100644
index 58ad1de..0000000
--- a/audio/aidl/default/include/equalizer-impl/EqualizerSw.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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/audio/effect/BnEffect.h>
-#include <fmq/AidlMessageQueue.h>
-#include <cstdlib>
-#include <memory>
-
-#include "effect-impl/EffectThread.h"
-
-namespace aidl::android::hardware::audio::effect {
-
-class EqualizerSwWorker : public EffectThread {
-    // EqualizerSwWorker(const std::string name){EffectThread(name)};
-    void process() override;
-};
-
-class EqualizerSw : public BnEffect {
-  public:
-    EqualizerSw() {
-        // create the worker
-        mWorker = std::make_unique<EqualizerSwWorker>();
-        LOG(DEBUG) << __func__;
-    };
-    ~EqualizerSw() {
-        cleanUp();
-        LOG(DEBUG) << __func__;
-    };
-    ndk::ScopedAStatus open(const Parameter::Common& common, const Parameter::Specific& specific,
-                            OpenEffectReturn* _aidl_return) override;
-    ndk::ScopedAStatus close() override;
-    ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
-
-    ndk::ScopedAStatus getState(State* _aidl_return) override;
-    ndk::ScopedAStatus command(CommandId in_commandId) override;
-    ndk::ScopedAStatus setParameter(const Parameter& in_param) override;
-    ndk::ScopedAStatus getParameter(const Parameter::Id& in_paramId,
-                                    Parameter* _aidl_return) override;
-
-  private:
-    // effect processing thread.
-    std::unique_ptr<EqualizerSwWorker> mWorker;
-    // Effect descriptor.
-    const Descriptor mDesc = {
-            .common = {.id = {.type = EqualizerTypeUUID, .uuid = EqualizerSwImplUUID}}};
-
-    // Parameters.
-    Parameter::Common mCommonParam;
-    Equalizer mEqualizerParam;  // TODO: the equalizer parameter needs to update
-
-    // Instance state INIT by default.
-    State mState = State::INIT;
-
-    typedef ::android::AidlMessageQueue<
-            Status, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
-            StatusMQ;
-    typedef ::android::AidlMessageQueue<
-            int8_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
-            DataMQ;
-
-    std::unique_ptr<StatusMQ> mStatusMQ;
-    std::unique_ptr<DataMQ> mInputMQ;
-    std::unique_ptr<DataMQ> mOutputMQ;
-
-    ndk::ScopedAStatus setCommonParameter(const Parameter::Common& common_param);
-    ndk::ScopedAStatus setSpecificParameter(const Parameter::Specific& specific);
-    bool createFmq(int statusDepth, int inBufferSize, int outBufferSize, OpenEffectReturn* ret);
-    void destroyFmq();
-    void cleanUp();
-};
-}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/loudnessEnhancer/Android.bp b/audio/aidl/default/loudnessEnhancer/Android.bp
new file mode 100644
index 0000000..3a0ac73
--- /dev/null
+++ b/audio/aidl/default/loudnessEnhancer/Android.bp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+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"],
+}
+
+cc_library_shared {
+    name: "libloudnessenhancersw",
+    defaults: [
+        "aidlaudioeffectservice_defaults",
+        "latest_android_media_audio_common_types_ndk_shared",
+        "latest_android_hardware_audio_effect_ndk_shared",
+    ],
+    srcs: [
+        "LoudnessEnhancerSw.cpp",
+        ":effectCommonFile",
+    ],
+    visibility: [
+        "//hardware/interfaces/audio/aidl/default",
+    ],
+}
diff --git a/audio/aidl/default/loudnessEnhancer/LoudnessEnhancerSw.cpp b/audio/aidl/default/loudnessEnhancer/LoudnessEnhancerSw.cpp
new file mode 100644
index 0000000..51645c7
--- /dev/null
+++ b/audio/aidl/default/loudnessEnhancer/LoudnessEnhancerSw.cpp
@@ -0,0 +1,118 @@
+/*
+ * 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 <cstddef>
+#define LOG_TAG "AHAL_LoudnessEnhancerSw"
+#include <Utils.h>
+#include <algorithm>
+#include <unordered_set>
+
+#include <android-base/logging.h>
+#include <fmq/AidlMessageQueue.h>
+
+#include "LoudnessEnhancerSw.h"
+
+using aidl::android::hardware::audio::effect::IEffect;
+using aidl::android::hardware::audio::effect::LoudnessEnhancerSw;
+using aidl::android::hardware::audio::effect::LoudnessEnhancerSwImplUUID;
+using aidl::android::hardware::audio::effect::State;
+using aidl::android::media::audio::common::AudioUuid;
+
+extern "C" binder_exception_t createEffect(const AudioUuid* in_impl_uuid,
+                                           std::shared_ptr<IEffect>* instanceSpp) {
+    if (!in_impl_uuid || *in_impl_uuid != LoudnessEnhancerSwImplUUID) {
+        LOG(ERROR) << __func__ << "uuid not supported";
+        return EX_ILLEGAL_ARGUMENT;
+    }
+    if (instanceSpp) {
+        *instanceSpp = ndk::SharedRefBase::make<LoudnessEnhancerSw>();
+        LOG(DEBUG) << __func__ << " instance " << instanceSpp->get() << " created";
+        return EX_NONE;
+    } else {
+        LOG(ERROR) << __func__ << " invalid input parameter!";
+        return EX_ILLEGAL_ARGUMENT;
+    }
+}
+
+extern "C" binder_exception_t destroyEffect(const std::shared_ptr<IEffect>& instanceSp) {
+    if (!instanceSp) {
+        return EX_NONE;
+    }
+    State state;
+    ndk::ScopedAStatus status = instanceSp->getState(&state);
+    if (!status.isOk() || State::INIT != state) {
+        LOG(ERROR) << __func__ << " instance " << instanceSp.get()
+                   << " in state: " << toString(state) << ", status: " << status.getDescription();
+        return EX_ILLEGAL_STATE;
+    }
+    LOG(DEBUG) << __func__ << " instance " << instanceSp.get() << " destroyed";
+    return EX_NONE;
+}
+
+namespace aidl::android::hardware::audio::effect {
+
+ndk::ScopedAStatus LoudnessEnhancerSw::getDescriptor(Descriptor* _aidl_return) {
+    LOG(DEBUG) << __func__ << kDescriptor.toString();
+    *_aidl_return = kDescriptor;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus LoudnessEnhancerSw::setParameterSpecific(const Parameter::Specific& specific) {
+    RETURN_IF(Parameter::Specific::loudnessEnhancer != specific.getTag(), EX_ILLEGAL_ARGUMENT,
+              "EffectNotSupported");
+    std::lock_guard lg(mMutex);
+    RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+
+    mSpecificParam = specific.get<Parameter::Specific::loudnessEnhancer>();
+    LOG(DEBUG) << __func__ << " success with: " << specific.toString();
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus LoudnessEnhancerSw::getParameterSpecific(const Parameter::Id& id,
+                                                            Parameter::Specific* specific) {
+    auto tag = id.getTag();
+    RETURN_IF(Parameter::Id::loudnessEnhancerTag != tag, EX_ILLEGAL_ARGUMENT, "wrongIdTag");
+    specific->set<Parameter::Specific::loudnessEnhancer>(mSpecificParam);
+    return ndk::ScopedAStatus::ok();
+}
+
+std::shared_ptr<EffectContext> LoudnessEnhancerSw::createContext(const Parameter::Common& common) {
+    if (mContext) {
+        LOG(DEBUG) << __func__ << " context already exist";
+        return mContext;
+    }
+    mContext = std::make_shared<LoudnessEnhancerSwContext>(1 /* statusFmqDepth */, common);
+    return mContext;
+}
+
+RetCode LoudnessEnhancerSw::releaseContext() {
+    if (mContext) {
+        mContext.reset();
+    }
+    return RetCode::SUCCESS;
+}
+
+// Processing method running in EffectWorker thread.
+IEffect::Status LoudnessEnhancerSw::effectProcessImpl(float* in, float* out, int process) {
+    // TODO: get data buffer and process.
+    LOG(DEBUG) << __func__ << " in " << in << " out " << out << " process " << process;
+    for (int i = 0; i < process; i++) {
+        *out++ = *in++;
+    }
+    return {STATUS_OK, process, process};
+}
+
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/loudnessEnhancer/LoudnessEnhancerSw.h b/audio/aidl/default/loudnessEnhancer/LoudnessEnhancerSw.h
new file mode 100644
index 0000000..c0de9c1
--- /dev/null
+++ b/audio/aidl/default/loudnessEnhancer/LoudnessEnhancerSw.h
@@ -0,0 +1,72 @@
+/*
+ * 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/audio/effect/BnEffect.h>
+#include <fmq/AidlMessageQueue.h>
+#include <cstdlib>
+#include <memory>
+
+#include "effect-impl/EffectImpl.h"
+#include "effect-impl/EffectUUID.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+class LoudnessEnhancerSwContext final : public EffectContext {
+  public:
+    LoudnessEnhancerSwContext(int statusDepth, const Parameter::Common& common)
+        : EffectContext(statusDepth, common) {
+        LOG(DEBUG) << __func__;
+    }
+    // TODO: add specific context here
+};
+
+class LoudnessEnhancerSw final : public EffectImpl {
+  public:
+    LoudnessEnhancerSw() { LOG(DEBUG) << __func__; }
+    ~LoudnessEnhancerSw() {
+        LOG(DEBUG) << __func__;
+        releaseContext();
+    }
+
+    ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
+    ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific) override;
+    ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id,
+                                            Parameter::Specific* specific) override;
+    IEffect::Status effectProcessImpl(float* in, float* out, int process) override;
+    std::shared_ptr<EffectContext> createContext(const Parameter::Common& common) override;
+    RetCode releaseContext() override;
+
+  private:
+    std::shared_ptr<LoudnessEnhancerSwContext> mContext;
+    /* capabilities */
+    const LoudnessEnhancer::Capability kCapability;
+    /* Effect descriptor */
+    const Descriptor kDescriptor = {
+            .common = {.id = {.type = LoudnessEnhancerTypeUUID,
+                              .uuid = LoudnessEnhancerSwImplUUID,
+                              .proxy = std::nullopt},
+                       .flags = {.type = Flags::Type::INSERT,
+                                 .insert = Flags::Insert::FIRST,
+                                 .volume = Flags::Volume::CTRL},
+                       .name = "LoudnessEnhancerSw"},
+            .capability = Capability::make<Capability::loudnessEnhancer>(kCapability)};
+
+    /* parameters */
+    LoudnessEnhancer mSpecificParam;
+};
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/reverb/Android.bp b/audio/aidl/default/reverb/Android.bp
new file mode 100644
index 0000000..955038c
--- /dev/null
+++ b/audio/aidl/default/reverb/Android.bp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+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"],
+}
+
+cc_library_shared {
+    name: "libreverbsw",
+    defaults: [
+        "aidlaudioeffectservice_defaults",
+        "latest_android_media_audio_common_types_ndk_shared",
+        "latest_android_hardware_audio_effect_ndk_shared",
+    ],
+    srcs: [
+        "ReverbSw.cpp",
+        ":effectCommonFile",
+    ],
+    visibility: [
+        "//hardware/interfaces/audio/aidl/default",
+    ],
+}
diff --git a/audio/aidl/default/reverb/ReverbSw.cpp b/audio/aidl/default/reverb/ReverbSw.cpp
new file mode 100644
index 0000000..639f1a2
--- /dev/null
+++ b/audio/aidl/default/reverb/ReverbSw.cpp
@@ -0,0 +1,118 @@
+/*
+ * 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 <cstddef>
+#define LOG_TAG "AHAL_ReverbSw"
+#include <Utils.h>
+#include <algorithm>
+#include <unordered_set>
+
+#include <android-base/logging.h>
+#include <fmq/AidlMessageQueue.h>
+
+#include "ReverbSw.h"
+
+using aidl::android::hardware::audio::effect::IEffect;
+using aidl::android::hardware::audio::effect::ReverbSw;
+using aidl::android::hardware::audio::effect::ReverbSwImplUUID;
+using aidl::android::hardware::audio::effect::State;
+using aidl::android::media::audio::common::AudioUuid;
+
+extern "C" binder_exception_t createEffect(const AudioUuid* in_impl_uuid,
+                                           std::shared_ptr<IEffect>* instanceSpp) {
+    if (!in_impl_uuid || *in_impl_uuid != ReverbSwImplUUID) {
+        LOG(ERROR) << __func__ << "uuid not supported";
+        return EX_ILLEGAL_ARGUMENT;
+    }
+    if (instanceSpp) {
+        *instanceSpp = ndk::SharedRefBase::make<ReverbSw>();
+        LOG(DEBUG) << __func__ << " instance " << instanceSpp->get() << " created";
+        return EX_NONE;
+    } else {
+        LOG(ERROR) << __func__ << " invalid input parameter!";
+        return EX_ILLEGAL_ARGUMENT;
+    }
+}
+
+extern "C" binder_exception_t destroyEffect(const std::shared_ptr<IEffect>& instanceSp) {
+    if (!instanceSp) {
+        return EX_NONE;
+    }
+    State state;
+    ndk::ScopedAStatus status = instanceSp->getState(&state);
+    if (!status.isOk() || State::INIT != state) {
+        LOG(ERROR) << __func__ << " instance " << instanceSp.get()
+                   << " in state: " << toString(state) << ", status: " << status.getDescription();
+        return EX_ILLEGAL_STATE;
+    }
+    LOG(DEBUG) << __func__ << " instance " << instanceSp.get() << " destroyed";
+    return EX_NONE;
+}
+
+namespace aidl::android::hardware::audio::effect {
+
+ndk::ScopedAStatus ReverbSw::getDescriptor(Descriptor* _aidl_return) {
+    LOG(DEBUG) << __func__ << kDescriptor.toString();
+    *_aidl_return = kDescriptor;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus ReverbSw::setParameterSpecific(const Parameter::Specific& specific) {
+    RETURN_IF(Parameter::Specific::reverb != specific.getTag(), EX_ILLEGAL_ARGUMENT,
+              "EffectNotSupported");
+    std::lock_guard lg(mMutex);
+    RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+
+    mSpecificParam = specific.get<Parameter::Specific::reverb>();
+    LOG(DEBUG) << __func__ << " success with: " << specific.toString();
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus ReverbSw::getParameterSpecific(const Parameter::Id& id,
+                                                  Parameter::Specific* specific) {
+    auto tag = id.getTag();
+    RETURN_IF(Parameter::Id::reverbTag != tag, EX_ILLEGAL_ARGUMENT, "wrongIdTag");
+    specific->set<Parameter::Specific::reverb>(mSpecificParam);
+    return ndk::ScopedAStatus::ok();
+}
+
+std::shared_ptr<EffectContext> ReverbSw::createContext(const Parameter::Common& common) {
+    if (mContext) {
+        LOG(DEBUG) << __func__ << " context already exist";
+        return mContext;
+    }
+    mContext = std::make_shared<ReverbSwContext>(1 /* statusFmqDepth */, common);
+    return mContext;
+}
+
+RetCode ReverbSw::releaseContext() {
+    if (mContext) {
+        mContext.reset();
+    }
+    return RetCode::SUCCESS;
+}
+
+// Processing method running in EffectWorker thread.
+IEffect::Status ReverbSw::effectProcessImpl(float* in, float* out, int process) {
+    // TODO: get data buffer and process.
+    LOG(DEBUG) << __func__ << " in " << in << " out " << out << " process " << process;
+    for (int i = 0; i < process; i++) {
+        *out++ = *in++;
+    }
+    return {STATUS_OK, process, process};
+}
+
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/reverb/ReverbSw.h b/audio/aidl/default/reverb/ReverbSw.h
new file mode 100644
index 0000000..e00956f
--- /dev/null
+++ b/audio/aidl/default/reverb/ReverbSw.h
@@ -0,0 +1,72 @@
+/*
+ * 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/audio/effect/BnEffect.h>
+#include <fmq/AidlMessageQueue.h>
+#include <cstdlib>
+#include <memory>
+
+#include "effect-impl/EffectImpl.h"
+#include "effect-impl/EffectUUID.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+class ReverbSwContext final : public EffectContext {
+  public:
+    ReverbSwContext(int statusDepth, const Parameter::Common& common)
+        : EffectContext(statusDepth, common) {
+        LOG(DEBUG) << __func__;
+    }
+    // TODO: add specific context here
+};
+
+class ReverbSw final : public EffectImpl {
+  public:
+    ReverbSw() { LOG(DEBUG) << __func__; }
+    ~ReverbSw() {
+        LOG(DEBUG) << __func__;
+        releaseContext();
+    }
+
+    ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
+    ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific) override;
+    ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id,
+                                            Parameter::Specific* specific) override;
+    IEffect::Status effectProcessImpl(float* in, float* out, int process) override;
+    std::shared_ptr<EffectContext> createContext(const Parameter::Common& common) override;
+    RetCode releaseContext() override;
+
+  private:
+    std::shared_ptr<ReverbSwContext> mContext;
+    /* capabilities */
+    const Reverb::Capability kCapability;
+    /* Effect descriptor */
+    const Descriptor kDescriptor = {
+            .common = {.id = {.type = ReverbTypeUUID,
+                              .uuid = ReverbSwImplUUID,
+                              .proxy = std::nullopt},
+                       .flags = {.type = Flags::Type::INSERT,
+                                 .insert = Flags::Insert::FIRST,
+                                 .volume = Flags::Volume::CTRL},
+                       .name = "ReverbSw"},
+            .capability = Capability::make<Capability::reverb>(kCapability)};
+
+    /* parameters */
+    Reverb mSpecificParam;
+};
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/virtualizer/Android.bp b/audio/aidl/default/virtualizer/Android.bp
new file mode 100644
index 0000000..ba38f5c
--- /dev/null
+++ b/audio/aidl/default/virtualizer/Android.bp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+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"],
+}
+
+cc_library_shared {
+    name: "libvirtualizersw",
+    defaults: [
+        "aidlaudioeffectservice_defaults",
+        "latest_android_media_audio_common_types_ndk_shared",
+        "latest_android_hardware_audio_effect_ndk_shared",
+    ],
+    srcs: [
+        "VirtualizerSw.cpp",
+        ":effectCommonFile",
+    ],
+    visibility: [
+        "//hardware/interfaces/audio/aidl/default",
+    ],
+}
diff --git a/audio/aidl/default/virtualizer/VirtualizerSw.cpp b/audio/aidl/default/virtualizer/VirtualizerSw.cpp
new file mode 100644
index 0000000..ccb7b4b
--- /dev/null
+++ b/audio/aidl/default/virtualizer/VirtualizerSw.cpp
@@ -0,0 +1,118 @@
+/*
+ * 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 <cstddef>
+#define LOG_TAG "AHAL_VirtualizerSw"
+#include <Utils.h>
+#include <algorithm>
+#include <unordered_set>
+
+#include <android-base/logging.h>
+#include <fmq/AidlMessageQueue.h>
+
+#include "VirtualizerSw.h"
+
+using aidl::android::hardware::audio::effect::IEffect;
+using aidl::android::hardware::audio::effect::State;
+using aidl::android::hardware::audio::effect::VirtualizerSw;
+using aidl::android::hardware::audio::effect::VirtualizerSwImplUUID;
+using aidl::android::media::audio::common::AudioUuid;
+
+extern "C" binder_exception_t createEffect(const AudioUuid* in_impl_uuid,
+                                           std::shared_ptr<IEffect>* instanceSpp) {
+    if (!in_impl_uuid || *in_impl_uuid != VirtualizerSwImplUUID) {
+        LOG(ERROR) << __func__ << "uuid not supported";
+        return EX_ILLEGAL_ARGUMENT;
+    }
+    if (instanceSpp) {
+        *instanceSpp = ndk::SharedRefBase::make<VirtualizerSw>();
+        LOG(DEBUG) << __func__ << " instance " << instanceSpp->get() << " created";
+        return EX_NONE;
+    } else {
+        LOG(ERROR) << __func__ << " invalid input parameter!";
+        return EX_ILLEGAL_ARGUMENT;
+    }
+}
+
+extern "C" binder_exception_t destroyEffect(const std::shared_ptr<IEffect>& instanceSp) {
+    if (!instanceSp) {
+        return EX_NONE;
+    }
+    State state;
+    ndk::ScopedAStatus status = instanceSp->getState(&state);
+    if (!status.isOk() || State::INIT != state) {
+        LOG(ERROR) << __func__ << " instance " << instanceSp.get()
+                   << " in state: " << toString(state) << ", status: " << status.getDescription();
+        return EX_ILLEGAL_STATE;
+    }
+    LOG(DEBUG) << __func__ << " instance " << instanceSp.get() << " destroyed";
+    return EX_NONE;
+}
+
+namespace aidl::android::hardware::audio::effect {
+
+ndk::ScopedAStatus VirtualizerSw::getDescriptor(Descriptor* _aidl_return) {
+    LOG(DEBUG) << __func__ << kDescriptor.toString();
+    *_aidl_return = kDescriptor;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus VirtualizerSw::setParameterSpecific(const Parameter::Specific& specific) {
+    RETURN_IF(Parameter::Specific::virtualizer != specific.getTag(), EX_ILLEGAL_ARGUMENT,
+              "EffectNotSupported");
+    std::lock_guard lg(mMutex);
+    RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+
+    mSpecificParam = specific.get<Parameter::Specific::virtualizer>();
+    LOG(DEBUG) << __func__ << " success with: " << specific.toString();
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus VirtualizerSw::getParameterSpecific(const Parameter::Id& id,
+                                                       Parameter::Specific* specific) {
+    auto tag = id.getTag();
+    RETURN_IF(Parameter::Id::virtualizerTag != tag, EX_ILLEGAL_ARGUMENT, "wrongIdTag");
+    specific->set<Parameter::Specific::virtualizer>(mSpecificParam);
+    return ndk::ScopedAStatus::ok();
+}
+
+std::shared_ptr<EffectContext> VirtualizerSw::createContext(const Parameter::Common& common) {
+    if (mContext) {
+        LOG(DEBUG) << __func__ << " context already exist";
+        return mContext;
+    }
+    mContext = std::make_shared<VirtualizerSwContext>(1 /* statusFmqDepth */, common);
+    return mContext;
+}
+
+RetCode VirtualizerSw::releaseContext() {
+    if (mContext) {
+        mContext.reset();
+    }
+    return RetCode::SUCCESS;
+}
+
+// Processing method running in EffectWorker thread.
+IEffect::Status VirtualizerSw::effectProcessImpl(float* in, float* out, int process) {
+    // TODO: get data buffer and process.
+    LOG(DEBUG) << __func__ << " in " << in << " out " << out << " process " << process;
+    for (int i = 0; i < process; i++) {
+        *out++ = *in++;
+    }
+    return {STATUS_OK, process, process};
+}
+
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/virtualizer/VirtualizerSw.h b/audio/aidl/default/virtualizer/VirtualizerSw.h
new file mode 100644
index 0000000..da1998b
--- /dev/null
+++ b/audio/aidl/default/virtualizer/VirtualizerSw.h
@@ -0,0 +1,72 @@
+/*
+ * 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/audio/effect/BnEffect.h>
+#include <fmq/AidlMessageQueue.h>
+#include <cstdlib>
+#include <memory>
+
+#include "effect-impl/EffectImpl.h"
+#include "effect-impl/EffectUUID.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+class VirtualizerSwContext final : public EffectContext {
+  public:
+    VirtualizerSwContext(int statusDepth, const Parameter::Common& common)
+        : EffectContext(statusDepth, common) {
+        LOG(DEBUG) << __func__;
+    }
+    // TODO: add specific context here
+};
+
+class VirtualizerSw final : public EffectImpl {
+  public:
+    VirtualizerSw() { LOG(DEBUG) << __func__; }
+    ~VirtualizerSw() {
+        LOG(DEBUG) << __func__;
+        releaseContext();
+    }
+
+    ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
+    ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific) override;
+    ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id,
+                                            Parameter::Specific* specific) override;
+    IEffect::Status effectProcessImpl(float* in, float* out, int process) override;
+    std::shared_ptr<EffectContext> createContext(const Parameter::Common& common) override;
+    RetCode releaseContext() override;
+
+  private:
+    std::shared_ptr<VirtualizerSwContext> mContext;
+    /* capabilities */
+    const Virtualizer::Capability kCapability;
+    /* Effect descriptor */
+    const Descriptor kDescriptor = {
+            .common = {.id = {.type = VirtualizerTypeUUID,
+                              .uuid = VirtualizerSwImplUUID,
+                              .proxy = std::nullopt},
+                       .flags = {.type = Flags::Type::INSERT,
+                                 .insert = Flags::Insert::FIRST,
+                                 .volume = Flags::Volume::CTRL},
+                       .name = "VirtualizerSw"},
+            .capability = Capability::make<Capability::virtualizer>(kCapability)};
+
+    /* parameters */
+    Virtualizer mSpecificParam;
+};
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/visualizer/Android.bp b/audio/aidl/default/visualizer/Android.bp
new file mode 100644
index 0000000..5041be8
--- /dev/null
+++ b/audio/aidl/default/visualizer/Android.bp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+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"],
+}
+
+cc_library_shared {
+    name: "libvisualizersw",
+    defaults: [
+        "aidlaudioeffectservice_defaults",
+        "latest_android_media_audio_common_types_ndk_shared",
+        "latest_android_hardware_audio_effect_ndk_shared",
+    ],
+    srcs: [
+        "VisualizerSw.cpp",
+        ":effectCommonFile",
+    ],
+    visibility: [
+        "//hardware/interfaces/audio/aidl/default",
+    ],
+}
diff --git a/audio/aidl/default/visualizer/VisualizerSw.cpp b/audio/aidl/default/visualizer/VisualizerSw.cpp
new file mode 100644
index 0000000..5a24f18
--- /dev/null
+++ b/audio/aidl/default/visualizer/VisualizerSw.cpp
@@ -0,0 +1,118 @@
+/*
+ * 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 <cstddef>
+#define LOG_TAG "AHAL_VisualizerSw"
+#include <Utils.h>
+#include <algorithm>
+#include <unordered_set>
+
+#include <android-base/logging.h>
+#include <fmq/AidlMessageQueue.h>
+
+#include "VisualizerSw.h"
+
+using aidl::android::hardware::audio::effect::IEffect;
+using aidl::android::hardware::audio::effect::State;
+using aidl::android::hardware::audio::effect::VisualizerSw;
+using aidl::android::hardware::audio::effect::VisualizerSwImplUUID;
+using aidl::android::media::audio::common::AudioUuid;
+
+extern "C" binder_exception_t createEffect(const AudioUuid* in_impl_uuid,
+                                           std::shared_ptr<IEffect>* instanceSpp) {
+    if (!in_impl_uuid || *in_impl_uuid != VisualizerSwImplUUID) {
+        LOG(ERROR) << __func__ << "uuid not supported";
+        return EX_ILLEGAL_ARGUMENT;
+    }
+    if (instanceSpp) {
+        *instanceSpp = ndk::SharedRefBase::make<VisualizerSw>();
+        LOG(DEBUG) << __func__ << " instance " << instanceSpp->get() << " created";
+        return EX_NONE;
+    } else {
+        LOG(ERROR) << __func__ << " invalid input parameter!";
+        return EX_ILLEGAL_ARGUMENT;
+    }
+}
+
+extern "C" binder_exception_t destroyEffect(const std::shared_ptr<IEffect>& instanceSp) {
+    if (!instanceSp) {
+        return EX_NONE;
+    }
+    State state;
+    ndk::ScopedAStatus status = instanceSp->getState(&state);
+    if (!status.isOk() || State::INIT != state) {
+        LOG(ERROR) << __func__ << " instance " << instanceSp.get()
+                   << " in state: " << toString(state) << ", status: " << status.getDescription();
+        return EX_ILLEGAL_STATE;
+    }
+    LOG(DEBUG) << __func__ << " instance " << instanceSp.get() << " destroyed";
+    return EX_NONE;
+}
+
+namespace aidl::android::hardware::audio::effect {
+
+ndk::ScopedAStatus VisualizerSw::getDescriptor(Descriptor* _aidl_return) {
+    LOG(DEBUG) << __func__ << kDescriptor.toString();
+    *_aidl_return = kDescriptor;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus VisualizerSw::setParameterSpecific(const Parameter::Specific& specific) {
+    RETURN_IF(Parameter::Specific::visualizer != specific.getTag(), EX_ILLEGAL_ARGUMENT,
+              "EffectNotSupported");
+    std::lock_guard lg(mMutex);
+    RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+
+    mSpecificParam = specific.get<Parameter::Specific::visualizer>();
+    LOG(DEBUG) << __func__ << " success with: " << specific.toString();
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus VisualizerSw::getParameterSpecific(const Parameter::Id& id,
+                                                      Parameter::Specific* specific) {
+    auto tag = id.getTag();
+    RETURN_IF(Parameter::Id::visualizerTag != tag, EX_ILLEGAL_ARGUMENT, "wrongIdTag");
+    specific->set<Parameter::Specific::visualizer>(mSpecificParam);
+    return ndk::ScopedAStatus::ok();
+}
+
+std::shared_ptr<EffectContext> VisualizerSw::createContext(const Parameter::Common& common) {
+    if (mContext) {
+        LOG(DEBUG) << __func__ << " context already exist";
+        return mContext;
+    }
+    mContext = std::make_shared<VisualizerSwContext>(1 /* statusFmqDepth */, common);
+    return mContext;
+}
+
+RetCode VisualizerSw::releaseContext() {
+    if (mContext) {
+        mContext.reset();
+    }
+    return RetCode::SUCCESS;
+}
+
+// Processing method running in EffectWorker thread.
+IEffect::Status VisualizerSw::effectProcessImpl(float* in, float* out, int process) {
+    // TODO: get data buffer and process.
+    LOG(DEBUG) << __func__ << " in " << in << " out " << out << " process " << process;
+    for (int i = 0; i < process; i++) {
+        *out++ = *in++;
+    }
+    return {STATUS_OK, process, process};
+}
+
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/visualizer/VisualizerSw.h b/audio/aidl/default/visualizer/VisualizerSw.h
new file mode 100644
index 0000000..21101dd
--- /dev/null
+++ b/audio/aidl/default/visualizer/VisualizerSw.h
@@ -0,0 +1,72 @@
+/*
+ * 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/audio/effect/BnEffect.h>
+#include <fmq/AidlMessageQueue.h>
+#include <cstdlib>
+#include <memory>
+
+#include "effect-impl/EffectImpl.h"
+#include "effect-impl/EffectUUID.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+class VisualizerSwContext final : public EffectContext {
+  public:
+    VisualizerSwContext(int statusDepth, const Parameter::Common& common)
+        : EffectContext(statusDepth, common) {
+        LOG(DEBUG) << __func__;
+    }
+    // TODO: add specific context here
+};
+
+class VisualizerSw final : public EffectImpl {
+  public:
+    VisualizerSw() { LOG(DEBUG) << __func__; }
+    ~VisualizerSw() {
+        LOG(DEBUG) << __func__;
+        releaseContext();
+    }
+
+    ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
+    ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific) override;
+    ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id,
+                                            Parameter::Specific* specific) override;
+    IEffect::Status effectProcessImpl(float* in, float* out, int process) override;
+    std::shared_ptr<EffectContext> createContext(const Parameter::Common& common) override;
+    RetCode releaseContext() override;
+
+  private:
+    std::shared_ptr<VisualizerSwContext> mContext;
+    /* capabilities */
+    const Visualizer::Capability kCapability;
+    /* Effect descriptor */
+    const Descriptor kDescriptor = {
+            .common = {.id = {.type = VisualizerTypeUUID,
+                              .uuid = VisualizerSwImplUUID,
+                              .proxy = std::nullopt},
+                       .flags = {.type = Flags::Type::INSERT,
+                                 .insert = Flags::Insert::FIRST,
+                                 .volume = Flags::Volume::CTRL},
+                       .name = "VisualizerSw"},
+            .capability = Capability::make<Capability::visualizer>(kCapability)};
+
+    /* parameters */
+    Visualizer mSpecificParam;
+};
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/volume/Android.bp b/audio/aidl/default/volume/Android.bp
new file mode 100644
index 0000000..505ee67
--- /dev/null
+++ b/audio/aidl/default/volume/Android.bp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+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"],
+}
+
+cc_library_shared {
+    name: "libvolumesw",
+    defaults: [
+        "aidlaudioeffectservice_defaults",
+        "latest_android_media_audio_common_types_ndk_shared",
+        "latest_android_hardware_audio_effect_ndk_shared",
+    ],
+    srcs: [
+        "VolumeSw.cpp",
+        ":effectCommonFile",
+    ],
+    visibility: [
+        "//hardware/interfaces/audio/aidl/default",
+    ],
+}
diff --git a/audio/aidl/default/volume/VolumeSw.cpp b/audio/aidl/default/volume/VolumeSw.cpp
new file mode 100644
index 0000000..e2f42d7
--- /dev/null
+++ b/audio/aidl/default/volume/VolumeSw.cpp
@@ -0,0 +1,118 @@
+/*
+ * 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 <cstddef>
+#define LOG_TAG "AHAL_VolumeSw"
+#include <Utils.h>
+#include <algorithm>
+#include <unordered_set>
+
+#include <android-base/logging.h>
+#include <fmq/AidlMessageQueue.h>
+
+#include "VolumeSw.h"
+
+using aidl::android::hardware::audio::effect::IEffect;
+using aidl::android::hardware::audio::effect::State;
+using aidl::android::hardware::audio::effect::VolumeSw;
+using aidl::android::hardware::audio::effect::VolumeSwImplUUID;
+using aidl::android::media::audio::common::AudioUuid;
+
+extern "C" binder_exception_t createEffect(const AudioUuid* in_impl_uuid,
+                                           std::shared_ptr<IEffect>* instanceSpp) {
+    if (!in_impl_uuid || *in_impl_uuid != VolumeSwImplUUID) {
+        LOG(ERROR) << __func__ << "uuid not supported";
+        return EX_ILLEGAL_ARGUMENT;
+    }
+    if (instanceSpp) {
+        *instanceSpp = ndk::SharedRefBase::make<VolumeSw>();
+        LOG(DEBUG) << __func__ << " instance " << instanceSpp->get() << " created";
+        return EX_NONE;
+    } else {
+        LOG(ERROR) << __func__ << " invalid input parameter!";
+        return EX_ILLEGAL_ARGUMENT;
+    }
+}
+
+extern "C" binder_exception_t destroyEffect(const std::shared_ptr<IEffect>& instanceSp) {
+    if (!instanceSp) {
+        return EX_NONE;
+    }
+    State state;
+    ndk::ScopedAStatus status = instanceSp->getState(&state);
+    if (!status.isOk() || State::INIT != state) {
+        LOG(ERROR) << __func__ << " instance " << instanceSp.get()
+                   << " in state: " << toString(state) << ", status: " << status.getDescription();
+        return EX_ILLEGAL_STATE;
+    }
+    LOG(DEBUG) << __func__ << " instance " << instanceSp.get() << " destroyed";
+    return EX_NONE;
+}
+
+namespace aidl::android::hardware::audio::effect {
+
+ndk::ScopedAStatus VolumeSw::getDescriptor(Descriptor* _aidl_return) {
+    LOG(DEBUG) << __func__ << kDescriptor.toString();
+    *_aidl_return = kDescriptor;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus VolumeSw::setParameterSpecific(const Parameter::Specific& specific) {
+    RETURN_IF(Parameter::Specific::volume != specific.getTag(), EX_ILLEGAL_ARGUMENT,
+              "EffectNotSupported");
+    std::lock_guard lg(mMutex);
+    RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+
+    mSpecificParam = specific.get<Parameter::Specific::volume>();
+    LOG(DEBUG) << __func__ << " success with: " << specific.toString();
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus VolumeSw::getParameterSpecific(const Parameter::Id& id,
+                                                  Parameter::Specific* specific) {
+    auto tag = id.getTag();
+    RETURN_IF(Parameter::Id::volumeTag != tag, EX_ILLEGAL_ARGUMENT, "wrongIdTag");
+    specific->set<Parameter::Specific::volume>(mSpecificParam);
+    return ndk::ScopedAStatus::ok();
+}
+
+std::shared_ptr<EffectContext> VolumeSw::createContext(const Parameter::Common& common) {
+    if (mContext) {
+        LOG(DEBUG) << __func__ << " context already exist";
+        return mContext;
+    }
+    mContext = std::make_shared<VolumeSwContext>(1 /* statusFmqDepth */, common);
+    return mContext;
+}
+
+RetCode VolumeSw::releaseContext() {
+    if (mContext) {
+        mContext.reset();
+    }
+    return RetCode::SUCCESS;
+}
+
+// Processing method running in EffectWorker thread.
+IEffect::Status VolumeSw::effectProcessImpl(float* in, float* out, int process) {
+    // TODO: get data buffer and process.
+    LOG(DEBUG) << __func__ << " in " << in << " out " << out << " process " << process;
+    for (int i = 0; i < process; i++) {
+        *out++ = *in++;
+    }
+    return {STATUS_OK, process, process};
+}
+
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/volume/VolumeSw.h b/audio/aidl/default/volume/VolumeSw.h
new file mode 100644
index 0000000..e46c864
--- /dev/null
+++ b/audio/aidl/default/volume/VolumeSw.h
@@ -0,0 +1,72 @@
+/*
+ * 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/audio/effect/BnEffect.h>
+#include <fmq/AidlMessageQueue.h>
+#include <cstdlib>
+#include <memory>
+
+#include "effect-impl/EffectImpl.h"
+#include "effect-impl/EffectUUID.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+class VolumeSwContext final : public EffectContext {
+  public:
+    VolumeSwContext(int statusDepth, const Parameter::Common& common)
+        : EffectContext(statusDepth, common) {
+        LOG(DEBUG) << __func__;
+    }
+    // TODO: add specific context here
+};
+
+class VolumeSw final : public EffectImpl {
+  public:
+    VolumeSw() { LOG(DEBUG) << __func__; }
+    ~VolumeSw() {
+        LOG(DEBUG) << __func__;
+        releaseContext();
+    }
+
+    ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
+    ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific) override;
+    ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id,
+                                            Parameter::Specific* specific) override;
+    IEffect::Status effectProcessImpl(float* in, float* out, int process) override;
+    std::shared_ptr<EffectContext> createContext(const Parameter::Common& common) override;
+    RetCode releaseContext() override;
+
+  private:
+    std::shared_ptr<VolumeSwContext> mContext;
+    /* capabilities */
+    const Volume::Capability kCapability;
+    /* Effect descriptor */
+    const Descriptor kDescriptor = {
+            .common = {.id = {.type = VolumeTypeUUID,
+                              .uuid = VolumeSwImplUUID,
+                              .proxy = std::nullopt},
+                       .flags = {.type = Flags::Type::INSERT,
+                                 .insert = Flags::Insert::FIRST,
+                                 .volume = Flags::Volume::CTRL},
+                       .name = "VolumeSw"},
+            .capability = Capability::make<Capability::volume>(kCapability)};
+
+    /* parameters */
+    Volume mSpecificParam;
+};
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/vts/Android.bp b/audio/aidl/vts/Android.bp
index 63fc415..8de1d79 100644
--- a/audio/aidl/vts/Android.bp
+++ b/audio/aidl/vts/Android.bp
@@ -60,6 +60,7 @@
         "android.hardware.common-V2-ndk",
         "android.hardware.common.fmq-V1-ndk",
     ],
+    header_libs: ["libaudioaidl_headers"],
     cflags: [
         "-Wall",
         "-Wextra",
@@ -75,6 +76,7 @@
 cc_test {
     name: "VtsHalAudioEffectTargetTest",
     defaults: [
+        "latest_android_hardware_audio_common_ndk_static",
         "latest_android_media_audio_common_types_ndk_static",
         "VtsHalTargetTestDefaults",
         "use_libaidlvintf_gtest_helper_static",
@@ -84,12 +86,49 @@
     ],
     shared_libs: [
         "libbinder_ndk",
+        "libfmq",
     ],
     static_libs: [
         "android.hardware.audio.effect-V1-ndk",
         "android.hardware.common-V2-ndk",
         "android.hardware.common.fmq-V1-ndk",
+        "libaudioaidlcommon",
     ],
+    header_libs: ["libaudioaidl_headers"],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wthread-safety",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
+
+cc_test {
+    name: "VtsHalEqualizerTargetTest",
+    defaults: [
+        "latest_android_hardware_audio_common_ndk_static",
+        "latest_android_media_audio_common_types_ndk_static",
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: [
+        "VtsHalEqualizerTargetTest.cpp",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+        "libfmq",
+    ],
+    static_libs: [
+        "android.hardware.audio.effect-V1-ndk",
+        "android.hardware.common-V2-ndk",
+        "android.hardware.common.fmq-V1-ndk",
+        "libaudioaidlcommon",
+    ],
+    header_libs: ["libaudioaidl_headers"],
     cflags: [
         "-Wall",
         "-Wextra",
diff --git a/audio/aidl/vts/EffectFactoryHelper.h b/audio/aidl/vts/EffectFactoryHelper.h
index 3cbca45..d58fcf2 100644
--- a/audio/aidl/vts/EffectFactoryHelper.h
+++ b/audio/aidl/vts/EffectFactoryHelper.h
@@ -24,13 +24,15 @@
 #include <android/binder_auto_utils.h>
 
 #include "TestUtils.h"
+#include "effect-impl/EffectUUID.h"
 
 using namespace android;
 
 using aidl::android::hardware::audio::effect::Descriptor;
+using aidl::android::hardware::audio::effect::EffectNullUuid;
 using aidl::android::hardware::audio::effect::IEffect;
 using aidl::android::hardware::audio::effect::IFactory;
-using aidl::android::hardware::audio::effect::Parameter;
+using aidl::android::hardware::audio::effect::Processing;
 using aidl::android::media::audio::common::AudioUuid;
 
 class EffectFactoryHelper {
@@ -51,14 +53,24 @@
 
     void QueryEffects(const std::optional<AudioUuid>& in_type,
                       const std::optional<AudioUuid>& in_instance,
+                      const std::optional<AudioUuid>& in_proxy,
                       std::vector<Descriptor::Identity>* _aidl_return) {
         ASSERT_NE(mEffectFactory, nullptr);
-        EXPECT_IS_OK(mEffectFactory->queryEffects(in_type, in_instance, _aidl_return));
+        EXPECT_IS_OK(mEffectFactory->queryEffects(in_type, in_instance, in_proxy, _aidl_return));
         mIds = *_aidl_return;
     }
 
-    void CreateEffects() {
+    void QueryProcessing(const std::optional<Processing::Type>& in_type,
+                         std::vector<Processing>* _aidl_return) {
         ASSERT_NE(mEffectFactory, nullptr);
+        EXPECT_IS_OK(mEffectFactory->queryProcessing(in_type, _aidl_return));
+        // only update the whole list if no filter applied
+        if (!in_type.has_value()) {
+            mProcesses = *_aidl_return;
+        }
+    }
+
+    void CreateEffects() {
         for (const auto& id : mIds) {
             std::shared_ptr<IEffect> effect;
             EXPECT_IS_OK(mEffectFactory->createEffect(id.uuid, &effect));
@@ -69,6 +81,27 @@
         }
     }
 
+    void QueryAndCreateEffects(const AudioUuid& type = EffectNullUuid) {
+        std::vector<Descriptor::Identity> ids;
+        ASSERT_NE(mEffectFactory, nullptr);
+
+        if (type == EffectNullUuid) {
+            EXPECT_IS_OK(
+                    mEffectFactory->queryEffects(std::nullopt, std::nullopt, std::nullopt, &ids));
+        } else {
+            EXPECT_IS_OK(mEffectFactory->queryEffects(type, std::nullopt, std::nullopt, &ids));
+        }
+        for (const auto& id : ids) {
+            ASSERT_EQ(id.type, type);
+            std::shared_ptr<IEffect> effect;
+            EXPECT_IS_OK(mEffectFactory->createEffect(id.uuid, &effect));
+            EXPECT_NE(effect, nullptr) << id.toString();
+            if (effect) {
+                mEffectIdMap[effect] = id;
+            }
+        }
+    }
+
     void CreateEffectsAndExpect(
             const std::vector<std::pair<Descriptor::Identity, binder_exception_t>>& uuid_status) {
         ASSERT_NE(mEffectFactory, nullptr);
@@ -90,7 +123,8 @@
 
     void QueryAndCreateAllEffects() {
         ASSERT_NE(mEffectFactory, nullptr);
-        EXPECT_IS_OK(mEffectFactory->queryEffects(std::nullopt, std::nullopt, &mCompleteIds));
+        EXPECT_IS_OK(mEffectFactory->queryEffects(std::nullopt, std::nullopt, std::nullopt,
+                                                  &mCompleteIds));
         for (const auto& id : mCompleteIds) {
             std::shared_ptr<IEffect> effect;
             EXPECT_IS_OK(mEffectFactory->createEffect(id.uuid, &effect));
@@ -115,8 +149,10 @@
 
     std::shared_ptr<IFactory> GetFactory() { return mEffectFactory; }
     const std::vector<Descriptor::Identity>& GetEffectIds() { return mIds; }
-    const std::vector<Descriptor::Identity>& GetCompleteEffectIdList() { return mCompleteIds; }
-    const std::map<std::shared_ptr<IEffect>, Descriptor::Identity>& GetEffectMap() {
+    const std::vector<Descriptor::Identity>& GetCompleteEffectIdList() const {
+        return mCompleteIds;
+    }
+    const std::map<std::shared_ptr<IEffect>, Descriptor::Identity>& GetEffectMap() const {
         return mEffectIdMap;
     }
     void ClearEffectMap() { mEffectIdMap.clear(); }
@@ -127,6 +163,7 @@
     AudioHalBinderServiceUtil binderUtil;
     std::vector<Descriptor::Identity> mIds;
     std::vector<Descriptor::Identity> mCompleteIds;
+    std::vector<Processing> mProcesses;
 
     std::map<std::shared_ptr<IEffect>, Descriptor::Identity> mEffectIdMap;
 };
diff --git a/audio/aidl/vts/EffectHelper.h b/audio/aidl/vts/EffectHelper.h
new file mode 100644
index 0000000..623ac37
--- /dev/null
+++ b/audio/aidl/vts/EffectHelper.h
@@ -0,0 +1,322 @@
+/*
+ * 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 <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <aidl/android/hardware/audio/effect/IEffect.h>
+#include <aidl/android/hardware/audio/effect/IFactory.h>
+#include <aidl/android/media/audio/common/AudioChannelLayout.h>
+#include <aidl/android/media/audio/common/AudioDeviceType.h>
+#include <android/binder_auto_utils.h>
+#include <fmq/AidlMessageQueue.h>
+
+#include "AudioHalBinderServiceUtil.h"
+#include "EffectFactoryHelper.h"
+#include "TestUtils.h"
+
+using namespace android;
+using aidl::android::hardware::audio::effect::CommandId;
+using aidl::android::hardware::audio::effect::Descriptor;
+using aidl::android::hardware::audio::effect::EffectNullUuid;
+using aidl::android::hardware::audio::effect::EffectZeroUuid;
+using aidl::android::hardware::audio::effect::IEffect;
+using aidl::android::hardware::audio::effect::Parameter;
+using aidl::android::hardware::audio::effect::State;
+using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
+using aidl::android::media::audio::common::AudioChannelLayout;
+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::AudioUuid;
+using aidl::android::media::audio::common::PcmType;
+
+const AudioFormatDescription DefaultFormat = {
+        .type = AudioFormatType::PCM, .pcm = PcmType::FLOAT_32_BIT, .encoding = ""};
+
+class EffectHelper {
+  public:
+    explicit EffectHelper(const std::string& name) : mFactoryHelper(EffectFactoryHelper(name)) {
+        mFactoryHelper.ConnectToFactoryService();
+    }
+
+    void OpenEffects(const AudioUuid& type = EffectNullUuid) {
+        auto open = [&](const std::shared_ptr<IEffect>& effect) {
+            ASSERT_NE(effect, nullptr);
+            IEffect::OpenEffectReturn ret;
+            EXPECT_IS_OK(effect->open(mCommon, mSpecific, &ret));
+            EffectParam params;
+            params.statusMQ = std::make_unique<StatusMQ>(ret.statusMQ);
+            params.inputMQ = std::make_unique<DataMQ>(ret.inputDataMQ);
+            params.outputMQ = std::make_unique<DataMQ>(ret.outputDataMQ);
+            mEffectParams.push_back(std::move(params));
+        };
+        EXPECT_NO_FATAL_FAILURE(ForEachEffect(open, type));
+    }
+
+    void CloseEffects(const binder_status_t status = EX_NONE) {
+        auto close = [&](const std::shared_ptr<IEffect>& effect) {
+            ASSERT_NE(effect, nullptr);
+            EXPECT_STATUS(status, effect->close());
+        };
+
+        EXPECT_NO_FATAL_FAILURE(ForEachEffect(close));
+    }
+
+    void CreateEffects(const int n = 1) {
+        for (int i = 0; i < n; i++) {
+            ASSERT_NO_FATAL_FAILURE(mFactoryHelper.QueryAndCreateAllEffects());
+        }
+    }
+
+    void CreateEffectsWithUUID(const AudioUuid& type = EffectNullUuid) {
+        ASSERT_NO_FATAL_FAILURE(mFactoryHelper.QueryAndCreateEffects(type));
+    }
+
+    void QueryEffects() { ASSERT_NO_FATAL_FAILURE(mFactoryHelper.QueryAndCreateAllEffects()); }
+
+    void DestroyEffects(const binder_status_t status = EX_NONE, const int remaining = 0) {
+        ASSERT_NO_FATAL_FAILURE(mFactoryHelper.DestroyEffects(status, remaining));
+        mEffectDescriptors.clear();
+    }
+
+    void GetEffectDescriptors() {
+        auto get = [&](const std::shared_ptr<IEffect>& effect) {
+            ASSERT_NE(effect, nullptr);
+            Descriptor desc;
+            EXPECT_IS_OK(effect->getDescriptor(&desc));
+            mEffectDescriptors.push_back(std::move(desc));
+        };
+        EXPECT_NO_FATAL_FAILURE(ForEachEffect(get));
+    }
+
+    void CommandEffects(CommandId command) {
+        auto close = [&](const std::shared_ptr<IEffect>& effect) {
+            ASSERT_NE(effect, nullptr);
+            EXPECT_IS_OK(effect->command(command));
+        };
+        EXPECT_NO_FATAL_FAILURE(ForEachEffect(close));
+    }
+
+    void CommandEffectsExpectStatus(CommandId command, const binder_status_t status) {
+        auto func = [&](const std::shared_ptr<IEffect>& effect) {
+            ASSERT_NE(effect, nullptr);
+            EXPECT_STATUS(status, effect->command(command));
+        };
+        EXPECT_NO_FATAL_FAILURE(ForEachEffect(func));
+    }
+
+    void ExpectState(State expected) {
+        auto get = [&](const std::shared_ptr<IEffect>& effect) {
+            ASSERT_NE(effect, nullptr);
+            State state = State::INIT;
+            EXPECT_IS_OK(effect->getState(&state));
+            EXPECT_EQ(expected, state);
+        };
+        EXPECT_NO_FATAL_FAILURE(ForEachEffect(get));
+    }
+
+    void SetParameter() {
+        auto func = [&](const std::shared_ptr<IEffect>& effect) {
+            ASSERT_NE(effect, nullptr);
+            Parameter param;
+            param.set<Parameter::common>(mCommon);
+            EXPECT_IS_OK(effect->setParameter(param));
+        };
+        EXPECT_NO_FATAL_FAILURE(ForEachEffect(func));
+    }
+
+    void VerifyParameters() {
+        auto func = [&](const std::shared_ptr<IEffect>& effect) {
+            ASSERT_NE(effect, nullptr);
+            Parameter paramCommonGet = Parameter(), paramCommonExpect = Parameter();
+            Parameter::Id id;
+            id.set<Parameter::Id::commonTag>(Parameter::common);
+            paramCommonExpect.set<Parameter::common>(mCommon);
+            EXPECT_IS_OK(effect->getParameter(id, &paramCommonGet));
+            EXPECT_EQ(paramCommonExpect, paramCommonGet)
+                    << paramCommonExpect.toString() << " vs " << paramCommonGet.toString();
+        };
+        EXPECT_NO_FATAL_FAILURE(ForEachEffect(func));
+    }
+
+    void QueryEffects(const std::optional<AudioUuid>& in_type,
+                      const std::optional<AudioUuid>& in_instance,
+                      const std::optional<AudioUuid>& in_proxy,
+                      std::vector<Descriptor::Identity>* _aidl_return) {
+        mFactoryHelper.QueryEffects(in_type, in_instance, in_proxy, _aidl_return);
+    }
+
+    template <typename Functor>
+    void ForEachEffect(Functor functor, const std::optional<AudioUuid>& type = EffectNullUuid) {
+        auto effectMap = mFactoryHelper.GetEffectMap();
+        for (const auto& it : effectMap) {
+            SCOPED_TRACE(it.second.toString());
+            if (type != EffectNullUuid && it.second.type != type) continue;
+            functor(it.first);
+        }
+    }
+
+    template <typename Functor>
+    void ForEachDescriptor(Functor functor) {
+        for (size_t i = 0; i < mEffectDescriptors.size(); i++) {
+            SCOPED_TRACE(mEffectDescriptors[i].toString());
+            functor(i, mEffectDescriptors[i]);
+        }
+    }
+
+    static const size_t mWriteMQBytes = 0x400;
+
+    enum class IO : char { INPUT = 0, OUTPUT = 1, INOUT = 2 };
+
+    void initParamCommonFormat(IO io = IO::INOUT,
+                               const AudioFormatDescription& format = DefaultFormat) {
+        if (io == IO::INPUT || io == IO::INOUT) {
+            mCommon.input.base.format = format;
+        }
+        if (io == IO::OUTPUT || io == IO::INOUT) {
+            mCommon.output.base.format = format;
+        }
+    }
+
+    void initParamCommonSampleRate(IO io = IO::INOUT, const int& sampleRate = 48000) {
+        if (io == IO::INPUT || io == IO::INOUT) {
+            mCommon.input.base.sampleRate = sampleRate;
+        }
+        if (io == IO::OUTPUT || io == IO::INOUT) {
+            mCommon.output.base.sampleRate = sampleRate;
+        }
+    }
+
+    void initParamCommonFrameCount(IO io = IO::INOUT, const long& frameCount = 48000) {
+        if (io == IO::INPUT || io == IO::INOUT) {
+            mCommon.input.frameCount = frameCount;
+        }
+        if (io == IO::OUTPUT || io == IO::INOUT) {
+            mCommon.output.frameCount = frameCount;
+        }
+    }
+    void initParamCommon(int session = 0, int ioHandle = -1, int iSampleRate = 48000,
+                         int oSampleRate = 48000, long iFrameCount = 0x100,
+                         long oFrameCount = 0x100) {
+        mCommon.session = session;
+        mCommon.ioHandle = ioHandle;
+
+        auto& input = mCommon.input;
+        auto& output = mCommon.output;
+        input.base.sampleRate = iSampleRate;
+        input.base.channelMask = mInputChannelLayout;
+        input.frameCount = iFrameCount;
+        input.base.format = DefaultFormat;
+        output.base.sampleRate = oSampleRate;
+        output.base.channelMask = mOutputChannelLayout;
+        output.base.format = DefaultFormat;
+        output.frameCount = oFrameCount;
+        output.base.format = DefaultFormat;
+        inputFrameSize = android::hardware::audio::common::getFrameSizeInBytes(
+                input.base.format, input.base.channelMask);
+        outputFrameSize = android::hardware::audio::common::getFrameSizeInBytes(
+                output.base.format, output.base.channelMask);
+    }
+
+    void setSpecific(Parameter::Specific& specific) { mSpecific = specific; }
+
+    // usually this function only call once.
+    void PrepareInputData(size_t bytes = mWriteMQBytes) {
+        size_t maxInputBytes = mWriteMQBytes;
+        for (auto& it : mEffectParams) {
+            auto& mq = it.inputMQ;
+            EXPECT_NE(nullptr, mq);
+            EXPECT_TRUE(mq->isValid());
+            const size_t bytesToWrite = mq->availableToWrite() * sizeof(float);
+            EXPECT_EQ(inputFrameSize * mCommon.input.frameCount, bytesToWrite);
+            EXPECT_NE(0UL, bytesToWrite);
+            EXPECT_TRUE(bytes <= bytesToWrite);
+            maxInputBytes = std::max(maxInputBytes, bytesToWrite);
+        }
+        mInputBuffer.resize(maxInputBytes / sizeof(float));
+        std::fill(mInputBuffer.begin(), mInputBuffer.end(), 0x5a);
+    }
+
+    void writeToFmq(size_t bytes = mWriteMQBytes) {
+        for (auto& it : mEffectParams) {
+            auto& mq = it.inputMQ;
+            EXPECT_NE(nullptr, mq);
+            const size_t bytesToWrite = mq->availableToWrite() * sizeof(float);
+            EXPECT_NE(0Ul, bytesToWrite);
+            EXPECT_TRUE(bytes <= bytesToWrite);
+            EXPECT_TRUE(mq->write(mInputBuffer.data(), bytes / sizeof(float)));
+        }
+    }
+
+    void readFromFmq(size_t expectBytes = mWriteMQBytes) {
+        for (auto& it : mEffectParams) {
+            IEffect::Status status{};
+            auto& statusMq = it.statusMQ;
+            EXPECT_NE(nullptr, statusMq);
+            EXPECT_TRUE(statusMq->readBlocking(&status, 1));
+            EXPECT_EQ(STATUS_OK, status.status);
+            EXPECT_EQ(expectBytes, (unsigned)status.fmqProduced * sizeof(float));
+
+            auto& outputMq = it.outputMQ;
+            EXPECT_NE(nullptr, outputMq);
+            EXPECT_EQ(expectBytes, outputMq->availableToRead() * sizeof(float));
+        }
+    }
+
+    void setInputChannelLayout(AudioChannelLayout input) { mInputChannelLayout = input; }
+    void setOutputChannelLayout(AudioChannelLayout output) { mOutputChannelLayout = output; }
+    const std::vector<Descriptor::Identity>& GetCompleteEffectIdList() const {
+        return mFactoryHelper.GetCompleteEffectIdList();
+    }
+    const std::vector<Descriptor>& getDescriptorVec() const { return mEffectDescriptors; }
+
+  private:
+    EffectFactoryHelper mFactoryHelper;
+
+    AudioChannelLayout mInputChannelLayout =
+            AudioChannelLayout::make<AudioChannelLayout::layoutMask>(
+                    AudioChannelLayout::LAYOUT_STEREO);
+    AudioChannelLayout mOutputChannelLayout =
+            AudioChannelLayout::make<AudioChannelLayout::layoutMask>(
+                    AudioChannelLayout::LAYOUT_STEREO);
+
+    Parameter::Common mCommon;
+    std::optional<Parameter::Specific> mSpecific = std::nullopt;
+
+    size_t inputFrameSize, outputFrameSize;
+    std::vector<float> mInputBuffer;  // reuse same buffer for all effects testing
+
+    typedef ::android::AidlMessageQueue<
+            IEffect::Status, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
+            StatusMQ;
+    typedef ::android::AidlMessageQueue<
+            float, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
+            DataMQ;
+
+    class EffectParam {
+      public:
+        std::unique_ptr<StatusMQ> statusMQ;
+        std::unique_ptr<DataMQ> inputMQ;
+        std::unique_ptr<DataMQ> outputMQ;
+    };
+    std::vector<EffectParam> mEffectParams;
+    std::vector<Descriptor> mEffectDescriptors;
+};
diff --git a/audio/aidl/vts/TestUtils.h b/audio/aidl/vts/TestUtils.h
index 2fc109a..5e4d56a 100644
--- a/audio/aidl/vts/TestUtils.h
+++ b/audio/aidl/vts/TestUtils.h
@@ -21,15 +21,6 @@
 #include <android/binder_auto_utils.h>
 #include <gtest/gtest_pred_impl.h>
 
-namespace ndk {
-
-std::ostream& operator<<(std::ostream& str, const ScopedAStatus& status) {
-    str << status.getDescription();
-    return str;
-}
-
-}  // namespace ndk
-
 namespace android::hardware::audio::common::testing {
 
 namespace detail {
diff --git a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
index 2381200..b415da4 100644
--- a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
@@ -34,6 +34,7 @@
 #include <aidl/android/media/audio/common/AudioIoFlags.h>
 #include <aidl/android/media/audio/common/AudioOutputFlags.h>
 #include <android-base/chrono_utils.h>
+#include <android/binder_enums.h>
 #include <fmq/AidlMessageQueue.h>
 
 #include "AudioHalBinderServiceUtil.h"
@@ -69,6 +70,7 @@
 using android::hardware::audio::common::isBitPositionFlagSet;
 using android::hardware::audio::common::StreamLogic;
 using android::hardware::audio::common::StreamWorker;
+using ndk::enum_range;
 using ndk::ScopedAStatus;
 
 template <typename T>
@@ -171,25 +173,26 @@
     AudioPortConfig mConfig;
 };
 
-class AudioCoreModule : public testing::TestWithParam<std::string> {
+// Can be used as a base for any test here, does not depend on the fixture GTest parameters.
+class AudioCoreModuleBase {
   public:
     // The default buffer size is used mostly for negative tests.
     static constexpr int kDefaultBufferSizeFrames = 256;
 
-    void SetUp() override {
-        ASSERT_NO_FATAL_FAILURE(ConnectToService());
+    void SetUpImpl(const std::string& moduleName) {
+        ASSERT_NO_FATAL_FAILURE(ConnectToService(moduleName));
         debug.flags().simulateDeviceConnections = true;
         ASSERT_NO_FATAL_FAILURE(debug.SetUp(module.get()));
     }
 
-    void TearDown() override {
+    void TearDownImpl() {
         if (module != nullptr) {
             EXPECT_IS_OK(module->setModuleDebug(ModuleDebug{}));
         }
     }
 
-    void ConnectToService() {
-        module = IModule::fromBinder(binderUtil.connectToService(GetParam()));
+    void ConnectToService(const std::string& moduleName) {
+        module = IModule::fromBinder(binderUtil.connectToService(moduleName));
         ASSERT_NE(module, nullptr);
     }
 
@@ -269,6 +272,13 @@
     WithDebugFlags debug;
 };
 
+class AudioCoreModule : public AudioCoreModuleBase, public testing::TestWithParam<std::string> {
+  public:
+    void SetUp() override { ASSERT_NO_FATAL_FAILURE(SetUpImpl(GetParam())); }
+
+    void TearDown() override { ASSERT_NO_FATAL_FAILURE(TearDownImpl()); }
+};
+
 class WithDevicePortConnectedState {
   public:
     explicit WithDevicePortConnectedState(const AudioPort& idAndData) : mIdAndData(idAndData) {}
@@ -352,21 +362,36 @@
     std::unique_ptr<DataMQ> mDataMQ;
 };
 
-class StreamCommonLogic : public StreamLogic {
+class StreamLogicDriver {
   public:
-    StreamDescriptor::Position getLastObservablePosition() {
-        std::lock_guard<std::mutex> lock(mLock);
-        return mLastReply.observable;
-    }
+    virtual ~StreamLogicDriver() = default;
+    // Return 'true' to stop the worker.
+    virtual bool done() = 0;
+    // For 'Writer' logic, if the 'actualSize' is 0, write is skipped.
+    // The 'fmqByteCount' from the returned command is passed as is to the HAL.
+    virtual StreamDescriptor::Command getNextCommand(int maxDataSize,
+                                                     int* actualSize = nullptr) = 0;
+    // Return 'true' to indicate that no further processing is needed,
+    // for example, the driver is expecting a bad status to be returned.
+    // The logic cycle will return with 'CONTINUE' status. Otherwise,
+    // the reply will be validated and then passed to 'processValidReply'.
+    virtual bool interceptRawReply(const StreamDescriptor::Reply& reply) = 0;
+    // Return 'false' to indicate that the contents of the reply are unexpected.
+    // Will abort the logic cycle.
+    virtual bool processValidReply(const StreamDescriptor::Reply& reply) = 0;
+};
 
+class StreamCommonLogic : public StreamLogic {
   protected:
-    explicit StreamCommonLogic(const StreamContext& context)
+    StreamCommonLogic(const StreamContext& context, StreamLogicDriver* driver)
         : mCommandMQ(context.getCommandMQ()),
           mReplyMQ(context.getReplyMQ()),
           mDataMQ(context.getDataMQ()),
-          mData(context.getBufferSizeBytes()) {}
+          mData(context.getBufferSizeBytes()),
+          mDriver(driver) {}
     StreamContext::CommandMQ* getCommandMQ() const { return mCommandMQ; }
     StreamContext::ReplyMQ* getReplyMQ() const { return mReplyMQ; }
+    StreamLogicDriver* getDriver() const { return mDriver; }
 
     std::string init() override { return ""; }
 
@@ -374,19 +399,20 @@
     StreamContext::ReplyMQ* mReplyMQ;
     StreamContext::DataMQ* mDataMQ;
     std::vector<int8_t> mData;
-    std::mutex mLock;
-    StreamDescriptor::Reply mLastReply GUARDED_BY(mLock);
+    StreamLogicDriver* const mDriver;
 };
 
 class StreamReaderLogic : public StreamCommonLogic {
   public:
-    explicit StreamReaderLogic(const StreamContext& context) : StreamCommonLogic(context) {}
+    StreamReaderLogic(const StreamContext& context, StreamLogicDriver* driver)
+        : StreamCommonLogic(context, driver) {}
 
   protected:
     Status cycle() override {
-        StreamDescriptor::Command command{};
-        command.code = StreamDescriptor::COMMAND_BURST;
-        command.fmqByteCount = mData.size();
+        if (getDriver()->done()) {
+            return Status::EXIT;
+        }
+        StreamDescriptor::Command command = getDriver()->getNextCommand(mData.size());
         if (!mCommandMQ->writeBlocking(&command, 1)) {
             LOG(ERROR) << __func__ << ": writing of command into MQ failed";
             return Status::ABORT;
@@ -396,6 +422,9 @@
             LOG(ERROR) << __func__ << ": reading of reply from MQ failed";
             return Status::ABORT;
         }
+        if (getDriver()->interceptRawReply(reply)) {
+            return Status::CONTINUE;
+        }
         if (reply.status != STATUS_OK) {
             LOG(ERROR) << __func__ << ": received error status: " << statusToString(reply.status);
             return Status::ABORT;
@@ -405,16 +434,41 @@
                        << ": received invalid byte count in the reply: " << reply.fmqByteCount;
             return Status::ABORT;
         }
-        {
-            std::lock_guard<std::mutex> lock(mLock);
-            mLastReply = reply;
+        if (static_cast<size_t>(reply.fmqByteCount) != mDataMQ->availableToRead()) {
+            LOG(ERROR) << __func__
+                       << ": the byte count in the reply is not the same as the amount of "
+                       << "data available in the MQ: " << reply.fmqByteCount
+                       << " != " << mDataMQ->availableToRead();
         }
-        const size_t readCount = std::min({mDataMQ->availableToRead(),
-                                           static_cast<size_t>(reply.fmqByteCount), mData.size()});
-        if (readCount == 0 || mDataMQ->read(mData.data(), readCount)) {
+        if (reply.latencyMs < 0 && reply.latencyMs != StreamDescriptor::LATENCY_UNKNOWN) {
+            LOG(ERROR) << __func__ << ": received invalid latency value: " << reply.latencyMs;
+            return Status::ABORT;
+        }
+        if (reply.xrunFrames < 0) {
+            LOG(ERROR) << __func__ << ": received invalid xrunFrames value: " << reply.xrunFrames;
+            return Status::ABORT;
+        }
+        if (std::find(enum_range<StreamDescriptor::State>().begin(),
+                      enum_range<StreamDescriptor::State>().end(),
+                      reply.state) == enum_range<StreamDescriptor::State>().end()) {
+            LOG(ERROR) << __func__ << ": received invalid stream state: " << toString(reply.state);
+            return Status::ABORT;
+        }
+        const bool acceptedReply = getDriver()->processValidReply(reply);
+        if (const size_t readCount = mDataMQ->availableToRead(); readCount > 0) {
+            std::vector<int8_t> data(readCount);
+            if (mDataMQ->read(data.data(), readCount)) {
+                memcpy(mData.data(), data.data(), std::min(mData.size(), data.size()));
+                goto checkAcceptedReply;
+            }
+            LOG(ERROR) << __func__ << ": reading of " << readCount << " data bytes from MQ failed";
+            return Status::ABORT;
+        }  // readCount == 0
+    checkAcceptedReply:
+        if (acceptedReply) {
             return Status::CONTINUE;
         }
-        LOG(ERROR) << __func__ << ": reading of " << readCount << " data bytes from MQ failed";
+        LOG(ERROR) << __func__ << ": unacceptable reply: " << reply.toString();
         return Status::ABORT;
     }
 };
@@ -422,17 +476,20 @@
 
 class StreamWriterLogic : public StreamCommonLogic {
   public:
-    explicit StreamWriterLogic(const StreamContext& context) : StreamCommonLogic(context) {}
+    StreamWriterLogic(const StreamContext& context, StreamLogicDriver* driver)
+        : StreamCommonLogic(context, driver) {}
 
   protected:
     Status cycle() override {
-        if (!mDataMQ->write(mData.data(), mData.size())) {
+        if (getDriver()->done()) {
+            return Status::EXIT;
+        }
+        int actualSize = 0;
+        StreamDescriptor::Command command = getDriver()->getNextCommand(mData.size(), &actualSize);
+        if (actualSize != 0 && !mDataMQ->write(mData.data(), mData.size())) {
             LOG(ERROR) << __func__ << ": writing of " << mData.size() << " bytes to MQ failed";
             return Status::ABORT;
         }
-        StreamDescriptor::Command command{};
-        command.code = StreamDescriptor::COMMAND_BURST;
-        command.fmqByteCount = mData.size();
         if (!mCommandMQ->writeBlocking(&command, 1)) {
             LOG(ERROR) << __func__ << ": writing of command into MQ failed";
             return Status::ABORT;
@@ -442,6 +499,9 @@
             LOG(ERROR) << __func__ << ": reading of reply from MQ failed";
             return Status::ABORT;
         }
+        if (getDriver()->interceptRawReply(reply)) {
+            return Status::CONTINUE;
+        }
         if (reply.status != STATUS_OK) {
             LOG(ERROR) << __func__ << ": received error status: " << statusToString(reply.status);
             return Status::ABORT;
@@ -451,11 +511,31 @@
                        << ": received invalid byte count in the reply: " << reply.fmqByteCount;
             return Status::ABORT;
         }
-        {
-            std::lock_guard<std::mutex> lock(mLock);
-            mLastReply = reply;
+        if (mDataMQ->availableToWrite() != mDataMQ->getQuantumCount()) {
+            LOG(ERROR) << __func__ << ": the HAL module did not consume all data from the data MQ: "
+                       << "available to write " << mDataMQ->availableToWrite()
+                       << ", total size: " << mDataMQ->getQuantumCount();
+            return Status::ABORT;
         }
-        return Status::CONTINUE;
+        if (reply.latencyMs < 0 && reply.latencyMs != StreamDescriptor::LATENCY_UNKNOWN) {
+            LOG(ERROR) << __func__ << ": received invalid latency value: " << reply.latencyMs;
+            return Status::ABORT;
+        }
+        if (reply.xrunFrames < 0) {
+            LOG(ERROR) << __func__ << ": received invalid xrunFrames value: " << reply.xrunFrames;
+            return Status::ABORT;
+        }
+        if (std::find(enum_range<StreamDescriptor::State>().begin(),
+                      enum_range<StreamDescriptor::State>().end(),
+                      reply.state) == enum_range<StreamDescriptor::State>().end()) {
+            LOG(ERROR) << __func__ << ": received invalid stream state: " << toString(reply.state);
+            return Status::ABORT;
+        }
+        if (getDriver()->processValidReply(reply)) {
+            return Status::CONTINUE;
+        }
+        LOG(ERROR) << __func__ << ": unacceptable reply: " << reply.toString();
+        return Status::ABORT;
     }
 };
 using StreamWriter = StreamWorker<StreamWriterLogic>;
@@ -466,52 +546,6 @@
     using Worker = std::conditional_t<is_input, StreamReader, StreamWriter>;
 };
 
-// A dedicated version to test replies to invalid commands.
-class StreamInvalidCommandLogic : public StreamCommonLogic {
-  public:
-    StreamInvalidCommandLogic(const StreamContext& context,
-                              const std::vector<StreamDescriptor::Command>& commands)
-        : StreamCommonLogic(context), mCommands(commands) {}
-
-    std::vector<std::string> getUnexpectedStatuses() {
-        std::lock_guard<std::mutex> lock(mLock);
-        return mUnexpectedStatuses;
-    }
-
-  protected:
-    Status cycle() override {
-        // Send all commands in one cycle to simplify testing.
-        // Extra logging helps to sort out issues with unexpected HAL behavior.
-        for (const auto& command : mCommands) {
-            LOG(INFO) << __func__ << ": writing command " << command.toString() << " into MQ...";
-            if (!getCommandMQ()->writeBlocking(&command, 1)) {
-                LOG(ERROR) << __func__ << ": writing of command into MQ failed";
-                return Status::ABORT;
-            }
-            StreamDescriptor::Reply reply{};
-            LOG(INFO) << __func__ << ": reading reply for command " << command.toString() << "...";
-            if (!getReplyMQ()->readBlocking(&reply, 1)) {
-                LOG(ERROR) << __func__ << ": reading of reply from MQ failed";
-                return Status::ABORT;
-            }
-            LOG(INFO) << __func__ << ": received status " << statusToString(reply.status)
-                      << " for command " << command.toString();
-            if (reply.status != STATUS_BAD_VALUE) {
-                std::string s = command.toString();
-                s.append(", ").append(statusToString(reply.status));
-                std::lock_guard<std::mutex> lock(mLock);
-                mUnexpectedStatuses.push_back(std::move(s));
-            }
-        };
-        return Status::EXIT;
-    }
-
-  private:
-    const std::vector<StreamDescriptor::Command> mCommands;
-    std::mutex mLock;
-    std::vector<std::string> mUnexpectedStatuses GUARDED_BY(mLock);
-};
-
 template <typename Stream>
 class WithStream {
   public:
@@ -1208,6 +1242,46 @@
     }
 }
 
+class StreamLogicDriverInvalidCommand : public StreamLogicDriver {
+  public:
+    StreamLogicDriverInvalidCommand(const std::vector<StreamDescriptor::Command>& commands)
+        : mCommands(commands) {}
+
+    std::string getUnexpectedStatuses() {
+        // This method is intended to be called after the worker thread has joined,
+        // thus no extra synchronization is needed.
+        std::string s;
+        if (!mStatuses.empty()) {
+            s = std::string("Pairs of (command, actual status): ")
+                        .append((android::internal::ToString(mStatuses)));
+        }
+        return s;
+    }
+
+    bool done() override { return mNextCommand >= mCommands.size(); }
+    StreamDescriptor::Command getNextCommand(int, int* actualSize) override {
+        if (actualSize != nullptr) *actualSize = 0;
+        return mCommands[mNextCommand++];
+    }
+    bool interceptRawReply(const StreamDescriptor::Reply& reply) override {
+        if (reply.status != STATUS_BAD_VALUE) {
+            std::string s = mCommands[mNextCommand - 1].toString();
+            s.append(", ").append(statusToString(reply.status));
+            mStatuses.push_back(std::move(s));
+            // If the HAL does not recognize the command as invalid,
+            // retrieve the data etc.
+            return reply.status != STATUS_OK;
+        }
+        return true;
+    }
+    bool processValidReply(const StreamDescriptor::Reply&) override { return true; }
+
+  private:
+    const std::vector<StreamDescriptor::Command> mCommands;
+    size_t mNextCommand = 0;
+    std::vector<std::string> mStatuses;
+};
+
 template <typename Stream>
 class AudioStream : public AudioCoreModule {
   public:
@@ -1315,19 +1389,6 @@
         EXPECT_NO_FATAL_FAILURE(OpenTwiceSamePortConfigImpl(portConfig.value()));
     }
 
-    void ReadOrWrite(bool useSetupSequence2, bool validateObservablePosition) {
-        const auto allPortConfigs =
-                moduleConfig->getPortConfigsForMixPorts(IOTraits<Stream>::is_input);
-        if (allPortConfigs.empty()) {
-            GTEST_SKIP() << "No mix ports have attached devices";
-        }
-        for (const auto& portConfig : allPortConfigs) {
-            EXPECT_NO_FATAL_FAILURE(
-                    ReadOrWriteImpl(portConfig, useSetupSequence2, validateObservablePosition))
-                    << portConfig.toString();
-        }
-    }
-
     void ResetPortConfigWithOpenStream() {
         const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits<Stream>::is_input);
         if (!portConfig.has_value()) {
@@ -1357,131 +1418,43 @@
                 << stream1.getPortId();
     }
 
-    template <class Worker>
-    void WaitForObservablePositionAdvance(Worker& worker) {
-        static constexpr int kWriteDurationUs = 50 * 1000;
-        static constexpr std::chrono::milliseconds kPositionChangeTimeout{10000};
-        int64_t framesInitial;
-        framesInitial = worker.getLastObservablePosition().frames;
-        ASSERT_FALSE(worker.hasError());
-        bool timedOut = false;
-        int64_t frames = framesInitial;
-        for (android::base::Timer elapsed;
-             frames <= framesInitial && !worker.hasError() &&
-             !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
-            usleep(kWriteDurationUs);
-            frames = worker.getLastObservablePosition().frames;
-        }
-        EXPECT_FALSE(timedOut);
-        EXPECT_FALSE(worker.hasError()) << worker.getError();
-        EXPECT_GT(frames, framesInitial);
-    }
-
-    void ReadOrWriteImpl(const AudioPortConfig& portConfig, bool useSetupSequence2,
-                         bool validateObservablePosition) {
-        if (!useSetupSequence2) {
-            ASSERT_NO_FATAL_FAILURE(
-                    ReadOrWriteSetupSequence1(portConfig, validateObservablePosition));
-        } else {
-            ASSERT_NO_FATAL_FAILURE(
-                    ReadOrWriteSetupSequence2(portConfig, validateObservablePosition));
-        }
-    }
-
-    // Set up a patch first, then open a stream.
-    void ReadOrWriteSetupSequence1(const AudioPortConfig& portConfig,
-                                   bool validateObservablePosition) {
-        auto devicePorts = moduleConfig->getAttachedDevicesPortsForMixPort(
-                IOTraits<Stream>::is_input, portConfig);
-        ASSERT_FALSE(devicePorts.empty());
-        auto devicePortConfig = moduleConfig->getSingleConfigForDevicePort(devicePorts[0]);
-        WithAudioPatch patch(IOTraits<Stream>::is_input, portConfig, devicePortConfig);
-        ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get()));
-
-        WithStream<Stream> stream(patch.getPortConfig(IOTraits<Stream>::is_input));
-        ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
-        typename IOTraits<Stream>::Worker worker(*stream.getContext());
-
-        ASSERT_TRUE(worker.start());
-        ASSERT_TRUE(worker.waitForAtLeastOneCycle());
-        if (validateObservablePosition) {
-            ASSERT_NO_FATAL_FAILURE(WaitForObservablePositionAdvance(worker));
-        }
-    }
-
-    // Open a stream, then set up a patch for it.
-    void ReadOrWriteSetupSequence2(const AudioPortConfig& portConfig,
-                                   bool validateObservablePosition) {
-        WithStream<Stream> stream(portConfig);
-        ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
-        typename IOTraits<Stream>::Worker worker(*stream.getContext());
-
-        auto devicePorts = moduleConfig->getAttachedDevicesPortsForMixPort(
-                IOTraits<Stream>::is_input, portConfig);
-        ASSERT_FALSE(devicePorts.empty());
-        auto devicePortConfig = moduleConfig->getSingleConfigForDevicePort(devicePorts[0]);
-        WithAudioPatch patch(IOTraits<Stream>::is_input, stream.getPortConfig(), devicePortConfig);
-        ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get()));
-
-        ASSERT_TRUE(worker.start());
-        ASSERT_TRUE(worker.waitForAtLeastOneCycle());
-        if (validateObservablePosition) {
-            ASSERT_NO_FATAL_FAILURE(WaitForObservablePositionAdvance(worker));
-        }
-    }
-
     void SendInvalidCommandImpl(const AudioPortConfig& portConfig) {
         std::vector<StreamDescriptor::Command> commands(6);
-        commands[0].code = -1;
-        commands[1].code = StreamDescriptor::COMMAND_BURST - 1;
-        commands[2].code = std::numeric_limits<int32_t>::min();
-        commands[3].code = std::numeric_limits<int32_t>::max();
-        commands[4].code = StreamDescriptor::COMMAND_BURST;
+        commands[0].code = StreamDescriptor::CommandCode(-1);
+        commands[1].code = StreamDescriptor::CommandCode(
+                static_cast<int32_t>(StreamDescriptor::CommandCode::START) - 1);
+        commands[2].code = StreamDescriptor::CommandCode(std::numeric_limits<int32_t>::min());
+        commands[3].code = StreamDescriptor::CommandCode(std::numeric_limits<int32_t>::max());
+        // TODO: For proper testing of input streams, need to put the stream into
+        // a state which accepts BURST commands.
+        commands[4].code = StreamDescriptor::CommandCode::BURST;
         commands[4].fmqByteCount = -1;
-        commands[5].code = StreamDescriptor::COMMAND_BURST;
+        commands[5].code = StreamDescriptor::CommandCode::BURST;
         commands[5].fmqByteCount = std::numeric_limits<int32_t>::min();
         WithStream<Stream> stream(portConfig);
         ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
-        StreamWorker<StreamInvalidCommandLogic> writer(*stream.getContext(), commands);
-        ASSERT_TRUE(writer.start());
-        writer.waitForAtLeastOneCycle();
-        auto unexpectedStatuses = writer.getUnexpectedStatuses();
-        EXPECT_EQ(0UL, unexpectedStatuses.size())
-                << "Pairs of (command, actual status): "
-                << android::internal::ToString(unexpectedStatuses);
+        StreamLogicDriverInvalidCommand driver(commands);
+        typename IOTraits<Stream>::Worker worker(*stream.getContext(), &driver);
+        ASSERT_TRUE(worker.start());
+        worker.join();
+        EXPECT_EQ("", driver.getUnexpectedStatuses());
     }
 };
 using AudioStreamIn = AudioStream<IStreamIn>;
 using AudioStreamOut = AudioStream<IStreamOut>;
 
-#define TEST_IO_STREAM(method_name)                                                \
+#define TEST_IN_AND_OUT_STREAM(method_name)                                        \
     TEST_P(AudioStreamIn, method_name) { ASSERT_NO_FATAL_FAILURE(method_name()); } \
     TEST_P(AudioStreamOut, method_name) { ASSERT_NO_FATAL_FAILURE(method_name()); }
-#define TEST_IO_STREAM_2(method_name, arg1, arg2)           \
-    TEST_P(AudioStreamIn, method_name##_##arg1##_##arg2) {  \
-        ASSERT_NO_FATAL_FAILURE(method_name(arg1, arg2));   \
-    }                                                       \
-    TEST_P(AudioStreamOut, method_name##_##arg1##_##arg2) { \
-        ASSERT_NO_FATAL_FAILURE(method_name(arg1, arg2));   \
-    }
 
-TEST_IO_STREAM(CloseTwice);
-TEST_IO_STREAM(OpenAllConfigs);
-TEST_IO_STREAM(OpenInvalidBufferSize);
-TEST_IO_STREAM(OpenInvalidDirection);
-TEST_IO_STREAM(OpenOverMaxCount);
-TEST_IO_STREAM(OpenTwiceSamePortConfig);
-// Use of constants makes comprehensible test names.
-constexpr bool SetupSequence1 = false;
-constexpr bool SetupSequence2 = true;
-constexpr bool SetupOnly = false;
-constexpr bool ValidateObservablePosition = true;
-TEST_IO_STREAM_2(ReadOrWrite, SetupSequence1, SetupOnly);
-TEST_IO_STREAM_2(ReadOrWrite, SetupSequence2, SetupOnly);
-TEST_IO_STREAM_2(ReadOrWrite, SetupSequence1, ValidateObservablePosition);
-TEST_IO_STREAM_2(ReadOrWrite, SetupSequence2, ValidateObservablePosition);
-TEST_IO_STREAM(ResetPortConfigWithOpenStream);
-TEST_IO_STREAM(SendInvalidCommand);
+TEST_IN_AND_OUT_STREAM(CloseTwice);
+TEST_IN_AND_OUT_STREAM(OpenAllConfigs);
+TEST_IN_AND_OUT_STREAM(OpenInvalidBufferSize);
+TEST_IN_AND_OUT_STREAM(OpenInvalidDirection);
+TEST_IN_AND_OUT_STREAM(OpenOverMaxCount);
+TEST_IN_AND_OUT_STREAM(OpenTwiceSamePortConfig);
+TEST_IN_AND_OUT_STREAM(ResetPortConfigWithOpenStream);
+TEST_IN_AND_OUT_STREAM(SendInvalidCommand);
 
 TEST_P(AudioStreamOut, OpenTwicePrimary) {
     const auto mixPorts = moduleConfig->getMixPorts(false);
@@ -1523,6 +1496,163 @@
             << "when no offload info is provided for a compressed offload mix port";
 }
 
+using CommandAndState = std::pair<StreamDescriptor::CommandCode, StreamDescriptor::State>;
+
+class StreamLogicDefaultDriver : public StreamLogicDriver {
+  public:
+    explicit StreamLogicDefaultDriver(const std::vector<CommandAndState>& commands)
+        : mCommands(commands) {}
+
+    // The three methods below is intended to be called after the worker
+    // thread has joined, thus no extra synchronization is needed.
+    bool hasObservablePositionIncrease() const { return mObservablePositionIncrease; }
+    bool hasRetrogradeObservablePosition() const { return mRetrogradeObservablePosition; }
+    std::string getUnexpectedStateTransition() const { return mUnexpectedTransition; }
+
+    bool done() override { return mNextCommand >= mCommands.size(); }
+    StreamDescriptor::Command getNextCommand(int maxDataSize, int* actualSize) override {
+        StreamDescriptor::Command command{};
+        command.code = mCommands[mNextCommand++].first;
+        const int dataSize = command.code == StreamDescriptor::CommandCode::BURST ? maxDataSize : 0;
+        command.fmqByteCount = dataSize;
+        if (actualSize != nullptr) {
+            // In the output scenario, reduce slightly the fmqByteCount to verify
+            // that the HAL module always consumes all data from the MQ.
+            if (command.fmqByteCount > 1) command.fmqByteCount--;
+            *actualSize = dataSize;
+        }
+        return command;
+    }
+    bool interceptRawReply(const StreamDescriptor::Reply&) override { return false; }
+    bool processValidReply(const StreamDescriptor::Reply& reply) override {
+        if (mPreviousFrames.has_value()) {
+            if (reply.observable.frames > mPreviousFrames.value()) {
+                mObservablePositionIncrease = true;
+            } else if (reply.observable.frames < mPreviousFrames.value()) {
+                mRetrogradeObservablePosition = true;
+            }
+        }
+        mPreviousFrames = reply.observable.frames;
+
+        const auto& lastCommandState = mCommands[mNextCommand - 1];
+        if (lastCommandState.second != reply.state) {
+            std::string s = std::string("Unexpected transition from the state ")
+                                    .append(mPreviousState)
+                                    .append(" to ")
+                                    .append(toString(reply.state))
+                                    .append(" caused by the command ")
+                                    .append(toString(lastCommandState.first));
+            LOG(ERROR) << __func__ << ": " << s;
+            mUnexpectedTransition = std::move(s);
+            return false;
+        }
+        return true;
+    }
+
+  protected:
+    const std::vector<CommandAndState>& mCommands;
+    size_t mNextCommand = 0;
+    std::optional<int64_t> mPreviousFrames;
+    std::string mPreviousState = "<initial state>";
+    bool mObservablePositionIncrease = false;
+    bool mRetrogradeObservablePosition = false;
+    std::string mUnexpectedTransition;
+};
+
+using NamedCommandSequence = std::pair<std::string, std::vector<CommandAndState>>;
+enum { PARAM_MODULE_NAME, PARAM_CMD_SEQ, PARAM_SETUP_SEQ };
+using StreamIoTestParameters =
+        std::tuple<std::string /*moduleName*/, NamedCommandSequence, bool /*useSetupSequence2*/>;
+template <typename Stream>
+class AudioStreamIo : public AudioCoreModuleBase,
+                      public testing::TestWithParam<StreamIoTestParameters> {
+  public:
+    void SetUp() override {
+        ASSERT_NO_FATAL_FAILURE(SetUpImpl(std::get<PARAM_MODULE_NAME>(GetParam())));
+        ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+    }
+
+    void Run() {
+        const auto allPortConfigs =
+                moduleConfig->getPortConfigsForMixPorts(IOTraits<Stream>::is_input);
+        if (allPortConfigs.empty()) {
+            GTEST_SKIP() << "No mix ports have attached devices";
+        }
+        for (const auto& portConfig : allPortConfigs) {
+            SCOPED_TRACE(portConfig.toString());
+            const auto& commandsAndStates = std::get<PARAM_CMD_SEQ>(GetParam()).second;
+            if (!std::get<PARAM_SETUP_SEQ>(GetParam())) {
+                ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq1(portConfig, commandsAndStates));
+            } else {
+                ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq2(portConfig, commandsAndStates));
+            }
+        }
+    }
+
+    bool ValidateObservablePosition(const AudioPortConfig& /*portConfig*/) {
+        // May return false based on the portConfig, e.g. for telephony ports.
+        return true;
+    }
+
+    // Set up a patch first, then open a stream.
+    void RunStreamIoCommandsImplSeq1(const AudioPortConfig& portConfig,
+                                     const std::vector<CommandAndState>& commandsAndStates) {
+        auto devicePorts = moduleConfig->getAttachedDevicesPortsForMixPort(
+                IOTraits<Stream>::is_input, portConfig);
+        ASSERT_FALSE(devicePorts.empty());
+        auto devicePortConfig = moduleConfig->getSingleConfigForDevicePort(devicePorts[0]);
+        WithAudioPatch patch(IOTraits<Stream>::is_input, portConfig, devicePortConfig);
+        ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get()));
+
+        WithStream<Stream> stream(patch.getPortConfig(IOTraits<Stream>::is_input));
+        ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+        StreamLogicDefaultDriver driver(commandsAndStates);
+        typename IOTraits<Stream>::Worker worker(*stream.getContext(), &driver);
+
+        ASSERT_TRUE(worker.start());
+        worker.join();
+        EXPECT_FALSE(worker.hasError()) << worker.getError();
+        EXPECT_EQ("", driver.getUnexpectedStateTransition());
+        if (ValidateObservablePosition(portConfig)) {
+            EXPECT_TRUE(driver.hasObservablePositionIncrease());
+            EXPECT_FALSE(driver.hasRetrogradeObservablePosition());
+        }
+    }
+
+    // Open a stream, then set up a patch for it.
+    void RunStreamIoCommandsImplSeq2(const AudioPortConfig& portConfig,
+                                     const std::vector<CommandAndState>& commandsAndStates) {
+        WithStream<Stream> stream(portConfig);
+        ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+        StreamLogicDefaultDriver driver(commandsAndStates);
+        typename IOTraits<Stream>::Worker worker(*stream.getContext(), &driver);
+
+        auto devicePorts = moduleConfig->getAttachedDevicesPortsForMixPort(
+                IOTraits<Stream>::is_input, portConfig);
+        ASSERT_FALSE(devicePorts.empty());
+        auto devicePortConfig = moduleConfig->getSingleConfigForDevicePort(devicePorts[0]);
+        WithAudioPatch patch(IOTraits<Stream>::is_input, stream.getPortConfig(), devicePortConfig);
+        ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get()));
+
+        ASSERT_TRUE(worker.start());
+        worker.join();
+        EXPECT_FALSE(worker.hasError()) << worker.getError();
+        EXPECT_EQ("", driver.getUnexpectedStateTransition());
+        if (ValidateObservablePosition(portConfig)) {
+            EXPECT_TRUE(driver.hasObservablePositionIncrease());
+            EXPECT_FALSE(driver.hasRetrogradeObservablePosition());
+        }
+    }
+};
+using AudioStreamIoIn = AudioStreamIo<IStreamIn>;
+using AudioStreamIoOut = AudioStreamIo<IStreamOut>;
+
+#define TEST_IN_AND_OUT_STREAM_IO(method_name)                                       \
+    TEST_P(AudioStreamIoIn, method_name) { ASSERT_NO_FATAL_FAILURE(method_name()); } \
+    TEST_P(AudioStreamIoOut, method_name) { ASSERT_NO_FATAL_FAILURE(method_name()); }
+
+TEST_IN_AND_OUT_STREAM_IO(Run);
+
 // Tests specific to audio patches. The fixure class is named 'AudioModulePatch'
 // to avoid clashing with 'AudioPatch' class.
 class AudioModulePatch : public AudioCoreModule {
@@ -1704,6 +1834,139 @@
                          testing::ValuesIn(android::getAidlHalInstanceNames(IModule::descriptor)),
                          android::PrintInstanceNameToString);
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioStreamOut);
+
+static const NamedCommandSequence kReadOrWriteSeq = std::make_pair(
+        std::string("ReadOrWrite"),
+        std::vector<CommandAndState>{
+                std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
+                std::make_pair(StreamDescriptor::CommandCode::BURST,
+                               StreamDescriptor::State::ACTIVE),
+                std::make_pair(StreamDescriptor::CommandCode::BURST,
+                               StreamDescriptor::State::ACTIVE),
+                std::make_pair(StreamDescriptor::CommandCode::BURST,
+                               StreamDescriptor::State::ACTIVE)});
+static const NamedCommandSequence kDrainInSeq = std::make_pair(
+        std::string("Drain"),
+        std::vector<CommandAndState>{
+                std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
+                std::make_pair(StreamDescriptor::CommandCode::BURST,
+                               StreamDescriptor::State::ACTIVE),
+                std::make_pair(StreamDescriptor::CommandCode::DRAIN,
+                               StreamDescriptor::State::DRAINING),
+                std::make_pair(StreamDescriptor::CommandCode::START,
+                               StreamDescriptor::State::ACTIVE),
+                std::make_pair(StreamDescriptor::CommandCode::DRAIN,
+                               StreamDescriptor::State::DRAINING),
+                // TODO: This will need to be changed once DRAIN starts taking time.
+                std::make_pair(StreamDescriptor::CommandCode::BURST,
+                               StreamDescriptor::State::STANDBY)});
+static const NamedCommandSequence kDrainOutSeq = std::make_pair(
+        std::string("Drain"),
+        std::vector<CommandAndState>{
+                std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
+                std::make_pair(StreamDescriptor::CommandCode::BURST,
+                               StreamDescriptor::State::ACTIVE),
+                // TODO: This will need to be changed once DRAIN starts taking time.
+                std::make_pair(StreamDescriptor::CommandCode::DRAIN,
+                               StreamDescriptor::State::IDLE)});
+// TODO: This will need to be changed once DRAIN starts taking time so we can pause it.
+static const NamedCommandSequence kDrainPauseOutSeq = std::make_pair(
+        std::string("DrainPause"),
+        std::vector<CommandAndState>{
+                std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
+                std::make_pair(StreamDescriptor::CommandCode::BURST,
+                               StreamDescriptor::State::ACTIVE),
+                std::make_pair(StreamDescriptor::CommandCode::DRAIN,
+                               StreamDescriptor::State::IDLE)});
+static const NamedCommandSequence kStandbySeq = std::make_pair(
+        std::string("Standby"),
+        std::vector<CommandAndState>{
+                std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
+                std::make_pair(StreamDescriptor::CommandCode::STANDBY,
+                               StreamDescriptor::State::STANDBY),
+                // Perform a read or write in order to advance observable position
+                // (this is verified by tests).
+                std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
+                std::make_pair(StreamDescriptor::CommandCode::BURST,
+                               StreamDescriptor::State::ACTIVE)});
+static const NamedCommandSequence kPauseInSeq = std::make_pair(
+        std::string("Pause"),
+        std::vector<CommandAndState>{
+                std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
+                std::make_pair(StreamDescriptor::CommandCode::BURST,
+                               StreamDescriptor::State::ACTIVE),
+                std::make_pair(StreamDescriptor::CommandCode::PAUSE,
+                               StreamDescriptor::State::PAUSED),
+                std::make_pair(StreamDescriptor::CommandCode::BURST,
+                               StreamDescriptor::State::ACTIVE),
+                std::make_pair(StreamDescriptor::CommandCode::PAUSE,
+                               StreamDescriptor::State::PAUSED),
+                std::make_pair(StreamDescriptor::CommandCode::FLUSH,
+                               StreamDescriptor::State::STANDBY)});
+static const NamedCommandSequence kPauseOutSeq = std::make_pair(
+        std::string("Pause"),
+        std::vector<CommandAndState>{
+                std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
+                std::make_pair(StreamDescriptor::CommandCode::BURST,
+                               StreamDescriptor::State::ACTIVE),
+                std::make_pair(StreamDescriptor::CommandCode::PAUSE,
+                               StreamDescriptor::State::PAUSED),
+                std::make_pair(StreamDescriptor::CommandCode::START,
+                               StreamDescriptor::State::ACTIVE),
+                std::make_pair(StreamDescriptor::CommandCode::PAUSE,
+                               StreamDescriptor::State::PAUSED),
+                std::make_pair(StreamDescriptor::CommandCode::BURST,
+                               StreamDescriptor::State::PAUSED),
+                std::make_pair(StreamDescriptor::CommandCode::START,
+                               StreamDescriptor::State::ACTIVE),
+                std::make_pair(StreamDescriptor::CommandCode::PAUSE,
+                               StreamDescriptor::State::PAUSED)});
+static const NamedCommandSequence kFlushInSeq = std::make_pair(
+        std::string("Flush"),
+        std::vector<CommandAndState>{
+                std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
+                std::make_pair(StreamDescriptor::CommandCode::BURST,
+                               StreamDescriptor::State::ACTIVE),
+                std::make_pair(StreamDescriptor::CommandCode::PAUSE,
+                               StreamDescriptor::State::PAUSED),
+                std::make_pair(StreamDescriptor::CommandCode::FLUSH,
+                               StreamDescriptor::State::STANDBY)});
+static const NamedCommandSequence kFlushOutSeq = std::make_pair(
+        std::string("Flush"),
+        std::vector<CommandAndState>{
+                std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
+                std::make_pair(StreamDescriptor::CommandCode::BURST,
+                               StreamDescriptor::State::ACTIVE),
+                std::make_pair(StreamDescriptor::CommandCode::PAUSE,
+                               StreamDescriptor::State::PAUSED),
+                std::make_pair(StreamDescriptor::CommandCode::FLUSH,
+                               StreamDescriptor::State::IDLE)});
+std::string GetStreamIoTestName(const testing::TestParamInfo<StreamIoTestParameters>& info) {
+    return android::PrintInstanceNameToString(
+                   testing::TestParamInfo<std::string>{std::get<PARAM_MODULE_NAME>(info.param),
+                                                       info.index})
+            .append("_")
+            .append(std::get<PARAM_CMD_SEQ>(info.param).first)
+            .append("_SetupSeq")
+            .append(std::get<PARAM_SETUP_SEQ>(info.param) ? "2" : "1");
+}
+INSTANTIATE_TEST_SUITE_P(
+        AudioStreamIoInTest, AudioStreamIoIn,
+        testing::Combine(testing::ValuesIn(android::getAidlHalInstanceNames(IModule::descriptor)),
+                         testing::Values(kReadOrWriteSeq, kDrainInSeq, kStandbySeq, kPauseInSeq,
+                                         kFlushInSeq),
+                         testing::Values(false, true)),
+        GetStreamIoTestName);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioStreamIoIn);
+INSTANTIATE_TEST_SUITE_P(
+        AudioStreamIoOutTest, AudioStreamIoOut,
+        testing::Combine(testing::ValuesIn(android::getAidlHalInstanceNames(IModule::descriptor)),
+                         testing::Values(kReadOrWriteSeq, kDrainOutSeq, kDrainPauseOutSeq,
+                                         kStandbySeq, kPauseOutSeq, kFlushOutSeq),
+                         testing::Values(false, true)),
+        GetStreamIoTestName);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioStreamIoOut);
+
 INSTANTIATE_TEST_SUITE_P(AudioPatchTest, AudioModulePatch,
                          testing::ValuesIn(android::getAidlHalInstanceNames(IModule::descriptor)),
                          android::PrintInstanceNameToString);
diff --git a/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp
index dd17a6f..d30dff4 100644
--- a/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp
@@ -37,7 +37,10 @@
 using namespace android;
 
 using aidl::android::hardware::audio::effect::Descriptor;
+using aidl::android::hardware::audio::effect::EffectNullUuid;
+using aidl::android::hardware::audio::effect::EffectZeroUuid;
 using aidl::android::hardware::audio::effect::IFactory;
+using aidl::android::hardware::audio::effect::Processing;
 using aidl::android::media::audio::common::AudioUuid;
 
 /// Effect factory testing.
@@ -49,17 +52,8 @@
 
     EffectFactoryHelper mFactory = EffectFactoryHelper(GetParam());
 
-    // TODO: these UUID can get from config file
-    // ec7178ec-e5e1-4432-a3f4-4657e6795210
-    const AudioUuid nullUuid = {static_cast<int32_t>(0xec7178ec),
-                                0xe5e1,
-                                0x4432,
-                                0xa3f4,
-                                {0x46, 0x57, 0xe6, 0x79, 0x52, 0x10}};
-    const AudioUuid zeroUuid = {
-            static_cast<int32_t>(0x0), 0x0, 0x0, 0x0, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
-    const Descriptor::Identity nullDesc = {.uuid = nullUuid};
-    const Descriptor::Identity zeroDesc = {.uuid = zeroUuid};
+    const Descriptor::Identity nullDesc = {.uuid = EffectNullUuid};
+    const Descriptor::Identity zeroDesc = {.uuid = EffectZeroUuid};
 };
 
 TEST_P(EffectFactoryTest, SetupAndTearDown) {
@@ -72,35 +66,35 @@
 
 TEST_P(EffectFactoryTest, QueriedDescriptorList) {
     std::vector<Descriptor::Identity> descriptors;
-    mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
+    mFactory.QueryEffects(std::nullopt, std::nullopt, std::nullopt, &descriptors);
     EXPECT_NE(descriptors.size(), 0UL);
 }
 
 TEST_P(EffectFactoryTest, DescriptorUUIDNotNull) {
     std::vector<Descriptor::Identity> descriptors;
-    mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
+    mFactory.QueryEffects(std::nullopt, std::nullopt, std::nullopt, &descriptors);
     // TODO: Factory eventually need to return the full list of MUST supported AOSP effects.
     for (auto& desc : descriptors) {
-        EXPECT_NE(desc.type, zeroUuid);
-        EXPECT_NE(desc.uuid, zeroUuid);
+        EXPECT_NE(desc.type, EffectNullUuid);
+        EXPECT_NE(desc.uuid, EffectNullUuid);
     }
 }
 
 TEST_P(EffectFactoryTest, QueriedDescriptorNotExistType) {
     std::vector<Descriptor::Identity> descriptors;
-    mFactory.QueryEffects(nullUuid, std::nullopt, &descriptors);
+    mFactory.QueryEffects(EffectNullUuid, std::nullopt, std::nullopt, &descriptors);
     EXPECT_EQ(descriptors.size(), 0UL);
 }
 
 TEST_P(EffectFactoryTest, QueriedDescriptorNotExistInstance) {
     std::vector<Descriptor::Identity> descriptors;
-    mFactory.QueryEffects(std::nullopt, nullUuid, &descriptors);
+    mFactory.QueryEffects(std::nullopt, EffectNullUuid, std::nullopt, &descriptors);
     EXPECT_EQ(descriptors.size(), 0UL);
 }
 
 TEST_P(EffectFactoryTest, CreateAndDestroyOnce) {
     std::vector<Descriptor::Identity> descriptors;
-    mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
+    mFactory.QueryEffects(std::nullopt, std::nullopt, std::nullopt, &descriptors);
     auto numIds = mFactory.GetEffectIds().size();
     EXPECT_NE(numIds, 0UL);
 
@@ -114,7 +108,7 @@
 
 TEST_P(EffectFactoryTest, CreateAndDestroyRepeat) {
     std::vector<Descriptor::Identity> descriptors;
-    mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
+    mFactory.QueryEffects(std::nullopt, std::nullopt, std::nullopt, &descriptors);
     auto numIds = mFactory.GetEffectIds().size();
     EXPECT_NE(numIds, 0UL);
 
@@ -134,7 +128,7 @@
 
 TEST_P(EffectFactoryTest, CreateMultipleInstanceOfSameEffect) {
     std::vector<Descriptor::Identity> descriptors;
-    mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
+    mFactory.QueryEffects(std::nullopt, std::nullopt, std::nullopt, &descriptors);
     auto numIds = mFactory.GetEffectIds().size();
     EXPECT_NE(numIds, 0UL);
 
@@ -173,7 +167,7 @@
 
 TEST_P(EffectFactoryTest, CreateAndRemoveReference) {
     std::vector<Descriptor::Identity> descriptors;
-    mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
+    mFactory.QueryEffects(std::nullopt, std::nullopt, std::nullopt, &descriptors);
     auto numIds = mFactory.GetEffectIds().size();
     EXPECT_NE(numIds, 0UL);
 
@@ -188,7 +182,7 @@
 
 TEST_P(EffectFactoryTest, CreateRemoveReferenceAndCreateDestroy) {
     std::vector<Descriptor::Identity> descriptors;
-    mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
+    mFactory.QueryEffects(std::nullopt, std::nullopt, std::nullopt, &descriptors);
     auto numIds = mFactory.GetEffectIds().size();
     EXPECT_NE(numIds, 0UL);
 
@@ -209,7 +203,7 @@
 
 TEST_P(EffectFactoryTest, CreateRestartAndCreateDestroy) {
     std::vector<Descriptor::Identity> descriptors;
-    mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
+    mFactory.QueryEffects(std::nullopt, std::nullopt, std::nullopt, &descriptors);
     auto numIds = mFactory.GetEffectIds().size();
     auto& effectMap = mFactory.GetEffectMap();
     mFactory.CreateEffects();
@@ -222,6 +216,12 @@
     EXPECT_EQ(effectMap.size(), 0UL);
 }
 
+TEST_P(EffectFactoryTest, QueryProcess) {
+    std::vector<Processing> processing;
+    mFactory.QueryProcessing(std::nullopt, &processing);
+    // TODO: verify the number of process in example implementation after audio_effects.xml migrated
+}
+
 INSTANTIATE_TEST_SUITE_P(EffectFactoryTest, EffectFactoryTest,
                          testing::ValuesIn(android::getAidlHalInstanceNames(IFactory::descriptor)),
                          android::PrintInstanceNameToString);
diff --git a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
index 23b20bd..3ea67bc 100644
--- a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "VtsHalAudioEffectTargetTest"
+
 #include <memory>
 #include <string>
 #include <unordered_map>
 #include <unordered_set>
 #include <vector>
 
-#define LOG_TAG "VtsHalAudioEffect"
-
 #include <aidl/Gtest.h>
 #include <aidl/Vintf.h>
 #include <android-base/logging.h>
@@ -30,13 +30,13 @@
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
 
+#include <Utils.h>
 #include <aidl/android/hardware/audio/effect/IEffect.h>
 #include <aidl/android/hardware/audio/effect/IFactory.h>
-#include <aidl/android/media/audio/common/AudioChannelLayout.h>
 #include <aidl/android/media/audio/common/AudioDeviceType.h>
 
 #include "AudioHalBinderServiceUtil.h"
-#include "EffectFactoryHelper.h"
+#include "EffectHelper.h"
 #include "TestUtils.h"
 
 using namespace android;
@@ -49,422 +49,314 @@
 using aidl::android::hardware::audio::effect::IFactory;
 using aidl::android::hardware::audio::effect::Parameter;
 using aidl::android::hardware::audio::effect::State;
-using aidl::android::media::audio::common::AudioChannelLayout;
 using aidl::android::media::audio::common::AudioDeviceType;
 
-class AudioEffect : public testing::TestWithParam<std::string> {
+class AudioEffectTest : public testing::TestWithParam<std::string>, public EffectHelper {
   public:
+    AudioEffectTest() : EffectHelper(GetParam()) {}
+
     void SetUp() override {
-        ASSERT_NO_FATAL_FAILURE(mFactoryHelper.ConnectToFactoryService());
         CreateEffects();
+        initParamCommonFormat();
         initParamCommon();
-        initParamSpecific();
     }
 
     void TearDown() override {
         CloseEffects();
         DestroyEffects();
     }
-
-    void OpenEffects() {
-        auto open = [&](const std::shared_ptr<IEffect>& effect) {
-            IEffect::OpenEffectReturn ret;
-            EXPECT_IS_OK(effect->open(mCommon, mSpecific, &ret));
-        };
-        EXPECT_NO_FATAL_FAILURE(ForEachEffect(open));
-    }
-
-    void CloseEffects(const binder_status_t status = EX_NONE) {
-        auto close = [&](const std::shared_ptr<IEffect>& effect) {
-            EXPECT_STATUS(status, effect->close());
-        };
-
-        EXPECT_NO_FATAL_FAILURE(ForEachEffect(close));
-    }
-
-    void CreateEffects(const int n = 1) {
-        for (int i = 0; i < n; i++) {
-            ASSERT_NO_FATAL_FAILURE(mFactoryHelper.QueryAndCreateAllEffects());
-        }
-    }
-
-    void DestroyEffects(const binder_status_t status = EX_NONE, const int remaining = 0) {
-        ASSERT_NO_FATAL_FAILURE(mFactoryHelper.DestroyEffects(status, remaining));
-    }
-
-    void GetEffectDescriptors() {
-        auto get = [](const std::shared_ptr<IEffect>& effect) {
-            Descriptor desc;
-            EXPECT_IS_OK(effect->getDescriptor(&desc));
-        };
-        EXPECT_NO_FATAL_FAILURE(ForEachEffect(get));
-    }
-
-    void CommandEffects(CommandId command) {
-        auto close = [&](const std::shared_ptr<IEffect>& effect) {
-            EXPECT_IS_OK(effect->command(command));
-        };
-        EXPECT_NO_FATAL_FAILURE(ForEachEffect(close));
-    }
-
-    void CommandEffectsExpectStatus(CommandId command, const binder_status_t status) {
-        auto func = [&](const std::shared_ptr<IEffect>& effect) {
-            EXPECT_STATUS(status, effect->command(command));
-        };
-        EXPECT_NO_FATAL_FAILURE(ForEachEffect(func));
-    }
-
-    void ExpectState(State expected) {
-        auto get = [&](const std::shared_ptr<IEffect>& effect) {
-            State state = State::INIT;
-            EXPECT_IS_OK(effect->getState(&state));
-            EXPECT_EQ(expected, state);
-        };
-        EXPECT_NO_FATAL_FAILURE(ForEachEffect(get));
-    }
-
-    void SetParameter() {
-        auto func = [&](const std::shared_ptr<IEffect>& effect) {
-            Parameter param;
-            param.set<Parameter::common>(mCommon);
-            EXPECT_IS_OK(effect->setParameter(param));
-        };
-        EXPECT_NO_FATAL_FAILURE(ForEachEffect(func));
-    }
-
-    void VerifyParameters() {
-        auto func = [&](const std::shared_ptr<IEffect>& effect) {
-            Parameter paramCommonGet = Parameter(), paramCommonExpect = Parameter();
-            Parameter::Id id;
-            id.set<Parameter::Id::commonTag>(0);
-            paramCommonExpect.set<Parameter::common>(mCommon);
-            EXPECT_IS_OK(effect->getParameter(id, &paramCommonGet));
-            EXPECT_EQ(paramCommonExpect, paramCommonGet)
-                    << paramCommonExpect.toString() << " vs " << paramCommonGet.toString();
-        };
-        EXPECT_NO_FATAL_FAILURE(ForEachEffect(func));
-    }
-
-    template <typename Functor>
-    void ForEachEffect(Functor functor) {
-        auto effectMap = mFactoryHelper.GetEffectMap();
-        for (const auto& it : effectMap) {
-            SCOPED_TRACE(it.second.toString());
-            functor(it.first);
-        }
-    }
-
-    void initParamCommon(int session = -1, int ioHandle = -1,
-                         AudioDeviceType deviceType = AudioDeviceType::NONE,
-                         int iSampleRate = 48000, int oSampleRate = 48000, long iFrameCount = 0x100,
-                         long oFrameCount = 0x100) {
-        mCommon.session = session;
-        mCommon.ioHandle = ioHandle;
-        mCommon.device.type = deviceType;
-        mCommon.input.base.sampleRate = iSampleRate;
-        mCommon.input.base.channelMask = mInputChannelLayout;
-        mCommon.input.frameCount = iFrameCount;
-        mCommon.output.base.sampleRate = oSampleRate;
-        mCommon.output.base.channelMask = mOutputChannelLayout;
-        mCommon.output.frameCount = oFrameCount;
-    }
-
-    void initParamSpecific(Parameter::Specific::Tag tag = Parameter::Specific::equalizer) {
-        switch (tag) {
-            case Parameter::Specific::equalizer:
-                mSpecific.set<Parameter::Specific::equalizer>();
-                break;
-            default:
-                return;
-        }
-    }
-
-    void setInputChannelLayout(AudioChannelLayout input) { mInputChannelLayout = input; }
-    void setOutputChannelLayout(AudioChannelLayout output) { mOutputChannelLayout = output; }
-
-    EffectFactoryHelper mFactoryHelper = EffectFactoryHelper(GetParam());
-
-  private:
-    AudioChannelLayout mInputChannelLayout =
-            AudioChannelLayout::make<AudioChannelLayout::layoutMask>(
-                    AudioChannelLayout::LAYOUT_STEREO);
-    AudioChannelLayout mOutputChannelLayout =
-            AudioChannelLayout::make<AudioChannelLayout::layoutMask>(
-                    AudioChannelLayout::LAYOUT_STEREO);
-
-    Parameter::Common mCommon;
-    Parameter::Specific mSpecific;
-    static IEffect::OpenEffectReturn mOpenReturn;
 };
 
-TEST_P(AudioEffect, OpenEffectTest) {
-    EXPECT_NO_FATAL_FAILURE(OpenEffects());
+TEST_P(AudioEffectTest, OpenEffectTest) {
+    OpenEffects();
 }
 
-TEST_P(AudioEffect, OpenAndCloseEffect) {
-    EXPECT_NO_FATAL_FAILURE(OpenEffects());
-    EXPECT_NO_FATAL_FAILURE(CloseEffects());
+TEST_P(AudioEffectTest, OpenAndCloseEffect) {
+    OpenEffects();
+    CloseEffects();
 }
 
-TEST_P(AudioEffect, CloseUnopenedEffectTest) {
-    EXPECT_NO_FATAL_FAILURE(CloseEffects());
+TEST_P(AudioEffectTest, CloseUnopenedEffectTest) {
+    CloseEffects();
 }
 
-TEST_P(AudioEffect, DoubleOpenCloseEffects) {
-    EXPECT_NO_FATAL_FAILURE(OpenEffects());
-    EXPECT_NO_FATAL_FAILURE(CloseEffects());
-    EXPECT_NO_FATAL_FAILURE(OpenEffects());
-    EXPECT_NO_FATAL_FAILURE(CloseEffects());
+TEST_P(AudioEffectTest, DoubleOpenCloseEffects) {
+    OpenEffects();
+    CloseEffects();
+    OpenEffects();
+    CloseEffects();
 
-    EXPECT_NO_FATAL_FAILURE(OpenEffects());
-    EXPECT_NO_FATAL_FAILURE(OpenEffects());
-    EXPECT_NO_FATAL_FAILURE(CloseEffects());
+    OpenEffects();
+    OpenEffects();
+    CloseEffects();
 
-    EXPECT_NO_FATAL_FAILURE(OpenEffects());
-    EXPECT_NO_FATAL_FAILURE(CloseEffects());
-    EXPECT_NO_FATAL_FAILURE(CloseEffects());
+    OpenEffects();
+    CloseEffects();
+    CloseEffects();
 }
 
-TEST_P(AudioEffect, GetDescriptors) {
-    EXPECT_NO_FATAL_FAILURE(GetEffectDescriptors());
+TEST_P(AudioEffectTest, GetDescriptors) {
+    GetEffectDescriptors();
 }
 
-TEST_P(AudioEffect, DescriptorIdExistAndUnique) {
+TEST_P(AudioEffectTest, DescriptorIdExistAndUnique) {
     auto checker = [&](const std::shared_ptr<IEffect>& effect) {
         Descriptor desc;
         std::vector<Descriptor::Identity> idList;
         EXPECT_IS_OK(effect->getDescriptor(&desc));
-        mFactoryHelper.QueryEffects(desc.common.id.type, desc.common.id.uuid, &idList);
-        EXPECT_EQ(idList.size(), 1UL);
+        QueryEffects(desc.common.id.type, desc.common.id.uuid, desc.common.id.proxy, &idList);
+        // Must have at least one instance.
+        EXPECT_NE(idList.size(), 0UL);
     };
-    EXPECT_NO_FATAL_FAILURE(ForEachEffect(checker));
+    ForEachEffect(checker);
 
     // Check unique with a set
     auto stringHash = [](const Descriptor::Identity& id) {
         return std::hash<std::string>()(id.toString());
     };
-    auto vec = mFactoryHelper.GetCompleteEffectIdList();
+    auto vec = GetCompleteEffectIdList();
     std::unordered_set<Descriptor::Identity, decltype(stringHash)> idSet(0, stringHash);
     for (auto it : vec) {
-        EXPECT_EQ(idSet.count(it), 0UL);
+        EXPECT_EQ(idSet.count(it), 0UL) << it.toString();
         idSet.insert(it);
     }
 }
 
 /// State testing.
 // An effect instance is in INIT state by default after it was created.
-TEST_P(AudioEffect, InitStateAfterCreation) {
+TEST_P(AudioEffectTest, InitStateAfterCreation) {
     ExpectState(State::INIT);
 }
 
 // An effect instance transfer to INIT state after it was open successfully with IEffect.open().
-TEST_P(AudioEffect, IdleStateAfterOpen) {
-    EXPECT_NO_FATAL_FAILURE(OpenEffects());
+TEST_P(AudioEffectTest, IdleStateAfterOpen) {
+    OpenEffects();
     ExpectState(State::IDLE);
-    EXPECT_NO_FATAL_FAILURE(CloseEffects());
+    CloseEffects();
 }
 
 // An effect instance is in PROCESSING state after it receive an START command.
-TEST_P(AudioEffect, ProcessingStateAfterStart) {
-    EXPECT_NO_FATAL_FAILURE(OpenEffects());
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
+TEST_P(AudioEffectTest, ProcessingStateAfterStart) {
+    OpenEffects();
+    CommandEffects(CommandId::START);
     ExpectState(State::PROCESSING);
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
-    EXPECT_NO_FATAL_FAILURE(CloseEffects());
+    CommandEffects(CommandId::STOP);
+    CloseEffects();
 }
 
 // An effect instance transfer to IDLE state after Command.Id.STOP in PROCESSING state.
-TEST_P(AudioEffect, IdleStateAfterStop) {
-    EXPECT_NO_FATAL_FAILURE(OpenEffects());
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
+TEST_P(AudioEffectTest, IdleStateAfterStop) {
+    OpenEffects();
+    CommandEffects(CommandId::START);
     ExpectState(State::PROCESSING);
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
+    CommandEffects(CommandId::STOP);
     ExpectState(State::IDLE);
-    EXPECT_NO_FATAL_FAILURE(CloseEffects());
+    CloseEffects();
 }
 
 // An effect instance transfer to IDLE state after Command.Id.RESET in PROCESSING state.
-TEST_P(AudioEffect, IdleStateAfterReset) {
-    EXPECT_NO_FATAL_FAILURE(OpenEffects());
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
+TEST_P(AudioEffectTest, IdleStateAfterReset) {
+    OpenEffects();
+    CommandEffects(CommandId::START);
     ExpectState(State::PROCESSING);
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::RESET));
+    CommandEffects(CommandId::RESET);
     ExpectState(State::IDLE);
-    EXPECT_NO_FATAL_FAILURE(CloseEffects());
+    CloseEffects();
 }
 
 // An effect instance transfer to INIT if instance receive a close() call.
-TEST_P(AudioEffect, InitStateAfterClose) {
-    EXPECT_NO_FATAL_FAILURE(OpenEffects());
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
+TEST_P(AudioEffectTest, InitStateAfterClose) {
+    OpenEffects();
+    CommandEffects(CommandId::START);
     ExpectState(State::PROCESSING);
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
+    CommandEffects(CommandId::STOP);
     ExpectState(State::IDLE);
-    EXPECT_NO_FATAL_FAILURE(CloseEffects());
+    CloseEffects();
     ExpectState(State::INIT);
 }
 
 // An effect instance shouldn't accept any command before open.
-TEST_P(AudioEffect, NoCommandAcceptedBeforeOpen) {
+TEST_P(AudioEffectTest, NoCommandAcceptedBeforeOpen) {
     ExpectState(State::INIT);
-    EXPECT_NO_FATAL_FAILURE(CommandEffectsExpectStatus(CommandId::START, EX_ILLEGAL_STATE));
-    EXPECT_NO_FATAL_FAILURE(CommandEffectsExpectStatus(CommandId::STOP, EX_ILLEGAL_STATE));
-    EXPECT_NO_FATAL_FAILURE(CommandEffectsExpectStatus(CommandId::RESET, EX_ILLEGAL_STATE));
+    CommandEffectsExpectStatus(CommandId::START, EX_ILLEGAL_STATE);
+    CommandEffectsExpectStatus(CommandId::STOP, EX_ILLEGAL_STATE);
+    CommandEffectsExpectStatus(CommandId::RESET, EX_ILLEGAL_STATE);
     ExpectState(State::INIT);
 }
 
 // No-op when receive STOP command in IDLE state.
-TEST_P(AudioEffect, StopCommandInIdleStateNoOp) {
+TEST_P(AudioEffectTest, StopCommandInIdleStateNoOp) {
     ExpectState(State::INIT);
-    EXPECT_NO_FATAL_FAILURE(OpenEffects());
+    OpenEffects();
     ExpectState(State::IDLE);
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
+    CommandEffects(CommandId::STOP);
     ExpectState(State::IDLE);
-    EXPECT_NO_FATAL_FAILURE(CloseEffects());
+    CloseEffects();
 }
 
 // No-op when receive STOP command in IDLE state.
-TEST_P(AudioEffect, ResetCommandInIdleStateNoOp) {
+TEST_P(AudioEffectTest, ResetCommandInIdleStateNoOp) {
     ExpectState(State::INIT);
-    EXPECT_NO_FATAL_FAILURE(OpenEffects());
+    OpenEffects();
     ExpectState(State::IDLE);
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::RESET));
+    CommandEffects(CommandId::RESET);
     ExpectState(State::IDLE);
-    EXPECT_NO_FATAL_FAILURE(CloseEffects());
+    CloseEffects();
 }
 
 // Repeat START and STOP command.
-TEST_P(AudioEffect, RepeatStartAndStop) {
-    EXPECT_NO_FATAL_FAILURE(OpenEffects());
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
+TEST_P(AudioEffectTest, RepeatStartAndStop) {
+    OpenEffects();
+    CommandEffects(CommandId::START);
     ExpectState(State::PROCESSING);
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
+    CommandEffects(CommandId::STOP);
     ExpectState(State::IDLE);
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
+    CommandEffects(CommandId::START);
     ExpectState(State::PROCESSING);
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
+    CommandEffects(CommandId::STOP);
     ExpectState(State::IDLE);
-    EXPECT_NO_FATAL_FAILURE(CloseEffects());
+    CloseEffects();
 }
 
 // Repeat START and RESET command.
-TEST_P(AudioEffect, RepeatStartAndReset) {
-    EXPECT_NO_FATAL_FAILURE(OpenEffects());
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
+TEST_P(AudioEffectTest, RepeatStartAndReset) {
+    OpenEffects();
+    CommandEffects(CommandId::START);
     ExpectState(State::PROCESSING);
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::RESET));
+    CommandEffects(CommandId::RESET);
     ExpectState(State::IDLE);
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
+    CommandEffects(CommandId::START);
     ExpectState(State::PROCESSING);
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::RESET));
+    CommandEffects(CommandId::RESET);
     ExpectState(State::IDLE);
-    EXPECT_NO_FATAL_FAILURE(CloseEffects());
+    CloseEffects();
 }
 
 // Repeat START and STOP command, try to close at PROCESSING state.
-TEST_P(AudioEffect, CloseProcessingStateEffects) {
-    EXPECT_NO_FATAL_FAILURE(OpenEffects());
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
+TEST_P(AudioEffectTest, CloseProcessingStateEffects) {
+    OpenEffects();
+    CommandEffects(CommandId::START);
     ExpectState(State::PROCESSING);
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
+    CommandEffects(CommandId::STOP);
     ExpectState(State::IDLE);
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
+    CommandEffects(CommandId::START);
     ExpectState(State::PROCESSING);
-    EXPECT_NO_FATAL_FAILURE(CloseEffects(EX_ILLEGAL_STATE));
+    CloseEffects(EX_ILLEGAL_STATE);
     // cleanup
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
+    CommandEffects(CommandId::STOP);
     ExpectState(State::IDLE);
 }
 
 // Expect EX_ILLEGAL_STATE if the effect instance is not in a proper state to be destroyed.
-TEST_P(AudioEffect, DestroyOpenEffects) {
+TEST_P(AudioEffectTest, DestroyOpenEffects) {
     // cleanup all effects.
-    EXPECT_NO_FATAL_FAILURE(CloseEffects());
-    ASSERT_NO_FATAL_FAILURE(DestroyEffects());
+    CloseEffects();
+    DestroyEffects();
 
     // open effects, destroy without close, expect to get EX_ILLEGAL_STATE status.
-    EXPECT_NO_FATAL_FAILURE(CreateEffects());
-    EXPECT_NO_FATAL_FAILURE(OpenEffects());
-    EXPECT_NO_FATAL_FAILURE(DestroyEffects(EX_ILLEGAL_STATE, 1));
-    EXPECT_NO_FATAL_FAILURE(CloseEffects());
+    CreateEffects();
+    OpenEffects();
+    auto vec = GetCompleteEffectIdList();
+    DestroyEffects(EX_ILLEGAL_STATE, vec.size());
+    CloseEffects();
 }
 
 /// Parameter testing.
 // Verify parameters pass in open can be successfully get.
-TEST_P(AudioEffect, VerifyParametersAfterOpen) {
-    EXPECT_NO_FATAL_FAILURE(OpenEffects());
-    EXPECT_NO_FATAL_FAILURE(VerifyParameters());
-    EXPECT_NO_FATAL_FAILURE(CloseEffects());
+TEST_P(AudioEffectTest, VerifyParametersAfterOpen) {
+    OpenEffects();
+    VerifyParameters();
+    CloseEffects();
 }
 
 // Verify parameters pass in set can be successfully get.
-TEST_P(AudioEffect, SetAndGetParameter) {
-    EXPECT_NO_FATAL_FAILURE(OpenEffects());
-    EXPECT_NO_FATAL_FAILURE(VerifyParameters());
-    initParamCommon(1 /* session */, 1 /* ioHandle */, AudioDeviceType::IN_DEFAULT /* deviceType */,
-                    44100 /* iSampleRate */, 44100 /* oSampleRate */);
-    EXPECT_NO_FATAL_FAILURE(SetParameter());
-    EXPECT_NO_FATAL_FAILURE(VerifyParameters());
-    EXPECT_NO_FATAL_FAILURE(CloseEffects());
+TEST_P(AudioEffectTest, SetAndGetParameter) {
+    OpenEffects();
+    VerifyParameters();
+    initParamCommon(1 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */,
+                    44100 /* oSampleRate */);
+    SetParameter();
+    VerifyParameters();
+    CloseEffects();
 }
 
 // Verify parameters pass in set can be successfully get.
-TEST_P(AudioEffect, SetAndGetParameterInProcessing) {
-    EXPECT_NO_FATAL_FAILURE(OpenEffects());
-    EXPECT_NO_FATAL_FAILURE(VerifyParameters());
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
+TEST_P(AudioEffectTest, SetAndGetParameterInProcessing) {
+    OpenEffects();
+    VerifyParameters();
+    CommandEffects(CommandId::START);
     ExpectState(State::PROCESSING);
-    initParamCommon(1 /* session */, 1 /* ioHandle */, AudioDeviceType::IN_DEFAULT /* deviceType */,
-                    44100 /* iSampleRate */, 44100 /* oSampleRate */);
-    EXPECT_NO_FATAL_FAILURE(SetParameter());
-    EXPECT_NO_FATAL_FAILURE(VerifyParameters());
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
+    initParamCommon(1 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */,
+                    44100 /* oSampleRate */);
+    SetParameter();
+    VerifyParameters();
+    CommandEffects(CommandId::STOP);
     ExpectState(State::IDLE);
-    EXPECT_NO_FATAL_FAILURE(CloseEffects());
+    CloseEffects();
 }
 
 // Parameters kept after reset.
-TEST_P(AudioEffect, ResetAndVerifyParameter) {
-    EXPECT_NO_FATAL_FAILURE(OpenEffects());
-    EXPECT_NO_FATAL_FAILURE(VerifyParameters());
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
+TEST_P(AudioEffectTest, ResetAndVerifyParameter) {
+    OpenEffects();
+    VerifyParameters();
+    CommandEffects(CommandId::START);
     ExpectState(State::PROCESSING);
-    initParamCommon(1 /* session */, 1 /* ioHandle */, AudioDeviceType::IN_DEFAULT /* deviceType */,
-                    44100 /* iSampleRate */, 44100 /* oSampleRate */);
-    EXPECT_NO_FATAL_FAILURE(SetParameter());
-    EXPECT_NO_FATAL_FAILURE(VerifyParameters());
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::RESET));
+    initParamCommon(1 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */,
+                    44100 /* oSampleRate */);
+    SetParameter();
+    VerifyParameters();
+    CommandEffects(CommandId::RESET);
     ExpectState(State::IDLE);
-    EXPECT_NO_FATAL_FAILURE(VerifyParameters());
-    EXPECT_NO_FATAL_FAILURE(CloseEffects());
+    VerifyParameters();
+    CloseEffects();
 }
 
+// TODO: need a way to support setting different sessionId to different effect instances
+#if 0
 // Multiple instances of same implementation running.
-TEST_P(AudioEffect, MultipleInstancesRunning) {
-    EXPECT_NO_FATAL_FAILURE(CreateEffects(3));
+TEST_P(AudioEffectTest, MultipleInstancesRunningWithDiffSessionId) {
+    CreateEffects();
     ExpectState(State::INIT);
-    EXPECT_NO_FATAL_FAILURE(OpenEffects());
+    OpenEffects();
     ExpectState(State::IDLE);
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
+    CommandEffects(CommandId::START);
     ExpectState(State::PROCESSING);
-    initParamCommon(1 /* session */, 1 /* ioHandle */, AudioDeviceType::IN_DEFAULT /* deviceType */,
-                    44100 /* iSampleRate */, 44100 /* oSampleRate */);
-    EXPECT_NO_FATAL_FAILURE(SetParameter());
-    EXPECT_NO_FATAL_FAILURE(VerifyParameters());
-    EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
+    initParamCommon(1 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */,
+                    44100 /* oSampleRate */);
+    SetParameter();
+    VerifyParameters();
+    CommandEffects(CommandId::STOP);
     ExpectState(State::IDLE);
-    EXPECT_NO_FATAL_FAILURE(VerifyParameters());
-    EXPECT_NO_FATAL_FAILURE(CloseEffects());
+    VerifyParameters();
+    CloseEffects();
+}
+#endif
+
+// Send data to effects and expect it to consume by check statusMQ.
+TEST_P(AudioEffectTest, ExpectEffectsToConsumeDataInMQ) {
+    OpenEffects();
+    PrepareInputData(mWriteMQBytes);
+
+    CommandEffects(CommandId::START);
+    writeToFmq(mWriteMQBytes);
+    readFromFmq(mWriteMQBytes);
+
+    ExpectState(State::PROCESSING);
+    CommandEffects(CommandId::STOP);
+    // cleanup
+    CommandEffects(CommandId::STOP);
+    ExpectState(State::IDLE);
+    CloseEffects();
 }
 
-INSTANTIATE_TEST_SUITE_P(AudioEffectTest, AudioEffect,
+INSTANTIATE_TEST_SUITE_P(AudioEffectTestTest, AudioEffectTest,
                          testing::ValuesIn(android::getAidlHalInstanceNames(IFactory::descriptor)),
                          android::PrintInstanceNameToString);
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioEffect);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioEffectTest);
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
     ABinderProcess_setThreadPoolMaxThreadCount(1);
     ABinderProcess_startThreadPool();
     return RUN_ALL_TESTS();
-}
\ No newline at end of file
+}
diff --git a/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp b/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp
new file mode 100644
index 0000000..4162551
--- /dev/null
+++ b/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp
@@ -0,0 +1,282 @@
+/*
+ * 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 <algorithm>
+#include <limits>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#define LOG_TAG "VtsHalEqualizerTest"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android/binder_interface_utils.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <gtest/gtest.h>
+
+#include <Utils.h>
+#include <aidl/android/hardware/audio/effect/IEffect.h>
+#include <aidl/android/hardware/audio/effect/IFactory.h>
+#include <aidl/android/media/audio/common/AudioChannelLayout.h>
+#include <aidl/android/media/audio/common/AudioDeviceType.h>
+
+#include "AudioHalBinderServiceUtil.h"
+#include "EffectHelper.h"
+#include "TestUtils.h"
+#include "effect-impl/EffectUUID.h"
+
+using namespace android;
+
+using aidl::android::hardware::audio::effect::Capability;
+using aidl::android::hardware::audio::effect::Descriptor;
+using aidl::android::hardware::audio::effect::EffectNullUuid;
+using aidl::android::hardware::audio::effect::Equalizer;
+using aidl::android::hardware::audio::effect::EqualizerTypeUUID;
+using aidl::android::hardware::audio::effect::IEffect;
+using aidl::android::hardware::audio::effect::IFactory;
+using aidl::android::hardware::audio::effect::Parameter;
+
+/**
+ * Here we focus on specific parameter checking, general IEffect interfaces testing performed in
+ * VtsAudioEfectTargetTest.
+ */
+enum ParamName { PARAM_INSTANCE_NAME, PARAM_PRESET_INDEX, PARAM_BAND_INDEX, PARAM_BAND_LEVEL };
+using EqualizerParamTestParam = std::tuple<std::string, int, int, int>;
+
+/*
+Testing parameter range, assuming the parameter supported by effect is in this range.
+This range is verified with IEffect.getDescriptor(), for any index supported vts expect EX_NONE
+from IEffect.setParameter(), otherwise expect EX_ILLEGAL_ARGUMENT.
+*/
+constexpr std::pair<int, int> kPresetIndexRange = {-1, 10};  // valid range [0, 9]
+constexpr std::pair<int, int> kBandIndexRange = {-1, 5};     // valid range [0, 4]
+constexpr std::pair<int, int> kBandLevelRange = {-5, 5};     // needs update with implementation
+
+class EqualizerParamTest : public ::testing::TestWithParam<EqualizerParamTestParam>,
+                           public EffectHelper {
+  public:
+    EqualizerParamTest()
+        : EffectHelper(std::get<PARAM_INSTANCE_NAME>(GetParam())),
+          mParamPresetIndex(std::get<PARAM_PRESET_INDEX>(GetParam())),
+          mParamBandIndex(std::get<PARAM_BAND_INDEX>(GetParam())),
+          mParamBandLevel(std::get<PARAM_BAND_LEVEL>(GetParam())) {}
+
+    void SetUp() override {
+        CreateEffectsWithUUID(EqualizerTypeUUID);
+        initParamCommonFormat();
+        initParamCommon();
+        initParamSpecific();
+        OpenEffects(EqualizerTypeUUID);
+        SCOPED_TRACE(testing::Message() << "preset: " << mParamPresetIndex << " bandIdx "
+                                        << mParamBandIndex << " level " << mParamBandLevel);
+    }
+
+    void TearDown() override {
+        CloseEffects();
+        DestroyEffects();
+        CleanUp();
+    }
+
+    int mParamPresetIndex = 0;
+    int mParamBandIndex = 0;
+    int mParamBandLevel = 0;
+
+    void SetAndGetEqualizerParameters() {
+        auto functor = [&](const std::shared_ptr<IEffect>& effect) {
+            for (auto& it : mTags) {
+                auto& tag = it.first;
+                auto& eq = it.second;
+
+                // validate parameter
+                Descriptor desc;
+                ASSERT_STATUS(EX_NONE, effect->getDescriptor(&desc));
+                const bool valid = isTagInRange(it.first, it.second, desc);
+                const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT;
+
+                // set
+                Parameter expectParam;
+                Parameter::Specific specific;
+                specific.set<Parameter::Specific::equalizer>(*eq.get());
+                expectParam.set<Parameter::specific>(specific);
+                EXPECT_STATUS(expected, effect->setParameter(expectParam))
+                        << expectParam.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);
+                    // if set success, then get should match
+                    EXPECT_STATUS(expected, effect->getParameter(id, &getParam));
+                    EXPECT_TRUE(isEqParameterExpected(expectParam, getParam));
+                }
+            }
+        };
+        EXPECT_NO_FATAL_FAILURE(ForEachEffect(functor));
+    }
+
+    bool isEqParameterExpected(const Parameter& expect, const Parameter& target) {
+        // if parameter same, then for sure matched
+        if (expect == target) return true;
+
+        // if not, see if target include the expect parameter, and others all default (0).
+        /*
+            This is verify the case of client setParameter to a single bandLevel ({3, -1} for
+           example), and return of getParameter must be [{0, 0}, {1, 0}, {2, 0}, {3, -1}, {4, 0}]
+        */
+        EXPECT_EQ(expect.getTag(), Parameter::specific);
+        EXPECT_EQ(target.getTag(), Parameter::specific);
+
+        Parameter::Specific expectSpec = expect.get<Parameter::specific>(),
+                            targetSpec = target.get<Parameter::specific>();
+        EXPECT_EQ(expectSpec.getTag(), Parameter::Specific::equalizer);
+        EXPECT_EQ(targetSpec.getTag(), Parameter::Specific::equalizer);
+
+        Equalizer expectEq = expectSpec.get<Parameter::Specific::equalizer>(),
+                  targetEq = targetSpec.get<Parameter::Specific::equalizer>();
+        EXPECT_EQ(expectEq.getTag(), targetEq.getTag());
+
+        auto eqTag = targetEq.getTag();
+        switch (eqTag) {
+            case Equalizer::bandLevels: {
+                auto expectBl = expectEq.get<Equalizer::bandLevels>();
+                auto targetBl = targetEq.get<Equalizer::bandLevels>();
+                return std::includes(targetBl.begin(), targetBl.end(), expectBl.begin(),
+                                     expectBl.end());
+            }
+            default:
+                return false;
+        }
+        return false;
+    }
+
+    void addPresetParam(int preset) {
+        Equalizer eq;
+        eq.set<Equalizer::preset>(preset);
+        mTags.push_back({Equalizer::preset, std::make_unique<Equalizer>(std::move(eq))});
+    }
+
+    void addBandLevelsParam(std::vector<Equalizer::BandLevel>& bandLevels) {
+        Equalizer eq;
+        eq.set<Equalizer::bandLevels>(bandLevels);
+        mTags.push_back({Equalizer::bandLevels, std::make_unique<Equalizer>(std::move(eq))});
+    }
+
+    bool isTagInRange(const Equalizer::Tag& tag, const std::unique_ptr<Equalizer>& eq,
+                      const Descriptor& desc) const {
+        std::cout << "xxx" << toString(tag) << " " << desc.toString();
+        const Equalizer::Capability& eqCap = desc.capability.get<Capability::equalizer>();
+        switch (tag) {
+            case Equalizer::preset: {
+                int index = eq->get<Equalizer::preset>();
+                return isPresetIndexInRange(eqCap, index);
+            }
+            case Equalizer::bandLevels: {
+                auto& bandLevel = eq->get<Equalizer::bandLevels>();
+                return isBandIndexInRange(eqCap, bandLevel);
+            }
+            default:
+                return false;
+        }
+        return false;
+    }
+
+    bool isPresetIndexInRange(const Equalizer::Capability& cap, int idx) 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 idx >= min->index && idx <= max->index;
+    }
+
+    bool isBandIndexInRange(const Equalizer::Capability& cap,
+                            const std::vector<Equalizer::BandLevel>& bandLevel) const {
+        for (auto& it : bandLevel) {
+            if (!isBandIndexInRange(cap, it.index)) return false;
+        }
+        return true;
+    }
+
+    bool isBandIndexInRange(const Equalizer::Capability& cap, int idx) 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 idx >= min->index && idx <= max->index;
+    }
+
+  private:
+    std::vector<std::pair<Equalizer::Tag, std::unique_ptr<Equalizer>>> mTags;
+
+    bool validCapabilityTag(Capability& cap) { return cap.getTag() == Capability::equalizer; }
+
+    void initParamSpecific() {
+        Equalizer eq;
+        eq.set<Equalizer::preset>(0);
+        Parameter::Specific specific;
+        specific.set<Parameter::Specific::equalizer>(eq);
+        setSpecific(specific);
+    }
+
+    void CleanUp() { mTags.clear(); }
+};
+
+TEST_P(EqualizerParamTest, SetAndGetPreset) {
+    EXPECT_NO_FATAL_FAILURE(addPresetParam(mParamPresetIndex));
+    SetAndGetEqualizerParameters();
+}
+
+TEST_P(EqualizerParamTest, SetAndGetSingleBand) {
+    std::vector<Equalizer::BandLevel> bandLevels;
+    Equalizer::BandLevel bandLevel = {mParamBandIndex, mParamBandLevel};
+    bandLevels.push_back(bandLevel);
+    EXPECT_NO_FATAL_FAILURE(addBandLevelsParam(bandLevels));
+    SetAndGetEqualizerParameters();
+}
+
+INSTANTIATE_TEST_SUITE_P(
+        EqualizerTest, EqualizerParamTest,
+        ::testing::Combine(
+                testing::ValuesIn(android::getAidlHalInstanceNames(IFactory::descriptor)),
+                testing::Range(kPresetIndexRange.first, kPresetIndexRange.second),
+                testing::Range(kBandIndexRange.first, kBandIndexRange.second),
+                testing::Range(kBandLevelRange.first, kBandLevelRange.second)),
+        [](const testing::TestParamInfo<EqualizerParamTest::ParamType>& info) {
+            std::string instance = std::get<PARAM_INSTANCE_NAME>(info.param);
+            std::string presetIdx = std::to_string(std::get<PARAM_PRESET_INDEX>(info.param));
+            std::string bandIdx = std::to_string(std::get<PARAM_BAND_INDEX>(info.param));
+            std::string bandLevel = std::to_string(std::get<PARAM_BAND_LEVEL>(info.param));
+
+            std::string name = instance + "_presetIndex" + presetIdx + "_bandIndex" + bandIdx +
+                               "_bandLevel" + bandLevel;
+            std::replace_if(
+                    name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
+            return name;
+        });
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EqualizerParamTest);
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ABinderProcess_setThreadPoolMaxThreadCount(1);
+    ABinderProcess_startThreadPool();
+    return RUN_ALL_TESTS();
+}
diff --git a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
index e46e5b4..0ca6a58 100644
--- a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
+++ b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
@@ -1014,9 +1014,8 @@
         if (mDataPosition == 0) mOnDataStart();
         const size_t dataSize = std::min(mData.size() - mDataPosition, mDataMQ->availableToWrite());
         bool success = mDataMQ->write(mData.data() + mDataPosition, dataSize);
+        bool wrapped = false;
         ALOGE_IF(!success, "data message queue write failed");
-        mDataPosition += dataSize;
-        if (mDataPosition >= mData.size()) mDataPosition = 0;
         mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY));
 
         uint32_t efState = 0;
@@ -1034,6 +1033,11 @@
                 ALOGE("bad write status: %d", writeStatus.retval);
                 success = false;
             }
+            mDataPosition += writeStatus.reply.written;
+            if (mDataPosition >= mData.size()) {
+                mDataPosition = 0;
+                wrapped = true;
+            }
         }
         if (ret == -EAGAIN || ret == -EINTR) {
             // Spurious wakeup. This normally retries no more than once.
@@ -1042,7 +1046,7 @@
             ALOGE("bad wait status: %d", ret);
             success = false;
         }
-        if (success && mDataPosition == 0) {
+        if (wrapped) {
             success = mOnDataWrap();
         }
         return success;
diff --git a/automotive/remoteaccess/Android.bp b/automotive/remoteaccess/Android.bp
new file mode 100644
index 0000000..ac04354
--- /dev/null
+++ b/automotive/remoteaccess/Android.bp
@@ -0,0 +1,39 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+    name: "android.hardware.automotive.remoteaccess",
+    vendor_available: true,
+    srcs: [
+        "android/hardware/automotive/remoteaccess/**/*.aidl",
+    ],
+    stability: "vintf",
+    backend: {
+        cpp: {
+            enabled: false,
+        },
+        java: {
+            sdk_version: "module_current",
+            min_sdk_version: "31",
+            apex_available: [
+                "//apex_available:platform",
+                "com.android.car.framework",
+            ],
+        },
+    },
+}
diff --git a/automotive/remoteaccess/OWNERS b/automotive/remoteaccess/OWNERS
new file mode 100644
index 0000000..d6969e5
--- /dev/null
+++ b/automotive/remoteaccess/OWNERS
@@ -0,0 +1,2 @@
+ericjeong@google.com
+shanyu@google.com
diff --git a/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/ApState.aidl b/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/ApState.aidl
new file mode 100644
index 0000000..da4f1d4
--- /dev/null
+++ b/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/ApState.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.automotive.remoteaccess;
+@VintfStability
+parcelable ApState {
+  boolean isReadyForRemoteTask;
+  boolean isWakeupRequired;
+}
diff --git a/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl b/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl
new file mode 100644
index 0000000..9b6eb2f
--- /dev/null
+++ b/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.remoteaccess;
+@VintfStability
+interface IRemoteAccess {
+  String getDeviceId();
+  String getWakeupServiceName();
+  void setRemoteTaskCallback(android.hardware.automotive.remoteaccess.IRemoteTaskCallback callback);
+  void clearRemoteTaskCallback();
+  void notifyApStateChange(in android.hardware.automotive.remoteaccess.ApState state);
+}
diff --git a/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/IRemoteTaskCallback.aidl b/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/IRemoteTaskCallback.aidl
new file mode 100644
index 0000000..295100e
--- /dev/null
+++ b/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/IRemoteTaskCallback.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.remoteaccess;
+@VintfStability
+interface IRemoteTaskCallback {
+  oneway void onRemoteTaskRequested(String clientId, in byte[] data);
+}
diff --git a/automotive/remoteaccess/android/hardware/automotive/remoteaccess/ApState.aidl b/automotive/remoteaccess/android/hardware/automotive/remoteaccess/ApState.aidl
new file mode 100644
index 0000000..c8eb3ef
--- /dev/null
+++ b/automotive/remoteaccess/android/hardware/automotive/remoteaccess/ApState.aidl
@@ -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.
+ */
+
+package android.hardware.automotive.remoteaccess;
+
+@VintfStability
+parcelable ApState {
+    /**
+     * Whether AP (application processor) is ready to receive remote tasks.
+     *
+     * If this is true. AP is powered on and the car service is ready to handle
+     * remote tasks.
+     */
+    boolean isReadyForRemoteTask;
+    /**
+     * Whether AP (application processor) needs to be woken up.
+     *
+     * While the AP is shutting down, this will be set to false to prevent the
+     * wakeup signal to interrupt the shutdown process. At the last step of the
+     * shutdown process, this will be set to true so that AP will be waken
+     * up when task arrives. After AP starts up, this will be set to false
+     * to prevent unnecessary wakeup signal.
+     */
+    boolean isWakeupRequired;
+}
diff --git a/automotive/remoteaccess/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl b/automotive/remoteaccess/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl
new file mode 100644
index 0000000..a198b03
--- /dev/null
+++ b/automotive/remoteaccess/android/hardware/automotive/remoteaccess/IRemoteAccess.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.automotive.remoteaccess;
+
+import android.hardware.automotive.remoteaccess.ApState;
+import android.hardware.automotive.remoteaccess.IRemoteTaskCallback;
+
+/**
+ * Interface representing a remote wakeup client.
+ *
+ * A wakeup client is a binary outside Android framework that communicates with
+ * a wakeup server and receives wake up command.
+ */
+@VintfStability
+interface IRemoteAccess {
+    /**
+     * Gets a unique device ID that could be recognized by wake up server.
+     *
+     * This device ID is provisioned during car production and is registered
+     * with the wake up server.
+     *
+     * @return a unique device ID.
+     */
+    String getDeviceId();
+
+    /**
+     * Gets the name for the remote wakeup server.
+     *
+     * This name will be provided to remote task server during registration
+     * and used by remote task server to find the remote wakeup server to
+     * use for waking up the device. This name must be pre-negotiated between
+     * the remote wakeup server/client and the remote task server/client and
+     * must be unique. We recommend the format to be a human readable string
+     * with reverse domain name notation (reverse-DNS), e.g.
+     * "com.google.vehicle.wakeup".
+     */
+    String getWakeupServiceName();
+
+    /**
+     * Sets a callback to be called when a remote task is requested.
+     *
+     * @param callback A callback to be called when a remote task is requested.
+     */
+    void setRemoteTaskCallback(IRemoteTaskCallback callback);
+
+    /**
+     * Clears a previously set remote task callback.
+     *
+     * If no callback was set, this operation is no-op.
+     */
+    void clearRemoteTaskCallback();
+
+    /**
+     * Notifies whether AP is ready to receive remote tasks.
+     *
+     * <p>Wakeup client should store and use this state until a new call with a
+     * different state arrives.
+     *
+     * <p>If {@code isReadyForRemoteTask} is true, the wakeup client may send
+     * the task received from the server to AP immediately.
+     *
+     * <p>If {@code isReadyForRemoteTask} is false, it must store the received
+     * remote tasks and wait until AP is ready to receive tasks. If it takes too
+     * long for AP to become ready, the task must be reported to remote task
+     * server as failed. Implementation must make sure no duplicate tasks are
+     * delivered to AP.
+     *
+     * <p>If {@code isWakeupRequired} is true, it must try to wake up AP when a
+     * remote task arrives or when there are pending requests.
+     *
+     * <p>If {@code isWakeupRequired} is false, it must not try to wake up AP.
+     */
+    void notifyApStateChange(in ApState state);
+}
diff --git a/automotive/remoteaccess/android/hardware/automotive/remoteaccess/IRemoteTaskCallback.aidl b/automotive/remoteaccess/android/hardware/automotive/remoteaccess/IRemoteTaskCallback.aidl
new file mode 100644
index 0000000..7a1616f
--- /dev/null
+++ b/automotive/remoteaccess/android/hardware/automotive/remoteaccess/IRemoteTaskCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.automotive.remoteaccess;
+
+/**
+ * The callback interface for car service to receive tasks from wakup client.
+ */
+@VintfStability
+interface IRemoteTaskCallback {
+    /**
+     * A callback that is called when a remote task is requested.
+     *
+     * @param clientId An ID to uniquely identify a remote task client.
+     * @param data Opaque task data passed to the remote task client.
+     */
+    oneway void onRemoteTaskRequested(String clientId, in byte[] data);
+}
diff --git a/automotive/remoteaccess/hal/default/Android.bp b/automotive/remoteaccess/hal/default/Android.bp
new file mode 100644
index 0000000..a2bf86c
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/Android.bp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_binary {
+    name: "android.hardware.automotive.remoteaccess@V1-default-service",
+    vendor: true,
+    vintf_fragments: ["remoteaccess-default-service.xml"],
+    init_rc: ["remoteaccess-default-service.rc"],
+    relative_install_path: "hw",
+    srcs: ["src/RemoteAccessImpl.cpp"],
+    whole_static_libs: [
+        "RemoteAccessService",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder_ndk",
+        "liblog",
+        "libutils",
+        "libgrpc++",
+        "libprotobuf-cpp-full",
+    ],
+    defaults: [
+        "vhalclient_defaults",
+    ],
+    cflags: [
+        "-Wno-unused-parameter",
+        "-DGRPC_SERVICE_ADDRESS=\"localhost:50051\"",
+    ],
+}
+
+cc_library {
+    name: "RemoteAccessService",
+    vendor_available: true,
+    local_include_dirs: ["include"],
+    export_include_dirs: ["include"],
+    srcs: [
+        "src/RemoteAccessService.cpp",
+    ],
+    whole_static_libs: [
+        "android.hardware.automotive.remoteaccess-V1-ndk",
+        "wakeup_client_protos",
+        "libvhalclient",
+    ],
+    defaults: [
+        "vhalclient_defaults",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder_ndk",
+        "libcutils",
+        "liblog",
+        "libutils",
+        "libgrpc++",
+        "libprotobuf-cpp-full",
+    ],
+    cflags: [
+        "-Wno-unused-parameter",
+    ],
+}
+
+cc_fuzz {
+    name: "android.hardware.automotive.remoteaccess@V1-default-service.aidl_fuzzer",
+    srcs: ["fuzzer/fuzzer.cpp"],
+    whole_static_libs: [
+        "RemoteAccessService",
+    ],
+    static_libs: [
+        "libgtest",
+        "libgmock",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder_ndk",
+        "liblog",
+        "libutils",
+        "libgrpc++",
+        "libprotobuf-cpp-full",
+    ],
+    defaults: [
+        "vhalclient_defaults",
+        "service_fuzzer_defaults",
+    ],
+    cflags: [
+        "-Wno-unused-parameter",
+        "-DGRPC_SERVICE_ADDRESS=\"localhost:50051\"",
+    ],
+    fuzz_config: {
+        cc: [
+            "shanyu@google.com",
+        ],
+    },
+}
diff --git a/automotive/remoteaccess/hal/default/fuzzer/fuzzer.cpp b/automotive/remoteaccess/hal/default/fuzzer/fuzzer.cpp
new file mode 100644
index 0000000..292c80e
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/fuzzer/fuzzer.cpp
@@ -0,0 +1,101 @@
+/*
+ * 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 <RemoteAccessService.h>
+#include <fuzzbinder/libbinder_ndk_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <gmock/gmock.h>
+#include <grpcpp/test/mock_stream.h>
+#include <wakeup_client.grpc.pb.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace remoteaccess {
+
+using ::grpc::ClientAsyncReaderInterface;
+using ::grpc::ClientAsyncResponseReaderInterface;
+using ::grpc::ClientContext;
+using ::grpc::ClientReader;
+using ::grpc::ClientReaderInterface;
+using ::grpc::CompletionQueue;
+using ::grpc::Status;
+using ::grpc::testing::MockClientReader;
+using ::testing::_;
+using ::testing::Return;
+
+class MockGrpcClientStub : public WakeupClient::StubInterface {
+  public:
+    ClientReaderInterface<GetRemoteTasksResponse>* GetRemoteTasksRaw(
+            [[maybe_unused]] ClientContext* context,
+            [[maybe_unused]] const GetRemoteTasksRequest& request) override {
+        MockClientReader<GetRemoteTasksResponse>* mockClientReader =
+                new MockClientReader<GetRemoteTasksResponse>();
+        ON_CALL(*mockClientReader, Finish()).WillByDefault(Return(Status::OK));
+        ON_CALL(*mockClientReader, Read(_)).WillByDefault(Return(false));
+        return mockClientReader;
+    }
+
+    Status NotifyWakeupRequired([[maybe_unused]] ClientContext* context,
+                                [[maybe_unused]] const NotifyWakeupRequiredRequest& request,
+                                [[maybe_unused]] NotifyWakeupRequiredResponse* response) {
+        return Status::OK;
+    }
+
+    // Async methods which we do not care.
+    ClientAsyncReaderInterface<GetRemoteTasksResponse>* AsyncGetRemoteTasksRaw(
+            [[maybe_unused]] ClientContext* context,
+            [[maybe_unused]] const GetRemoteTasksRequest& request,
+            [[maybe_unused]] CompletionQueue* cq, [[maybe_unused]] void* tag) {
+        return nullptr;
+    }
+
+    ClientAsyncReaderInterface<GetRemoteTasksResponse>* PrepareAsyncGetRemoteTasksRaw(
+            [[maybe_unused]] ClientContext* context,
+            [[maybe_unused]] const GetRemoteTasksRequest& request,
+            [[maybe_unused]] CompletionQueue* cq) {
+        return nullptr;
+    }
+
+    ClientAsyncResponseReaderInterface<NotifyWakeupRequiredResponse>* AsyncNotifyWakeupRequiredRaw(
+            [[maybe_unused]] ClientContext* context,
+            [[maybe_unused]] const NotifyWakeupRequiredRequest& request,
+            [[maybe_unused]] CompletionQueue* cq) {
+        return nullptr;
+    }
+
+    ClientAsyncResponseReaderInterface<NotifyWakeupRequiredResponse>*
+    PrepareAsyncNotifyWakeupRequiredRaw([[maybe_unused]] ClientContext* context,
+                                        [[maybe_unused]] const NotifyWakeupRequiredRequest& request,
+                                        [[maybe_unused]] CompletionQueue* c) {
+        return nullptr;
+    }
+};
+
+}  // namespace remoteaccess
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    android::hardware::automotive::remoteaccess::MockGrpcClientStub stub;
+    std::shared_ptr<android::hardware::automotive::remoteaccess::RemoteAccessService> service =
+            ndk::SharedRefBase::make<
+                    android::hardware::automotive::remoteaccess::RemoteAccessService>(&stub);
+    android::fuzzService(service->asBinder().get(), FuzzedDataProvider(data, size));
+
+    return 0;
+}
diff --git a/automotive/remoteaccess/hal/default/include/RemoteAccessService.h b/automotive/remoteaccess/hal/default/include/RemoteAccessService.h
new file mode 100644
index 0000000..74c2af4
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/include/RemoteAccessService.h
@@ -0,0 +1,116 @@
+/*
+ * 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 <IVhalClient.h>
+#include <aidl/android/hardware/automotive/remoteaccess/ApState.h>
+#include <aidl/android/hardware/automotive/remoteaccess/BnRemoteAccess.h>
+#include <aidl/android/hardware/automotive/remoteaccess/BnRemoteTaskCallback.h>
+#include <aidl/android/hardware/automotive/remoteaccess/IRemoteTaskCallback.h>
+#include <android-base/thread_annotations.h>
+#include <android/binder_auto_utils.h>
+#include <utils/SystemClock.h>
+#include <wakeup_client.grpc.pb.h>
+
+#include <string>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace remoteaccess {
+
+// A IRemoteTaskCallback implementation for debug purpose.
+class DebugRemoteTaskCallback final
+    : public aidl::android::hardware::automotive::remoteaccess::BnRemoteTaskCallback {
+  public:
+    DebugRemoteTaskCallback() { mStartTimeMillis = android::uptimeMillis(); };
+
+    ndk::ScopedAStatus onRemoteTaskRequested(const std::string& clientId,
+                                             const std::vector<uint8_t>& data) override;
+    std::string printTasks();
+
+  private:
+    struct TaskData {
+        std::string clientId;
+        std::vector<uint8_t> data;
+    };
+
+    std::mutex mLock;
+    int64_t mStartTimeMillis;
+    std::vector<TaskData> mTasks;
+};
+
+class RemoteAccessService
+    : public aidl::android::hardware::automotive::remoteaccess::BnRemoteAccess {
+  public:
+    explicit RemoteAccessService(WakeupClient::StubInterface* grpcStub);
+
+    ~RemoteAccessService();
+
+    ndk::ScopedAStatus getDeviceId(std::string* deviceId) override;
+
+    ndk::ScopedAStatus getWakeupServiceName(std::string* wakeupServiceName) override;
+
+    ndk::ScopedAStatus setRemoteTaskCallback(
+            const std::shared_ptr<
+                    aidl::android::hardware::automotive::remoteaccess::IRemoteTaskCallback>&
+                    callback) override;
+
+    ndk::ScopedAStatus clearRemoteTaskCallback() override;
+
+    ndk::ScopedAStatus notifyApStateChange(
+            const aidl::android::hardware::automotive::remoteaccess::ApState& newState) override;
+
+    binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
+
+  private:
+    // For testing.
+    friend class RemoteAccessServiceUnitTest;
+
+    static bool checkDumpPermission();
+
+    WakeupClient::StubInterface* mGrpcStub;
+    std::thread mThread;
+    std::mutex mLock;
+    std::condition_variable mCv;
+    std::shared_ptr<aidl::android::hardware::automotive::remoteaccess::IRemoteTaskCallback>
+            mRemoteTaskCallback GUARDED_BY(mLock);
+    std::unique_ptr<grpc::ClientContext> mGetRemoteTasksContext GUARDED_BY(mLock);
+    // Associated with mCv to notify the task loop to stop waiting and exit.
+    bool mTaskWaitStopped GUARDED_BY(mLock);
+    // A mutex to make sure startTaskLoop does not overlap with stopTaskLoop.
+    std::mutex mStartStopTaskLoopLock;
+    bool mTaskLoopRunning GUARDED_BY(mStartStopTaskLoopLock);
+    // Default wait time before retry connecting to remote access client is 10s.
+    size_t mRetryWaitInMs = 10'000;
+    std::shared_ptr<DebugRemoteTaskCallback> mDebugCallback;
+
+    void runTaskLoop();
+    void maybeStartTaskLoop();
+    void maybeStopTaskLoop();
+    ndk::ScopedAStatus getDeviceIdWithClient(
+            android::frameworks::automotive::vhal::IVhalClient& client, std::string* deviceId);
+
+    void setRetryWaitInMs(size_t retryWaitInMs) { mRetryWaitInMs = retryWaitInMs; }
+    void dumpHelp(int fd);
+};
+
+}  // namespace remoteaccess
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/remoteaccess/hal/default/proto/Android.bp b/automotive/remoteaccess/hal/default/proto/Android.bp
new file mode 100644
index 0000000..3e0dba1
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/proto/Android.bp
@@ -0,0 +1,79 @@
+// 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 {
+    // 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"],
+}
+
+genrule {
+    name: "wakeup_client_pb_h",
+    tools: [
+        "aprotoc",
+        "protoc-gen-grpc-cpp-plugin",
+    ],
+    cmd: "$(location aprotoc) -I$$(dirname $(in)) -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)",
+    srcs: [
+        "wakeup_client.proto",
+    ],
+    out: [
+        "wakeup_client.pb.h",
+        "wakeup_client.grpc.pb.h",
+    ],
+}
+
+genrule {
+    name: "wakeup_client_pb_cc",
+    tools: [
+        "aprotoc",
+        "protoc-gen-grpc-cpp-plugin",
+    ],
+    cmd: "$(location aprotoc) -I$$(dirname $(in)) -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)",
+    srcs: [
+        "wakeup_client.proto",
+    ],
+    out: [
+        "wakeup_client.pb.cc",
+        "wakeup_client.grpc.pb.cc",
+    ],
+}
+
+cc_library_static {
+    name: "wakeup_client_protos",
+    vendor_available: true,
+    host_supported: true,
+    include_dirs: [
+        "external/protobuf/src",
+    ],
+    generated_headers: [
+        "wakeup_client_pb_h",
+    ],
+    export_generated_headers: [
+        "wakeup_client_pb_h",
+    ],
+    generated_sources: [
+        "wakeup_client_pb_cc",
+    ],
+    shared_libs: [
+        "libgrpc++",
+        "libprotobuf-cpp-full",
+    ],
+    cflags: [
+        "-Wno-unused-parameter",
+    ],
+}
diff --git a/automotive/remoteaccess/hal/default/proto/wakeup_client.proto b/automotive/remoteaccess/hal/default/proto/wakeup_client.proto
new file mode 100644
index 0000000..4fe0d01
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/proto/wakeup_client.proto
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+package android.hardware.automotive.remoteaccess;
+
+/**
+ * Service provided by a wakeup client running on TCU.
+ */
+service WakeupClient {
+    /**
+     * Establish a long-live connection to receive remote tasks.
+     *
+     * <p>For the server, whenever a remote task arrives, if the connection is
+     * alive, it will use the return stream to return a task's information.
+     *
+     * <p>If the connection is not alive, the server must stores the remote task
+     * until a new connection is established (which means AP is ready to
+     * receive remote task again) and send the stored tasks.
+     *
+     * <p>If the server closes the connection, the client will try to
+     * reestablish the connection.
+     */
+    rpc GetRemoteTasks(GetRemoteTasksRequest) returns (stream GetRemoteTasksResponse) {}
+
+    /**
+     * Notifies whether AP is required to be waken up when remote task arrives.
+     *
+     * <p>Wakeup client should store and use this state until a new call with a
+     * different state arrives.
+     *
+     * <p>If {@code isWakeupRequired} in the request is true, it must wake up AP
+     * when a remote task arrives.
+     *
+     * <p>If {@code isWakeupRequired} in the request is false, it must not try
+     * to wake up AP.
+     */
+    rpc NotifyWakeupRequired(NotifyWakeupRequiredRequest) returns (NotifyWakeupRequiredResponse) {}
+}
+
+message GetRemoteTasksRequest {}
+
+message GetRemoteTasksResponse {
+    string clientId = 1;
+    bytes data = 2;
+}
+
+message NotifyWakeupRequiredRequest {
+    bool isWakeupRequired = 1;
+}
+
+message NotifyWakeupRequiredResponse {}
diff --git a/automotive/remoteaccess/hal/default/remoteaccess-default-service.rc b/automotive/remoteaccess/hal/default/remoteaccess-default-service.rc
new file mode 100644
index 0000000..b7a9cdc
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/remoteaccess-default-service.rc
@@ -0,0 +1,4 @@
+service vendor.remoteaccess-default /vendor/bin/hw/android.hardware.automotive.remoteaccess@V1-default-service
+    class hal
+    user vehicle_network
+    group system inet
diff --git a/automotive/remoteaccess/hal/default/remoteaccess-default-service.xml b/automotive/remoteaccess/hal/default/remoteaccess-default-service.xml
new file mode 100644
index 0000000..d050a1b
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/remoteaccess-default-service.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.automotive.remoteaccess</name>
+        <version>1</version>
+        <fqname>IRemoteAccess/default</fqname>
+    </hal>
+</manifest>
diff --git a/automotive/remoteaccess/hal/default/src/RemoteAccessImpl.cpp b/automotive/remoteaccess/hal/default/src/RemoteAccessImpl.cpp
new file mode 100644
index 0000000..8720c2f
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/src/RemoteAccessImpl.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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 "RemoteAccessImpl"
+
+#include "RemoteAccessService.h"
+
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <grpcpp/create_channel.h>
+#include <stdlib.h>
+#include <utils/Log.h>
+
+constexpr char SERVICE_NAME[] = "android.hardware.automotive.remoteaccess.IRemoteAccess/default";
+
+int main(int /* argc */, char* /* argv */[]) {
+    ALOGI("Registering RemoteAccessService as service...");
+
+#ifndef GRPC_SERVICE_ADDRESS
+    ALOGE("GRPC_SERVICE_ADDRESS is not defined, exiting");
+    exit(1);
+#endif
+    auto channel = grpc::CreateChannel(GRPC_SERVICE_ADDRESS, grpc::InsecureChannelCredentials());
+    auto clientStub = android::hardware::automotive::remoteaccess::WakeupClient::NewStub(channel);
+    auto service = ndk::SharedRefBase::make<
+            android::hardware::automotive::remoteaccess::RemoteAccessService>(clientStub.get());
+
+    binder_exception_t err = AServiceManager_addService(service->asBinder().get(), SERVICE_NAME);
+    if (err != EX_NONE) {
+        ALOGE("failed to register android.hardware.automotive.remote.IRemoteAccess service, "
+              "exception: %d",
+              err);
+        exit(1);
+    }
+
+    if (!ABinderProcess_setThreadPoolMaxThreadCount(1)) {
+        ALOGE("%s", "failed to set thread pool max thread count");
+        exit(1);
+    }
+    ABinderProcess_startThreadPool();
+
+    ALOGI("RemoteAccess service Ready");
+
+    ABinderProcess_joinThreadPool();
+
+    ALOGW("Should not reach here");
+
+    return 0;
+}
diff --git a/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp b/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp
new file mode 100644
index 0000000..5cd58d3
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp
@@ -0,0 +1,355 @@
+/*
+ * 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 "RemoteAccessService.h"
+
+#include <VehicleUtils.h>
+#include <aidl/android/hardware/automotive/vehicle/VehicleProperty.h>
+#include <android-base/stringprintf.h>
+#include <android/binder_status.h>
+#include <grpc++/grpc++.h>
+#include <private/android_filesystem_config.h>
+#include <utils/Log.h>
+#include <chrono>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace remoteaccess {
+
+namespace {
+
+using ::aidl::android::hardware::automotive::remoteaccess::ApState;
+using ::aidl::android::hardware::automotive::remoteaccess::IRemoteTaskCallback;
+using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
+using ::android::base::ScopedLockAssertion;
+using ::android::base::StringAppendF;
+using ::android::base::StringPrintf;
+using ::android::frameworks::automotive::vhal::IVhalClient;
+using ::android::hardware::automotive::vehicle::toInt;
+using ::grpc::ClientContext;
+using ::grpc::ClientReaderInterface;
+using ::grpc::Status;
+using ::grpc::StatusCode;
+using ::ndk::ScopedAStatus;
+
+const std::string WAKEUP_SERVICE_NAME = "com.google.vehicle.wakeup";
+constexpr char COMMAND_SET_AP_STATE[] = "--set-ap-state";
+constexpr char COMMAND_START_DEBUG_CALLBACK[] = "--start-debug-callback";
+constexpr char COMMAND_STOP_DEBUG_CALLBACK[] = "--stop-debug-callback";
+constexpr char COMMAND_SHOW_TASK[] = "--show-task";
+constexpr char COMMAND_GET_DEVICE_ID[] = "--get-device-id";
+
+std::vector<uint8_t> stringToBytes(const std::string& s) {
+    const char* data = s.data();
+    return std::vector<uint8_t>(data, data + s.size());
+}
+
+ScopedAStatus rpcStatusToScopedAStatus(const Status& status, const std::string& errorMsg) {
+    return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+            status.error_code(), (errorMsg + ", error: " + status.error_message()).c_str());
+}
+
+std::string printBytes(const std::vector<uint8_t>& bytes) {
+    std::string s;
+    for (size_t i = 0; i < bytes.size(); i++) {
+        StringAppendF(&s, "%02x", bytes[i]);
+    }
+    return s;
+}
+
+bool checkBoolFlag(const char* flag) {
+    return !strcmp(flag, "1") || !strcmp(flag, "0");
+}
+
+void dprintErrorStatus(int fd, const char* detail, const ScopedAStatus& status) {
+    dprintf(fd, "%s, code: %d, error: %s\n", detail, status.getStatus(), status.getMessage());
+}
+
+}  // namespace
+
+RemoteAccessService::RemoteAccessService(WakeupClient::StubInterface* grpcStub)
+    : mGrpcStub(grpcStub){};
+
+RemoteAccessService::~RemoteAccessService() {
+    maybeStopTaskLoop();
+}
+
+void RemoteAccessService::maybeStartTaskLoop() {
+    std::lock_guard<std::mutex> lockGuard(mStartStopTaskLoopLock);
+    if (mTaskLoopRunning) {
+        return;
+    }
+
+    mThread = std::thread([this]() { runTaskLoop(); });
+
+    mTaskLoopRunning = true;
+}
+
+void RemoteAccessService::maybeStopTaskLoop() {
+    std::lock_guard<std::mutex> lockGuard(mStartStopTaskLoopLock);
+    if (!mTaskLoopRunning) {
+        return;
+    }
+
+    {
+        std::lock_guard<std::mutex> lockGuard(mLock);
+        // Try to stop the reading stream.
+        if (mGetRemoteTasksContext) {
+            mGetRemoteTasksContext->TryCancel();
+            mGetRemoteTasksContext.reset();
+        }
+        mTaskWaitStopped = true;
+        mCv.notify_all();
+    }
+    if (mThread.joinable()) {
+        mThread.join();
+    }
+
+    mTaskLoopRunning = false;
+}
+
+void RemoteAccessService::runTaskLoop() {
+    GetRemoteTasksRequest request = {};
+    std::unique_ptr<ClientReaderInterface<GetRemoteTasksResponse>> reader;
+    while (true) {
+        {
+            std::lock_guard<std::mutex> lockGuard(mLock);
+            mGetRemoteTasksContext.reset(new ClientContext());
+            reader = mGrpcStub->GetRemoteTasks(mGetRemoteTasksContext.get(), request);
+        }
+        GetRemoteTasksResponse response;
+        while (reader->Read(&response)) {
+            ALOGI("Receiving one task from remote task client");
+
+            std::shared_ptr<IRemoteTaskCallback> callback;
+            {
+                std::lock_guard<std::mutex> lockGuard(mLock);
+                callback = mRemoteTaskCallback;
+            }
+            if (callback == nullptr) {
+                ALOGD("No callback registered, task ignored");
+                continue;
+            }
+            ALOGD("Calling onRemoteTaskRequested callback for client ID: %s",
+                  response.clientid().c_str());
+            ScopedAStatus callbackStatus = callback->onRemoteTaskRequested(
+                    response.clientid(), stringToBytes(response.data()));
+            if (!callbackStatus.isOk()) {
+                ALOGE("Failed to call onRemoteTaskRequested callback, status: %d, message: %s",
+                      callbackStatus.getStatus(), callbackStatus.getMessage());
+            }
+        }
+        Status status = reader->Finish();
+
+        ALOGE("GetRemoteTasks stream breaks, code: %d, message: %s, sleeping for 10s and retry",
+              status.error_code(), status.error_message().c_str());
+        // The long lasting connection should not return. But if the server returns, retry after
+        // 10s.
+        {
+            std::unique_lock lk(mLock);
+            if (mCv.wait_for(lk, std::chrono::milliseconds(mRetryWaitInMs), [this] {
+                    ScopedLockAssertion lockAssertion(mLock);
+                    return mTaskWaitStopped;
+                })) {
+                // If the stopped flag is set, we are quitting, exit the loop.
+                break;
+            }
+        }
+    }
+}
+
+ScopedAStatus RemoteAccessService::getDeviceId(std::string* deviceId) {
+#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+    auto vhalClient = IVhalClient::tryCreate();
+    if (vhalClient == nullptr) {
+        ALOGE("Failed to connect to VHAL");
+        return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                /*errorCode=*/0, "Failed to connect to VHAL to get device ID");
+    }
+    return getDeviceIdWithClient(*vhalClient.get(), deviceId);
+#else
+    // Don't use VHAL client in fuzzing since IPC is not allowed.
+    return ScopedAStatus::ok();
+#endif
+}
+
+ScopedAStatus RemoteAccessService::getDeviceIdWithClient(IVhalClient& vhalClient,
+                                                         std::string* deviceId) {
+    auto result = vhalClient.getValueSync(
+            *vhalClient.createHalPropValue(toInt(VehicleProperty::INFO_VIN)));
+    if (!result.ok()) {
+        return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                /*errorCode=*/0,
+                ("failed to get INFO_VIN from VHAL: " + result.error().message()).c_str());
+    }
+    *deviceId = (*result)->getStringValue();
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus RemoteAccessService::getWakeupServiceName(std::string* wakeupServiceName) {
+    *wakeupServiceName = WAKEUP_SERVICE_NAME;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus RemoteAccessService::setRemoteTaskCallback(
+        const std::shared_ptr<IRemoteTaskCallback>& callback) {
+    std::lock_guard<std::mutex> lockGuard(mLock);
+    mRemoteTaskCallback = callback;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus RemoteAccessService::clearRemoteTaskCallback() {
+    std::lock_guard<std::mutex> lockGuard(mLock);
+    mRemoteTaskCallback.reset();
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus RemoteAccessService::notifyApStateChange(const ApState& newState) {
+    ClientContext context;
+    NotifyWakeupRequiredRequest request = {};
+    request.set_iswakeuprequired(newState.isWakeupRequired);
+    NotifyWakeupRequiredResponse response = {};
+    Status status = mGrpcStub->NotifyWakeupRequired(&context, request, &response);
+    if (!status.ok()) {
+        return rpcStatusToScopedAStatus(status, "Failed to notify isWakeupRequired");
+    }
+
+    if (newState.isReadyForRemoteTask) {
+        maybeStartTaskLoop();
+    } else {
+        maybeStopTaskLoop();
+    }
+    return ScopedAStatus::ok();
+}
+
+bool RemoteAccessService::checkDumpPermission() {
+    uid_t uid = AIBinder_getCallingUid();
+    return uid == AID_ROOT || uid == AID_SHELL || uid == AID_SYSTEM;
+}
+
+void RemoteAccessService::dumpHelp(int fd) {
+    dprintf(fd, "%s",
+            (std::string("RemoteAccess HAL debug interface, Usage: \n") + COMMAND_SET_AP_STATE +
+             " [0/1](isReadyForRemoteTask) [0/1](isWakeupRequired)  Set the new AP state\n" +
+             COMMAND_START_DEBUG_CALLBACK +
+             " Start a debug callback that will record the received tasks\n" +
+             COMMAND_STOP_DEBUG_CALLBACK + " Stop the debug callback\n" + COMMAND_SHOW_TASK +
+             " Show tasks received by debug callback\n" + COMMAND_GET_DEVICE_ID +
+             " Get device id\n")
+                    .c_str());
+}
+
+binder_status_t RemoteAccessService::dump(int fd, const char** args, uint32_t numArgs) {
+    if (!checkDumpPermission()) {
+        dprintf(fd, "Caller must be root, system or shell\n");
+        return STATUS_PERMISSION_DENIED;
+    }
+
+    if (numArgs == 0) {
+        dumpHelp(fd);
+        return STATUS_OK;
+    }
+
+    if (!strcmp(args[0], COMMAND_SET_AP_STATE)) {
+        if (numArgs < 3) {
+            dumpHelp(fd);
+            return STATUS_OK;
+        }
+        ApState apState = {};
+        const char* remoteTaskFlag = args[1];
+        if (!strcmp(remoteTaskFlag, "1") && !strcmp(remoteTaskFlag, "0")) {
+            dumpHelp(fd);
+            return STATUS_OK;
+        }
+        if (!checkBoolFlag(args[1])) {
+            dumpHelp(fd);
+            return STATUS_OK;
+        }
+        if (!strcmp(args[1], "1")) {
+            apState.isReadyForRemoteTask = true;
+        }
+        if (!checkBoolFlag(args[2])) {
+            dumpHelp(fd);
+            return STATUS_OK;
+        }
+        if (!strcmp(args[2], "1")) {
+            apState.isWakeupRequired = true;
+        }
+        auto status = notifyApStateChange(apState);
+        if (!status.isOk()) {
+            dprintErrorStatus(fd, "Failed to set AP state", status);
+        } else {
+            dprintf(fd, "successfully set the new AP state\n");
+        }
+    } else if (!strcmp(args[0], COMMAND_START_DEBUG_CALLBACK)) {
+        mDebugCallback = ndk::SharedRefBase::make<DebugRemoteTaskCallback>();
+        setRemoteTaskCallback(mDebugCallback);
+        dprintf(fd, "Debug callback registered\n");
+    } else if (!strcmp(args[0], COMMAND_STOP_DEBUG_CALLBACK)) {
+        if (mDebugCallback) {
+            mDebugCallback.reset();
+        }
+        clearRemoteTaskCallback();
+        dprintf(fd, "Debug callback unregistered\n");
+    } else if (!strcmp(args[0], COMMAND_SHOW_TASK)) {
+        if (mDebugCallback) {
+            dprintf(fd, "%s", mDebugCallback->printTasks().c_str());
+        } else {
+            dprintf(fd, "Debug callback is not currently used, use \"%s\" first.\n",
+                    COMMAND_START_DEBUG_CALLBACK);
+        }
+    } else if (!strcmp(args[0], COMMAND_GET_DEVICE_ID)) {
+        std::string deviceId;
+        auto status = getDeviceId(&deviceId);
+        if (!status.isOk()) {
+            dprintErrorStatus(fd, "Failed to get device ID", status);
+        } else {
+            dprintf(fd, "Device Id: %s\n", deviceId.c_str());
+        }
+    } else {
+        dumpHelp(fd);
+    }
+
+    return STATUS_OK;
+}
+
+ScopedAStatus DebugRemoteTaskCallback::onRemoteTaskRequested(const std::string& clientId,
+                                                             const std::vector<uint8_t>& data) {
+    std::lock_guard<std::mutex> lockGuard(mLock);
+    mTasks.push_back({
+            .clientId = clientId,
+            .data = data,
+    });
+    return ScopedAStatus::ok();
+}
+
+std::string DebugRemoteTaskCallback::printTasks() {
+    std::lock_guard<std::mutex> lockGuard(mLock);
+    std::string s = StringPrintf("Received %zu tasks in %f seconds", mTasks.size(),
+                                 (android::uptimeMillis() - mStartTimeMillis) / 1000.);
+    for (size_t i = 0; i < mTasks.size(); i++) {
+        StringAppendF(&s, "Client Id: %s, Data: %s\n", mTasks[i].clientId.c_str(),
+                      printBytes(mTasks[i].data).c_str());
+    }
+    return s;
+}
+
+}  // namespace remoteaccess
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/remoteaccess/hal/default/test/Android.bp b/automotive/remoteaccess/hal/default/test/Android.bp
new file mode 100644
index 0000000..227175a
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/test/Android.bp
@@ -0,0 +1,47 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+    name: "RemoteAccessServiceUnitTest",
+    vendor: true,
+    srcs: ["*.cpp"],
+    whole_static_libs: [
+        "RemoteAccessService",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder_ndk",
+        "liblog",
+        "libutils",
+        "libgrpc++",
+        "libprotobuf-cpp-full",
+    ],
+    // libgrpc++.so is installed as root, require root to access it.
+    require_root: true,
+    static_libs: [
+        "libgtest",
+        "libgmock",
+    ],
+    defaults: [
+        "vhalclient_defaults",
+    ],
+    cflags: [
+        "-Wno-unused-parameter",
+    ],
+    test_suites: ["device-tests"],
+}
diff --git a/automotive/remoteaccess/hal/default/test/RemoteAccessServiceUnitTest.cpp b/automotive/remoteaccess/hal/default/test/RemoteAccessServiceUnitTest.cpp
new file mode 100644
index 0000000..a220aeb
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/test/RemoteAccessServiceUnitTest.cpp
@@ -0,0 +1,375 @@
+/*
+ * 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 "RemoteAccessService.h"
+
+#include <AidlHalPropValue.h>
+#include <IVhalClient.h>
+#include <aidl/android/hardware/automotive/remoteaccess/ApState.h>
+#include <aidl/android/hardware/automotive/remoteaccess/BnRemoteTaskCallback.h>
+#include <aidl/android/hardware/automotive/vehicle/VehiclePropValue.h>
+#include <gmock/gmock.h>
+#include <grpcpp/test/mock_stream.h>
+#include <gtest/gtest.h>
+#include <wakeup_client.grpc.pb.h>
+#include <chrono>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace remoteaccess {
+
+namespace {
+
+using ::android::base::ScopedLockAssertion;
+using ::android::frameworks::automotive::vhal::AidlHalPropValue;
+using ::android::frameworks::automotive::vhal::IHalPropConfig;
+using ::android::frameworks::automotive::vhal::IHalPropValue;
+using ::android::frameworks::automotive::vhal::ISubscriptionCallback;
+using ::android::frameworks::automotive::vhal::ISubscriptionClient;
+using ::android::frameworks::automotive::vhal::IVhalClient;
+
+using ::aidl::android::hardware::automotive::remoteaccess::ApState;
+using ::aidl::android::hardware::automotive::remoteaccess::BnRemoteTaskCallback;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
+
+using ::grpc::ClientAsyncReaderInterface;
+using ::grpc::ClientAsyncResponseReaderInterface;
+using ::grpc::ClientContext;
+using ::grpc::ClientReader;
+using ::grpc::ClientReaderInterface;
+using ::grpc::CompletionQueue;
+using ::grpc::Status;
+using ::grpc::testing::MockClientReader;
+using ::ndk::ScopedAStatus;
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+
+constexpr char kTestVin[] = "test_VIN";
+
+}  // namespace
+
+class MockGrpcClientStub : public WakeupClient::StubInterface {
+  public:
+    MOCK_METHOD(ClientReaderInterface<GetRemoteTasksResponse>*, GetRemoteTasksRaw,
+                (ClientContext * context, const GetRemoteTasksRequest& request));
+    MOCK_METHOD(Status, NotifyWakeupRequired,
+                (ClientContext * context, const NotifyWakeupRequiredRequest& request,
+                 NotifyWakeupRequiredResponse* response));
+    // Async methods which we do not care.
+    MOCK_METHOD(ClientAsyncReaderInterface<GetRemoteTasksResponse>*, AsyncGetRemoteTasksRaw,
+                (ClientContext * context, const GetRemoteTasksRequest& request, CompletionQueue* cq,
+                 void* tag));
+    MOCK_METHOD(ClientAsyncReaderInterface<GetRemoteTasksResponse>*, PrepareAsyncGetRemoteTasksRaw,
+                (ClientContext * context, const GetRemoteTasksRequest& request,
+                 CompletionQueue* cq));
+    MOCK_METHOD(ClientAsyncResponseReaderInterface<NotifyWakeupRequiredResponse>*,
+                AsyncNotifyWakeupRequiredRaw,
+                (ClientContext * context, const NotifyWakeupRequiredRequest& request,
+                 CompletionQueue* cq));
+    MOCK_METHOD(ClientAsyncResponseReaderInterface<NotifyWakeupRequiredResponse>*,
+                PrepareAsyncNotifyWakeupRequiredRaw,
+                (ClientContext * context, const NotifyWakeupRequiredRequest& request,
+                 CompletionQueue* cq));
+};
+
+class FakeVhalClient final : public android::frameworks::automotive::vhal::IVhalClient {
+  public:
+    template <class T>
+    using VhalClientResult = android::hardware::automotive::vehicle::VhalResult<T>;
+
+    inline bool isAidlVhal() { return true; }
+
+    VhalClientResult<std::unique_ptr<IHalPropValue>> getValueSync(
+            const IHalPropValue& requestValue) override {
+        auto propValue = std::make_unique<AidlHalPropValue>(requestValue.getPropId());
+        propValue->setStringValue(kTestVin);
+        return propValue;
+    }
+
+    std::unique_ptr<IHalPropValue> createHalPropValue(int32_t propId) override {
+        return std::make_unique<AidlHalPropValue>(propId);
+    }
+
+    // Functions we do not care.
+    std::unique_ptr<IHalPropValue> createHalPropValue([[maybe_unused]] int32_t propId,
+                                                      [[maybe_unused]] int32_t areaId) override {
+        return nullptr;
+    }
+
+    void getValue([[maybe_unused]] const IHalPropValue& requestValue,
+                  [[maybe_unused]] std::shared_ptr<GetValueCallbackFunc> callback) override {}
+
+    void setValue([[maybe_unused]] const IHalPropValue& requestValue,
+                  [[maybe_unused]] std::shared_ptr<SetValueCallbackFunc> callback) override {}
+
+    VhalClientResult<void> setValueSync([[maybe_unused]] const IHalPropValue& requestValue) {
+        return {};
+    }
+
+    VhalClientResult<void> addOnBinderDiedCallback(
+            [[maybe_unused]] std::shared_ptr<OnBinderDiedCallbackFunc> callback) override {
+        return {};
+    }
+
+    VhalClientResult<void> removeOnBinderDiedCallback(
+            [[maybe_unused]] std::shared_ptr<OnBinderDiedCallbackFunc> callback) override {
+        return {};
+    }
+
+    VhalClientResult<std::vector<std::unique_ptr<IHalPropConfig>>> getAllPropConfigs() override {
+        return std::vector<std::unique_ptr<IHalPropConfig>>();
+    }
+
+    VhalClientResult<std::vector<std::unique_ptr<IHalPropConfig>>> getPropConfigs(
+            [[maybe_unused]] std::vector<int32_t> propIds) override {
+        return std::vector<std::unique_ptr<IHalPropConfig>>();
+    }
+
+    std::unique_ptr<ISubscriptionClient> getSubscriptionClient(
+            [[maybe_unused]] std::shared_ptr<ISubscriptionCallback> callback) override {
+        return nullptr;
+    }
+};
+
+class FakeRemoteTaskCallback : public BnRemoteTaskCallback {
+  public:
+    ScopedAStatus onRemoteTaskRequested(const std::string& clientId,
+                                        const std::vector<uint8_t>& data) override {
+        std::lock_guard<std::mutex> lockGuard(mLock);
+        mDataByClientId[clientId] = data;
+        mTaskCount++;
+        mCv.notify_all();
+        return ScopedAStatus::ok();
+    }
+
+    std::vector<uint8_t> getData(const std::string& clientId) { return mDataByClientId[clientId]; }
+
+    bool wait(size_t taskCount, size_t timeoutInSec) {
+        std::unique_lock<std::mutex> lock(mLock);
+        return mCv.wait_for(lock, std::chrono::seconds(timeoutInSec), [taskCount, this] {
+            ScopedLockAssertion lockAssertion(mLock);
+            return mTaskCount >= taskCount;
+        });
+    }
+
+  private:
+    std::mutex mLock;
+    std::unordered_map<std::string, std::vector<uint8_t>> mDataByClientId GUARDED_BY(mLock);
+    size_t mTaskCount GUARDED_BY(mLock) = 0;
+    std::condition_variable mCv;
+};
+
+class RemoteAccessServiceUnitTest : public ::testing::Test {
+  public:
+    virtual void SetUp() override {
+        mGrpcWakeupClientStub = std::make_unique<MockGrpcClientStub>();
+        mService = ndk::SharedRefBase::make<RemoteAccessService>(mGrpcWakeupClientStub.get());
+    }
+
+    MockGrpcClientStub* getGrpcWakeupClientStub() { return mGrpcWakeupClientStub.get(); }
+
+    RemoteAccessService* getService() { return mService.get(); }
+
+    void setRetryWaitInMs(size_t retryWaitInMs) { mService->setRetryWaitInMs(retryWaitInMs); }
+
+    ScopedAStatus getDeviceIdWithClient(IVhalClient& vhalClient, std::string* deviceId) {
+        return mService->getDeviceIdWithClient(vhalClient, deviceId);
+    }
+
+  private:
+    std::unique_ptr<MockGrpcClientStub> mGrpcWakeupClientStub;
+    std::shared_ptr<RemoteAccessService> mService;
+};
+
+TEST_F(RemoteAccessServiceUnitTest, TestGetWakeupServiceName) {
+    std::string serviceName;
+
+    ScopedAStatus status = getService()->getWakeupServiceName(&serviceName);
+
+    EXPECT_TRUE(status.isOk());
+    EXPECT_EQ(serviceName, "com.google.vehicle.wakeup");
+}
+
+TEST_F(RemoteAccessServiceUnitTest, TestNotifyApStateChangeWakeupRequired) {
+    bool isWakeupRequired = false;
+    EXPECT_CALL(*getGrpcWakeupClientStub(), NotifyWakeupRequired)
+            .WillOnce([&isWakeupRequired]([[maybe_unused]] ClientContext* context,
+                                          const NotifyWakeupRequiredRequest& request,
+                                          [[maybe_unused]] NotifyWakeupRequiredResponse* response) {
+                isWakeupRequired = request.iswakeuprequired();
+                return Status();
+            });
+
+    ApState newState = {
+            .isWakeupRequired = true,
+    };
+    ScopedAStatus status = getService()->notifyApStateChange(newState);
+
+    EXPECT_TRUE(status.isOk());
+    EXPECT_TRUE(isWakeupRequired);
+}
+
+TEST_F(RemoteAccessServiceUnitTest, TestGetRemoteTasks) {
+    GetRemoteTasksResponse response1;
+    std::vector<uint8_t> testData = {0xde, 0xad, 0xbe, 0xef};
+    response1.set_clientid("1");
+    response1.set_data(testData.data(), testData.size());
+    GetRemoteTasksResponse response2;
+    response2.set_clientid("2");
+    std::shared_ptr<FakeRemoteTaskCallback> callback =
+            ndk::SharedRefBase::make<FakeRemoteTaskCallback>();
+
+    ON_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw)
+            .WillByDefault(
+                    [response1, response2]([[maybe_unused]] ClientContext* context,
+                                           [[maybe_unused]] const GetRemoteTasksRequest& request) {
+                        // mockReader ownership will be transferred to the client so we don't own it
+                        // here.
+                        MockClientReader<GetRemoteTasksResponse>* mockClientReader =
+                                new MockClientReader<GetRemoteTasksResponse>();
+                        EXPECT_CALL(*mockClientReader, Finish()).WillOnce(Return(Status::OK));
+                        EXPECT_CALL(*mockClientReader, Read(_))
+                                .WillOnce(DoAll(SetArgPointee<0>(response1), Return(true)))
+                                .WillOnce(DoAll(SetArgPointee<0>(response2), Return(true)))
+                                .WillRepeatedly(Return(false));
+                        return mockClientReader;
+                    });
+
+    getService()->setRemoteTaskCallback(callback);
+    // Start the long live connection to receive tasks.
+    ApState newState = {
+            .isReadyForRemoteTask = true,
+    };
+    ASSERT_TRUE(getService()->notifyApStateChange(newState).isOk());
+
+    ASSERT_TRUE(callback->wait(/*taskCount=*/2, /*timeoutInSec=*/10))
+            << "Did not receive enough tasks";
+    EXPECT_EQ(callback->getData("1"), testData);
+    EXPECT_EQ(callback->getData("2"), std::vector<uint8_t>());
+}
+
+TEST_F(RemoteAccessServiceUnitTest, TestGetRemoteTasksRetryConnection) {
+    GetRemoteTasksResponse response;
+    std::shared_ptr<FakeRemoteTaskCallback> callback =
+            ndk::SharedRefBase::make<FakeRemoteTaskCallback>();
+
+    ON_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw)
+            .WillByDefault([response]([[maybe_unused]] ClientContext* context,
+                                      [[maybe_unused]] const GetRemoteTasksRequest& request) {
+                // mockReader ownership will be transferred to the client so we don't own it here.
+                MockClientReader<GetRemoteTasksResponse>* mockClientReader =
+                        new MockClientReader<GetRemoteTasksResponse>();
+                EXPECT_CALL(*mockClientReader, Finish()).WillOnce(Return(Status::OK));
+                // Connection fails after receiving one task. Should retry after some time.
+                EXPECT_CALL(*mockClientReader, Read(_))
+                        .WillOnce(DoAll(SetArgPointee<0>(response), Return(true)))
+                        .WillRepeatedly(Return(false));
+                return mockClientReader;
+            });
+
+    getService()->setRemoteTaskCallback(callback);
+    setRetryWaitInMs(100);
+    // Start the long live connection to receive tasks.
+    ApState newState = {
+            .isReadyForRemoteTask = true,
+    };
+    ASSERT_TRUE(getService()->notifyApStateChange(newState).isOk());
+
+    ASSERT_TRUE(callback->wait(/*taskCount=*/2, /*timeoutInSec=*/10))
+            << "Did not receive enough tasks";
+}
+
+TEST_F(RemoteAccessServiceUnitTest, TestGetRemoteTasksDefaultNotReady) {
+    GetRemoteTasksResponse response1;
+    std::vector<uint8_t> testData = {0xde, 0xad, 0xbe, 0xef};
+    response1.set_clientid("1");
+    response1.set_data(testData.data(), testData.size());
+    GetRemoteTasksResponse response2;
+    response2.set_clientid("2");
+    std::shared_ptr<FakeRemoteTaskCallback> callback =
+            ndk::SharedRefBase::make<FakeRemoteTaskCallback>();
+
+    EXPECT_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw).Times(0);
+
+    // Default state is not ready for remote tasks, so no callback will be called.
+    getService()->setRemoteTaskCallback(callback);
+
+    std::this_thread::sleep_for(std::chrono::milliseconds(100));
+}
+
+TEST_F(RemoteAccessServiceUnitTest, TestGetRemoteTasksNotReadyAfterReady) {
+    GetRemoteTasksResponse response1;
+    std::vector<uint8_t> testData = {0xde, 0xad, 0xbe, 0xef};
+    response1.set_clientid("1");
+    response1.set_data(testData.data(), testData.size());
+    GetRemoteTasksResponse response2;
+    response2.set_clientid("2");
+    std::shared_ptr<FakeRemoteTaskCallback> callback =
+            ndk::SharedRefBase::make<FakeRemoteTaskCallback>();
+
+    ON_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw)
+            .WillByDefault(
+                    [response1, response2]([[maybe_unused]] ClientContext* context,
+                                           [[maybe_unused]] const GetRemoteTasksRequest& request) {
+                        // mockReader ownership will be transferred to the client so we don't own it
+                        // here.
+                        MockClientReader<GetRemoteTasksResponse>* mockClientReader =
+                                new MockClientReader<GetRemoteTasksResponse>();
+                        EXPECT_CALL(*mockClientReader, Finish()).WillOnce(Return(Status::OK));
+                        EXPECT_CALL(*mockClientReader, Read(_))
+                                .WillOnce(DoAll(SetArgPointee<0>(response1), Return(true)))
+                                .WillOnce(DoAll(SetArgPointee<0>(response2), Return(true)))
+                                .WillRepeatedly(Return(false));
+                        return mockClientReader;
+                    });
+    // Should only be called once when is is ready for remote task.
+    EXPECT_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw).Times(1);
+
+    getService()->setRemoteTaskCallback(callback);
+    setRetryWaitInMs(100);
+    // Start the long live connection to receive tasks.
+    ApState newState = {
+            .isReadyForRemoteTask = true,
+    };
+    ASSERT_TRUE(getService()->notifyApStateChange(newState).isOk());
+    ASSERT_TRUE(callback->wait(/*taskCount=*/2, /*timeoutInSec=*/10))
+            << "Did not receive enough tasks";
+
+    // Stop the long live connection.
+    newState.isReadyForRemoteTask = false;
+    ASSERT_TRUE(getService()->notifyApStateChange(newState).isOk());
+
+    // Wait for the retry delay, but the loop should already exit.
+    std::this_thread::sleep_for(std::chrono::milliseconds(150));
+}
+
+TEST_F(RemoteAccessServiceUnitTest, testGetDeviceId) {
+    std::string deviceId;
+
+    FakeVhalClient vhalClient;
+
+    ASSERT_TRUE(getDeviceIdWithClient(vhalClient, &deviceId).isOk());
+    ASSERT_EQ(deviceId, kTestVin);
+}
+
+}  // namespace remoteaccess
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/remoteaccess/test_grpc_server/README.md b/automotive/remoteaccess/test_grpc_server/README.md
new file mode 100644
index 0000000..28035de
--- /dev/null
+++ b/automotive/remoteaccess/test_grpc_server/README.md
@@ -0,0 +1,310 @@
+# Test GRPC Server.
+
+A test GRPC server that implements wakeup_client.proto. This test server acts
+as a reference implementation for a remote wakeup client running on TCU. The
+test server does not communicate with any actual network server. It has the
+following behavior:
+
+* It starts a GRPC server on 'DGRPC_SERVICE_ADDRESS' compile flag which is
+  localhost:50051. The GRPC server provides the service according to
+  hardware/interfaces/automotive/remoteaccess/hal/default/proto/wakeup_client.proto.
+
+  In real implementation, DGRPC_SERVICE_ADDRESS can be specified to any IP
+  address where the TCU can be exposed to Application Processor. The default
+  remote access HAL implementation
+  (hardware/interfaces/automotive/remoteaccess/hal/default/Android.bp) also
+  uses DGRPC_SERVICE_ADDRESS to find this GRPC server, so it must have the
+  same IP address.
+
+* It generates a fake task using FakeTaskGenerator every 'kTaskIntervalInMs' ms.
+
+  In real implementation, it should receive task from the remote server.
+
+* Each fake task has an increasing unique client ID. The task data is always
+  what's defined for 'DATA' variable.
+
+  In real implementation, the client ID and task data should come from the
+  remote server.
+
+* The generated tasks are put into a task queue which is a priority queue sorted
+  by task received time.
+
+  In real implementation, if the server provides a task timestamp, then this
+  queue can be sorted by that task timestamp instead.
+
+* When the Application processor is started, the remote access HAL running on
+  Android will call 'GetRemoteTasks' to establish a long-live connection. This
+  connection is used to deliver all task data from remote wakeup client to
+  remote access HAL, which eventually to car service and applications.
+
+  When the 'GetRemoteTasks' is called, the wakeup client must send all the
+  pending tasks through the 'ServerWriter'. If no task is pending, then it must
+  block and wait for a new task to arrive.
+
+  If one task data fails to be sent through the channel, it likely means
+  the other side (Application processor) is shutting down or has closed the
+  channel. The wakeup client must put the task back to the pending queue and
+  wait for a new 'GetRemoteTasks' request to retry sending the task.
+
+* When a new task arrives, if 'WakeupRequired' is true, then try to wakeup
+  the Application Processor by sending a specific CAN message. It is possible that
+  the waking up is already in progress. This is okay since Vehicle Processor
+  should ignore wakeup message if a wakeup is already in progress.
+
+* When 'WakeupRequired' is updated from false to true, if there are unexpired
+  pending tasks in the task queue, try to wakeup Application Processor.
+
+  This is to handle the situation when a task arrives while the device is
+  shutting down. During the device shutdown, the channel to deliver the remote
+  tasks to Application Processor is shutdown so the new task will be added to the
+  task queue. 'WakeupRequired' will be set to false to prevent the wakeup
+  message preventing the shutdown. After the shutdown is complete,
+  'WakeupRequired' will be set to true and this wakeup client must try to wake
+  up the device again to execute the pending tasks.
+
+* Every pending task has a timeout: 'kTaskTimeoutInMs'. If the pending task
+  is not delivered to remote access HAL before the timeout (through
+  GetRemoteTasks), the task timed out and a warning message is logged.
+
+  In real implementation, this kTaskTimeoutInMs has to be set long enough to
+  allow an Android bootup to happen. 20s is a reasonable value. When a task
+  timed out, the wakeup client should also report to remote task server about
+  the task timeout failure.
+
+## How to build the test wakeup client
+
+* Under android root: `source build/envsetup.sh`
+
+* `lunch sdk_car_x86_64-userdebug`
+
+* `make -j TestWakeupClientServer`
+
+## How to push the test wakeup client to a TCU which runs Android.
+
+* Make the target device writable:
+
+  `adb root`
+
+  `adb remount` (remount might take a while)
+
+  `adb reboot`
+
+  `adb root`
+
+  `adb remount`
+
+* Under android root: `cd $ANDROID_PRODUCT_OUT`
+
+* `adb push vendor/bin/TestWakeupClientServer /vendor/bin`
+
+* `adb shell`
+
+* `su`
+
+* `/vendor/bin/TestWakeupClientServer`
+
+## How to build and test the test wakeup client using one car emulator.
+
+In this test setup we will use one car emulator
+(sdk_car_x86_64-userdebug). We assume both the TCU and the remote access HAL
+runs on the same Android system, and they communicate through local loopback
+interface.
+
+* Under android root, `source build/envsetup.sh`
+
+* `lunch sdk_car_x86_64-userdebug`
+
+* `m -j`
+
+* Run the emulator, the '-read-only' flag is required to run multiple instances:
+
+  `emulator -writable-system -read-only`
+
+* The android lunch target: sdk_car_x86_64-userdebug and
+  cf_x86_64_auto-userdebug already contains the default remote access HAL. For
+  other lunch target, you can add the default remote access HAL by adding
+  'android.hardware.automotive.remoteaccess@V1-default-service' to
+  'PRODUCT_PACKAGES' variable in mk file, see `device/generic/car/common/car.mk`
+  as example.
+
+  To verify whether remote access HAL is running, you can use the following
+  command to check:
+
+  `dumpsys android.hardware.automotive.remoteaccess.IRemoteAccess/default`
+
+* Make the target device writable:
+
+  `adb root`
+
+  `adb remount` (remount might take a while)
+
+  `adb reboot`
+
+  `adb root`
+
+  `adb remount`
+
+* `make -j TestWakeupClientServer`
+
+* `adb push $ANDROID_PRODUCT_OUT/vendor/bin/TestWakeupClientServer /vendor/bin`
+
+* `adb shell`
+
+* `emulator_car_x86_64:/ # su`
+
+* `emulator_car_x86_64:/ # /vendor/bin/TestWakeupClientServer`
+
+* Remote access HAL should start by default when the car emulator starts. Now
+  the test wake up client should also be running and generating fake tasks.
+
+  Start a new session under android root
+
+  `source build/envsetup.sh`
+
+  `lunch sdk_car_x86_64-userdebug`
+
+  `adb shell`
+
+  `emulator_car_x86_64:/ # su`
+
+* Issue the command to start a simple debug callback that will capture all the
+  received tasks at the remote access HAL side:
+
+  `emulator_car_x86_64:/ # dumpsys android.hardware.automotive.remoteaccess.IRemoteAccess/default --start-debug-callback`
+
+* Issue the following debug command to remote access HAL to establish the
+  communication channel between it and the test wakeup client. This command
+  also notifies that wakeup is not required:
+
+  `emulator_car_x86_64:/ # dumpsys android.hardware.automotive.remoteaccess.IRemoteAccess/default --set-ap-state 1 0`
+
+* Wait for a while, issue the following command to show the received fake tasks:
+
+  `emulator_car_x86_64:/ # dumpsys android.hardware.automotive.remoteaccess.IRemoteAccess/default --show-task`
+
+  You should expect to see some received tasks printed out.
+
+* Simulate the Application Processor is shutting down by issuing the following
+  command:
+
+  `emulator_car_x86_64:/ # dumpsys android.hardware.automotive.remoteaccess.IRemoteAccess/default --set-ap-state 0 0`
+
+* Wait for a while, issue the following command to show received tasks again:
+
+  `emulator_car_x86_64:/ # dumpsys android.hardware.automotive.remoteaccess.IRemoteAccess/default --show-task`
+
+  You should expect to see no new tasks received since remote access HAL already
+  closed the communication channel.
+
+* Simulate the Application Processor is already shutdown and wake up is required
+  now:
+
+  `emulator_car_x86_64:/ # dumpsys android.hardware.automotive.remoteaccess.IRemoteAccess/default --set-ap-state 0 1`
+
+  Now you should expect to see the test wakeup client printing out messages
+  that it is trying to wake up application processor.
+
+* Simulate the Application Processor is waken up:
+
+  `emulator_car_x86_64:/ # dumpsys android.hardware.automotive.remoteaccess.IRemoteAccess/default --set-ap-state 1 0`
+
+* A new communication channel should have been established and all pending
+  non-expired tasks should be delivered to the remote access HAL.
+
+  `emulator_car_x86_64:/ # dumpsys android.hardware.automotive.remoteaccess.IRemoteAccess/default --show-task`
+
+* Now you can issue `ctrl c` on the first adb shell to stop the test wakeup
+  client.
+
+* After the test, you can use `ctrl D` to exit the adb shell.
+
+## How to build and test the test wakeup client using two car emulators.
+
+In this test case, we are going to use two car emulators, one as the
+Application Processor, one as the TCU.
+
+* Change the IP address to allow IP communication between different emulator
+  instances. For detail about why we change it this way, see [interconnecting
+  emulator instance](https://developer.android.com/studio/run/emulator-networking#connecting).
+
+  Change 'DGRPC_SERVICE_ADDRESS' in `[android_root]/hardware/interfaces/automotive/remoteaccess/test_grpc_server/impl/Android.bp` to
+  `10.0.2.15:50051`.
+
+  Change `DGRPC_SERVICE_ADDRESS` in '[android_root]/hardware/interfaces/automotive/remoteaccess/hal/defaut/Android.bp' to
+  `10.0.2.2:50051`.
+
+* Under android root: `source build/envsetup.sh`
+
+* `lunch sdk_car_x86_64-userdebug`
+
+*  `m -j`
+
+* Start one car emulator as TCU
+
+  `emulator -writable-system -read-only`
+
+* Start a new shell session. Connect to the emulator's console,
+  see [Start and stop a console session](https://developer.android.com/studio/run/emulator-console#console-session)
+  for detail.
+
+  `telnet localhost 5554`
+
+* `auth auth_token` where auth_token must match the contents of the
+  `~/.emulator_console_auth_token` file.
+
+* `redir add tcp:50051:50051`
+
+* Exit the telnet session using 'ctrl-C'
+
+  Make the target device writable:
+
+  Under android root:
+
+  `source build/envsetup.sh`
+
+  `lunch sdk_car_x86_64-userdebug`
+
+  `adb root`
+
+  `adb remount` (remount might take a while)
+
+  `adb reboot`
+
+  `adb root`
+
+  `adb remount`
+
+* `make -j TestWakeupClientServer`
+
+* `adb push $ANDROID_PRODUCT_OUT/vendor/bin/TestWakeupClientServer /vendor/bin`
+
+* `adb shell`
+
+* `emulator_car_x86_64:/ # su`
+
+* `emulator_car_x86_64:/ # /vendor/bin/TestWakeupClientServer`
+
+* Start a new shell under android root, start another car emulator as the Application Processor:
+
+  `source build/envsetup.sh`
+
+  `lunch sdk_car_x86_64-userdebug`
+
+  `emulator -writable-system -read-only`
+
+* Open a new shell under android root:
+
+  `source build/envsetup.sh`
+
+  `lunch sdk_car_x86_64-userdebug`
+
+* Connect to adb shell for the application processor:
+
+  `adb -s emulator-5556 shell`
+
+  `emulator_car_x86_64:/ # su`
+
+* Follow the test instructions for one car emulator using the 'dumpsys'
+  commands.
+
+* After the test, you can use `ctrl D` to exit the adb shell.
diff --git a/automotive/remoteaccess/test_grpc_server/impl/Android.bp b/automotive/remoteaccess/test_grpc_server/impl/Android.bp
new file mode 100644
index 0000000..e978c8c
--- /dev/null
+++ b/automotive/remoteaccess/test_grpc_server/impl/Android.bp
@@ -0,0 +1,42 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // 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"],
+}
+
+cc_binary {
+    name: "TestWakeupClientServer",
+    vendor: true,
+    srcs: ["src/*.cpp"],
+    local_include_dirs: ["include"],
+    shared_libs: [
+        "libbase",
+        "libutils",
+        "libgrpc++",
+        "libprotobuf-cpp-full",
+    ],
+    whole_static_libs: [
+        "wakeup_client_protos",
+    ],
+    cflags: [
+        "-Wno-unused-parameter",
+        "-DGRPC_SERVICE_ADDRESS=\"localhost:50051\"",
+    ],
+}
diff --git a/automotive/remoteaccess/test_grpc_server/impl/include/TestWakeupClientServiceImpl.h b/automotive/remoteaccess/test_grpc_server/impl/include/TestWakeupClientServiceImpl.h
new file mode 100644
index 0000000..12bd93b
--- /dev/null
+++ b/automotive/remoteaccess/test_grpc_server/impl/include/TestWakeupClientServiceImpl.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <utils/Looper.h>
+#include <wakeup_client.grpc.pb.h>
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <string>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace remoteaccess {
+
+// A class to generate fake task for testing. Not required for real implementation. In real
+// implementation, the task should come from remote task server. This class is thread-safe.
+class FakeTaskGenerator final {
+  public:
+    GetRemoteTasksResponse generateTask();
+
+  private:
+    // Simulates the client ID for each task.
+    std::atomic<int> mCurrentClientId = 0;
+    constexpr static uint8_t DATA[] = {0xde, 0xad, 0xbe, 0xef};
+};
+
+struct TaskInfo {
+    // This is unique per-task. Note that a task might be popped and put back into the task queue,
+    // it will have a new task ID but the same clientId in the task data.
+    int taskId;
+    int64_t timestampInMs;
+    GetRemoteTasksResponse taskData;
+};
+
+struct TaskInfoComparator {
+    // We want the smallest timestamp and smallest task ID on top.
+    bool operator()(const TaskInfo& l, const TaskInfo& r) {
+        return l.timestampInMs > r.timestampInMs ||
+               (l.timestampInMs == r.timestampInMs && l.taskId > r.taskId);
+    }
+};
+
+// forward-declaration.
+class TaskQueue;
+
+class TaskTimeoutMessageHandler final : public android::MessageHandler {
+  public:
+    TaskTimeoutMessageHandler(TaskQueue* taskQueue);
+    void handleMessage(const android::Message& message) override;
+
+  private:
+    TaskQueue* mTaskQueue;
+};
+
+// TaskQueue is thread-safe.
+class TaskQueue final {
+  public:
+    TaskQueue();
+    ~TaskQueue();
+
+    void add(const GetRemoteTasksResponse& response);
+    std::optional<GetRemoteTasksResponse> maybePopOne();
+    void waitForTask();
+    void stopWait();
+    void handleTaskTimeout();
+    bool isEmpty();
+
+  private:
+    std::thread mCheckTaskTimeoutThread;
+    std::mutex mLock;
+    std::priority_queue<TaskInfo, std::vector<TaskInfo>, TaskInfoComparator> mTasks
+            GUARDED_BY(mLock);
+    // A variable to notify mTasks is not empty.
+    std::condition_variable mTasksNotEmptyCv;
+    bool mStopped GUARDED_BY(mLock);
+    android::sp<Looper> mLooper;
+    android::sp<TaskTimeoutMessageHandler> mTaskTimeoutMessageHandler;
+    std::atomic<int> mTaskIdCounter = 0;
+
+    void checkForTestTimeoutLoop();
+    void waitForTaskWithLock(std::unique_lock<std::mutex>& lock);
+};
+
+class TestWakeupClientServiceImpl final : public WakeupClient::Service {
+  public:
+    TestWakeupClientServiceImpl();
+
+    ~TestWakeupClientServiceImpl();
+
+    grpc::Status GetRemoteTasks(grpc::ServerContext* context, const GetRemoteTasksRequest* request,
+                                grpc::ServerWriter<GetRemoteTasksResponse>* writer) override;
+
+    grpc::Status NotifyWakeupRequired(grpc::ServerContext* context,
+                                      const NotifyWakeupRequiredRequest* request,
+                                      NotifyWakeupRequiredResponse* response) override;
+
+  private:
+    // This is a thread for communicating with remote wakeup server (via network) and receive tasks
+    // from it.
+    std::thread mThread;
+    // A variable to notify server is stopping.
+    std::condition_variable mServerStoppedCv;
+    // Whether wakeup AP is required for executing tasks.
+    std::atomic<bool> mWakeupRequired = false;
+    std::mutex mLock;
+    bool mServerStopped GUARDED_BY(mLock);
+
+    // Thread-safe. For test impl only.
+    FakeTaskGenerator mFakeTaskGenerator;
+    // Thread-sfae.
+    TaskQueue mTaskQueue;
+
+    void fakeTaskGenerateLoop();
+
+    void wakeupApplicationProcessor();
+};
+
+}  // namespace remoteaccess
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/remoteaccess/test_grpc_server/impl/src/TestWakeupClientServiceImpl.cpp b/automotive/remoteaccess/test_grpc_server/impl/src/TestWakeupClientServiceImpl.cpp
new file mode 100644
index 0000000..795265f
--- /dev/null
+++ b/automotive/remoteaccess/test_grpc_server/impl/src/TestWakeupClientServiceImpl.cpp
@@ -0,0 +1,255 @@
+/*
+ * 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 "TestWakeupClientServiceImpl.h"
+
+#include <android-base/stringprintf.h>
+#include <inttypes.h>
+#include <utils/Looper.h>
+#include <utils/SystemClock.h>
+#include <chrono>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace remoteaccess {
+
+namespace {
+
+using ::android::uptimeMillis;
+using ::android::base::ScopedLockAssertion;
+using ::android::base::StringPrintf;
+using ::grpc::ServerContext;
+using ::grpc::ServerWriter;
+using ::grpc::Status;
+
+constexpr int kTaskIntervalInMs = 5'000;
+constexpr int64_t KTaskTimeoutInMs = 20'000;
+
+}  // namespace
+
+GetRemoteTasksResponse FakeTaskGenerator::generateTask() {
+    int clientId = mCurrentClientId++;
+    GetRemoteTasksResponse response;
+    response.set_data(std::string(reinterpret_cast<const char*>(DATA), sizeof(DATA)));
+    std::string clientIdStr = StringPrintf("%d", clientId);
+    response.set_clientid(clientIdStr);
+    return response;
+}
+
+TaskTimeoutMessageHandler::TaskTimeoutMessageHandler(TaskQueue* taskQueue)
+    : mTaskQueue(taskQueue) {}
+
+void TaskTimeoutMessageHandler::handleMessage(const android::Message& message) {
+    mTaskQueue->handleTaskTimeout();
+}
+
+TaskQueue::TaskQueue() {
+    mTaskTimeoutMessageHandler = android::sp<TaskTimeoutMessageHandler>::make(this);
+    mLooper = Looper::prepare(/*opts=*/0);
+    mCheckTaskTimeoutThread = std::thread([this] { checkForTestTimeoutLoop(); });
+}
+
+TaskQueue::~TaskQueue() {
+    {
+        std::lock_guard<std::mutex> lockGuard(mLock);
+        mStopped = true;
+    }
+    while (true) {
+        // Remove all pending timeout handlers from queue.
+        if (!maybePopOne().has_value()) {
+            break;
+        }
+    }
+    if (mCheckTaskTimeoutThread.joinable()) {
+        mCheckTaskTimeoutThread.join();
+    }
+}
+
+std::optional<GetRemoteTasksResponse> TaskQueue::maybePopOne() {
+    std::lock_guard<std::mutex> lockGuard(mLock);
+    if (mTasks.size() == 0) {
+        return std::nullopt;
+    }
+    TaskInfo response = std::move(mTasks.top());
+    mTasks.pop();
+    mLooper->removeMessages(mTaskTimeoutMessageHandler, response.taskId);
+    return std::move(response.taskData);
+}
+
+void TaskQueue::add(const GetRemoteTasksResponse& task) {
+    std::lock_guard<std::mutex> lockGuard(mLock);
+    if (mStopped) {
+        return;
+    }
+    int taskId = mTaskIdCounter++;
+    mTasks.push(TaskInfo{
+            .taskId = taskId,
+            .timestampInMs = uptimeMillis(),
+            .taskData = task,
+    });
+    android::Message message(taskId);
+    mLooper->sendMessageDelayed(KTaskTimeoutInMs * 1000, mTaskTimeoutMessageHandler, message);
+    mTasksNotEmptyCv.notify_all();
+}
+
+void TaskQueue::waitForTask() {
+    std::unique_lock<std::mutex> lock(mLock);
+    waitForTaskWithLock(lock);
+}
+
+void TaskQueue::waitForTaskWithLock(std::unique_lock<std::mutex>& lock) {
+    mTasksNotEmptyCv.wait(lock, [this] {
+        ScopedLockAssertion lockAssertion(mLock);
+        return mTasks.size() > 0 || mStopped;
+    });
+}
+
+void TaskQueue::stopWait() {
+    std::lock_guard<std::mutex> lockGuard(mLock);
+    mStopped = true;
+    mTasksNotEmptyCv.notify_all();
+}
+
+bool TaskQueue::isEmpty() {
+    std::lock_guard<std::mutex> lockGuard(mLock);
+    return mTasks.size() == 0 || mStopped;
+}
+
+void TaskQueue::checkForTestTimeoutLoop() {
+    Looper::setForThread(mLooper);
+
+    while (true) {
+        {
+            std::unique_lock<std::mutex> lock(mLock);
+            if (mStopped) {
+                return;
+            }
+        }
+
+        mLooper->pollAll(/*timeoutMillis=*/-1);
+    }
+}
+
+void TaskQueue::handleTaskTimeout() {
+    // We know which task timed-out from the taskId in the message. However, there is no easy way
+    // to remove a specific task with the task ID from the priority_queue, so we just check from
+    // the top of the queue (which have the oldest tasks).
+    std::lock_guard<std::mutex> lockGuard(mLock);
+    int64_t now = uptimeMillis();
+    while (mTasks.size() > 0) {
+        const TaskInfo& taskInfo = mTasks.top();
+        if (taskInfo.timestampInMs + KTaskTimeoutInMs > now) {
+            break;
+        }
+        // In real implementation, this should report task failure to remote wakeup server.
+        printf("Task for client ID: %s timed-out, added at %" PRId64 " ms, now %" PRId64 " ms",
+               taskInfo.taskData.clientid().c_str(), taskInfo.timestampInMs, now);
+        mTasks.pop();
+    }
+}
+
+TestWakeupClientServiceImpl::TestWakeupClientServiceImpl() {
+    mThread = std::thread([this] { fakeTaskGenerateLoop(); });
+}
+
+TestWakeupClientServiceImpl::~TestWakeupClientServiceImpl() {
+    {
+        std::lock_guard<std::mutex> lockGuard(mLock);
+        mServerStopped = true;
+        mServerStoppedCv.notify_all();
+    }
+    mTaskQueue.stopWait();
+    if (mThread.joinable()) {
+        mThread.join();
+    }
+}
+
+void TestWakeupClientServiceImpl::fakeTaskGenerateLoop() {
+    // In actual implementation, this should communicate with the remote server and receives tasks
+    // from it. Here we simulate receiving one remote task every {kTaskIntervalInMs}ms.
+    while (true) {
+        mTaskQueue.add(mFakeTaskGenerator.generateTask());
+        printf("Received a new task\n");
+        if (mWakeupRequired) {
+            wakeupApplicationProcessor();
+        }
+
+        printf("Sleeping for %d seconds until next task\n", kTaskIntervalInMs);
+
+        std::unique_lock lk(mLock);
+        if (mServerStoppedCv.wait_for(lk, std::chrono::milliseconds(kTaskIntervalInMs), [this] {
+                ScopedLockAssertion lockAssertion(mLock);
+                return mServerStopped;
+            })) {
+            // If the stopped flag is set, we are quitting, exit the loop.
+            return;
+        }
+    }
+}
+
+Status TestWakeupClientServiceImpl::GetRemoteTasks(ServerContext* context,
+                                                   const GetRemoteTasksRequest* request,
+                                                   ServerWriter<GetRemoteTasksResponse>* writer) {
+    printf("GetRemoteTasks called\n");
+    while (true) {
+        mTaskQueue.waitForTask();
+
+        while (true) {
+            auto maybeTask = mTaskQueue.maybePopOne();
+            if (!maybeTask.has_value()) {
+                // No task left, loop again and wait for another task(s).
+                break;
+            }
+            // Loop through all the task in the queue but obtain lock for each element so we don't
+            // hold lock while writing the response.
+            const GetRemoteTasksResponse& response = maybeTask.value();
+            if (!writer->Write(response)) {
+                // Broken stream, maybe the client is shutting down.
+                printf("Failed to deliver remote task to remote access HAL\n");
+                // The task failed to be sent, add it back to the queue. The order might change, but
+                // it is okay.
+                mTaskQueue.add(response);
+                return Status::CANCELLED;
+            }
+        }
+    }
+    return Status::OK;
+}
+
+Status TestWakeupClientServiceImpl::NotifyWakeupRequired(ServerContext* context,
+                                                         const NotifyWakeupRequiredRequest* request,
+                                                         NotifyWakeupRequiredResponse* response) {
+    if (request->iswakeuprequired() && !mWakeupRequired && !mTaskQueue.isEmpty()) {
+        // If wakeup is now required and previously not required, this means we have finished
+        // shutting down the device. If there are still pending tasks, try waking up AP again
+        // to finish executing those tasks.
+        wakeupApplicationProcessor();
+    }
+    mWakeupRequired = request->iswakeuprequired();
+    return Status::OK;
+}
+
+void TestWakeupClientServiceImpl::wakeupApplicationProcessor() {
+    printf("Waking up application processor...\n");
+    // TODO(b/254547153): Send can bus message using socket CAN once we know what the message is.
+}
+
+}  // namespace remoteaccess
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/remoteaccess/test_grpc_server/impl/src/main.cpp b/automotive/remoteaccess/test_grpc_server/impl/src/main.cpp
new file mode 100644
index 0000000..52698b5
--- /dev/null
+++ b/automotive/remoteaccess/test_grpc_server/impl/src/main.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+#include "TestWakeupClientServiceImpl.h"
+
+#include <grpc/grpc.h>
+#include <grpcpp/security/server_credentials.h>
+#include <grpcpp/server.h>
+#include <grpcpp/server_builder.h>
+
+using ::android::hardware::automotive::remoteaccess::TestWakeupClientServiceImpl;
+using ::grpc::Server;
+using ::grpc::ServerBuilder;
+using ::grpc::ServerWriter;
+
+void RunServer() {
+    std::string serverAddress(GRPC_SERVICE_ADDRESS);
+    std::shared_ptr<TestWakeupClientServiceImpl> service =
+            std::make_unique<TestWakeupClientServiceImpl>();
+
+    ServerBuilder builder;
+    builder.AddListeningPort(serverAddress, grpc::InsecureServerCredentials());
+    builder.RegisterService(service.get());
+    std::unique_ptr<Server> server(builder.BuildAndStart());
+    printf("Test Remote Access GRPC Server listening on %s\n", serverAddress.c_str());
+    server->Wait();
+}
+
+int main(int argc, char** argv) {
+    RunServer();
+    return 0;
+}
diff --git a/automotive/vehicle/aidl/impl/default_config/include/DefaultConfig.h b/automotive/vehicle/aidl/impl/default_config/include/DefaultConfig.h
index e00f775..65cd795 100644
--- a/automotive/vehicle/aidl/impl/default_config/include/DefaultConfig.h
+++ b/automotive/vehicle/aidl/impl/default_config/include/DefaultConfig.h
@@ -124,6 +124,13 @@
 
         {.config =
                  {
+                         .prop = toInt(VehicleProperty::INFO_VIN),
+                         .access = VehiclePropertyAccess::READ,
+                         .changeMode = VehiclePropertyChangeMode::STATIC,
+                 },
+         .initialValue = {.stringValue = "1GCARVIN123456789"}},
+        {.config =
+                 {
                          .prop = toInt(VehicleProperty::INFO_MAKE),
                          .access = VehiclePropertyAccess::READ,
                          .changeMode = VehiclePropertyChangeMode::STATIC,
diff --git a/bluetooth/1.0/default/OWNERS b/bluetooth/1.0/default/OWNERS
deleted file mode 100644
index 5df5bfe..0000000
--- a/bluetooth/1.0/default/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-eisenbach@google.com
-mylesgw@google.com
-pavlin@google.com
diff --git a/bluetooth/1.0/default/bluetooth_hci.cc b/bluetooth/1.0/default/bluetooth_hci.cc
index a2211f4..869c723 100644
--- a/bluetooth/1.0/default/bluetooth_hci.cc
+++ b/bluetooth/1.0/default/bluetooth_hci.cc
@@ -33,7 +33,8 @@
 
 class BluetoothDeathRecipient : public hidl_death_recipient {
  public:
-  BluetoothDeathRecipient(const sp<IBluetoothHci> hci) : mHci(hci) {}
+  BluetoothDeathRecipient(const sp<IBluetoothHci> hci)
+    : mHci(hci), has_died_(false) {}
 
   virtual void serviceDied(
       uint64_t /*cookie*/,
@@ -51,7 +52,7 @@
 };
 
 BluetoothHci::BluetoothHci()
-    : death_recipient_(new BluetoothDeathRecipient(this)) {}
+    : death_recipient_(new BluetoothDeathRecipient(this)) {bt_enabled = 0;}
 
 Return<void> BluetoothHci::initialize(
     const ::android::sp<IBluetoothHciCallbacks>& cb) {
@@ -61,8 +62,19 @@
     return Void();
   }
 
+  if (bt_enabled == 1) {
+    ALOGE("initialize was called!");
+    return Void();
+  }
+  bt_enabled = 1;
   death_recipient_->setHasDied(false);
   cb->linkToDeath(death_recipient_, 0);
+  unlink_cb_ = [cb](sp<BluetoothDeathRecipient>& death_recipient) {
+    if (death_recipient->getHasDied())
+      ALOGI("Skipping unlink call, service died.");
+    else
+      cb->unlinkToDeath(death_recipient);
+  };
 
   bool rc = VendorInterface::Initialize(
       [cb](bool status) {
@@ -112,6 +124,12 @@
 
 Return<void> BluetoothHci::close() {
   ALOGI("BluetoothHci::close()");
+
+  if (bt_enabled != 1) {
+    ALOGE("should initialize first!");
+    return Void();
+  }
+  bt_enabled = 0;
   unlink_cb_(death_recipient_);
   VendorInterface::Shutdown();
   return Void();
@@ -134,6 +152,11 @@
 
 void BluetoothHci::sendDataToController(const uint8_t type,
                                         const hidl_vec<uint8_t>& data) {
+  if (bt_enabled != 1) {
+    ALOGE("should initialize first!");
+    return;
+  }
+
   VendorInterface::get()->Send(type, data.data(), data.size());
 }
 
diff --git a/bluetooth/1.0/default/bluetooth_hci.h b/bluetooth/1.0/default/bluetooth_hci.h
index c966990..5130c87 100644
--- a/bluetooth/1.0/default/bluetooth_hci.h
+++ b/bluetooth/1.0/default/bluetooth_hci.h
@@ -48,6 +48,7 @@
   void sendDataToController(const uint8_t type, const hidl_vec<uint8_t>& data);
   ::android::sp<BluetoothDeathRecipient> death_recipient_;
   std::function<void(sp<BluetoothDeathRecipient>&)> unlink_cb_;
+  int bt_enabled;
 };
 
 extern "C" IBluetoothHci* HIDL_FETCH_IBluetoothHci(const char* name);
diff --git a/bluetooth/1.0/default/vendor_interface.cc b/bluetooth/1.0/default/vendor_interface.cc
index 1d15dd6..c23d667 100644
--- a/bluetooth/1.0/default/vendor_interface.cc
+++ b/bluetooth/1.0/default/vendor_interface.cc
@@ -36,6 +36,8 @@
     "BLUETOOTH_VENDOR_LIB_INTERFACE";
 
 static const int INVALID_FD = -1;
+std::mutex vendor_mutex_;
+std::mutex initcb_mutex_;
 
 namespace {
 
@@ -47,13 +49,25 @@
   uint16_t opcode;
 } internal_command;
 
+enum {
+  VENDOR_STATE_INIT = 1,
+  VENDOR_STATE_OPENING,	/* during opening */
+  VENDOR_STATE_OPENED,	/* open in fops_open */
+  VENDOR_STATE_CLOSING,	/* during closing */
+  VENDOR_STATE_CLOSED,	/* closed */
+
+  VENDOR_STATE_MSG_NUM
+} ;
+
+uint8_t vstate = VENDOR_STATE_INIT;
+
 // True when LPM is not enabled yet or wake is not asserted.
 bool lpm_wake_deasserted;
 uint32_t lpm_timeout_ms;
 bool recent_activity_flag;
 
 VendorInterface* g_vendor_interface = nullptr;
-std::mutex wakeup_mutex_;
+static VendorInterface vendor_interface;
 
 HC_BT_HDR* WrapPacketAndCopy(uint16_t event, const hidl_vec<uint8_t>& data) {
   size_t packet_size = data.size() + sizeof(HC_BT_HDR);
@@ -167,11 +181,8 @@
     InitializeCompleteCallback initialize_complete_cb,
     PacketReadCallback event_cb, PacketReadCallback acl_cb,
     PacketReadCallback sco_cb, PacketReadCallback iso_cb) {
-  if (g_vendor_interface) {
-    ALOGE("%s: No previous Shutdown()?", __func__);
-    return false;
-  }
-  g_vendor_interface = new VendorInterface();
+  ALOGI("%s: VendorInterface::Initialize", __func__);
+  g_vendor_interface = &vendor_interface;
   return g_vendor_interface->Open(initialize_complete_cb, event_cb, acl_cb,
                                   sco_cb, iso_cb);
 }
@@ -179,9 +190,8 @@
 void VendorInterface::Shutdown() {
   LOG_ALWAYS_FATAL_IF(!g_vendor_interface, "%s: No Vendor interface!",
                       __func__);
+  ALOGI("%s: VendorInterface::Shutdown", __func__);
   g_vendor_interface->Close();
-  delete g_vendor_interface;
-  g_vendor_interface = nullptr;
 }
 
 VendorInterface* VendorInterface::get() { return g_vendor_interface; }
@@ -191,144 +201,189 @@
                            PacketReadCallback acl_cb,
                            PacketReadCallback sco_cb,
                            PacketReadCallback iso_cb) {
-  initialize_complete_cb_ = initialize_complete_cb;
+  {
+    std::unique_lock<std::mutex> guard(vendor_mutex_);
+    if (vstate == VENDOR_STATE_OPENED) {
+      ALOGW("VendorInterface opened!");
+      return true;
+    }
 
-  // Initialize vendor interface
+    if ((vstate == VENDOR_STATE_CLOSING) ||
+        (vstate == VENDOR_STATE_OPENING)) {
+      ALOGW("VendorInterface open/close is on-going !");
+      return true;
+    }
 
-  lib_handle_ = dlopen(VENDOR_LIBRARY_NAME, RTLD_NOW);
-  if (!lib_handle_) {
-    ALOGE("%s unable to open %s (%s)", __func__, VENDOR_LIBRARY_NAME,
-          dlerror());
-    return false;
-  }
+    vstate = VENDOR_STATE_OPENING;
+    ALOGI("%s: VendorInterface::Open", __func__);
 
-  lib_interface_ = reinterpret_cast<bt_vendor_interface_t*>(
-      dlsym(lib_handle_, VENDOR_LIBRARY_SYMBOL_NAME));
-  if (!lib_interface_) {
-    ALOGE("%s unable to find symbol %s in %s (%s)", __func__,
-          VENDOR_LIBRARY_SYMBOL_NAME, VENDOR_LIBRARY_NAME, dlerror());
-    return false;
-  }
+    initialize_complete_cb_ = initialize_complete_cb;
+    // Initialize vendor interface
+
+    lib_handle_ = dlopen(VENDOR_LIBRARY_NAME, RTLD_NOW);
+    if (!lib_handle_) {
+      ALOGE("%s unable to open %s (%s)", __func__, VENDOR_LIBRARY_NAME,
+            dlerror());
+      return false;
+    }
+
+    lib_interface_ = reinterpret_cast<bt_vendor_interface_t*>(
+        dlsym(lib_handle_, VENDOR_LIBRARY_SYMBOL_NAME));
+    if (!lib_interface_) {
+      ALOGE("%s unable to find symbol %s in %s (%s)", __func__,
+            VENDOR_LIBRARY_SYMBOL_NAME, VENDOR_LIBRARY_NAME, dlerror());
+      return false;
+    }
 
   // Get the local BD address
 
-  uint8_t local_bda[BluetoothAddress::kBytes];
-  if (!BluetoothAddress::get_local_address(local_bda)) {
-    LOG_ALWAYS_FATAL("%s: No Bluetooth Address!", __func__);
-  }
-  int status = lib_interface_->init(&lib_callbacks, (unsigned char*)local_bda);
-  if (status) {
-    ALOGE("%s unable to initialize vendor library: %d", __func__, status);
-    return false;
-  }
+    uint8_t local_bda[BluetoothAddress::kBytes] = {0, 0, 0, 0, 0, 0};
+    if (!BluetoothAddress::get_local_address(local_bda)) {
+      // BT driver will get BD address from NVRAM for MTK solution
+      ALOGW("%s: No pre-set Bluetooth Address!", __func__);
+    }
+    int status = lib_interface_->init(&lib_callbacks, (unsigned char*)local_bda);
+    if (status) {
+      ALOGE("%s unable to initialize vendor library: %d", __func__, status);
+      return false;
+    }
 
-  ALOGD("%s vendor library loaded", __func__);
+    ALOGD("%s vendor library loaded", __func__);
 
   // Power on the controller
 
-  int power_state = BT_VND_PWR_ON;
-  lib_interface_->op(BT_VND_OP_POWER_CTRL, &power_state);
+    int power_state = BT_VND_PWR_ON;
+    lib_interface_->op(BT_VND_OP_POWER_CTRL, &power_state);
 
   // Get the UART socket(s)
 
-  int fd_list[CH_MAX] = {0};
-  int fd_count = lib_interface_->op(BT_VND_OP_USERIAL_OPEN, &fd_list);
+    int fd_list[CH_MAX] = {0};
+    int fd_count = lib_interface_->op(BT_VND_OP_USERIAL_OPEN, &fd_list);
 
-  if (fd_count < 1 || fd_count > CH_MAX - 1) {
-    ALOGE("%s: fd_count %d is invalid!", __func__, fd_count);
-    return false;
-  }
-
-  for (int i = 0; i < fd_count; i++) {
-    if (fd_list[i] == INVALID_FD) {
-      ALOGE("%s: fd %d is invalid!", __func__, fd_list[i]);
+    if (fd_count < 1 || fd_count > CH_MAX - 1) {
+      ALOGE("%s: fd_count %d is invalid!", __func__, fd_count);
       return false;
     }
-  }
 
-  event_cb_ = event_cb;
-  PacketReadCallback intercept_events = [this](const hidl_vec<uint8_t>& event) {
-    HandleIncomingEvent(event);
-  };
+    for (int i = 0; i < fd_count; i++) {
+      if (fd_list[i] == INVALID_FD) {
+        ALOGE("%s: fd %d is invalid!", __func__, fd_list[i]);
+        return false;
+      }
+    }
 
-  if (fd_count == 1) {
-    hci::H4Protocol* h4_hci =
-        new hci::H4Protocol(fd_list[0], intercept_events, acl_cb, sco_cb, iso_cb);
-    fd_watcher_.WatchFdForNonBlockingReads(
-        fd_list[0], [h4_hci](int fd) { h4_hci->OnDataReady(fd); });
-    hci_ = h4_hci;
-  } else {
-    hci::MctProtocol* mct_hci =
-        new hci::MctProtocol(fd_list, intercept_events, acl_cb);
-    fd_watcher_.WatchFdForNonBlockingReads(
-        fd_list[CH_EVT], [mct_hci](int fd) { mct_hci->OnEventDataReady(fd); });
-    fd_watcher_.WatchFdForNonBlockingReads(
-        fd_list[CH_ACL_IN], [mct_hci](int fd) { mct_hci->OnAclDataReady(fd); });
-    hci_ = mct_hci;
-  }
+    event_cb_ = event_cb;
+    PacketReadCallback intercept_events = [this](const hidl_vec<uint8_t>& event) {
+      HandleIncomingEvent(event);
+    };
+
+    if (fd_count == 1) {
+      hci::H4Protocol* h4_hci =
+          new hci::H4Protocol(fd_list[0], intercept_events, acl_cb, sco_cb, iso_cb);
+      fd_watcher_.WatchFdForNonBlockingReads(
+          fd_list[0], [h4_hci](int fd) { h4_hci->OnDataReady(fd); });
+      hci_ = h4_hci;
+    } else {
+      hci::MctProtocol* mct_hci =
+          new hci::MctProtocol(fd_list, intercept_events, acl_cb);
+      fd_watcher_.WatchFdForNonBlockingReads(
+          fd_list[CH_EVT], [mct_hci](int fd) { mct_hci->OnEventDataReady(fd); });
+      fd_watcher_.WatchFdForNonBlockingReads(
+          fd_list[CH_ACL_IN],
+          [mct_hci](int fd) { mct_hci->OnAclDataReady(fd); });
+      hci_ = mct_hci;
+    }
 
   // Initially, the power management is off.
-  lpm_wake_deasserted = true;
+    lpm_wake_deasserted = true;
 
   // Start configuring the firmware
-  firmware_startup_timer_ = new FirmwareStartupTimer();
-  lib_interface_->op(BT_VND_OP_FW_CFG, nullptr);
+    firmware_startup_timer_ = new FirmwareStartupTimer();
+    lib_interface_->op(BT_VND_OP_FW_CFG, nullptr);
 
+    vstate = VENDOR_STATE_OPENED;
+    ALOGI("%s: VendorInterface::Open done!!!", __func__);
+  }  // vendor_mutex_ done
   return true;
 }
 
 void VendorInterface::Close() {
   // These callbacks may send HCI events (vendor-dependent), so make sure to
   // StopWatching the file descriptor after this.
+
+  if (vstate != VENDOR_STATE_OPENED) {
+    ALOGW("VendorInterface is not allow close(%d)", vstate);
+    return;
+  }
+  vstate = VENDOR_STATE_CLOSING;
+  ALOGI("%s: VendorInterface::Close", __func__);
+
   if (lib_interface_ != nullptr) {
+    lib_interface_->cleanup();
     bt_vendor_lpm_mode_t mode = BT_VND_LPM_DISABLE;
     lib_interface_->op(BT_VND_OP_LPM_SET_MODE, &mode);
   }
 
-  fd_watcher_.StopWatchingFileDescriptors();
+  {
+    std::unique_lock<std::mutex> guard(vendor_mutex_);
 
-  if (hci_ != nullptr) {
-    delete hci_;
-    hci_ = nullptr;
-  }
+    fd_watcher_.StopWatchingFileDescriptors();
+    if (hci_ != nullptr) {
+      delete hci_;
+      hci_ = nullptr;
+    }
 
-  if (lib_interface_ != nullptr) {
-    lib_interface_->op(BT_VND_OP_USERIAL_CLOSE, nullptr);
+    if (lib_interface_ != nullptr) {
+      lib_interface_->op(BT_VND_OP_USERIAL_CLOSE, nullptr);
 
-    int power_state = BT_VND_PWR_OFF;
-    lib_interface_->op(BT_VND_OP_POWER_CTRL, &power_state);
+      int power_state = BT_VND_PWR_OFF;
+      lib_interface_->op(BT_VND_OP_POWER_CTRL, &power_state);
 
-    lib_interface_->cleanup();
-    lib_interface_ = nullptr;
-  }
+      lib_interface_ = nullptr;
+    }
 
-  if (lib_handle_ != nullptr) {
-    dlclose(lib_handle_);
-    lib_handle_ = nullptr;
-  }
+    if (lib_handle_ != nullptr) {
+      dlclose(lib_handle_);
+      lib_handle_ = nullptr;
+    }
 
-  if (firmware_startup_timer_ != nullptr) {
-    delete firmware_startup_timer_;
-    firmware_startup_timer_ = nullptr;
-  }
+    if (firmware_startup_timer_ != nullptr) {
+      delete firmware_startup_timer_;
+      firmware_startup_timer_ = nullptr;
+    }
+    vstate = VENDOR_STATE_CLOSED;
+  }  // vendor_mutex_ done
+  ALOGI("%s: VendorInterface::Close done!!!", __func__);
 }
 
 size_t VendorInterface::Send(uint8_t type, const uint8_t* data, size_t length) {
-  std::unique_lock<std::mutex> lock(wakeup_mutex_);
-  recent_activity_flag = true;
+  {
+    std::unique_lock<std::mutex> guard(vendor_mutex_);
 
-  if (lpm_wake_deasserted == true) {
-    // Restart the timer.
-    fd_watcher_.ConfigureTimeout(std::chrono::milliseconds(lpm_timeout_ms),
+    if (vstate != VENDOR_STATE_OPENED) {
+      ALOGW("VendorInterface is not open yet(%d)!", vstate);
+      return 0;
+    }
+    ALOGI("%s: VendorInterface::Send", __func__);
+
+    if (lib_interface_ == nullptr) {
+      ALOGE("lib_interface_ is null");
+      return 0;
+    }
+    recent_activity_flag = true;
+    if (lpm_wake_deasserted == true) {
+      // Restart the timer.
+      fd_watcher_.ConfigureTimeout(std::chrono::milliseconds(lpm_timeout_ms),
                                  [this]() { OnTimeout(); });
-    // Assert wake.
-    lpm_wake_deasserted = false;
-    bt_vendor_lpm_wake_state_t wakeState = BT_VND_LPM_WAKE_ASSERT;
-    lib_interface_->op(BT_VND_OP_LPM_WAKE_SET_STATE, &wakeState);
-    ALOGV("%s: Sent wake before (%02x)", __func__, data[0] | (data[1] << 8));
-  }
+      // Assert wake.
+      lpm_wake_deasserted = false;
+      bt_vendor_lpm_wake_state_t wakeState = BT_VND_LPM_WAKE_ASSERT;
+      lib_interface_->op(BT_VND_OP_LPM_WAKE_SET_STATE, &wakeState);
+      ALOGV("%s: Sent wake before (%02x)", __func__, data[0] | (data[1] << 8));
+    }
 
-  return hci_->Send(type, data, length);
+    return hci_ ? hci_->Send(type, data, length) : 0;
+  }  // vendor_mutex_ done
 }
 
 void VendorInterface::OnFirmwareConfigured(uint8_t result) {
@@ -339,25 +394,36 @@
     firmware_startup_timer_ = nullptr;
   }
 
-  if (initialize_complete_cb_ != nullptr) {
-    initialize_complete_cb_(result == 0);
-    initialize_complete_cb_ = nullptr;
+  {
+    std::unique_lock<std::mutex> guard(initcb_mutex_);
+    ALOGD("%s OnFirmwareConfigured get lock", __func__);
+    if (initialize_complete_cb_ != nullptr) {
+      LOG_ALWAYS_FATAL_IF((result != 0),
+          "%s: Failed to init firmware!", __func__);
+      initialize_complete_cb_(result == 0);
+    }
+  }  // initcb_mutex_ done
+
+  if (lib_interface_ != nullptr) {
+    lib_interface_->op(BT_VND_OP_GET_LPM_IDLE_TIMEOUT, &lpm_timeout_ms);
+    ALOGI("%s: lpm_timeout_ms %d", __func__, lpm_timeout_ms);
+
+    bt_vendor_lpm_mode_t mode = BT_VND_LPM_ENABLE;
+    lib_interface_->op(BT_VND_OP_LPM_SET_MODE, &mode);
+
+    ALOGD("%s Calling StartLowPowerWatchdog()", __func__);
+    fd_watcher_.ConfigureTimeout(std::chrono::milliseconds(lpm_timeout_ms),
+                                 [this]() { OnTimeout(); });
+  }
+  else {
+    ALOGE("lib_interface_ is null");
   }
 
-  lib_interface_->op(BT_VND_OP_GET_LPM_IDLE_TIMEOUT, &lpm_timeout_ms);
-  ALOGI("%s: lpm_timeout_ms %d", __func__, lpm_timeout_ms);
-
-  bt_vendor_lpm_mode_t mode = BT_VND_LPM_ENABLE;
-  lib_interface_->op(BT_VND_OP_LPM_SET_MODE, &mode);
-
-  ALOGD("%s Calling StartLowPowerWatchdog()", __func__);
-  fd_watcher_.ConfigureTimeout(std::chrono::milliseconds(lpm_timeout_ms),
-                               [this]() { OnTimeout(); });
+  initialize_complete_cb_ = nullptr;
 }
 
 void VendorInterface::OnTimeout() {
   ALOGV("%s", __func__);
-  std::unique_lock<std::mutex> lock(wakeup_mutex_);
   if (recent_activity_flag == false) {
     lpm_wake_deasserted = true;
     bt_vendor_lpm_wake_state_t wakeState = BT_VND_LPM_WAKE_DEASSERT;
diff --git a/bluetooth/1.0/default/vendor_interface.h b/bluetooth/1.0/default/vendor_interface.h
index 040f31a..2df3946 100644
--- a/bluetooth/1.0/default/vendor_interface.h
+++ b/bluetooth/1.0/default/vendor_interface.h
@@ -22,6 +22,8 @@
 #include "bt_vendor_lib.h"
 #include "hci_protocol.h"
 
+extern std::mutex initcb_mutex_;
+
 namespace android {
 namespace hardware {
 namespace bluetooth {
@@ -45,10 +47,9 @@
   size_t Send(uint8_t type, const uint8_t* data, size_t length);
 
   void OnFirmwareConfigured(uint8_t result);
-
- private:
   virtual ~VendorInterface() = default;
 
+ private:
   bool Open(InitializeCompleteCallback initialize_complete_cb,
             PacketReadCallback event_cb, PacketReadCallback acl_cb,
             PacketReadCallback sco_cb, PacketReadCallback iso_cb);
diff --git a/bluetooth/1.0/vts/OWNERS b/bluetooth/1.0/vts/OWNERS
deleted file mode 100644
index 58d3a66..0000000
--- a/bluetooth/1.0/vts/OWNERS
+++ /dev/null
@@ -1,8 +0,0 @@
-zachoverflow@google.com
-siyuanh@google.com
-mylesgw@google.com
-jpawlowski@google.com
-apanicke@google.com
-stng@google.com
-hsz@google.com
-
diff --git a/bluetooth/1.0/vts/functional/OWNERS b/bluetooth/1.0/vts/functional/OWNERS
deleted file mode 100644
index 7f02612..0000000
--- a/bluetooth/1.0/vts/functional/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 27441
-bluetooth-reviews@google.com
diff --git a/bluetooth/1.1/default/OWNERS b/bluetooth/1.1/default/OWNERS
deleted file mode 100644
index 0c01df6..0000000
--- a/bluetooth/1.1/default/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-zachoverflow@google.com
-mylesgw@google.com
-jpawlowski@google.com
diff --git a/bluetooth/1.1/vts/OWNERS b/bluetooth/1.1/vts/OWNERS
deleted file mode 100644
index ff6fd93..0000000
--- a/bluetooth/1.1/vts/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-zachoverflow@google.com
-siyuanh@google.com
-mylesgw@google.com
-jpawlowski@google.com
-hsz@google.com
-
diff --git a/bluetooth/OWNERS b/bluetooth/OWNERS
new file mode 100644
index 0000000..f401b8c
--- /dev/null
+++ b/bluetooth/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 27441
+
+henrichataing@google.com
+mylesgw@google.com
+siyuanh@google.com
diff --git a/bluetooth/aidl/Android.bp b/bluetooth/aidl/Android.bp
new file mode 100644
index 0000000..1788ed3
--- /dev/null
+++ b/bluetooth/aidl/Android.bp
@@ -0,0 +1,37 @@
+// This is the expected build file, but it may not be right in all cases
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+    name: "android.hardware.bluetooth",
+    vendor_available: true,
+    host_supported: true,
+    srcs: ["android/hardware/bluetooth/*.aidl"],
+    stability: "vintf",
+    backend: {
+        cpp: {
+            // FIXME should this be disabled?
+            // prefer NDK backend which can be used anywhere
+            // If you disable this, you also need to delete the C++
+            // translate code.
+            enabled: true,
+        },
+        java: {
+            sdk_version: "module_current",
+        },
+        ndk: {
+            apex_available: [
+                "//apex_available:platform",
+                "com.android.btservices",
+            ],
+            min_sdk_version: "33",
+        },
+    },
+}
diff --git a/bluetooth/aidl/aidl_api/android.hardware.bluetooth/current/android/hardware/bluetooth/IBluetoothHci.aidl b/bluetooth/aidl/aidl_api/android.hardware.bluetooth/current/android/hardware/bluetooth/IBluetoothHci.aidl
new file mode 100644
index 0000000..8b1cad2
--- /dev/null
+++ b/bluetooth/aidl/aidl_api/android.hardware.bluetooth/current/android/hardware/bluetooth/IBluetoothHci.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.bluetooth;
+@VintfStability
+interface IBluetoothHci {
+  void close();
+  void initialize(in android.hardware.bluetooth.IBluetoothHciCallbacks callback);
+  void sendAclData(in byte[] data);
+  void sendHciCommand(in byte[] command);
+  void sendIsoData(in byte[] data);
+  void sendScoData(in byte[] data);
+}
diff --git a/bluetooth/aidl/aidl_api/android.hardware.bluetooth/current/android/hardware/bluetooth/IBluetoothHciCallbacks.aidl b/bluetooth/aidl/aidl_api/android.hardware.bluetooth/current/android/hardware/bluetooth/IBluetoothHciCallbacks.aidl
new file mode 100644
index 0000000..aecff7f
--- /dev/null
+++ b/bluetooth/aidl/aidl_api/android.hardware.bluetooth/current/android/hardware/bluetooth/IBluetoothHciCallbacks.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.bluetooth;
+@VintfStability
+interface IBluetoothHciCallbacks {
+  void aclDataReceived(in byte[] data);
+  void hciEventReceived(in byte[] event);
+  void initializationComplete(in android.hardware.bluetooth.Status status);
+  void isoDataReceived(in byte[] data);
+  void scoDataReceived(in byte[] data);
+}
diff --git a/bluetooth/aidl/aidl_api/android.hardware.bluetooth/current/android/hardware/bluetooth/Status.aidl b/bluetooth/aidl/aidl_api/android.hardware.bluetooth/current/android/hardware/bluetooth/Status.aidl
new file mode 100644
index 0000000..da429b8
--- /dev/null
+++ b/bluetooth/aidl/aidl_api/android.hardware.bluetooth/current/android/hardware/bluetooth/Status.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.bluetooth;
+@Backing(type="int") @VintfStability
+enum Status {
+  SUCCESS = 0,
+  ALREADY_INITIALIZED = 1,
+  UNABLE_TO_OPEN_INTERFACE = 2,
+  HARDWARE_INITIALIZATION_ERROR = 3,
+  UNKNOWN = 4,
+}
diff --git a/bluetooth/aidl/android/hardware/bluetooth/IBluetoothHci.aidl b/bluetooth/aidl/android/hardware/bluetooth/IBluetoothHci.aidl
new file mode 100644
index 0000000..db12986
--- /dev/null
+++ b/bluetooth/aidl/android/hardware/bluetooth/IBluetoothHci.aidl
@@ -0,0 +1,73 @@
+/*
+ * Copyright 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.bluetooth;
+
+import android.hardware.bluetooth.IBluetoothHciCallbacks;
+
+/**
+ * The Host Controller Interface (HCI) is the layer defined by the Bluetooth
+ * specification between the software that runs on the host and the Bluetooth
+ * controller chip. This boundary is the natural choice for a Hardware
+ * Abstraction Layer (HAL). Dealing only in HCI packets and events simplifies
+ * the stack and abstracts away power management, initialization, and other
+ * implementation-specific details related to the hardware.
+ */
+@VintfStability
+interface IBluetoothHci {
+    /**
+     * Close the HCI interface
+     */
+    void close();
+
+    /**
+     * Initialize the Bluetooth interface and set the callbacks.
+     */
+    void initialize(in IBluetoothHciCallbacks callback);
+
+    /**
+     * Send an HCI ACL data packet (as specified in the Bluetooth Specification
+     * V4.2, Vol 2, Part 5, Section 5.4.2) to the Bluetooth controller.
+     * Packets must be processed in order.
+     * @param data HCI data packet to be sent
+     */
+    void sendAclData(in byte[] data);
+
+    /**
+     * Send an HCI command (as specified in the Bluetooth Specification
+     * V4.2, Vol 2, Part 5, Section 5.4.1) to the Bluetooth controller.
+     * Commands must be executed in order.
+     *
+     * @param command is the HCI command to be sent
+     */
+    void sendHciCommand(in byte[] command);
+
+    /**
+     * Send an ISO data packet (as specified in the Bluetooth Core
+     * Specification v5.2) to the Bluetooth controller.
+     * Packets must be processed in order.
+     * @param data HCI data packet to be sent
+     */
+    void sendIsoData(in byte[] data);
+
+    /**
+     * Send an SCO data packet (as specified in the Bluetooth Specification
+     * V4.2, Vol 2, Part 5, Section 5.4.3) to the Bluetooth controller.
+     * Packets must be processed in order.
+     * @param data HCI data packet to be sent
+     */
+    void sendScoData(in byte[] data);
+}
diff --git a/bluetooth/aidl/android/hardware/bluetooth/IBluetoothHciCallbacks.aidl b/bluetooth/aidl/android/hardware/bluetooth/IBluetoothHciCallbacks.aidl
new file mode 100644
index 0000000..000333e
--- /dev/null
+++ b/bluetooth/aidl/android/hardware/bluetooth/IBluetoothHciCallbacks.aidl
@@ -0,0 +1,56 @@
+/*
+ * Copyright 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.bluetooth;
+
+import android.hardware.bluetooth.Status;
+
+/**
+ * The interface from the Bluetooth Controller to the stack.
+ */
+@VintfStability
+interface IBluetoothHciCallbacks {
+    /**
+     * Send an ACL data packet from the controller to the host.
+     * @param data the ACL HCI packet to be passed to the host stack
+     */
+    void aclDataReceived(in byte[] data);
+
+    /**
+     * This function is invoked when an HCI event is received from the
+     * Bluetooth controller to be forwarded to the Bluetooth stack.
+     * @param event is the HCI event to be sent to the Bluetooth stack.
+     */
+    void hciEventReceived(in byte[] event);
+
+    /**
+     * Invoked when the Bluetooth controller initialization has been
+     * completed.
+     */
+    void initializationComplete(in Status status);
+
+    /**
+     * Send a ISO data packet from the controller to the host.
+     * @param data the ISO HCI packet to be passed to the host stack
+     */
+    void isoDataReceived(in byte[] data);
+
+    /**
+     * Send a SCO data packet from the controller to the host.
+     * @param data the SCO HCI packet to be passed to the host stack
+     */
+    void scoDataReceived(in byte[] data);
+}
diff --git a/bluetooth/aidl/android/hardware/bluetooth/Status.aidl b/bluetooth/aidl/android/hardware/bluetooth/Status.aidl
new file mode 100644
index 0000000..4ec251a
--- /dev/null
+++ b/bluetooth/aidl/android/hardware/bluetooth/Status.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright 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.bluetooth;
+
+@VintfStability
+@Backing(type="int")
+enum Status {
+    SUCCESS,
+    ALREADY_INITIALIZED,
+    UNABLE_TO_OPEN_INTERFACE,
+    HARDWARE_INITIALIZATION_ERROR,
+    UNKNOWN,
+}
diff --git a/bluetooth/aidl/default/Android.bp b/bluetooth/aidl/default/Android.bp
new file mode 100644
index 0000000..d1761f5
--- /dev/null
+++ b/bluetooth/aidl/default/Android.bp
@@ -0,0 +1,79 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_defaults {
+    name: "android.hardware.bluetooth-service-build-defaults",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+    ],
+    shared_libs: [
+        "android.hardware.bluetooth-V1-ndk",
+        "libbase",
+        "libbinder_ndk",
+        "libcutils",
+        "libhidlbase",
+        "liblog",
+        "libutils",
+    ],
+    static_libs: [
+        "android.hardware.bluetooth.async",
+        "android.hardware.bluetooth.hci",
+    ],
+}
+
+cc_library_static {
+    name: "libbluetoothhcihalimpl",
+    vendor_available: true,
+    host_supported: true,
+    defaults: ["android.hardware.bluetooth-service-build-defaults"],
+    srcs: [
+        "BluetoothHci.cpp",
+    ],
+}
+
+cc_binary {
+    name: "android.hardware.bluetooth-service.default",
+    relative_install_path: "hw",
+    init_rc: ["bluetooth-service-default.rc"],
+    vintf_fragments: ["bluetooth-service-default.xml"],
+    vendor: true,
+    defaults: ["android.hardware.bluetooth-service-build-defaults"],
+    srcs: [
+        "service.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.bluetooth-V1-ndk",
+        "libbase",
+        "libbinder_ndk",
+        "libhidlbase",
+        "libutils",
+        "liblog",
+    ],
+    static_libs: [
+        "libbluetoothhcihalimpl",
+    ],
+}
+
+cc_fuzz {
+    name: "android.hardware.bluetooth-service.default_fuzzer",
+    host_supported: true,
+    defaults: ["service_fuzzer_defaults"],
+    srcs: [
+        "test/fuzzer.cpp",
+    ],
+    static_libs: [
+        "android.hardware.bluetooth.async",
+        "android.hardware.bluetooth.hci",
+        "android.hardware.bluetooth-V1-ndk",
+        "libbluetoothhcihalimpl",
+        "liblog",
+    ],
+    fuzz_config: {
+        componentid: 27441,
+        cc: [
+            "mylesgw@google.com",
+        ],
+    },
+}
diff --git a/bluetooth/aidl/default/BluetoothHci.cpp b/bluetooth/aidl/default/BluetoothHci.cpp
new file mode 100644
index 0000000..4d4896d
--- /dev/null
+++ b/bluetooth/aidl/default/BluetoothHci.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright 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 "android.hardware.bluetooth.service.default"
+
+#include "BluetoothHci.h"
+
+#include <cutils/properties.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <poll.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <termios.h>
+
+#include "log/log.h"
+
+namespace {
+int SetTerminalRaw(int fd) {
+  termios terminal_settings;
+  int rval = tcgetattr(fd, &terminal_settings);
+  if (rval < 0) {
+    return rval;
+  }
+  cfmakeraw(&terminal_settings);
+  rval = tcsetattr(fd, TCSANOW, &terminal_settings);
+  return rval;
+}
+}  // namespace
+
+using namespace ::android::hardware::bluetooth::hci;
+using namespace ::android::hardware::bluetooth::async;
+
+namespace aidl::android::hardware::bluetooth::impl {
+
+void OnDeath(void* cookie);
+
+class BluetoothDeathRecipient {
+ public:
+  BluetoothDeathRecipient(BluetoothHci* hci) : mHci(hci) {}
+
+  void LinkToDeath(const std::shared_ptr<IBluetoothHciCallbacks>& cb) {
+    mCb = cb;
+    clientDeathRecipient_ = AIBinder_DeathRecipient_new(OnDeath);
+    auto linkToDeathReturnStatus = AIBinder_linkToDeath(
+        mCb->asBinder().get(), clientDeathRecipient_, this /* cookie */);
+    LOG_ALWAYS_FATAL_IF(linkToDeathReturnStatus != STATUS_OK,
+                        "Unable to link to death recipient");
+  }
+
+  void UnlinkToDeath(const std::shared_ptr<IBluetoothHciCallbacks>& cb) {
+    LOG_ALWAYS_FATAL_IF(cb != mCb, "Unable to unlink mismatched pointers");
+  }
+
+  void serviceDied() {
+    if (mCb != nullptr && !AIBinder_isAlive(mCb->asBinder().get())) {
+      ALOGE("Bluetooth remote service has died");
+    } else {
+      ALOGE("BluetoothDeathRecipient::serviceDied called but service not dead");
+      return;
+    }
+    has_died_ = true;
+    mHci->close();
+  }
+  BluetoothHci* mHci;
+  std::shared_ptr<IBluetoothHciCallbacks> mCb;
+  AIBinder_DeathRecipient* clientDeathRecipient_;
+  bool getHasDied() const { return has_died_; }
+
+ private:
+  bool has_died_{false};
+};
+
+void OnDeath(void* cookie) {
+  auto* death_recipient = static_cast<BluetoothDeathRecipient*>(cookie);
+  death_recipient->serviceDied();
+}
+
+BluetoothHci::BluetoothHci(const std::string& dev_path) {
+  char property_bytes[PROPERTY_VALUE_MAX];
+  property_get("vendor.ser.bt-uart", property_bytes, dev_path.c_str());
+  mDevPath = std::string(property_bytes);
+  mDeathRecipient = std::make_shared<BluetoothDeathRecipient>(this);
+}
+
+ndk::ScopedAStatus BluetoothHci::initialize(
+    const std::shared_ptr<IBluetoothHciCallbacks>& cb) {
+  ALOGI(__func__);
+
+  mFd = open(mDevPath.c_str(), O_RDWR);
+  if (mFd < 0) {
+    ALOGE("Could not connect to bt: %s (%s)", mDevPath.c_str(),
+          strerror(errno));
+    return ndk::ScopedAStatus::fromServiceSpecificError(STATUS_BAD_VALUE);
+  }
+  if (int ret = SetTerminalRaw(mFd) < 0) {
+    ALOGE("Could not make %s a raw terminal %d(%s)", mDevPath.c_str(), ret,
+          strerror(errno));
+    return ndk::ScopedAStatus::fromServiceSpecificError(STATUS_BAD_VALUE);
+  }
+
+  mCb = cb;
+  if (mCb == nullptr) {
+    ALOGE("cb == nullptr! -> Unable to call initializationComplete(ERR)");
+    return ndk::ScopedAStatus::fromServiceSpecificError(STATUS_BAD_VALUE);
+  }
+
+  mDeathRecipient->LinkToDeath(mCb);
+
+  auto init_ret = cb->initializationComplete(Status::SUCCESS);
+  if (!init_ret.isOk()) {
+    if (!mDeathRecipient->getHasDied()) {
+      ALOGE("Error sending init callback, but no death notification.");
+    }
+    return ndk::ScopedAStatus::fromServiceSpecificError(
+        STATUS_FAILED_TRANSACTION);
+  }
+  mH4 = std::make_shared<H4Protocol>(
+      mFd,
+      [](const std::vector<uint8_t>& /* raw_command */) {
+        LOG_ALWAYS_FATAL("Unexpected command!");
+      },
+      [this](const std::vector<uint8_t>& raw_event) {
+        mCb->hciEventReceived(raw_event);
+      },
+      [this](const std::vector<uint8_t>& raw_acl) {
+        mCb->hciEventReceived(raw_acl);
+      },
+      [this](const std::vector<uint8_t>& raw_sco) {
+        mCb->hciEventReceived(raw_sco);
+      },
+      [this](const std::vector<uint8_t>& raw_iso) {
+        mCb->hciEventReceived(raw_iso);
+      },
+      [this]() {
+        ALOGI("HCI socket device disconnected");
+        mFdWatcher.StopWatchingFileDescriptors();
+      });
+  mFdWatcher.WatchFdForNonBlockingReads(mFd,
+                                        [this](int) { mH4->OnDataReady(); });
+  return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus BluetoothHci::close() {
+  ALOGI(__func__);
+  mFdWatcher.StopWatchingFileDescriptors();
+  ::close(mFd);
+
+  return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus BluetoothHci::sendHciCommand(
+    const std::vector<uint8_t>& packet) {
+  send(PacketType::COMMAND, packet);
+  return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus BluetoothHci::sendAclData(
+    const std::vector<uint8_t>& packet) {
+  send(PacketType::ACL_DATA, packet);
+  return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus BluetoothHci::sendScoData(
+    const std::vector<uint8_t>& packet) {
+  send(PacketType::SCO_DATA, packet);
+  return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus BluetoothHci::sendIsoData(
+    const std::vector<uint8_t>& packet) {
+  send(PacketType::ISO_DATA, packet);
+  return ndk::ScopedAStatus::ok();
+}
+
+void BluetoothHci::send(PacketType type, const std::vector<uint8_t>& v) {
+  mH4->Send(type, v);
+}
+
+}  // namespace aidl::android::hardware::bluetooth::impl
diff --git a/bluetooth/aidl/default/BluetoothHci.h b/bluetooth/aidl/default/BluetoothHci.h
new file mode 100644
index 0000000..0ed0623
--- /dev/null
+++ b/bluetooth/aidl/default/BluetoothHci.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 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/bluetooth/BnBluetoothHci.h>
+#include <aidl/android/hardware/bluetooth/IBluetoothHciCallbacks.h>
+#include <log/log.h>
+
+#include <string>
+
+#include "async_fd_watcher.h"
+#include "h4_protocol.h"
+
+namespace aidl::android::hardware::bluetooth::impl {
+
+class BluetoothDeathRecipient;
+
+// This Bluetooth HAL implementation connects with a serial port at dev_path_.
+class BluetoothHci : public BnBluetoothHci {
+ public:
+  BluetoothHci(const std::string& dev_path = "/dev/hvc5");
+
+  ndk::ScopedAStatus initialize(
+      const std::shared_ptr<IBluetoothHciCallbacks>& cb) override;
+
+  ndk::ScopedAStatus sendHciCommand(
+      const std::vector<uint8_t>& packet) override;
+
+  ndk::ScopedAStatus sendAclData(const std::vector<uint8_t>& packet) override;
+
+  ndk::ScopedAStatus sendScoData(const std::vector<uint8_t>& packet) override;
+
+  ndk::ScopedAStatus sendIsoData(const std::vector<uint8_t>& packet) override;
+
+  ndk::ScopedAStatus close() override;
+
+  static void OnPacketReady();
+
+  static BluetoothHci* get();
+
+ private:
+  int mFd{-1};
+  std::shared_ptr<IBluetoothHciCallbacks> mCb = nullptr;
+
+  std::shared_ptr<::android::hardware::bluetooth::hci::H4Protocol> mH4;
+
+  std::shared_ptr<BluetoothDeathRecipient> mDeathRecipient;
+
+  std::string mDevPath;
+
+  ::android::hardware::bluetooth::async::AsyncFdWatcher mFdWatcher;
+
+  void send(::android::hardware::bluetooth::hci::PacketType type,
+            const std::vector<uint8_t>& packet);
+};
+
+}  // namespace aidl::android::hardware::bluetooth::impl
diff --git a/bluetooth/aidl/default/bluetooth-service-default.rc b/bluetooth/aidl/default/bluetooth-service-default.rc
new file mode 100644
index 0000000..1841c77
--- /dev/null
+++ b/bluetooth/aidl/default/bluetooth-service-default.rc
@@ -0,0 +1,6 @@
+service bluetooth_hal_service /vendor/bin/hw/android.hardware.bluetooth-service.default
+    class hal
+    capabilities BLOCK_SUSPEND NET_ADMIN SYS_NICE
+    user bluetooth
+    group bluetooth
+    task_profiles HighPerformance
diff --git a/bluetooth/aidl/default/bluetooth-service-default.xml b/bluetooth/aidl/default/bluetooth-service-default.xml
new file mode 100644
index 0000000..bb05995
--- /dev/null
+++ b/bluetooth/aidl/default/bluetooth-service-default.xml
@@ -0,0 +1,6 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.bluetooth</name>
+        <fqname>IBluetoothHci/default</fqname>
+    </hal>
+</manifest>
diff --git a/bluetooth/aidl/default/service.cpp b/bluetooth/aidl/default/service.cpp
new file mode 100644
index 0000000..9af2a08
--- /dev/null
+++ b/bluetooth/aidl/default/service.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright 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 "aidl.android.hardware.bluetooth.service.default"
+
+#include <aidl/android/hardware/bluetooth/IBluetoothHci.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include "BluetoothHci.h"
+
+using ::aidl::android::hardware::bluetooth::impl::BluetoothHci;
+using ::android::hardware::configureRpcThreadpool;
+using ::android::hardware::joinRpcThreadpool;
+
+int main(int /* argc */, char** /* argv */) {
+  ALOGI("Bluetooth HAL starting");
+  if (!ABinderProcess_setThreadPoolMaxThreadCount(1)) {
+    ALOGI("failed to set thread pool max thread count");
+    return 1;
+  }
+
+  std::shared_ptr<BluetoothHci> service =
+      ndk::SharedRefBase::make<BluetoothHci>();
+  std::string instance = std::string() + BluetoothHci::descriptor + "/default";
+  auto result =
+      AServiceManager_addService(service->asBinder().get(), instance.c_str());
+  if (result == STATUS_OK) {
+    ABinderProcess_joinThreadPool();
+  } else {
+    ALOGE("Could not register as a service!");
+  }
+  return 0;
+}
diff --git a/bluetooth/aidl/default/test/fuzzer.cpp b/bluetooth/aidl/default/test/fuzzer.cpp
new file mode 100644
index 0000000..e7a1eef
--- /dev/null
+++ b/bluetooth/aidl/default/test/fuzzer.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright 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 <fuzzbinder/libbinder_ndk_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include "BluetoothHci.h"
+
+using aidl::android::hardware::bluetooth::impl::BluetoothHci;
+using android::fuzzService;
+using ndk::SharedRefBase;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  auto service = SharedRefBase::make<BluetoothHci>();
+
+  fuzzService(service->asBinder().get(), FuzzedDataProvider(data, size));
+
+  return 0;
+}
diff --git a/bluetooth/async/Android.bp b/bluetooth/async/Android.bp
new file mode 100644
index 0000000..bd7d7b8
--- /dev/null
+++ b/bluetooth/async/Android.bp
@@ -0,0 +1,41 @@
+package {
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library_static {
+    name: "android.hardware.bluetooth.async",
+    vendor_available: true,
+    host_supported: true,
+    srcs: [
+        "async_fd_watcher.cc",
+    ],
+    export_include_dirs: ["."],
+    shared_libs: [
+        "liblog",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
+
+cc_test {
+    name: "bluetooth-vendor-interface-async-test",
+    host_supported: true,
+    srcs: [
+        "test/async_fd_watcher_unittest.cc",
+    ],
+    shared_libs: [
+        "liblog",
+        "libutils",
+    ],
+    static_libs: [
+        "android.hardware.bluetooth.async",
+        "libgmock",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    test_suites: ["general-tests"],
+}
diff --git a/bluetooth/async/async_fd_watcher.cc b/bluetooth/async/async_fd_watcher.cc
new file mode 100644
index 0000000..fb634ff
--- /dev/null
+++ b/bluetooth/async/async_fd_watcher.cc
@@ -0,0 +1,176 @@
+/*
+ * Copyright 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 "async_fd_watcher.h"
+
+#include <string.h>
+
+#include <algorithm>
+#include <atomic>
+#include <condition_variable>
+#include <map>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include "fcntl.h"
+#include "log/log.h"
+#include "sys/select.h"
+#include "unistd.h"
+
+static const int INVALID_FD = -1;
+
+namespace android::hardware::bluetooth::async {
+
+int AsyncFdWatcher::WatchFdForNonBlockingReads(
+    int file_descriptor, const ReadCallback& on_read_fd_ready_callback) {
+  // Add file descriptor and callback
+  {
+    std::unique_lock<std::mutex> guard(internal_mutex_);
+    watched_fds_[file_descriptor] = on_read_fd_ready_callback;
+  }
+
+  // Start the thread if not started yet
+  return tryStartThread();
+}
+
+int AsyncFdWatcher::ConfigureTimeout(
+    const std::chrono::milliseconds timeout,
+    const TimeoutCallback& on_timeout_callback) {
+  // Add timeout and callback
+  {
+    std::unique_lock<std::mutex> guard(timeout_mutex_);
+    timeout_cb_ = on_timeout_callback;
+    timeout_ms_ = timeout;
+  }
+
+  notifyThread();
+  return 0;
+}
+
+void AsyncFdWatcher::StopWatchingFileDescriptors() { stopThread(); }
+
+AsyncFdWatcher::~AsyncFdWatcher() {}
+
+// Make sure to call this with at least one file descriptor ready to be
+// watched upon or the thread routine will return immediately
+int AsyncFdWatcher::tryStartThread() {
+  if (std::atomic_exchange(&running_, true)) return 0;
+
+  // Set up the communication channel
+  int pipe_fds[2];
+  if (pipe2(pipe_fds, O_NONBLOCK)) return -1;
+
+  notification_listen_fd_ = pipe_fds[0];
+  notification_write_fd_ = pipe_fds[1];
+
+  thread_ = std::thread([this]() { ThreadRoutine(); });
+  if (!thread_.joinable()) return -1;
+
+  return 0;
+}
+
+int AsyncFdWatcher::stopThread() {
+  if (!std::atomic_exchange(&running_, false)) return 0;
+
+  notifyThread();
+  if (std::this_thread::get_id() != thread_.get_id()) {
+    thread_.join();
+  }
+
+  {
+    std::unique_lock<std::mutex> guard(internal_mutex_);
+    watched_fds_.clear();
+  }
+
+  {
+    std::unique_lock<std::mutex> guard(timeout_mutex_);
+    timeout_cb_ = nullptr;
+  }
+
+  close(notification_listen_fd_);
+  close(notification_write_fd_);
+
+  return 0;
+}
+
+int AsyncFdWatcher::notifyThread() {
+  uint8_t buffer[] = {0};
+  if (TEMP_FAILURE_RETRY(write(notification_write_fd_, &buffer, 1)) < 0) {
+    return -1;
+  }
+  return 0;
+}
+
+void AsyncFdWatcher::ThreadRoutine() {
+  while (running_) {
+    fd_set read_fds;
+    FD_ZERO(&read_fds);
+    FD_SET(notification_listen_fd_, &read_fds);
+    int max_read_fd = INVALID_FD;
+    for (auto& it : watched_fds_) {
+      FD_SET(it.first, &read_fds);
+      max_read_fd = std::max(max_read_fd, it.first);
+    }
+
+    struct timeval timeout;
+    struct timeval* timeout_ptr = NULL;
+    if (timeout_ms_ > std::chrono::milliseconds(0)) {
+      timeout.tv_sec = timeout_ms_.count() / 1000;
+      timeout.tv_usec = (timeout_ms_.count() % 1000) * 1000;
+      timeout_ptr = &timeout;
+    }
+
+    // Wait until there is data available to read on some FD.
+    int nfds = std::max(notification_listen_fd_, max_read_fd);
+    int retval = select(nfds + 1, &read_fds, NULL, NULL, timeout_ptr);
+
+    // There was some error.
+    if (retval < 0) continue;
+
+    // Timeout.
+    if (retval == 0) {
+      // Allow the timeout callback to modify the timeout.
+      TimeoutCallback saved_cb;
+      {
+        std::unique_lock<std::mutex> guard(timeout_mutex_);
+        if (timeout_ms_ > std::chrono::milliseconds(0)) saved_cb = timeout_cb_;
+      }
+      if (saved_cb != nullptr) saved_cb();
+      continue;
+    }
+
+    // Read data from the notification FD.
+    if (FD_ISSET(notification_listen_fd_, &read_fds)) {
+      char buffer[] = {0};
+      TEMP_FAILURE_RETRY(read(notification_listen_fd_, buffer, 1));
+      continue;
+    }
+
+    // Invoke the data ready callbacks if appropriate.
+    {
+      // Hold the mutex to make sure that the callbacks are still valid.
+      std::unique_lock<std::mutex> guard(internal_mutex_);
+      for (auto& it : watched_fds_) {
+        if (FD_ISSET(it.first, &read_fds)) {
+          it.second(it.first);
+        }
+      }
+    }
+  }
+}
+
+}  // namespace android::hardware::bluetooth::async
diff --git a/bluetooth/async/async_fd_watcher.h b/bluetooth/async/async_fd_watcher.h
new file mode 100644
index 0000000..b50a7a0
--- /dev/null
+++ b/bluetooth/async/async_fd_watcher.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 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 <map>
+#include <mutex>
+#include <thread>
+
+namespace android::hardware::bluetooth::async {
+
+using ReadCallback = std::function<void(int)>;
+using TimeoutCallback = std::function<void(void)>;
+
+class AsyncFdWatcher {
+ public:
+  AsyncFdWatcher() = default;
+  ~AsyncFdWatcher();
+
+  int WatchFdForNonBlockingReads(int file_descriptor,
+                                 const ReadCallback& on_read_fd_ready_callback);
+  int ConfigureTimeout(const std::chrono::milliseconds timeout,
+                       const TimeoutCallback& on_timeout_callback);
+  void StopWatchingFileDescriptors();
+
+ private:
+  AsyncFdWatcher(const AsyncFdWatcher&) = delete;
+  AsyncFdWatcher& operator=(const AsyncFdWatcher&) = delete;
+
+  int tryStartThread();
+  int stopThread();
+  int notifyThread();
+  void ThreadRoutine();
+
+  std::atomic_bool running_{false};
+  std::thread thread_;
+  std::mutex internal_mutex_;
+  std::mutex timeout_mutex_;
+
+  std::map<int, ReadCallback> watched_fds_;
+  int notification_listen_fd_;
+  int notification_write_fd_;
+  TimeoutCallback timeout_cb_;
+  std::chrono::milliseconds timeout_ms_;
+};
+
+}  // namespace android::hardware::bluetooth::async
diff --git a/bluetooth/async/test/async_fd_watcher_unittest.cc b/bluetooth/async/test/async_fd_watcher_unittest.cc
new file mode 100644
index 0000000..6d75f10
--- /dev/null
+++ b/bluetooth/async/test/async_fd_watcher_unittest.cc
@@ -0,0 +1,377 @@
+/*
+ * Copyright 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 "async_fd_watcher_unittest"
+
+#include "async_fd_watcher.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cstdint>
+#include <cstring>
+#include <vector>
+
+namespace android::hardware::bluetooth::async_test {
+
+using android::hardware::bluetooth::async::AsyncFdWatcher;
+
+class AsyncFdWatcherSocketTest : public ::testing::Test {
+ public:
+  static const uint16_t kPort = 6111;
+  static const size_t kBufferSize = 16;
+
+  bool CheckBufferEquals() {
+    return strcmp(server_buffer_, client_buffer_) == 0;
+  }
+
+ protected:
+  int StartServer() {
+    ALOGD("%s", __func__);
+    struct sockaddr_in serv_addr;
+    int fd = socket(AF_INET, SOCK_STREAM, 0);
+    EXPECT_FALSE(fd < 0);
+
+    memset(&serv_addr, 0, sizeof(serv_addr));
+    serv_addr.sin_family = AF_INET;
+    serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+    serv_addr.sin_port = htons(kPort);
+    int reuse_flag = 1;
+    EXPECT_FALSE(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse_flag,
+                            sizeof(reuse_flag)) < 0);
+    EXPECT_FALSE(bind(fd, (sockaddr*)&serv_addr, sizeof(serv_addr)) < 0);
+
+    ALOGD("%s before listen", __func__);
+    listen(fd, 1);
+    return fd;
+  }
+
+  int AcceptConnection(int fd) {
+    ALOGD("%s", __func__);
+    struct sockaddr_in cli_addr;
+    memset(&cli_addr, 0, sizeof(cli_addr));
+    socklen_t clilen = sizeof(cli_addr);
+
+    int connection_fd = accept(fd, (struct sockaddr*)&cli_addr, &clilen);
+    EXPECT_FALSE(connection_fd < 0);
+
+    return connection_fd;
+  }
+
+  void ReadIncomingMessage(int fd) {
+    ALOGD("%s", __func__);
+    int n = TEMP_FAILURE_RETRY(read(fd, server_buffer_, kBufferSize - 1));
+    EXPECT_FALSE(n < 0);
+
+    if (n == 0) {  // got EOF
+      ALOGD("%s: EOF", __func__);
+    } else {
+      ALOGD("%s: Got something", __func__);
+      n = write(fd, "1", 1);
+    }
+  }
+
+  void SetUp() override {
+    ALOGD("%s", __func__);
+    memset(server_buffer_, 0, kBufferSize);
+    memset(client_buffer_, 0, kBufferSize);
+  }
+
+  void ConfigureServer() {
+    socket_fd_ = StartServer();
+
+    conn_watcher_.WatchFdForNonBlockingReads(socket_fd_, [this](int fd) {
+      int connection_fd = AcceptConnection(fd);
+      ALOGD("%s: Conn_watcher fd = %d", __func__, fd);
+
+      conn_watcher_.ConfigureTimeout(std::chrono::seconds(0), []() {
+        bool connection_timeout_cleared = false;
+        ASSERT_TRUE(connection_timeout_cleared);
+      });
+
+      ALOGD("%s: 3", __func__);
+      async_fd_watcher_.WatchFdForNonBlockingReads(
+          connection_fd, [this](int fd) { ReadIncomingMessage(fd); });
+
+      // Time out if it takes longer than a second.
+      SetTimeout(std::chrono::seconds(1));
+    });
+    conn_watcher_.ConfigureTimeout(std::chrono::seconds(1), []() {
+      bool connection_timeout = true;
+      ASSERT_FALSE(connection_timeout);
+    });
+  }
+
+  void CleanUpServer() {
+    async_fd_watcher_.StopWatchingFileDescriptors();
+    conn_watcher_.StopWatchingFileDescriptors();
+    close(socket_fd_);
+  }
+
+  void TearDown() override {
+    ALOGD("%s 3", __func__);
+    EXPECT_TRUE(CheckBufferEquals());
+  }
+
+  void OnTimeout() {
+    ALOGD("%s", __func__);
+    timed_out_ = true;
+  }
+
+  void ClearTimeout() {
+    ALOGD("%s", __func__);
+    timed_out_ = false;
+  }
+
+  bool TimedOut() {
+    ALOGD("%s %d", __func__, timed_out_ ? 1 : 0);
+    return timed_out_;
+  }
+
+  void SetTimeout(std::chrono::milliseconds timeout_ms) {
+    ALOGD("%s", __func__);
+    async_fd_watcher_.ConfigureTimeout(timeout_ms, [this]() { OnTimeout(); });
+    ClearTimeout();
+  }
+
+  int ConnectClient() {
+    ALOGD("%s", __func__);
+    int socket_cli_fd = socket(AF_INET, SOCK_STREAM, 0);
+    EXPECT_FALSE(socket_cli_fd < 0);
+
+    struct sockaddr_in serv_addr;
+    memset((void*)&serv_addr, 0, sizeof(serv_addr));
+    serv_addr.sin_family = AF_INET;
+    serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+    serv_addr.sin_port = htons(kPort);
+
+    int result =
+        connect(socket_cli_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
+    EXPECT_FALSE(result < 0);
+
+    return socket_cli_fd;
+  }
+
+  void WriteFromClient(int socket_cli_fd) {
+    ALOGD("%s", __func__);
+    strcpy(client_buffer_, "1");
+    int n = write(socket_cli_fd, client_buffer_, strlen(client_buffer_));
+    EXPECT_TRUE(n > 0);
+  }
+
+  void AwaitServerResponse(int socket_cli_fd) {
+    ALOGD("%s", __func__);
+    int n = read(socket_cli_fd, client_buffer_, 1);
+    ALOGD("%s done", __func__);
+    EXPECT_TRUE(n > 0);
+  }
+
+ private:
+  AsyncFdWatcher async_fd_watcher_;
+  AsyncFdWatcher conn_watcher_;
+  int socket_fd_;
+  char server_buffer_[kBufferSize];
+  char client_buffer_[kBufferSize];
+  bool timed_out_;
+};
+
+// Use a single AsyncFdWatcher to signal a connection to the server socket.
+TEST_F(AsyncFdWatcherSocketTest, Connect) {
+  int socket_fd = StartServer();
+
+  AsyncFdWatcher conn_watcher;
+  conn_watcher.WatchFdForNonBlockingReads(socket_fd, [this](int fd) {
+    int connection_fd = AcceptConnection(fd);
+    close(connection_fd);
+  });
+
+  // Fail if the client doesn't connect within 1 second.
+  conn_watcher.ConfigureTimeout(std::chrono::seconds(1), []() {
+    bool connection_timeout = true;
+    ASSERT_FALSE(connection_timeout);
+  });
+
+  int socket_cli_fd = ConnectClient();
+  conn_watcher.StopWatchingFileDescriptors();
+  close(socket_fd);
+  close(socket_cli_fd);
+}
+
+// Use a single AsyncFdWatcher to signal a connection to the server socket.
+TEST_F(AsyncFdWatcherSocketTest, TimedOutConnect) {
+  int socket_fd = StartServer();
+  bool timed_out = false;
+  bool* timeout_ptr = &timed_out;
+
+  AsyncFdWatcher conn_watcher;
+  conn_watcher.WatchFdForNonBlockingReads(socket_fd, [this](int fd) {
+    int connection_fd = AcceptConnection(fd);
+    close(connection_fd);
+  });
+
+  // Set the timeout flag after 100ms.
+  conn_watcher.ConfigureTimeout(std::chrono::milliseconds(100),
+                                [timeout_ptr]() { *timeout_ptr = true; });
+  EXPECT_FALSE(timed_out);
+  sleep(1);
+  EXPECT_TRUE(timed_out);
+  conn_watcher.StopWatchingFileDescriptors();
+  close(socket_fd);
+}
+
+// Modify the timeout in a timeout callback.
+TEST_F(AsyncFdWatcherSocketTest, TimedOutSchedulesTimeout) {
+  int socket_fd = StartServer();
+  bool timed_out = false;
+  bool timed_out2 = false;
+
+  AsyncFdWatcher conn_watcher;
+  conn_watcher.WatchFdForNonBlockingReads(socket_fd, [this](int fd) {
+    int connection_fd = AcceptConnection(fd);
+    close(connection_fd);
+  });
+
+  // Set a timeout flag in each callback.
+  conn_watcher.ConfigureTimeout(std::chrono::milliseconds(500),
+                                [&conn_watcher, &timed_out, &timed_out2]() {
+                                  timed_out = true;
+                                  conn_watcher.ConfigureTimeout(
+                                      std::chrono::seconds(1),
+                                      [&timed_out2]() { timed_out2 = true; });
+                                });
+  EXPECT_FALSE(timed_out);
+  EXPECT_FALSE(timed_out2);
+  sleep(1);
+  EXPECT_TRUE(timed_out);
+  EXPECT_FALSE(timed_out2);
+  sleep(1);
+  EXPECT_TRUE(timed_out);
+  EXPECT_TRUE(timed_out2);
+  conn_watcher.StopWatchingFileDescriptors();
+  close(socket_fd);
+}
+
+MATCHER_P(ReadAndMatchSingleChar, byte,
+          "Reads a byte from the file descriptor and matches the value against "
+          "byte") {
+  char inbuf[1] = {0};
+
+  int n = TEMP_FAILURE_RETRY(read(arg, inbuf, 1));
+
+  TEMP_FAILURE_RETRY(write(arg, inbuf, 1));
+  if (n != 1) {
+    return false;
+  }
+  return inbuf[0] == byte;
+};
+
+// Use a single AsyncFdWatcher to watch two file descriptors.
+TEST_F(AsyncFdWatcherSocketTest, WatchTwoFileDescriptors) {
+  int sockfd1[2];
+  int sockfd2[2];
+  socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd1);
+  socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd2);
+
+  testing::MockFunction<void(int)> cb1;
+  testing::MockFunction<void(int)> cb2;
+
+  AsyncFdWatcher watcher;
+  watcher.WatchFdForNonBlockingReads(sockfd1[0], cb1.AsStdFunction());
+
+  watcher.WatchFdForNonBlockingReads(sockfd2[0], cb2.AsStdFunction());
+
+  EXPECT_CALL(cb1, Call(ReadAndMatchSingleChar('1')));
+  char one_buf[1] = {'1'};
+  TEMP_FAILURE_RETRY(write(sockfd1[1], one_buf, sizeof(one_buf)));
+
+  EXPECT_CALL(cb2, Call(ReadAndMatchSingleChar('2')));
+  char two_buf[1] = {'2'};
+  TEMP_FAILURE_RETRY(write(sockfd2[1], two_buf, sizeof(two_buf)));
+
+  // Blocking read instead of a flush.
+  TEMP_FAILURE_RETRY(read(sockfd1[1], one_buf, sizeof(one_buf)));
+  TEMP_FAILURE_RETRY(read(sockfd2[1], two_buf, sizeof(two_buf)));
+
+  watcher.StopWatchingFileDescriptors();
+}
+
+// Use two AsyncFdWatchers to set up a server socket.
+TEST_F(AsyncFdWatcherSocketTest, ClientServer) {
+  ConfigureServer();
+  int socket_cli_fd = ConnectClient();
+
+  WriteFromClient(socket_cli_fd);
+
+  AwaitServerResponse(socket_cli_fd);
+
+  close(socket_cli_fd);
+  CleanUpServer();
+}
+
+// Use two AsyncFdWatchers to set up a server socket, which times out.
+TEST_F(AsyncFdWatcherSocketTest, TimeOutTest) {
+  ConfigureServer();
+  int socket_cli_fd = ConnectClient();
+
+  while (!TimedOut()) sleep(1);
+
+  close(socket_cli_fd);
+  CleanUpServer();
+}
+
+// Use two AsyncFdWatchers to set up a server socket, which times out.
+TEST_F(AsyncFdWatcherSocketTest, RepeatedTimeOutTest) {
+  ConfigureServer();
+  int socket_cli_fd = ConnectClient();
+  ClearTimeout();
+
+  // Time out when there are no writes.
+  EXPECT_FALSE(TimedOut());
+  sleep(2);
+  EXPECT_TRUE(TimedOut());
+  ClearTimeout();
+
+  // Don't time out when there is a write.
+  WriteFromClient(socket_cli_fd);
+  AwaitServerResponse(socket_cli_fd);
+  EXPECT_FALSE(TimedOut());
+  ClearTimeout();
+
+  // Time out when the write is late.
+  sleep(2);
+  WriteFromClient(socket_cli_fd);
+  AwaitServerResponse(socket_cli_fd);
+  EXPECT_TRUE(TimedOut());
+  ClearTimeout();
+
+  // Time out when there is a pause after a write.
+  WriteFromClient(socket_cli_fd);
+  sleep(2);
+  AwaitServerResponse(socket_cli_fd);
+  EXPECT_TRUE(TimedOut());
+  ClearTimeout();
+
+  close(socket_cli_fd);
+  CleanUpServer();
+}
+
+}  // namespace android::hardware::bluetooth::async_test
diff --git a/bluetooth/hci/Android.bp b/bluetooth/hci/Android.bp
new file mode 100644
index 0000000..f0f6e8f
--- /dev/null
+++ b/bluetooth/hci/Android.bp
@@ -0,0 +1,46 @@
+package {
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library_static {
+    name: "android.hardware.bluetooth.hci",
+    vendor_available: true,
+    host_supported: true,
+    defaults: ["hidl_defaults"],
+    srcs: [
+        "hci_packetizer.cc",
+        "h4_protocol.cc",
+    ],
+    export_include_dirs: ["."],
+    shared_libs: [
+        "libbase",
+        "libhidlbase",
+        "liblog",
+        "libutils",
+    ],
+}
+
+cc_test {
+    name: "bluetooth-vendor-interface-hci-test",
+    host_supported: true,
+    defaults: ["hidl_defaults"],
+    srcs: [
+        "test/h4_protocol_unittest.cc",
+    ],
+    shared_libs: [
+        "libbase",
+        "libhidlbase",
+        "liblog",
+        "libutils",
+    ],
+    static_libs: [
+        "android.hardware.bluetooth.async",
+        "android.hardware.bluetooth.hci",
+        "libgmock",
+    ],
+    sanitize: {
+        address: true,
+        cfi: true,
+    },
+    test_suites: ["general-tests"],
+}
diff --git a/bluetooth/hci/h4_protocol.cc b/bluetooth/hci/h4_protocol.cc
new file mode 100644
index 0000000..97ba7aa
--- /dev/null
+++ b/bluetooth/hci/h4_protocol.cc
@@ -0,0 +1,144 @@
+/*
+ * Copyright 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 "h4_protocol.h"
+
+#define LOG_TAG "android.hardware.bluetooth.hci-h4"
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/uio.h>
+
+#include "log/log.h"
+
+namespace android::hardware::bluetooth::hci {
+
+H4Protocol::H4Protocol(int fd, PacketReadCallback cmd_cb,
+                       PacketReadCallback acl_cb, PacketReadCallback sco_cb,
+                       PacketReadCallback event_cb, PacketReadCallback iso_cb,
+                       DisconnectCallback disconnect_cb)
+    : uart_fd_(fd),
+      cmd_cb_(std::move(cmd_cb)),
+      acl_cb_(std::move(acl_cb)),
+      sco_cb_(std::move(sco_cb)),
+      event_cb_(std::move(event_cb)),
+      iso_cb_(std::move(iso_cb)),
+      disconnect_cb_(std::move(disconnect_cb)) {}
+
+size_t H4Protocol::Send(PacketType type, const std::vector<uint8_t>& vector) {
+  return Send(type, vector.data(), vector.size());
+}
+
+size_t H4Protocol::Send(PacketType type, const uint8_t* data, size_t length) {
+  /* For HCI communication over USB dongle, multiple write results in
+   * response timeout as driver expect type + data at once to process
+   * the command, so using "writev"(for atomicity) here.
+   */
+  struct iovec iov[2];
+  ssize_t ret = 0;
+  iov[0].iov_base = &type;
+  iov[0].iov_len = sizeof(type);
+  iov[1].iov_base = (void*)data;
+  iov[1].iov_len = length;
+  while (1) {
+    ret = TEMP_FAILURE_RETRY(writev(uart_fd_, iov, 2));
+    if (ret == -1) {
+      if (errno == EAGAIN) {
+        ALOGE("%s error writing to UART (%s)", __func__, strerror(errno));
+        continue;
+      }
+    } else if (ret == 0) {
+      // Nothing written :(
+      ALOGE("%s zero bytes written - something went wrong...", __func__);
+      break;
+    }
+    break;
+  }
+  return ret;
+}
+
+size_t H4Protocol::OnPacketReady(const std::vector<uint8_t>& packet) {
+  switch (hci_packet_type_) {
+    case PacketType::COMMAND:
+      cmd_cb_(packet);
+      break;
+    case PacketType::ACL_DATA:
+      acl_cb_(packet);
+      break;
+    case PacketType::SCO_DATA:
+      sco_cb_(packet);
+      break;
+    case PacketType::EVENT:
+      event_cb_(packet);
+      break;
+    case PacketType::ISO_DATA:
+      iso_cb_(packet);
+      break;
+    default: {
+      LOG_ALWAYS_FATAL("Bad packet type 0x%x",
+                       static_cast<int>(hci_packet_type_));
+    }
+  }
+  return packet.size();
+}
+
+void H4Protocol::SendDataToPacketizer(uint8_t* buffer, size_t length) {
+  std::vector<uint8_t> input_buffer{buffer, buffer + length};
+  size_t buffer_offset = 0;
+  while (buffer_offset < input_buffer.size()) {
+    if (hci_packet_type_ == PacketType::UNKNOWN) {
+      hci_packet_type_ =
+          static_cast<PacketType>(input_buffer.data()[buffer_offset]);
+      buffer_offset += 1;
+    } else {
+      bool packet_ready = hci_packetizer_.OnDataReady(
+          hci_packet_type_, input_buffer, buffer_offset);
+      if (packet_ready) {
+        // Call packet callback and move offset.
+        buffer_offset += OnPacketReady(hci_packetizer_.GetPacket());
+        // Get ready for the next type byte.
+        hci_packet_type_ = PacketType::UNKNOWN;
+      } else {
+        // The data was consumed, but there wasn't a packet.
+        buffer_offset = input_buffer.size();
+      }
+    }
+  }
+}
+
+void H4Protocol::OnDataReady() {
+  if (disconnected_) {
+    return;
+  }
+  uint8_t buffer[kMaxPacketLength];
+  ssize_t bytes_read =
+      TEMP_FAILURE_RETRY(read(uart_fd_, buffer, kMaxPacketLength));
+  if (bytes_read == 0) {
+    ALOGI("No bytes read, calling the disconnect callback");
+    disconnected_ = true;
+    disconnect_cb_();
+    return;
+  }
+  if (bytes_read < 0) {
+    ALOGW("error reading from UART (%s)", strerror(errno));
+    return;
+  }
+  SendDataToPacketizer(buffer, bytes_read);
+}
+
+}  // namespace android::hardware::bluetooth::hci
diff --git a/bluetooth/hci/h4_protocol.h b/bluetooth/hci/h4_protocol.h
new file mode 100644
index 0000000..5daee83
--- /dev/null
+++ b/bluetooth/hci/h4_protocol.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 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 <memory>
+#include <vector>
+
+#include "hci_internals.h"
+#include "hci_packetizer.h"
+
+namespace android::hardware::bluetooth::hci {
+
+using PacketReadCallback = std::function<void(const std::vector<uint8_t>&)>;
+using DisconnectCallback = std::function<void(void)>;
+
+class H4Protocol {
+ public:
+  H4Protocol(int fd, PacketReadCallback cmd_cb, PacketReadCallback acl_cb,
+             PacketReadCallback sco_cb, PacketReadCallback event_cb,
+             PacketReadCallback iso_cb, DisconnectCallback disconnect_cb);
+
+  size_t Send(PacketType type, const uint8_t* data, size_t length);
+  size_t Send(PacketType type, const std::vector<uint8_t>& data);
+
+  void OnDataReady();
+
+ protected:
+  size_t OnPacketReady(const std::vector<uint8_t>& packet);
+  void SendDataToPacketizer(uint8_t* buffer, size_t length);
+
+ private:
+  int uart_fd_;
+  bool disconnected_{false};
+
+  PacketReadCallback cmd_cb_;
+  PacketReadCallback acl_cb_;
+  PacketReadCallback sco_cb_;
+  PacketReadCallback event_cb_;
+  PacketReadCallback iso_cb_;
+  DisconnectCallback disconnect_cb_;
+
+  PacketType hci_packet_type_{PacketType::UNKNOWN};
+  HciPacketizer hci_packetizer_;
+
+  /**
+   * Question : Why read in single chunk rather than multiple reads?
+   * Answer: Using multiple reads does not work with some BT USB dongles.
+   * Reading in single shot gives expected response.
+   * ACL max length is 2 bytes, so using 64K as the buffer length.
+   */
+  static constexpr size_t kMaxPacketLength = 64 * 1024;
+};
+
+}  // namespace android::hardware::bluetooth::hci
diff --git a/bluetooth/hci/hci_internals.h b/bluetooth/hci/hci_internals.h
new file mode 100644
index 0000000..71b6191
--- /dev/null
+++ b/bluetooth/hci/hci_internals.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 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 <cstdlib>
+
+namespace android::hardware::bluetooth::hci {
+
+// HCI UART transport packet types (Volume 4, Part A, 2)
+enum class PacketType : uint8_t {
+  UNKNOWN = 0,
+  COMMAND = 1,
+  ACL_DATA = 2,
+  SCO_DATA = 3,
+  EVENT = 4,
+  ISO_DATA = 5,
+};
+
+// 2 bytes for opcode, 1 byte for parameter length (Volume 4, Part E, 5.4.1)
+static constexpr size_t kCommandHeaderSize = 3;
+static constexpr size_t kCommandLengthOffset = 2;
+
+// 2 bytes for handle, 2 bytes for data length (Volume 4, Part E, 5.4.2)
+static constexpr size_t kAclHeaderSize = 4;
+static constexpr size_t kAclLengthOffset = 2;
+
+// 2 bytes for handle, 1 byte for data length (Volume 4, Part E, 5.4.3)
+static constexpr size_t kScoHeaderSize = 3;
+static constexpr size_t kScoLengthOffset = 2;
+
+// 1 byte for event code, 1 byte for parameter length (Volume 4, Part E, 5.4.4)
+static constexpr size_t kEventHeaderSize = 2;
+static constexpr size_t kEventLengthOffset = 1;
+
+// 2 bytes for handle, 2 bytes for data length (Volume 4, Part E, 5.4.5)
+static constexpr size_t kIsoHeaderSize = 4;
+static constexpr size_t kIsoLengthOffset = 2;
+
+static constexpr size_t kMaxHeaderSize = kAclHeaderSize;
+
+}  // namespace android::hardware::bluetooth::hci
diff --git a/bluetooth/hci/hci_packetizer.cc b/bluetooth/hci/hci_packetizer.cc
new file mode 100644
index 0000000..5b6c443
--- /dev/null
+++ b/bluetooth/hci/hci_packetizer.cc
@@ -0,0 +1,100 @@
+/*
+ * Copyright 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 "hci_packetizer.h"
+
+#define LOG_TAG "android.hardware.bluetooth.hci-packetizer"
+#include "log/log.h"
+
+namespace android::hardware::bluetooth::hci {
+
+namespace {
+
+const size_t header_size_for_type[] = {0,
+                                       kCommandHeaderSize,
+                                       kAclHeaderSize,
+                                       kScoHeaderSize,
+                                       kEventHeaderSize,
+                                       kIsoHeaderSize};
+const size_t packet_length_offset_for_type[] = {0,
+                                                kCommandLengthOffset,
+                                                kAclLengthOffset,
+                                                kScoLengthOffset,
+                                                kEventLengthOffset,
+                                                kIsoLengthOffset};
+
+size_t HciGetPacketLengthForType(PacketType type,
+                                 const std::vector<uint8_t>& header) {
+  size_t offset = packet_length_offset_for_type[static_cast<uint8_t>(type)];
+  if (type != PacketType::ACL_DATA && type != PacketType::ISO_DATA) {
+    return header[offset];
+  }
+  return (((header[offset + 1]) << 8) | header[offset]);
+}
+
+}  // namespace
+
+const std::vector<uint8_t>& HciPacketizer::GetPacket() const { return packet_; }
+
+bool HciPacketizer::OnDataReady(PacketType packet_type,
+                                const std::vector<uint8_t>& buffer,
+                                size_t offset) {
+  bool packet_completed = false;
+  size_t bytes_available = buffer.size() - offset;
+  switch (state_) {
+    case HCI_HEADER: {
+      size_t header_size =
+          header_size_for_type[static_cast<size_t>(packet_type)];
+      if (bytes_remaining_ == 0) {
+        bytes_remaining_ = header_size;
+        packet_.clear();
+      }
+      size_t bytes_to_copy = std::min(bytes_remaining_, bytes_available);
+      packet_.insert(packet_.end(), buffer.begin() + offset,
+                     buffer.begin() + offset + bytes_to_copy);
+      bytes_remaining_ -= bytes_to_copy;
+      bytes_available -= bytes_to_copy;
+      if (bytes_remaining_ == 0) {
+        bytes_remaining_ = HciGetPacketLengthForType(packet_type, packet_);
+        if (bytes_remaining_ > 0) {
+          state_ = HCI_PAYLOAD;
+          if (bytes_available > 0) {
+            packet_completed =
+                OnDataReady(packet_type, buffer, offset + bytes_to_copy);
+          }
+        } else {
+          packet_completed = true;
+        }
+      }
+      break;
+    }
+
+    case HCI_PAYLOAD: {
+      size_t bytes_to_copy = std::min(bytes_remaining_, bytes_available);
+      packet_.insert(packet_.end(), buffer.begin() + offset,
+                     buffer.begin() + offset + bytes_to_copy);
+      bytes_remaining_ -= bytes_to_copy;
+      if (bytes_remaining_ == 0) {
+        state_ = HCI_HEADER;
+        packet_completed = true;
+      }
+      break;
+    }
+  }
+  return packet_completed;
+}
+
+}  // namespace android::hardware::bluetooth::hci
diff --git a/bluetooth/hci/hci_packetizer.h b/bluetooth/hci/hci_packetizer.h
new file mode 100644
index 0000000..ba3e841
--- /dev/null
+++ b/bluetooth/hci/hci_packetizer.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 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 <functional>
+#include <memory>
+#include <vector>
+
+#include "hci_internals.h"
+
+namespace android::hardware::bluetooth::hci {
+
+class HciPacketizer {
+ public:
+  HciPacketizer() = default;
+  bool OnDataReady(PacketType packet_type, const std::vector<uint8_t>& data,
+                   size_t offset);
+  const std::vector<uint8_t>& GetPacket() const;
+
+ protected:
+  enum State { HCI_HEADER, HCI_PAYLOAD };
+  State state_{HCI_HEADER};
+  std::vector<uint8_t> packet_;
+  size_t bytes_remaining_{0};
+};
+
+}  // namespace android::hardware::bluetooth::hci
diff --git a/bluetooth/hci/test/h4_protocol_unittest.cc b/bluetooth/hci/test/h4_protocol_unittest.cc
new file mode 100644
index 0000000..d6f74fc
--- /dev/null
+++ b/bluetooth/hci/test/h4_protocol_unittest.cc
@@ -0,0 +1,441 @@
+/*
+ * Copyright 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 "bt_h4_unittest"
+
+#include "h4_protocol.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cstdint>
+#include <cstring>
+#include <future>
+#include <vector>
+
+#include "async_fd_watcher.h"
+#include "log/log.h"
+
+using android::hardware::bluetooth::async::AsyncFdWatcher;
+using namespace android::hardware::bluetooth::hci;
+using ::testing::Eq;
+
+static char sample_data1[100] = "A point is that which has no part.";
+static char sample_data2[100] = "A line is breadthless length.";
+static char sample_data3[100] = "The ends of a line are points.";
+static char sample_data4[100] =
+    "A plane surface is a surface which lies evenly with the straight ...";
+static char acl_data[100] =
+    "A straight line is a line which lies evenly with the points on itself.";
+static char sco_data[100] =
+    "A surface is that which has length and breadth only.";
+static char event_data[100] = "The edges of a surface are lines.";
+static char iso_data[100] =
+    "A plane angle is the inclination to one another of two lines in a ...";
+
+MATCHER_P3(PacketMatches, header_, header_length, payload,
+           "Match header_length bytes of header and then the payload") {
+  size_t payload_length = strlen(payload);
+  if (header_length + payload_length != arg.size()) {
+    return false;
+  }
+
+  if (memcmp(header_, arg.data(), header_length) != 0) {
+    return false;
+  }
+
+  return memcmp(payload, arg.data() + header_length, payload_length) == 0;
+};
+
+ACTION_P(Notify, barrier) {
+  ALOGD("%s", __func__);
+  barrier->set_value();
+}
+
+class H4ProtocolTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ALOGD("%s", __func__);
+
+    int sockfd[2];
+    socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd);
+    chip_uart_fd_ = sockfd[1];
+    stack_uart_fd_ = sockfd[0];
+    h4_hci_ = std::make_shared<H4Protocol>(
+        stack_uart_fd_, cmd_cb_.AsStdFunction(), acl_cb_.AsStdFunction(),
+        sco_cb_.AsStdFunction(), event_cb_.AsStdFunction(),
+        iso_cb_.AsStdFunction(), disconnect_cb_.AsStdFunction());
+  }
+
+  void TearDown() override {
+    close(stack_uart_fd_);
+    close(chip_uart_fd_);
+  }
+
+  virtual void CallDataReady() { h4_hci_->OnDataReady(); }
+
+  void SendAndReadUartOutbound(PacketType type, char* data) {
+    ALOGD("%s sending", __func__);
+    int data_length = strlen(data);
+    h4_hci_->Send(type, (uint8_t*)data, data_length);
+
+    int uart_length = data_length + 1;  // + 1 for data type code
+    int i;
+
+    ALOGD("%s reading", __func__);
+    for (i = 0; i < uart_length; i++) {
+      fd_set read_fds;
+      FD_ZERO(&read_fds);
+      FD_SET(chip_uart_fd_, &read_fds);
+      TEMP_FAILURE_RETRY(
+          select(chip_uart_fd_ + 1, &read_fds, nullptr, nullptr, nullptr));
+
+      char byte;
+      TEMP_FAILURE_RETRY(read(chip_uart_fd_, &byte, 1));
+
+      EXPECT_EQ(i == 0 ? static_cast<uint8_t>(type) : data[i - 1], byte);
+    }
+
+    EXPECT_EQ(i, uart_length);
+  }
+
+  void ExpectInboundAclData(char* payload, std::promise<void>* promise) {
+    // h4 type[1] + handle[2] + size[2]
+    header_[0] = static_cast<uint8_t>(PacketType::ACL_DATA);
+    header_[1] = 19;
+    header_[2] = 92;
+    int length = strlen(payload);
+    header_[3] = length & 0xFF;
+    header_[4] = (length >> 8) & 0xFF;
+    ALOGD("(%d bytes) %s", length, payload);
+
+    EXPECT_CALL(acl_cb_,
+                Call(PacketMatches(header_ + 1, kAclHeaderSize, payload)))
+        .WillOnce(Notify(promise));
+  }
+
+  void WaitForTimeout(size_t timeout_ms, std::promise<void>* promise) {
+    auto future = promise->get_future();
+    auto status = future.wait_for(std::chrono::milliseconds(timeout_ms));
+    EXPECT_EQ(status, std::future_status::ready);
+  }
+
+  void WriteInboundAclData(char* payload) {
+    // Use the header_ computed in ExpectInboundAclData
+    TEMP_FAILURE_RETRY(write(chip_uart_fd_, header_, kAclHeaderSize + 1));
+    TEMP_FAILURE_RETRY(write(chip_uart_fd_, payload, strlen(payload)));
+  }
+
+  void ExpectInboundScoData(char* payload, std::promise<void>* promise) {
+    // h4 type[1] + handle[2] + size[1]
+    header_[0] = static_cast<uint8_t>(PacketType::SCO_DATA);
+    header_[1] = 20;
+    header_[2] = 17;
+    header_[3] = strlen(payload) & 0xFF;
+    EXPECT_CALL(sco_cb_,
+                Call(PacketMatches(header_ + 1, kScoHeaderSize, payload)))
+        .WillOnce(Notify(promise));
+  }
+
+  void WriteInboundScoData(char* payload) {
+    // Use the header_ computed in ExpectInboundScoData
+    ALOGD("%s writing", __func__);
+    TEMP_FAILURE_RETRY(write(chip_uart_fd_, header_, kScoHeaderSize + 1));
+    TEMP_FAILURE_RETRY(write(chip_uart_fd_, payload, strlen(payload)));
+  }
+
+  void ExpectInboundEvent(char* payload, std::promise<void>* promise) {
+    // h4 type[1] + event_code[1] + size[1]
+    header_[0] = static_cast<uint8_t>(PacketType::EVENT);
+    header_[1] = 9;
+    header_[2] = strlen(payload) & 0xFF;
+    EXPECT_CALL(event_cb_,
+                Call(PacketMatches(header_ + 1, kEventHeaderSize, payload)))
+        .WillOnce(Notify(promise));
+  }
+
+  void WriteInboundEvent(char* payload) {
+    // Use the header_ computed in ExpectInboundEvent
+    char preamble[3] = {static_cast<uint8_t>(PacketType::EVENT), 9, 0};
+    preamble[2] = strlen(payload) & 0xFF;
+    ALOGD("%s writing", __func__);
+    TEMP_FAILURE_RETRY(write(chip_uart_fd_, header_, kEventHeaderSize + 1));
+    TEMP_FAILURE_RETRY(write(chip_uart_fd_, payload, strlen(payload)));
+  }
+
+  void ExpectInboundIsoData(char* payload, std::promise<void>* promise) {
+    // h4 type[1] + handle[2] + size[1]
+    header_[0] = static_cast<uint8_t>(PacketType::ISO_DATA);
+    header_[1] = 19;
+    header_[2] = 92;
+    int length = strlen(payload);
+    header_[3] = length & 0xFF;
+    header_[4] = (length >> 8) & 0x3F;
+
+    EXPECT_CALL(iso_cb_,
+                Call(PacketMatches(header_ + 1, kIsoHeaderSize, payload)))
+        .WillOnce(Notify(promise));
+  }
+
+  void WriteInboundIsoData(char* payload) {
+    // Use the header_ computed in ExpectInboundIsoData
+    ALOGD("%s writing", __func__);
+    TEMP_FAILURE_RETRY(write(chip_uart_fd_, header_, kIsoHeaderSize + 1));
+    TEMP_FAILURE_RETRY(write(chip_uart_fd_, payload, strlen(payload)));
+  }
+
+  void WriteAndExpectManyInboundAclDataPackets(char* payload) {
+    size_t kNumPackets = 20;
+    // h4 type[1] + handle[2] + size[2]
+    char preamble[5] = {static_cast<uint8_t>(PacketType::ACL_DATA), 19, 92, 0,
+                        0};
+    int length = strlen(payload);
+    preamble[3] = length & 0xFF;
+    preamble[4] = (length >> 8) & 0xFF;
+
+    EXPECT_CALL(acl_cb_, Call(PacketMatches(preamble + 1, sizeof(preamble) - 1,
+                                            payload)))
+        .Times(kNumPackets);
+
+    for (size_t i = 0; i < kNumPackets; i++) {
+      TEMP_FAILURE_RETRY(write(chip_uart_fd_, preamble, sizeof(preamble)));
+      TEMP_FAILURE_RETRY(write(chip_uart_fd_, payload, strlen(payload)));
+    }
+
+    CallDataReady();
+  }
+
+  testing::MockFunction<void(const std::vector<uint8_t>&)> cmd_cb_;
+  testing::MockFunction<void(const std::vector<uint8_t>&)> event_cb_;
+  testing::MockFunction<void(const std::vector<uint8_t>&)> acl_cb_;
+  testing::MockFunction<void(const std::vector<uint8_t>&)> sco_cb_;
+  testing::MockFunction<void(const std::vector<uint8_t>&)> iso_cb_;
+  testing::MockFunction<void(void)> disconnect_cb_;
+  std::shared_ptr<H4Protocol> h4_hci_;
+  int chip_uart_fd_;
+  int stack_uart_fd_;
+
+  char header_[5];
+};
+
+// Test sending data sends correct data onto the UART
+TEST_F(H4ProtocolTest, TestSends) {
+  SendAndReadUartOutbound(PacketType::COMMAND, sample_data1);
+  SendAndReadUartOutbound(PacketType::ACL_DATA, sample_data2);
+  SendAndReadUartOutbound(PacketType::SCO_DATA, sample_data3);
+  SendAndReadUartOutbound(PacketType::ISO_DATA, sample_data4);
+}
+
+// Ensure we properly parse data coming from the UART
+TEST_F(H4ProtocolTest, TestReads) {
+  std::promise<void> acl_promise;
+  std::promise<void> sco_promise;
+  std::promise<void> event_promise;
+  std::promise<void> iso_promise;
+
+  ExpectInboundAclData(acl_data, &acl_promise);
+  WriteInboundAclData(acl_data);
+  CallDataReady();
+  ExpectInboundScoData(sco_data, &sco_promise);
+  WriteInboundScoData(sco_data);
+  CallDataReady();
+  ExpectInboundEvent(event_data, &event_promise);
+  WriteInboundEvent(event_data);
+  CallDataReady();
+  ExpectInboundIsoData(iso_data, &iso_promise);
+  WriteInboundIsoData(iso_data);
+  CallDataReady();
+
+  WaitForTimeout(100, &acl_promise);
+  WaitForTimeout(100, &sco_promise);
+  WaitForTimeout(100, &event_promise);
+  WaitForTimeout(100, &iso_promise);
+}
+
+TEST_F(H4ProtocolTest, TestMultiplePackets) {
+  WriteAndExpectManyInboundAclDataPackets(sco_data);
+}
+
+TEST_F(H4ProtocolTest, TestDisconnect) {
+  EXPECT_CALL(disconnect_cb_, Call());
+  close(chip_uart_fd_);
+  CallDataReady();
+}
+
+TEST_F(H4ProtocolTest, TestPartialWrites) {
+  size_t payload_len = strlen(acl_data);
+  const size_t kNumIntervals = payload_len + 1;
+  // h4 type[1] + handle[2] + size[2]
+  header_[0] = static_cast<uint8_t>(PacketType::ACL_DATA);
+  header_[1] = 19;
+  header_[2] = 92;
+  header_[3] = payload_len & 0xFF;
+  header_[4] = (payload_len >> 8) & 0xFF;
+
+  EXPECT_CALL(acl_cb_,
+              Call(PacketMatches(header_ + 1, sizeof(header_) - 1, acl_data)))
+      .Times(kNumIntervals);
+
+  for (size_t interval = 1; interval < kNumIntervals + 1; interval++) {
+    // Use the header_ data that expect already set up.
+    if (interval < kAclHeaderSize) {
+      TEMP_FAILURE_RETRY(write(chip_uart_fd_, header_, interval));
+      CallDataReady();
+      TEMP_FAILURE_RETRY(write(chip_uart_fd_, header_ + interval,
+                               kAclHeaderSize + 1 - interval));
+      CallDataReady();
+    } else {
+      TEMP_FAILURE_RETRY(write(chip_uart_fd_, header_, kAclHeaderSize + 1));
+      CallDataReady();
+    }
+
+    for (size_t bytes = 0; bytes + interval <= payload_len; bytes += interval) {
+      TEMP_FAILURE_RETRY(write(chip_uart_fd_, acl_data + bytes, interval));
+      CallDataReady();
+    }
+    size_t extra_bytes = payload_len % interval;
+    if (extra_bytes) {
+      TEMP_FAILURE_RETRY(write(
+          chip_uart_fd_, acl_data + payload_len - extra_bytes, extra_bytes));
+      CallDataReady();
+    }
+  }
+}
+
+class H4ProtocolAsyncTest : public H4ProtocolTest {
+ protected:
+  void SetUp() override {
+    H4ProtocolTest::SetUp();
+    fd_watcher_.WatchFdForNonBlockingReads(
+        stack_uart_fd_, [this](int) { h4_hci_->OnDataReady(); });
+  }
+
+  void TearDown() override { fd_watcher_.StopWatchingFileDescriptors(); }
+
+  void CallDataReady() override {
+    // The Async test can't call data ready.
+    FAIL();
+  }
+
+  void SendAndReadUartOutbound(PacketType type, char* data) {
+    ALOGD("%s sending", __func__);
+    int data_length = strlen(data);
+    h4_hci_->Send(type, (uint8_t*)data, data_length);
+
+    int uart_length = data_length + 1;  // + 1 for data type code
+    int i;
+
+    ALOGD("%s reading", __func__);
+    for (i = 0; i < uart_length; i++) {
+      fd_set read_fds;
+      FD_ZERO(&read_fds);
+      FD_SET(chip_uart_fd_, &read_fds);
+      TEMP_FAILURE_RETRY(
+          select(chip_uart_fd_ + 1, &read_fds, nullptr, nullptr, nullptr));
+
+      char byte;
+      TEMP_FAILURE_RETRY(read(chip_uart_fd_, &byte, 1));
+
+      EXPECT_EQ(i == 0 ? static_cast<uint8_t>(type) : data[i - 1], byte);
+    }
+
+    EXPECT_EQ(i, uart_length);
+  }
+
+  void WriteAndExpectInboundAclData(char* payload) {
+    std::promise<void> promise;
+    ExpectInboundAclData(payload, &promise);
+    WriteInboundAclData(payload);
+    WaitForTimeout(100, &promise);
+  }
+
+  void WriteAndExpectInboundScoData(char* payload) {
+    std::promise<void> promise;
+    ExpectInboundScoData(payload, &promise);
+    WriteInboundScoData(payload);
+    WaitForTimeout(100, &promise);
+  }
+
+  void WriteAndExpectInboundEvent(char* payload) {
+    std::promise<void> promise;
+    ExpectInboundEvent(payload, &promise);
+    WriteInboundEvent(payload);
+    WaitForTimeout(100, &promise);
+  }
+
+  void WriteAndExpectInboundIsoData(char* payload) {
+    std::promise<void> promise;
+    ExpectInboundIsoData(payload, &promise);
+    WriteInboundIsoData(payload);
+    WaitForTimeout(100, &promise);
+  }
+
+  void WriteAndExpectManyInboundAclDataPackets(char* payload) {
+    const size_t kNumPackets = 20;
+    // h4 type[1] + handle[2] + size[2]
+    char preamble[5] = {static_cast<uint8_t>(PacketType::ACL_DATA), 19, 92, 0,
+                        0};
+    int length = strlen(payload);
+    preamble[3] = length & 0xFF;
+    preamble[4] = (length >> 8) & 0xFF;
+
+    EXPECT_CALL(acl_cb_, Call(PacketMatches(preamble + 1, sizeof(preamble) - 1,
+                                            payload)))
+        .Times(kNumPackets);
+
+    for (size_t i = 0; i < kNumPackets; i++) {
+      TEMP_FAILURE_RETRY(write(chip_uart_fd_, preamble, sizeof(preamble)));
+      TEMP_FAILURE_RETRY(write(chip_uart_fd_, payload, strlen(payload)));
+    }
+
+    WriteAndExpectInboundEvent(event_data);
+  }
+
+  AsyncFdWatcher fd_watcher_;
+};
+
+// Test sending data sends correct data onto the UART
+TEST_F(H4ProtocolAsyncTest, TestSends) {
+  SendAndReadUartOutbound(PacketType::COMMAND, sample_data1);
+  SendAndReadUartOutbound(PacketType::ACL_DATA, sample_data2);
+  SendAndReadUartOutbound(PacketType::SCO_DATA, sample_data3);
+  SendAndReadUartOutbound(PacketType::ISO_DATA, sample_data4);
+}
+
+// Ensure we properly parse data coming from the UART
+TEST_F(H4ProtocolAsyncTest, TestReads) {
+  WriteAndExpectInboundAclData(acl_data);
+  WriteAndExpectInboundScoData(sco_data);
+  WriteAndExpectInboundEvent(event_data);
+  WriteAndExpectInboundIsoData(iso_data);
+}
+
+TEST_F(H4ProtocolAsyncTest, TestMultiplePackets) {
+  WriteAndExpectManyInboundAclDataPackets(sco_data);
+}
+
+TEST_F(H4ProtocolAsyncTest, TestDisconnect) {
+  std::promise<void> promise;
+  EXPECT_CALL(disconnect_cb_, Call()).WillOnce(Notify(&promise));
+  close(chip_uart_fd_);
+
+  // Fail if it takes longer than 100 ms.
+  WaitForTimeout(100, &promise);
+}
diff --git a/boot/aidl/vts/functional/VtsHalBootAidlTargetTest.cpp b/boot/aidl/vts/functional/VtsHalBootAidlTargetTest.cpp
index ab3c789..93c8376 100644
--- a/boot/aidl/vts/functional/VtsHalBootAidlTargetTest.cpp
+++ b/boot/aidl/vts/functional/VtsHalBootAidlTargetTest.cpp
@@ -33,7 +33,7 @@
 using std::unordered_set;
 
 // The main test class for the Boot HIDL HAL.
-class BootHidlTest : public ::testing::TestWithParam<std::string> {
+class BootAidlTest : public ::testing::TestWithParam<std::string> {
   public:
     virtual void SetUp() override {
         const auto instance_name = GetParam();
@@ -48,14 +48,14 @@
 };
 
 // validity check Boot::getNumberSlots().
-TEST_P(BootHidlTest, GetNumberSlots) {
+TEST_P(BootAidlTest, GetNumberSlots) {
     int32_t slots{};
     boot->getNumberSlots(&slots);
     ASSERT_LE(2, slots);
 }
 
 // validity check Boot::getCurrentSlot().
-TEST_P(BootHidlTest, GetCurrentSlot) {
+TEST_P(BootAidlTest, GetCurrentSlot) {
     int curSlot = -1;
     boot->getCurrentSlot(&curSlot);
     int slots = 0;
@@ -64,7 +64,7 @@
 }
 
 // validity check Boot::markBootSuccessful().
-TEST_P(BootHidlTest, MarkBootSuccessful) {
+TEST_P(BootAidlTest, MarkBootSuccessful) {
     const auto result = boot->markBootSuccessful();
     ASSERT_TRUE(result.isOk());
     int curSlot = 0;
@@ -74,7 +74,7 @@
     ASSERT_TRUE(ret);
 }
 
-TEST_P(BootHidlTest, SetActiveBootSlot) {
+TEST_P(BootAidlTest, SetActiveBootSlot) {
     int curSlot = -1;
     boot->getCurrentSlot(&curSlot);
     ASSERT_GE(curSlot, 0);
@@ -107,7 +107,7 @@
     }
 }
 
-TEST_P(BootHidlTest, SetSlotAsUnbootable) {
+TEST_P(BootAidlTest, SetSlotAsUnbootable) {
     int curSlot = -1;
     boot->getCurrentSlot(&curSlot);
     ASSERT_GE(curSlot, 0);
@@ -139,7 +139,7 @@
 }
 
 // validity check Boot::isSlotBootable() on good and bad inputs.
-TEST_P(BootHidlTest, IsSlotBootable) {
+TEST_P(BootAidlTest, IsSlotBootable) {
     for (int s = 0; s < 2; s++) {
         bool bootable = false;
         const auto res = boot->isSlotBootable(s, &bootable);
@@ -153,7 +153,7 @@
 }
 
 // validity check Boot::isSlotMarkedSuccessful() on good and bad inputs.
-TEST_P(BootHidlTest, IsSlotMarkedSuccessful) {
+TEST_P(BootAidlTest, IsSlotMarkedSuccessful) {
     for (int32_t s = 0; s < 2; s++) {
         bool isSuccess = false;
         const auto res = boot->isSlotMarkedSuccessful(s, &isSuccess);
@@ -166,7 +166,7 @@
 }
 
 // validity check Boot::getSuffix() on good and bad inputs.
-TEST_P(BootHidlTest, GetSuffix) {
+TEST_P(BootAidlTest, GetSuffix) {
     string suffixStr;
     unordered_set<string> suffixes;
     int numSlots = 0;
@@ -190,5 +190,6 @@
 }
 
 INSTANTIATE_TEST_SUITE_P(
-        PerInstance, BootHidlTest,
+        PerInstance, BootAidlTest,
         testing::ValuesIn(android::getAidlHalInstanceNames(IBootControl::descriptor)));
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BootAidlTest);
diff --git a/cas/aidl/Android.bp b/cas/aidl/Android.bp
new file mode 100644
index 0000000..32f12b1
--- /dev/null
+++ b/cas/aidl/Android.bp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+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.cas",
+    vendor_available: true,
+    srcs: ["android/hardware/cas/*.aidl"],
+    stability: "vintf",
+    imports: ["android.hardware.common-V2"],
+    backend: {
+        cpp: {
+            enabled: false,
+        },
+        java: {
+            sdk_version: "module_current",
+        },
+    },
+}
diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/AidlCasPluginDescriptor.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/AidlCasPluginDescriptor.aidl
new file mode 100644
index 0000000..7b8099f
--- /dev/null
+++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/AidlCasPluginDescriptor.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.cas;
+@VintfStability
+parcelable AidlCasPluginDescriptor {
+  int caSystemId;
+  String name;
+}
diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/DestinationBuffer.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/DestinationBuffer.aidl
new file mode 100644
index 0000000..dd355af
--- /dev/null
+++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/DestinationBuffer.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.cas;
+@VintfStability
+union DestinationBuffer {
+  android.hardware.cas.SharedBuffer nonsecureMemory;
+  android.hardware.common.NativeHandle secureMemory;
+}
diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/ICas.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/ICas.aidl
new file mode 100644
index 0000000..e169beb
--- /dev/null
+++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/ICas.aidl
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.cas;
+@VintfStability
+interface ICas {
+  void closeSession(in byte[] sessionId);
+  byte[] openSession(in android.hardware.cas.SessionIntent intent, in android.hardware.cas.ScramblingMode mode);
+  void processEcm(in byte[] sessionId, in byte[] ecm);
+  void processEmm(in byte[] emm);
+  void provision(in String provisionString);
+  void refreshEntitlements(in int refreshType, in byte[] refreshData);
+  void release();
+  void sendEvent(in int event, in int arg, in byte[] eventData);
+  void sendSessionEvent(in byte[] sessionId, in int event, in int arg, in byte[] eventData);
+  void setPrivateData(in byte[] pvtData);
+  void setSessionPrivateData(in byte[] sessionId, in byte[] pvtData);
+}
diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/ICasListener.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/ICasListener.aidl
new file mode 100644
index 0000000..ebc13ce
--- /dev/null
+++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/ICasListener.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.cas;
+@VintfStability
+interface ICasListener {
+  void onEvent(in int event, in int arg, in byte[] data);
+  void onSessionEvent(in byte[] sessionId, in int event, in int arg, in byte[] data);
+  void onStatusUpdate(in android.hardware.cas.StatusEvent event, in int number);
+}
diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/IDescrambler.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/IDescrambler.aidl
new file mode 100644
index 0000000..9bf7903
--- /dev/null
+++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/IDescrambler.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.cas;
+@VintfStability
+interface IDescrambler {
+  int descramble(in android.hardware.cas.ScramblingControl scramblingControl, in android.hardware.cas.SubSample[] subSamples, in android.hardware.cas.SharedBuffer srcBuffer, in long srcOffset, in android.hardware.cas.DestinationBuffer dstBuffer, in long dstOffset);
+  void release();
+  boolean requiresSecureDecoderComponent(in String mime);
+  void setMediaCasSession(in byte[] sessionId);
+}
diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/IMediaCasService.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/IMediaCasService.aidl
new file mode 100644
index 0000000..f5c8018
--- /dev/null
+++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/IMediaCasService.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.cas;
+@VintfStability
+interface IMediaCasService {
+  android.hardware.cas.IDescrambler createDescrambler(in int CA_system_id);
+  android.hardware.cas.ICas createPlugin(in int CA_system_id, in android.hardware.cas.ICasListener listener);
+  android.hardware.cas.AidlCasPluginDescriptor[] enumeratePlugins();
+  boolean isDescramblerSupported(in int CA_system_id);
+  boolean isSystemIdSupported(in int CA_system_id);
+}
diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/ScramblingControl.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/ScramblingControl.aidl
new file mode 100644
index 0000000..d71d4be
--- /dev/null
+++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/ScramblingControl.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.cas;
+@Backing(type="int") @VintfStability
+enum ScramblingControl {
+  UNSCRAMBLED = 0,
+  RESERVED = 1,
+  EVENKEY = 2,
+  ODDKEY = 3,
+}
diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/ScramblingMode.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/ScramblingMode.aidl
new file mode 100644
index 0000000..e3923c7
--- /dev/null
+++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/ScramblingMode.aidl
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.cas;
+@Backing(type="int") @VintfStability
+enum ScramblingMode {
+  RESERVED = 0,
+  DVB_CSA1 = 1,
+  DVB_CSA2 = 2,
+  DVB_CSA3_STANDARD = 3,
+  DVB_CSA3_MINIMAL = 4,
+  DVB_CSA3_ENHANCE = 5,
+  DVB_CISSA_V1 = 6,
+  DVB_IDSA = 7,
+  MULTI2 = 8,
+  AES128 = 9,
+  AES_CBC = 10,
+  AES_ECB = 11,
+  AES_SCTE52 = 12,
+  TDES_ECB = 13,
+  TDES_SCTE52 = 14,
+}
diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/SessionIntent.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/SessionIntent.aidl
new file mode 100644
index 0000000..af95f80
--- /dev/null
+++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/SessionIntent.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.cas;
+@Backing(type="int") @VintfStability
+enum SessionIntent {
+  LIVE = 0,
+  PLAYBACK = 1,
+  RECORD = 2,
+  TIMESHIFT = 3,
+}
diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/SharedBuffer.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/SharedBuffer.aidl
new file mode 100644
index 0000000..a18aa57
--- /dev/null
+++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/SharedBuffer.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.cas;
+@VintfStability
+parcelable SharedBuffer {
+  android.hardware.common.Ashmem heapBase;
+  long offset;
+  long size;
+}
diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/Status.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/Status.aidl
new file mode 100644
index 0000000..3d3a8a0
--- /dev/null
+++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/Status.aidl
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.cas;
+@VintfStability
+parcelable Status {
+  const int OK = 0;
+  const int ERROR_CAS_NO_LICENSE = -1;
+  const int ERROR_CAS_LICENSE_EXPIRED = -2;
+  const int ERROR_CAS_SESSION_NOT_OPENED = -3;
+  const int ERROR_CAS_CANNOT_HANDLE = -4;
+  const int ERROR_CAS_INVALID_STATE = -5;
+  const int BAD_VALUE = -6;
+  const int ERROR_CAS_NOT_PROVISIONED = -7;
+  const int ERROR_CAS_RESOURCE_BUSY = -8;
+  const int ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION = -9;
+  const int ERROR_CAS_TAMPER_DETECTED = -10;
+  const int ERROR_CAS_DEVICE_REVOKED = -11;
+  const int ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED = -12;
+  const int ERROR_CAS_DECRYPT = -13;
+  const int ERROR_CAS_UNKNOWN = -14;
+  const int ERROR_CAS_NEED_ACTIVATION = -15;
+  const int ERROR_CAS_NEED_PAIRING = -16;
+  const int ERROR_CAS_NO_CARD = -17;
+  const int ERROR_CAS_CARD_MUTE = -18;
+  const int ERROR_CAS_CARD_INVALID = -19;
+  const int ERROR_CAS_BLACKOUT = -20;
+  const int ERROR_CAS_REBOOTING = -21;
+}
diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/StatusEvent.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/StatusEvent.aidl
new file mode 100644
index 0000000..178cabc
--- /dev/null
+++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/StatusEvent.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.cas;
+@Backing(type="byte") @VintfStability
+enum StatusEvent {
+  PLUGIN_PHYSICAL_MODULE_CHANGED = 0,
+  PLUGIN_SESSION_NUMBER_CHANGED = 1,
+}
diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/SubSample.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/SubSample.aidl
new file mode 100644
index 0000000..d9ee3b4
--- /dev/null
+++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/SubSample.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.cas;
+@VintfStability
+parcelable SubSample {
+  int numBytesOfClearData;
+  int numBytesOfEncryptedData;
+}
diff --git a/cas/aidl/android/hardware/cas/AidlCasPluginDescriptor.aidl b/cas/aidl/android/hardware/cas/AidlCasPluginDescriptor.aidl
new file mode 100644
index 0000000..55b328a
--- /dev/null
+++ b/cas/aidl/android/hardware/cas/AidlCasPluginDescriptor.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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.cas;
+
+/**
+ * Describes a CAS plugin with its system ID and name.
+ */
+@VintfStability
+parcelable AidlCasPluginDescriptor {
+    int caSystemId;
+    String name;
+}
diff --git a/cas/aidl/android/hardware/cas/DestinationBuffer.aidl b/cas/aidl/android/hardware/cas/DestinationBuffer.aidl
new file mode 100644
index 0000000..068f29d
--- /dev/null
+++ b/cas/aidl/android/hardware/cas/DestinationBuffer.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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.cas;
+
+import android.hardware.cas.SharedBuffer;
+import android.hardware.common.NativeHandle;
+
+@VintfStability
+union DestinationBuffer {
+    /**
+     * If type == SHARED_MEMORY, the descrambled data must be written
+     * to user-space non-secure shared memory.
+     */
+    SharedBuffer nonsecureMemory;
+
+    /**
+     * If type == NATIVE_HANDLE, the descrambled data must be written
+     * to secure memory referenced by the vendor's buffer allocator.
+     */
+    NativeHandle secureMemory;
+}
diff --git a/cas/aidl/android/hardware/cas/ICas.aidl b/cas/aidl/android/hardware/cas/ICas.aidl
new file mode 100644
index 0000000..4c938c7
--- /dev/null
+++ b/cas/aidl/android/hardware/cas/ICas.aidl
@@ -0,0 +1,121 @@
+/*
+ * 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.cas;
+
+import android.hardware.cas.ScramblingMode;
+import android.hardware.cas.SessionIntent;
+
+/**
+ * ICas is the API to control the CAS. It is used to manage sessions, provision/refresh the cas
+ * system, and process the EMM/ECM messages. It also allows bi-directional, scheme-specific
+ * communications between the client and the cas system.
+ */
+@VintfStability
+interface ICas {
+    /**
+     * Close a session.
+     *
+     * @param sessionId The id of the session to be closed.
+     */
+    void closeSession(in byte[] sessionId);
+
+    /**
+     * Open a session to descramble one or more streams by specifying intention
+     * and scrambling mode.
+     *
+     * @param intent the intention of the session to be opened.
+     * @param mode the scrambling mode the session will use.
+     *
+     * @return sessionId The id of the newly opened session.
+     */
+    byte[] openSession(in SessionIntent intent, in ScramblingMode mode);
+
+    /**
+     * Process an ECM from the ECM stream for this session’s elementary stream.
+     *
+     * @param sessionId the id of the session which the ecm data applies to.
+     * @param ecm a byte array containing the ecm data.
+     */
+    void processEcm(in byte[] sessionId, in byte[] ecm);
+
+    /**
+     * Process an in-band EMM from the EMM stream.
+     *
+     * @param emm a byte array containing the emm data.
+     */
+    void processEmm(in byte[] emm);
+
+    /**
+     * Initiate a provisioning operation for a CA system.
+     *
+     * @param provisionString string containing information needed for the
+     * provisioning operation, the format of which is scheme and implementation
+     * specific.
+     */
+    void provision(in String provisionString);
+
+    /**
+     * Notify the CA system to refresh entitlement keys.
+     *
+     * @param refreshType the type of the refreshment.
+     * @param refreshData private data associated with the refreshment.
+     */
+    void refreshEntitlements(in int refreshType, in byte[] refreshData);
+
+    /**
+     * Release the descrambler instance.
+     */
+    void release();
+
+    /**
+     * Send an scheme-specific event to the CasPlugin.
+     *
+     * @param event an integer denoting a scheme-specific event to be sent.
+     * @param arg a scheme-specific integer argument for the event.
+     * @param data a byte array containing scheme-specific data for the event.
+     */
+    void sendEvent(in int event, in int arg, in byte[] eventData);
+
+    /**
+     * Send an scheme-specific session event to the CasPlugin.
+     *
+     * @param sessionId the id of an opened session.
+     * @param event an integer denoting a scheme-specific event to be sent.
+     * @param arg a scheme-specific integer argument for the event.
+     * @param data a byte array containing scheme-specific data for the event.
+     */
+    void sendSessionEvent(in byte[] sessionId, in int event, in int arg, in byte[] eventData);
+
+    /**
+     * Provide the CA private data from a CA_descriptor in the conditional
+     * access table to a CasPlugin.
+     *
+     * @param pvtData a byte array containing the private data, the format of
+     * which is scheme-specific and opaque to the framework.
+     */
+    void setPrivateData(in byte[] pvtData);
+
+    /**
+     * Provide the CA private data from a CA_descriptor in the program map
+     * table to a session.
+     *
+     * @param sessionId the id of the session which the private data applies to.
+     * @param pvtData a byte array containing the private data, the format of
+     * which is scheme-specific and opaque to the framework.
+     */
+    void setSessionPrivateData(in byte[] sessionId, in byte[] pvtData);
+}
diff --git a/cas/aidl/android/hardware/cas/ICasListener.aidl b/cas/aidl/android/hardware/cas/ICasListener.aidl
new file mode 100644
index 0000000..32d843f
--- /dev/null
+++ b/cas/aidl/android/hardware/cas/ICasListener.aidl
@@ -0,0 +1,58 @@
+/*
+ * 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.cas;
+
+import android.hardware.cas.StatusEvent;
+
+@VintfStability
+interface ICasListener {
+    /**
+     * Notify the listener of a scheme-specific event from the CA system.
+     *
+     * @param event an integer whose meaning is scheme-specific.
+     * @param arg an integer whose meaning is scheme-specific.
+     * @param data a byte array of data whose format and meaning are
+     * scheme-specific.
+     */
+    void onEvent(in int event, in int arg, in byte[] data);
+
+    /**
+     * Notify the listener of a scheme-specific session event from CA system.
+     *
+     * @param sessionId the id of an opened session.
+     * @param event an integer whose meaning is scheme-specific.
+     * @param arg an integer whose meaning is scheme-specific.
+     * @param data a byte array of data whose format and meaning are
+     * scheme-specific.
+     */
+    void onSessionEvent(in byte[] sessionId, in int event, in int arg, in byte[] data);
+
+    /**
+     * Notify the listener that the status of CAS system has changed.
+     *
+     * @param event the event type of status change.
+     * @param number value for status event.
+     *               For PLUGIN_PHYSICAL_MODULE_CHANGED event:
+     *               the positive number presents how many plugins are inserted;
+     *               the negative number presents how many plugins are removed.
+     *               Client must enumerate plugins after receive the event.
+     *               For PLUGIN_SESSION_NUMBER_CHANGED event:
+     *               the number presents how many sessions are supported
+     *               in the plugin.
+     */
+    void onStatusUpdate(in StatusEvent event, in int number);
+}
diff --git a/cas/aidl/android/hardware/cas/IDescrambler.aidl b/cas/aidl/android/hardware/cas/IDescrambler.aidl
new file mode 100644
index 0000000..33fbe75
--- /dev/null
+++ b/cas/aidl/android/hardware/cas/IDescrambler.aidl
@@ -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.
+ */
+
+package android.hardware.cas;
+
+import android.hardware.cas.DestinationBuffer;
+import android.hardware.cas.ScramblingControl;
+import android.hardware.cas.SharedBuffer;
+import android.hardware.cas.SubSample;
+
+/**
+ * IDescrambler is the API to control the descrambling operations.
+ */
+@VintfStability
+interface IDescrambler {
+    /**
+     * Descramble the data in a source SharedBuffer, described by an array of
+     * SubSample structures.
+     *
+     * @param scramblingControl an enumeration indicating the key that the subsamples
+     * were scrambled with.
+     * @param subSamples an array of SubSample structures describing the number of
+     * clear and scrambled bytes within each subsample.
+     * @param srcBuffer the SharedBuffer containing the source scrambled data.
+     * @param srcOffset the position where the source scrambled data starts at.
+     * @param dstBuffer the DestinationBuffer to hold the descrambled data.
+     * @param dstOffset the position where the descrambled data should start at.
+     *
+     * @return bytesWritten Number of bytes that have been successfully written.
+     */
+    int descramble(in ScramblingControl scramblingControl, in SubSample[] subSamples,
+            in SharedBuffer srcBuffer, in long srcOffset, in DestinationBuffer dstBuffer,
+            in long dstOffset);
+
+    /**
+     * Release the descrambler instance.
+     */
+    void release();
+
+    /**
+     * Query if the scrambling scheme requires the use of a secure decoder
+     * to decode data of the given mime type.
+     *
+     * @param mime the mime type of the media data.
+     * @return whether the descrambler requires a secure decoder.
+     */
+    boolean requiresSecureDecoderComponent(in String mime);
+
+    /**
+     * Associate a MediaCas session with this MediaDescrambler instance.
+     *
+     * @param sessionId the id of the session to associate with this descrambler instance.
+     */
+    void setMediaCasSession(in byte[] sessionId);
+}
diff --git a/cas/aidl/android/hardware/cas/IMediaCasService.aidl b/cas/aidl/android/hardware/cas/IMediaCasService.aidl
new file mode 100644
index 0000000..8bc31f6
--- /dev/null
+++ b/cas/aidl/android/hardware/cas/IMediaCasService.aidl
@@ -0,0 +1,71 @@
+/*
+ * 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.cas;
+
+import android.hardware.cas.AidlCasPluginDescriptor;
+import android.hardware.cas.ICas;
+import android.hardware.cas.ICasListener;
+import android.hardware.cas.IDescrambler;
+
+/**
+ * IMediaCasService is the main entry point for interacting with a vendor's
+ * cas HAL to create cas and descrambler plugin instances. A cas plugin instance
+ * opens cas sessions which are used to obtain keys for a descrambler session,
+ * which can in turn be used to descramble protected video content.
+ */
+@VintfStability
+interface IMediaCasService {
+    /**
+     * Construct a new instance of a DescramblerPlugin given a CA_system_id.
+     *
+     * @param CA_system_id the id of the CA system.
+     * @return the newly created plugin interface.
+     */
+    IDescrambler createDescrambler(in int CA_system_id);
+
+    /**
+     * Construct a new instance of a CasPlugin given a CA_system_id.
+     *
+     * @param CA_system_id the id of the CA system.
+     * @param listener the event listener to receive events coming from the CasPlugin.
+     * @return the newly created CasPlugin interface.
+     */
+    ICas createPlugin(in int CA_system_id, in ICasListener listener);
+
+    /**
+     * List all available CA systems on the device.
+     *
+     * @return an array of descriptors for the available CA systems.
+     */
+    AidlCasPluginDescriptor[] enumeratePlugins();
+
+    /**
+     * Query if the descrambling scheme for a CA system is supported on this device.
+     *
+     * @param CA_system_id the id of the CA system.
+     * @return whether the specified descrambling scheme is supported on this device.
+     */
+    boolean isDescramblerSupported(in int CA_system_id);
+
+    /**
+     * Query if a certain CA system is supported on this device.
+     *
+     * @param CA_system_id the id of the CA system.
+     * @return whether the specified CA system is supported on this device.
+     */
+    boolean isSystemIdSupported(in int CA_system_id);
+}
diff --git a/cas/aidl/android/hardware/cas/ScramblingControl.aidl b/cas/aidl/android/hardware/cas/ScramblingControl.aidl
new file mode 100644
index 0000000..b36787c
--- /dev/null
+++ b/cas/aidl/android/hardware/cas/ScramblingControl.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.cas;
+
+/**
+ * Enumerates the keys used to scramble the content.
+ */
+@VintfStability
+@Backing(type="int")
+enum ScramblingControl {
+    UNSCRAMBLED = 0,
+    RESERVED = 1,
+    EVENKEY = 2,
+    ODDKEY = 3,
+}
diff --git a/cas/aidl/android/hardware/cas/ScramblingMode.aidl b/cas/aidl/android/hardware/cas/ScramblingMode.aidl
new file mode 100644
index 0000000..9d73eba
--- /dev/null
+++ b/cas/aidl/android/hardware/cas/ScramblingMode.aidl
@@ -0,0 +1,98 @@
+/*
+ * 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.cas;
+
+/**
+ * The Scrambling Mode.
+ */
+@VintfStability
+@Backing(type="int")
+enum ScramblingMode {
+    RESERVED = 0,
+
+    /**
+     * DVB (Digital Video Broadcasting) CSA1 (Common Scrambling Algorithm 1) is
+     * the default mode and shall be used when the scrambling descriptor
+     * is not present in the program map section. DVB scrambling mode is
+     * specified in ETSI EN 300 468 specification.
+     */
+    DVB_CSA1,
+
+    DVB_CSA2,
+
+    /**
+     * DVB-CSA3 in standard mode.
+     */
+    DVB_CSA3_STANDARD,
+
+    /**
+     * DVB-CSA3 in minimally enhanced mode.
+     */
+    DVB_CSA3_MINIMAL,
+
+    /**
+     * DVB-CSA3 in fully enhanced mode.
+     */
+    DVB_CSA3_ENHANCE,
+
+    /**
+     * DVB-CISSA version 1.
+     */
+    DVB_CISSA_V1,
+
+    /**
+     * ATIS-0800006 IIF Default Scrambling Algorithm (IDSA).
+     */
+    DVB_IDSA,
+
+    /**
+     * a symmetric key algorithm.
+     */
+    MULTI2,
+
+    /**
+     * Advanced Encryption System (AES) 128-bit Encryption mode.
+     */
+    AES128,
+
+    /**
+     * Advanced Encryption System (AES) Cipher Block Chaining (CBC) mode.
+     */
+    AES_CBC,
+
+    /**
+     * Advanced Encryption System (AES) Electronic Code Book (ECB) mode.
+     */
+    AES_ECB,
+
+    /**
+     * Advanced Encryption System (AES) Society of Cable Telecommunications
+     * Engineers (SCTE) 52 mode.
+     */
+    AES_SCTE52,
+
+    /**
+     * Triple Data Encryption Algorithm (TDES) Electronic Code Book (ECB) mode.
+     */
+    TDES_ECB,
+
+    /**
+     * Triple Data Encryption Algorithm (TDES) Society of Cable Telecommunications
+     * Engineers (SCTE) 52 mode.
+     */
+    TDES_SCTE52,
+}
diff --git a/cas/aidl/android/hardware/cas/SessionIntent.aidl b/cas/aidl/android/hardware/cas/SessionIntent.aidl
new file mode 100644
index 0000000..844deab
--- /dev/null
+++ b/cas/aidl/android/hardware/cas/SessionIntent.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cas;
+
+/**
+ * The intented usage for the session.
+ */
+@VintfStability
+@Backing(type="int")
+enum SessionIntent {
+    /**
+     * Live Stream.
+     */
+    LIVE,
+
+    /**
+     * Playback Recorded Stream.
+     */
+    PLAYBACK,
+
+    /**
+     * Record Live Stream.
+     */
+    RECORD,
+
+    /**
+     * View the content with Time Shift capability
+     */
+    TIMESHIFT,
+}
diff --git a/cas/aidl/android/hardware/cas/SharedBuffer.aidl b/cas/aidl/android/hardware/cas/SharedBuffer.aidl
new file mode 100644
index 0000000..8a94ff7
--- /dev/null
+++ b/cas/aidl/android/hardware/cas/SharedBuffer.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cas;
+
+import android.hardware.common.Ashmem;
+
+/**
+ * SharedBuffer describes a shared buffer which is defined by a heapBase, an
+ * offset and a size. The offset is relative to the shared memory base for the
+ * memory region identified by heapBase.
+ */
+@VintfStability
+parcelable SharedBuffer {
+    /**
+     * Ashmem shared memory
+     */
+    Ashmem heapBase;
+
+    /**
+     * The offset from the shared memory base
+     */
+    long offset;
+
+    /**
+     * The size of the shared buffer in bytes
+     */
+    long size;
+}
diff --git a/cas/aidl/android/hardware/cas/Status.aidl b/cas/aidl/android/hardware/cas/Status.aidl
new file mode 100644
index 0000000..b2be34b
--- /dev/null
+++ b/cas/aidl/android/hardware/cas/Status.aidl
@@ -0,0 +1,150 @@
+/*
+ * 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.cas;
+
+@VintfStability
+parcelable Status {
+    /**
+     * The CAS plugin must return OK when an operation completes without any
+     * errors.
+     */
+    const int OK = 0;
+
+    /**
+     * The CAS plugin must return ERROR_CAS_NO_LICENSE, when descrambling is
+     * attempted and no license keys have been provided.
+     */
+    const int ERROR_CAS_NO_LICENSE = -1;
+
+    /**
+     * ERROR_CAS_LICENSE_EXPIRED must be returned when an attempt is made
+     * to use a license and the keys in that license have expired.
+     */
+    const int ERROR_CAS_LICENSE_EXPIRED = -2;
+
+    /**
+     * The CAS plugin must return ERROR_CAS_SESSION_NOT_OPENED when an
+     * attempt is made to use a session that has not been opened.
+     */
+    const int ERROR_CAS_SESSION_NOT_OPENED = -3;
+
+    /**
+     * The CAS plugin must return ERROR_CAS_CANNOT_HANDLE when an unsupported
+     * data format or operation is attempted.
+     */
+    const int ERROR_CAS_CANNOT_HANDLE = -4;
+
+    /**
+     * ERROR_CAS_INVALID_STATE must be returned when the device is in a state
+     * where it is not able to perform descrambling.
+     */
+    const int ERROR_CAS_INVALID_STATE = -5;
+
+    /**
+     * The CAS plugin must return BAD_VALUE whenever an illegal parameter is
+     * passed to one of the interface functions.
+     */
+    const int BAD_VALUE = -6;
+
+    /**
+     * The CAS plugin must return ERROR_CAS_NOT_PROVISIONED when the device
+     * has not yet been provisioned.
+     */
+    const int ERROR_CAS_NOT_PROVISIONED = -7;
+
+    /**
+     * ERROR_CAS_RESOURCE_BUSY must be returned when resources, such as CAS
+     * sessions or secure buffers are not available to perform a requested
+     * operation because they are already in use.
+     */
+    const int ERROR_CAS_RESOURCE_BUSY = -8;
+
+    /**
+     * The CAS Plugin must return ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION
+     * when the output protection level enabled on the device is not
+     * sufficient to meet the requirements in the license policy. HDCP is an
+     * example of a form of output protection.
+     */
+    const int ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION = -9;
+
+    /**
+     * The CAS Plugin must return ERROR_CAS_TAMPER_DETECTED if an attempt to
+     * tamper with the CAS system is detected.
+     */
+    const int ERROR_CAS_TAMPER_DETECTED = -10;
+
+    /**
+     * The CAS Plugin must return ERROR_CAS_DEVICE_REVOKED if the response
+     * indicates that the device has been revoked. Device revocation means
+     * that the device is no longer permitted to play content.
+     */
+    const int ERROR_CAS_DEVICE_REVOKED = -11;
+
+    /**
+     * The CAS plugin must return ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED when
+     * descrambling is failing because the session is not initialized properly.
+     */
+    const int ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED = -12;
+
+    /**
+     * The CAS Plugin must return ERROR_CAS_DECRYPT if the DescramblerPlugin's
+     * descramble operation fails.
+     */
+    const int ERROR_CAS_DECRYPT = -13;
+
+    /**
+     * ERROR_CAS_UNKNOWN must be returned when a fatal failure occurs and no
+     * other defined error is appropriate.
+     */
+    const int ERROR_CAS_UNKNOWN = -14;
+
+    /**
+     * ERROR_CAS_NEED_ACTIVATION is used to trigger device activation process.
+     */
+    const int ERROR_CAS_NEED_ACTIVATION = -15;
+
+    /**
+     * ERROR_CAS_NEED_PAIRING is used to trigger pairing process.
+     */
+    const int ERROR_CAS_NEED_PAIRING = -16;
+
+    /**
+     * ERROR_CAS_NO_CARD is used to report no smart card for descrambling.
+     */
+    const int ERROR_CAS_NO_CARD = -17;
+
+    /**
+     * ERROR_CAS_CARD_MUTE is used to report smart card is muted for
+     * descrambling.
+     */
+    const int ERROR_CAS_CARD_MUTE = -18;
+
+    /**
+     *  ERROR_CAS_CARD_INVALID is used to report smart card isn't valid.
+     */
+    const int ERROR_CAS_CARD_INVALID = -19;
+
+    /**
+     *  ERROR_CAS_BLACKOUT is used to report geographical blackout.
+     */
+    const int ERROR_CAS_BLACKOUT = -20;
+
+    /**
+     * ERROR_CAS_REBOOTING is used to report CAS is during rebooting.
+     */
+    const int ERROR_CAS_REBOOTING = -21;
+}
diff --git a/cas/aidl/android/hardware/cas/StatusEvent.aidl b/cas/aidl/android/hardware/cas/StatusEvent.aidl
new file mode 100644
index 0000000..0f62634
--- /dev/null
+++ b/cas/aidl/android/hardware/cas/StatusEvent.aidl
@@ -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.
+ */
+
+package android.hardware.cas;
+
+/**
+ * The Event Type for status change.
+ */
+@VintfStability
+@Backing(type="byte")
+enum StatusEvent {
+    /**
+     * The status of CAS plugin was changed due to physical module insertion or
+     * removal. Client must call enumeratePlugins to update plugins' status.
+     */
+    PLUGIN_PHYSICAL_MODULE_CHANGED,
+
+    /**
+     * The status of supported session number was changed due to physical module
+     * insertion or removal. Client must update session resource according to
+     * latest StatusMessage from the StatusEvent. The plugin supports unlimited
+     * session by default.
+     */
+    PLUGIN_SESSION_NUMBER_CHANGED,
+}
diff --git a/cas/aidl/android/hardware/cas/SubSample.aidl b/cas/aidl/android/hardware/cas/SubSample.aidl
new file mode 100644
index 0000000..c1353cb
--- /dev/null
+++ b/cas/aidl/android/hardware/cas/SubSample.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.cas;
+
+/**
+ * A subsample consists of some number of bytes of clear (unscrambled)
+ * data followed by a number of bytes of scrambled data.
+ */
+@VintfStability
+parcelable SubSample {
+    int numBytesOfClearData;
+    int numBytesOfEncryptedData;
+}
diff --git a/cas/aidl/default/Android.bp b/cas/aidl/default/Android.bp
new file mode 100755
index 0000000..3c16d57
--- /dev/null
+++ b/cas/aidl/default/Android.bp
@@ -0,0 +1,93 @@
+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"],
+}
+
+cc_library_static {
+    name: "libcasexampleimpl",
+    vendor_available: true,
+
+    srcs: [
+        "CasImpl.cpp",
+        "DescramblerImpl.cpp",
+        "MediaCasService.cpp",
+        "SharedLibrary.cpp",
+        "TypeConvert.cpp",
+    ],
+
+    shared_libs: [
+        "android.hardware.cas-V1-ndk",
+        "libbinder_ndk",
+        "liblog",
+        "libutils",
+        "libcutils",
+    ],
+    static_libs: [
+        "libaidlcommonsupport",
+    ],
+    header_libs: [
+        "libstagefright_foundation_headers",
+        "media_plugin_headers",
+    ],
+}
+
+cc_defaults {
+    name: "cas_service_example_defaults",
+    vendor: true,
+    relative_install_path: "hw",
+
+    srcs: ["service.cpp"],
+
+    static_libs: [
+        "libaidlcommonsupport",
+        "libcasexampleimpl",
+    ],
+    shared_libs: [
+        "android.hardware.cas-V1-ndk",
+        "libbinder_ndk",
+        "liblog",
+        "libutils",
+        "libcutils",
+    ],
+    header_libs: ["media_plugin_headers"],
+    vintf_fragments: ["android.hardware.cas-service.xml"],
+}
+
+cc_binary {
+    name: "android.hardware.cas-service.example",
+    defaults: ["cas_service_example_defaults"],
+    init_rc: ["cas-default.rc"],
+}
+
+cc_binary {
+    name: "android.hardware.cas-service.example-lazy",
+    defaults: ["cas_service_example_defaults"],
+    init_rc: ["cas-default-lazy.rc"],
+    cflags: ["-DLAZY_SERVICE"],
+}
+
+cc_fuzz {
+    name: "android.hardware.cas-service_fuzzer",
+    vendor: true,
+
+    defaults: ["service_fuzzer_defaults"],
+    srcs: ["fuzzer.cpp"],
+
+    shared_libs: [
+        "android.hardware.cas-V1-ndk",
+        "libcutils",
+        "liblog",
+    ],
+    static_libs: [
+        "libaidlcommonsupport",
+        "libcasexampleimpl",
+    ],
+    header_libs: ["media_plugin_headers"],
+    fuzz_config: {
+        componentid: 1344,
+    },
+}
diff --git a/cas/aidl/default/CasImpl.cpp b/cas/aidl/default/CasImpl.cpp
new file mode 100755
index 0000000..2d31b35
--- /dev/null
+++ b/cas/aidl/default/CasImpl.cpp
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ icensed 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 "android.hardware.cas-CasImpl"
+
+#include <media/cas/CasAPI.h>
+#include <utils/Log.h>
+
+#include "CasImpl.h"
+#include "TypeConvert.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace cas {
+
+CasImpl::CasImpl(const shared_ptr<ICasListener>& listener) : mListener(listener) {
+    ALOGV("CTOR");
+}
+
+CasImpl::~CasImpl() {
+    ALOGV("DTOR");
+    release();
+}
+
+// static
+void CasImpl::OnEvent(void* appData, int32_t event, int32_t arg, uint8_t* data, size_t size) {
+    if (appData == NULL) {
+        ALOGE("Invalid appData!");
+        return;
+    }
+    CasImpl* casImpl = static_cast<CasImpl*>(appData);
+    casImpl->onEvent(event, arg, data, size);
+}
+
+// static
+void CasImpl::CallBackExt(void* appData, int32_t event, int32_t arg, uint8_t* data, size_t size,
+                          const CasSessionId* sessionId) {
+    if (appData == NULL) {
+        ALOGE("Invalid appData!");
+        return;
+    }
+    CasImpl* casImpl = static_cast<CasImpl*>(appData);
+    casImpl->onEvent(sessionId, event, arg, data, size);
+}
+
+// static
+void CasImpl::StatusUpdate(void* appData, int32_t event, int32_t arg) {
+    if (appData == NULL) {
+        ALOGE("Invalid appData!");
+        return;
+    }
+    CasImpl* casImpl = static_cast<CasImpl*>(appData);
+    casImpl->onStatusUpdate(event, arg);
+}
+
+void CasImpl::init(CasPlugin* plugin) {
+    shared_ptr<CasPlugin> holder(plugin);
+    atomic_store(&mPluginHolder, holder);
+}
+
+void CasImpl::onEvent(int32_t event, int32_t arg, uint8_t* data, size_t size) {
+    if (mListener == NULL) {
+        return;
+    }
+
+    vector<uint8_t> eventData;
+    if (data != NULL) {
+        eventData.assign(data, data + size);
+    }
+
+    mListener->onEvent(event, arg, eventData);
+}
+
+void CasImpl::onEvent(const CasSessionId* sessionId, int32_t event, int32_t arg, uint8_t* data,
+                      size_t size) {
+    if (mListener == NULL) {
+        return;
+    }
+
+    vector<uint8_t> eventData;
+    if (data != NULL) {
+        eventData.assign(data, data + size);
+    }
+
+    if (sessionId != NULL) {
+        mListener->onSessionEvent(*sessionId, event, arg, eventData);
+    } else {
+        mListener->onEvent(event, arg, eventData);
+    }
+}
+
+void CasImpl::onStatusUpdate(int32_t event, int32_t arg) {
+    if (mListener == NULL) {
+        return;
+    }
+    mListener->onStatusUpdate(static_cast<StatusEvent>(event), arg);
+}
+
+ScopedAStatus CasImpl::setPluginStatusUpdateCallback() {
+    ALOGV("%s", __FUNCTION__);
+    shared_ptr<CasPlugin> holder = atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        return toStatus(INVALID_OPERATION);
+    }
+    return toStatus(holder->setStatusCallback(&CasImpl::StatusUpdate));
+}
+
+ScopedAStatus CasImpl::setPrivateData(const vector<uint8_t>& pvtData) {
+    ALOGV("%s", __FUNCTION__);
+    shared_ptr<CasPlugin> holder = atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        return toStatus(INVALID_OPERATION);
+    }
+    return toStatus(holder->setPrivateData(pvtData));
+}
+
+ScopedAStatus CasImpl::openSession(SessionIntent intent, ScramblingMode mode,
+                                   vector<uint8_t>* sessionId) {
+    ALOGV("%s", __FUNCTION__);
+
+    shared_ptr<CasPlugin> holder = atomic_load(&mPluginHolder);
+    status_t err = INVALID_OPERATION;
+    if (holder.get() != nullptr) {
+        err = holder->openSession(static_cast<uint32_t>(intent), static_cast<uint32_t>(mode),
+                                  sessionId);
+        holder.reset();
+    }
+
+    return toStatus(err);
+}
+
+ScopedAStatus CasImpl::setSessionPrivateData(const vector<uint8_t>& sessionId,
+                                             const vector<uint8_t>& pvtData) {
+    ALOGV("%s: sessionId=%s", __FUNCTION__, sessionIdToString(sessionId).string());
+    shared_ptr<CasPlugin> holder = atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        return toStatus(INVALID_OPERATION);
+    }
+    return toStatus(holder->setSessionPrivateData(sessionId, pvtData));
+}
+
+ScopedAStatus CasImpl::closeSession(const vector<uint8_t>& sessionId) {
+    ALOGV("%s: sessionId=%s", __FUNCTION__, sessionIdToString(sessionId).string());
+    shared_ptr<CasPlugin> holder = atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        return toStatus(INVALID_OPERATION);
+    }
+    return toStatus(holder->closeSession(sessionId));
+}
+
+ScopedAStatus CasImpl::processEcm(const vector<uint8_t>& sessionId, const vector<uint8_t>& ecm) {
+    ALOGV("%s: sessionId=%s", __FUNCTION__, sessionIdToString(sessionId).string());
+    shared_ptr<CasPlugin> holder = atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        return toStatus(INVALID_OPERATION);
+    }
+
+    return toStatus(holder->processEcm(sessionId, ecm));
+}
+
+ScopedAStatus CasImpl::processEmm(const vector<uint8_t>& emm) {
+    ALOGV("%s", __FUNCTION__);
+    shared_ptr<CasPlugin> holder = atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        return toStatus(INVALID_OPERATION);
+    }
+
+    return toStatus(holder->processEmm(emm));
+}
+
+ScopedAStatus CasImpl::sendEvent(int32_t event, int32_t arg, const vector<uint8_t>& eventData) {
+    ALOGV("%s", __FUNCTION__);
+    shared_ptr<CasPlugin> holder = atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        return toStatus(INVALID_OPERATION);
+    }
+
+    status_t err = holder->sendEvent(event, arg, eventData);
+    return toStatus(err);
+}
+
+ScopedAStatus CasImpl::sendSessionEvent(const vector<uint8_t>& sessionId, int32_t event,
+                                        int32_t arg, const vector<uint8_t>& eventData) {
+    ALOGV("%s", __FUNCTION__);
+    shared_ptr<CasPlugin> holder = atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        return toStatus(INVALID_OPERATION);
+    }
+
+    status_t err = holder->sendSessionEvent(sessionId, event, arg, eventData);
+    return toStatus(err);
+}
+
+ScopedAStatus CasImpl::provision(const string& provisionString) {
+    ALOGV("%s: provisionString=%s", __FUNCTION__, provisionString.c_str());
+    shared_ptr<CasPlugin> holder = atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        return toStatus(INVALID_OPERATION);
+    }
+
+    return toStatus(holder->provision(String8(provisionString.c_str())));
+}
+
+ScopedAStatus CasImpl::refreshEntitlements(int32_t refreshType,
+                                           const vector<uint8_t>& refreshData) {
+    ALOGV("%s", __FUNCTION__);
+    shared_ptr<CasPlugin> holder = atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        return toStatus(INVALID_OPERATION);
+    }
+
+    status_t err = holder->refreshEntitlements(refreshType, refreshData);
+    return toStatus(err);
+}
+
+ScopedAStatus CasImpl::release() {
+    ALOGV("%s: plugin=%p", __FUNCTION__, mPluginHolder.get());
+
+    shared_ptr<CasPlugin> holder(nullptr);
+    atomic_store(&mPluginHolder, holder);
+
+    return ScopedAStatus::ok();
+}
+
+}  // namespace cas
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/cas/aidl/default/CasImpl.h b/cas/aidl/default/CasImpl.h
new file mode 100755
index 0000000..84a8115
--- /dev/null
+++ b/cas/aidl/default/CasImpl.h
@@ -0,0 +1,93 @@
+/*
+ * 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 <aidl/android/hardware/cas/BnCas.h>
+#include <aidl/android/hardware/cas/ICasListener.h>
+#include <media/stagefright/foundation/ABase.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace cas {
+
+using namespace ::android;
+using namespace std;
+using ndk::ScopedAStatus;
+
+class CasImpl : public BnCas {
+  public:
+    CasImpl(const shared_ptr<ICasListener>& listener);
+    virtual ~CasImpl();
+
+    static void OnEvent(void* appData, int32_t event, int32_t arg, uint8_t* data, size_t size);
+
+    static void CallBackExt(void* appData, int32_t event, int32_t arg, uint8_t* data, size_t size,
+                            const CasSessionId* sessionId);
+
+    static void StatusUpdate(void* appData, int32_t event, int32_t arg);
+
+    void init(CasPlugin* plugin);
+    void onEvent(int32_t event, int32_t arg, uint8_t* data, size_t size);
+
+    void onEvent(const CasSessionId* sessionId, int32_t event, int32_t arg, uint8_t* data,
+                 size_t size);
+
+    void onStatusUpdate(int32_t event, int32_t arg);
+
+    // ICas inherits
+
+    ScopedAStatus setPluginStatusUpdateCallback();
+
+    virtual ScopedAStatus setPrivateData(const vector<uint8_t>& pvtData) override;
+
+    virtual ScopedAStatus openSession(SessionIntent intent, ScramblingMode mode,
+                                      vector<uint8_t>* sessionId) override;
+
+    virtual ScopedAStatus closeSession(const vector<uint8_t>& sessionId) override;
+
+    virtual ScopedAStatus setSessionPrivateData(const vector<uint8_t>& sessionId,
+                                                const vector<uint8_t>& pvtData) override;
+
+    virtual ScopedAStatus processEcm(const vector<uint8_t>& sessionId,
+                                     const vector<uint8_t>& ecm) override;
+
+    virtual ScopedAStatus processEmm(const vector<uint8_t>& emm) override;
+
+    virtual ScopedAStatus sendEvent(int32_t event, int32_t arg,
+                                    const vector<uint8_t>& eventData) override;
+
+    virtual ScopedAStatus sendSessionEvent(const vector<uint8_t>& sessionId, int32_t event,
+                                           int32_t arg, const vector<uint8_t>& eventData) override;
+
+    virtual ScopedAStatus provision(const string& provisionString) override;
+
+    virtual ScopedAStatus refreshEntitlements(int32_t refreshType,
+                                              const vector<uint8_t>& refreshData) override;
+
+    virtual ScopedAStatus release() override;
+
+  private:
+    struct PluginHolder;
+    shared_ptr<CasPlugin> mPluginHolder;
+    shared_ptr<ICasListener> mListener;
+
+    DISALLOW_EVIL_CONSTRUCTORS(CasImpl);
+};
+
+}  // namespace cas
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/cas/aidl/default/DescramblerImpl.cpp b/cas/aidl/default/DescramblerImpl.cpp
new file mode 100755
index 0000000..a96fd46
--- /dev/null
+++ b/cas/aidl/default/DescramblerImpl.cpp
@@ -0,0 +1,193 @@
+/*
+ * 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 "android.hardware.cas-DescramblerImpl"
+
+#include <aidlcommonsupport/NativeHandle.h>
+#include <inttypes.h>
+#include <media/cas/DescramblerAPI.h>
+#include <media/hardware/CryptoAPI.h>
+#include <media/stagefright/foundation/AUtils.h>
+#include <sys/mman.h>
+#include <utils/Log.h>
+
+#include "DescramblerImpl.h"
+#include "TypeConvert.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace cas {
+
+#define CHECK_SUBSAMPLE_DEF(type)                                                                 \
+    static_assert(sizeof(SubSample) == sizeof(type::SubSample), "SubSample: size doesn't match"); \
+    static_assert(offsetof(SubSample, numBytesOfClearData) ==                                     \
+                          offsetof(type::SubSample, mNumBytesOfClearData),                        \
+                  "SubSample: numBytesOfClearData offset doesn't match");                         \
+    static_assert(offsetof(SubSample, numBytesOfEncryptedData) ==                                 \
+                          offsetof(type::SubSample, mNumBytesOfEncryptedData),                    \
+                  "SubSample: numBytesOfEncryptedData offset doesn't match")
+
+CHECK_SUBSAMPLE_DEF(DescramblerPlugin);
+CHECK_SUBSAMPLE_DEF(CryptoPlugin);
+
+DescramblerImpl::DescramblerImpl(DescramblerPlugin* plugin) : mPluginHolder(plugin) {
+    ALOGV("CTOR: plugin=%p", mPluginHolder.get());
+}
+
+DescramblerImpl::~DescramblerImpl() {
+    ALOGV("DTOR: plugin=%p", mPluginHolder.get());
+    release();
+}
+
+ScopedAStatus DescramblerImpl::setMediaCasSession(const vector<uint8_t>& in_sessionId) {
+    ALOGV("%s: sessionId=%s", __FUNCTION__, sessionIdToString(in_sessionId).string());
+
+    shared_ptr<DescramblerPlugin> holder = atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        return toStatus(INVALID_OPERATION);
+    }
+
+    return toStatus(holder->setMediaCasSession(in_sessionId));
+}
+
+ScopedAStatus DescramblerImpl::requiresSecureDecoderComponent(const string& in_mime,
+                                                              bool* _aidl_return) {
+    shared_ptr<DescramblerPlugin> holder = atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        *_aidl_return = false;
+    }
+
+    *_aidl_return = holder->requiresSecureDecoderComponent(String8(in_mime.c_str()));
+    return ScopedAStatus::ok();
+}
+
+static inline bool validateRangeForSize(int64_t offset, int64_t length, int64_t size) {
+    return isInRange<int64_t, uint64_t>(0, (uint64_t)size, offset, (uint64_t)length);
+}
+
+ScopedAStatus DescramblerImpl::descramble(ScramblingControl scramblingControl,
+                                          const vector<SubSample>& subSamples,
+                                          const SharedBuffer& srcBuffer, int64_t srcOffset,
+                                          const DestinationBuffer& dstBuffer, int64_t dstOffset,
+                                          int32_t* _aidl_return) {
+    ALOGV("%s", __FUNCTION__);
+
+    // heapbase's size is stored in int64_t, but mapMemory's mmap will map size in
+    // size_t. If size is over SIZE_MAX, mapMemory mapMemory could succeed but the
+    // mapped memory's actual size will be smaller than the reported size.
+    if (srcBuffer.heapBase.size > SIZE_MAX) {
+        ALOGE("Invalid memory size: %" PRIu64 "", srcBuffer.heapBase.size);
+        android_errorWriteLog(0x534e4554, "79376389");
+        return toStatus(BAD_VALUE);
+    }
+
+    void* srcPtr = mmap(NULL, srcBuffer.heapBase.size, PROT_READ | PROT_WRITE, MAP_SHARED,
+                        srcBuffer.heapBase.fd.get(), 0);
+
+    // Validate if the offset and size in the SharedBuffer is consistent with the
+    // mapped heapbase, since the offset and size is controlled by client.
+    if (srcPtr == NULL) {
+        ALOGE("Failed to map src buffer.");
+        return toStatus(BAD_VALUE);
+    }
+    if (!validateRangeForSize(srcBuffer.offset, srcBuffer.size, srcBuffer.heapBase.size)) {
+        ALOGE("Invalid src buffer range: offset %" PRIu64 ", size %" PRIu64
+              ", srcMem"
+              "size %" PRIu64 "",
+              srcBuffer.offset, srcBuffer.size, srcBuffer.heapBase.size);
+        android_errorWriteLog(0x534e4554, "67962232");
+        return toStatus(BAD_VALUE);
+    }
+
+    // use 64-bit here to catch bad subsample size that might be overflowing.
+    uint64_t totalBytesInSubSamples = 0;
+    for (size_t i = 0; i < subSamples.size(); i++) {
+        uint32_t numBytesOfClearData = subSamples[i].numBytesOfClearData;
+        uint32_t numBytesOfEncryptedData = subSamples[i].numBytesOfEncryptedData;
+        totalBytesInSubSamples += (uint64_t)numBytesOfClearData + numBytesOfEncryptedData;
+    }
+    // Further validate if the specified srcOffset and requested total subsample size
+    // is consistent with the source shared buffer size.
+    if (!validateRangeForSize(srcOffset, totalBytesInSubSamples, srcBuffer.size)) {
+        ALOGE("Invalid srcOffset and subsample size: "
+              "srcOffset %" PRIu64 ", totalBytesInSubSamples %" PRIu64
+              ", srcBuffer"
+              "size %" PRIu64 "",
+              srcOffset, totalBytesInSubSamples, srcBuffer.size);
+        android_errorWriteLog(0x534e4554, "67962232");
+        return toStatus(BAD_VALUE);
+    }
+    srcPtr = (uint8_t*)srcPtr + srcBuffer.offset;
+
+    void* dstPtr = NULL;
+    if (dstBuffer.getTag() == DestinationBuffer::Tag::nonsecureMemory) {
+        // When using shared memory, src buffer is also used as dst,
+        // we don't map it again here.
+        dstPtr = srcPtr;
+
+        // In this case the dst and src would be the same buffer, need to validate
+        // dstOffset against the buffer size too.
+        if (!validateRangeForSize(dstOffset, totalBytesInSubSamples, srcBuffer.size)) {
+            ALOGE("Invalid dstOffset and subsample size: "
+                  "dstOffset %" PRIu64 ", totalBytesInSubSamples %" PRIu64
+                  ", srcBuffer"
+                  "size %" PRIu64 "",
+                  dstOffset, totalBytesInSubSamples, srcBuffer.size);
+            android_errorWriteLog(0x534e4554, "67962232");
+            return toStatus(BAD_VALUE);
+        }
+    } else {
+        native_handle_t* handle = makeFromAidl(dstBuffer.get<DestinationBuffer::secureMemory>());
+        dstPtr = static_cast<void*>(handle);
+    }
+
+    // Get a local copy of the shared_ptr for the plugin. Note that before
+    // calling the callback, this shared_ptr must be manually reset, since
+    // the client side could proceed as soon as the callback is called
+    // without waiting for this method to go out of scope.
+    shared_ptr<DescramblerPlugin> holder = atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        return toStatus(INVALID_OPERATION);
+    }
+
+    // Casting SubSample to DescramblerPlugin::SubSample, but need to ensure
+    // structs are actually identical
+
+    auto returnStatus =
+            holder->descramble(dstBuffer.getTag() != DestinationBuffer::Tag::nonsecureMemory,
+                               (DescramblerPlugin::ScramblingControl)scramblingControl,
+                               subSamples.size(), (DescramblerPlugin::SubSample*)subSamples.data(),
+                               srcPtr, srcOffset, dstPtr, dstOffset, NULL);
+
+    holder.reset();
+    *_aidl_return = returnStatus;
+    return toStatus(returnStatus >= 0 ? OK : returnStatus);
+}
+
+ScopedAStatus DescramblerImpl::release() {
+    ALOGV("%s: plugin=%p", __FUNCTION__, mPluginHolder.get());
+
+    shared_ptr<DescramblerPlugin> holder(nullptr);
+    atomic_store(&mPluginHolder, holder);
+
+    return ScopedAStatus::ok();
+}
+
+}  // namespace cas
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/cas/aidl/default/DescramblerImpl.h b/cas/aidl/default/DescramblerImpl.h
new file mode 100755
index 0000000..2efc1a0
--- /dev/null
+++ b/cas/aidl/default/DescramblerImpl.h
@@ -0,0 +1,56 @@
+/*
+ * 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 <aidl/android/hardware/cas/BnDescrambler.h>
+#include <media/stagefright/foundation/ABase.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace cas {
+
+using namespace ::android;
+using namespace std;
+using ndk::ScopedAStatus;
+
+class DescramblerImpl : public BnDescrambler {
+  public:
+    DescramblerImpl(DescramblerPlugin* plugin);
+    virtual ~DescramblerImpl();
+
+    virtual ScopedAStatus setMediaCasSession(const vector<uint8_t>& in_sessionId) override;
+
+    virtual ScopedAStatus requiresSecureDecoderComponent(const string& in_mime,
+                                                         bool* _aidl_return) override;
+
+    virtual ScopedAStatus descramble(ScramblingControl in_scramblingControl,
+                                     const vector<SubSample>& in_subSamples,
+                                     const SharedBuffer& in_srcBuffer, int64_t in_srcOffset,
+                                     const DestinationBuffer& in_dstBuffer, int64_t in_dstOffset,
+                                     int32_t* _aidl_return) override;
+
+    virtual ScopedAStatus release() override;
+
+  private:
+    shared_ptr<DescramblerPlugin> mPluginHolder;
+
+    DISALLOW_EVIL_CONSTRUCTORS(DescramblerImpl);
+};
+
+}  // namespace cas
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/cas/aidl/default/FactoryLoader.h b/cas/aidl/default/FactoryLoader.h
new file mode 100755
index 0000000..f90b109
--- /dev/null
+++ b/cas/aidl/default/FactoryLoader.h
@@ -0,0 +1,219 @@
+/*
+ * 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 <dirent.h>
+#include <dlfcn.h>
+#include <media/cas/CasAPI.h>
+#include <utils/KeyedVector.h>
+#include <utils/Mutex.h>
+#include "SharedLibrary.h"
+
+using namespace std;
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace cas {
+
+using namespace ::android;
+
+template <class T>
+class FactoryLoader {
+  public:
+    FactoryLoader(const char* name) : mFactory(NULL), mCreateFactoryFuncName(name) {}
+
+    virtual ~FactoryLoader() { closeFactory(); }
+
+    bool findFactoryForScheme(int32_t CA_system_id, shared_ptr<SharedLibrary>* library = NULL,
+                              T** factory = NULL);
+
+    bool enumeratePlugins(vector<AidlCasPluginDescriptor>* results);
+
+  private:
+    typedef T* (*CreateFactoryFunc)();
+
+    Mutex mMapLock;
+    T* mFactory;
+    const char* mCreateFactoryFuncName;
+    shared_ptr<SharedLibrary> mLibrary;
+    KeyedVector<int32_t, String8> mCASystemIdToLibraryPathMap;
+    KeyedVector<String8, shared_ptr<SharedLibrary>> mLibraryPathToOpenLibraryMap;
+
+    bool loadFactoryForSchemeFromPath(const String8& path, int32_t CA_system_id,
+                                      shared_ptr<SharedLibrary>* library, T** factory);
+
+    bool queryPluginsFromPath(const String8& path, vector<AidlCasPluginDescriptor>* results);
+
+    bool openFactory(const String8& path);
+    void closeFactory();
+};
+
+template <class T>
+bool FactoryLoader<T>::findFactoryForScheme(int32_t CA_system_id,
+                                            shared_ptr<SharedLibrary>* library, T** factory) {
+    if (library != NULL) {
+        library->reset();
+    }
+    if (factory != NULL) {
+        *factory = NULL;
+    }
+
+    Mutex::Autolock autoLock(mMapLock);
+
+    // first check cache
+    ssize_t index = mCASystemIdToLibraryPathMap.indexOfKey(CA_system_id);
+    if (index >= 0) {
+        return loadFactoryForSchemeFromPath(mCASystemIdToLibraryPathMap[index], CA_system_id,
+                                            library, factory);
+    }
+
+    // no luck, have to search
+#ifdef __LP64__
+    String8 dirPath("/vendor/lib64/mediacas");
+#else
+    String8 dirPath("/vendor/lib/mediacas");
+#endif
+    DIR* pDir = opendir(dirPath.string());
+
+    if (pDir == NULL) {
+        ALOGE("Failed to open plugin directory %s", dirPath.string());
+        return false;
+    }
+
+    struct dirent* pEntry;
+    while ((pEntry = readdir(pDir))) {
+        String8 pluginPath = dirPath + "/" + pEntry->d_name;
+        if (pluginPath.getPathExtension() == ".so") {
+            if (loadFactoryForSchemeFromPath(pluginPath, CA_system_id, library, factory)) {
+                mCASystemIdToLibraryPathMap.add(CA_system_id, pluginPath);
+                closedir(pDir);
+
+                return true;
+            }
+        }
+    }
+
+    closedir(pDir);
+
+    ALOGE("Failed to find plugin");
+    return false;
+}
+
+template <class T>
+bool FactoryLoader<T>::enumeratePlugins(vector<AidlCasPluginDescriptor>* results) {
+    ALOGI("enumeratePlugins");
+
+    results->clear();
+
+#ifdef __LP64__
+    String8 dirPath("/vendor/lib64/mediacas");
+#else
+    String8 dirPath("/vendor/lib/mediacas");
+#endif
+    DIR* pDir = opendir(dirPath.string());
+
+    if (pDir == NULL) {
+        ALOGE("Failed to open plugin directory %s", dirPath.string());
+        return false;
+    }
+
+    Mutex::Autolock autoLock(mMapLock);
+
+    struct dirent* pEntry;
+    while ((pEntry = readdir(pDir))) {
+        String8 pluginPath = dirPath + "/" + pEntry->d_name;
+        if (pluginPath.getPathExtension() == ".so") {
+            queryPluginsFromPath(pluginPath, results);
+        }
+    }
+    return true;
+}
+
+template <class T>
+bool FactoryLoader<T>::loadFactoryForSchemeFromPath(const String8& path, int32_t CA_system_id,
+                                                    shared_ptr<SharedLibrary>* library,
+                                                    T** factory) {
+    closeFactory();
+
+    if (!openFactory(path) || !mFactory->isSystemIdSupported(CA_system_id)) {
+        closeFactory();
+        return false;
+    }
+
+    if (library != NULL) {
+        *library = mLibrary;
+    }
+    if (factory != NULL) {
+        *factory = mFactory;
+    }
+    return true;
+}
+
+template <class T>
+bool FactoryLoader<T>::queryPluginsFromPath(const String8& path,
+                                            vector<AidlCasPluginDescriptor>* results) {
+    closeFactory();
+
+    vector<CasPluginDescriptor> descriptors;
+    if (!openFactory(path) || mFactory->queryPlugins(&descriptors) != OK) {
+        closeFactory();
+        return false;
+    }
+
+    for (auto it = descriptors.begin(); it != descriptors.end(); it++) {
+        results->push_back(
+                AidlCasPluginDescriptor{.caSystemId = it->CA_system_id, .name = it->name.c_str()});
+    }
+    return true;
+}
+
+template <class T>
+bool FactoryLoader<T>::openFactory(const String8& path) {
+    // get strong pointer to open shared library
+    ssize_t index = mLibraryPathToOpenLibraryMap.indexOfKey(path);
+    if (index >= 0) {
+        mLibrary = mLibraryPathToOpenLibraryMap[index];
+    } else {
+        index = mLibraryPathToOpenLibraryMap.add(path, NULL);
+    }
+
+    if (!mLibrary.get()) {
+        mLibrary = ::ndk::SharedRefBase::make<SharedLibrary>(path);
+        if (!*mLibrary) {
+            return false;
+        }
+
+        mLibraryPathToOpenLibraryMap.replaceValueAt(index, mLibrary);
+    }
+
+    CreateFactoryFunc createFactory = (CreateFactoryFunc)mLibrary->lookup(mCreateFactoryFuncName);
+    if (createFactory == NULL || (mFactory = createFactory()) == NULL) {
+        return false;
+    }
+    return true;
+}
+
+template <class T>
+void FactoryLoader<T>::closeFactory() {
+    delete mFactory;
+    mFactory = NULL;
+    mLibrary.reset();
+}
+
+}  // namespace cas
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/cas/aidl/default/MediaCasService.cpp b/cas/aidl/default/MediaCasService.cpp
new file mode 100755
index 0000000..8285613
--- /dev/null
+++ b/cas/aidl/default/MediaCasService.cpp
@@ -0,0 +1,101 @@
+/*
+ * 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 "android.hardware.cas-MediaCasService"
+
+#include <media/cas/CasAPI.h>
+#include <media/cas/DescramblerAPI.h>
+#include <utils/Log.h>
+
+#include "CasImpl.h"
+#include "DescramblerImpl.h"
+#include "MediaCasService.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace cas {
+
+MediaCasService::MediaCasService()
+    : mCasLoader("createCasFactory"), mDescramblerLoader("createDescramblerFactory") {}
+
+MediaCasService::~MediaCasService() {}
+
+ScopedAStatus MediaCasService::enumeratePlugins(
+        vector<AidlCasPluginDescriptor>* aidlCasPluginDescriptors) {
+    ALOGV("%s", __FUNCTION__);
+
+    mCasLoader.enumeratePlugins(aidlCasPluginDescriptors);
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus MediaCasService::isSystemIdSupported(int32_t CA_system_id, bool* _aidl_return) {
+    ALOGV("isSystemIdSupported: CA_system_id=%d", CA_system_id);
+
+    *_aidl_return = mCasLoader.findFactoryForScheme(CA_system_id);
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus MediaCasService::createPlugin(int32_t CA_system_id,
+                                            const shared_ptr<ICasListener>& listener,
+                                            shared_ptr<ICas>* _aidl_return) {
+    ALOGV("%s: CA_system_id=%d", __FUNCTION__, CA_system_id);
+    if (listener == NULL) ALOGV("%s: Listener is NULL", __FUNCTION__);
+
+    CasFactory* factory;
+    shared_ptr<SharedLibrary> library;
+    if (mCasLoader.findFactoryForScheme(CA_system_id, &library, &factory)) {
+        CasPlugin* plugin = NULL;
+        shared_ptr<CasImpl> casImpl = ::ndk::SharedRefBase::make<CasImpl>(listener);
+        if (factory->createPlugin(CA_system_id, casImpl.get(), &CasImpl::CallBackExt, &plugin) ==
+                    OK &&
+            plugin != NULL) {
+            casImpl->init(plugin);
+            *_aidl_return = casImpl;
+            casImpl->setPluginStatusUpdateCallback();
+        }
+    }
+
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus MediaCasService::isDescramblerSupported(int32_t CA_system_id, bool* _aidl_return) {
+    ALOGV("%s: CA_system_id=%d", __FUNCTION__, CA_system_id);
+
+    *_aidl_return = mDescramblerLoader.findFactoryForScheme(CA_system_id);
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus MediaCasService::createDescrambler(int32_t CA_system_id,
+                                                 shared_ptr<IDescrambler>* _aidl_return) {
+    ALOGV("%s: CA_system_id=%d", __FUNCTION__, CA_system_id);
+
+    DescramblerFactory* factory;
+    shared_ptr<SharedLibrary> library;
+    if (mDescramblerLoader.findFactoryForScheme(CA_system_id, &library, &factory)) {
+        DescramblerPlugin* plugin = NULL;
+        if (factory->createPlugin(CA_system_id, &plugin) == OK && plugin != NULL) {
+            *_aidl_return = ::ndk::SharedRefBase::make<DescramblerImpl>(plugin);
+        }
+    }
+
+    return ScopedAStatus::ok();
+}
+
+}  // namespace cas
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/cas/aidl/default/MediaCasService.h b/cas/aidl/default/MediaCasService.h
new file mode 100755
index 0000000..9662313
--- /dev/null
+++ b/cas/aidl/default/MediaCasService.h
@@ -0,0 +1,59 @@
+/*
+ * 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 <aidl/android/hardware/cas/BnMediaCasService.h>
+#include <media/cas/DescramblerAPI.h>
+
+#include "FactoryLoader.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace cas {
+
+using namespace ::android;
+using namespace std;
+using ndk::ScopedAStatus;
+
+class MediaCasService : public BnMediaCasService {
+  public:
+    MediaCasService();
+
+    virtual ScopedAStatus enumeratePlugins(
+            vector<AidlCasPluginDescriptor>* aidlCasPluginDescriptors) override;
+
+    virtual ScopedAStatus isSystemIdSupported(int32_t in_CA_system_id, bool* _aidl_return) override;
+
+    virtual ScopedAStatus createPlugin(int32_t in_CA_system_id,
+                                       const shared_ptr<ICasListener>& in_listener,
+                                       shared_ptr<ICas>* _aidl_return) override;
+
+    virtual ScopedAStatus isDescramblerSupported(int32_t in_CA_system_id,
+                                                 bool* _aidl_return) override;
+
+    virtual ScopedAStatus createDescrambler(int32_t in_CA_system_id,
+                                            shared_ptr<IDescrambler>* _aidl_return) override;
+
+  private:
+    FactoryLoader<CasFactory> mCasLoader;
+    FactoryLoader<DescramblerFactory> mDescramblerLoader;
+
+    virtual ~MediaCasService();
+};
+
+}  // namespace cas
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/cas/aidl/default/OWNERS b/cas/aidl/default/OWNERS
new file mode 100755
index 0000000..4c55752
--- /dev/null
+++ b/cas/aidl/default/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 1344
+quxiangfang@google.com
+hgchen@google.com
diff --git a/cas/aidl/default/SharedLibrary.cpp b/cas/aidl/default/SharedLibrary.cpp
new file mode 100755
index 0000000..e79f383
--- /dev/null
+++ b/cas/aidl/default/SharedLibrary.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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 "android.hardware.cas-SharedLibrary"
+
+#include "SharedLibrary.h"
+#include <dlfcn.h>
+#include <utils/Log.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace cas {
+
+SharedLibrary::SharedLibrary(const String8& path) {
+    mLibHandle = dlopen(path.string(), RTLD_NOW);
+}
+
+SharedLibrary::~SharedLibrary() {
+    if (mLibHandle != NULL) {
+        dlclose(mLibHandle);
+        mLibHandle = NULL;
+    }
+}
+
+bool SharedLibrary::operator!() const {
+    return mLibHandle == NULL;
+}
+
+void* SharedLibrary::lookup(const char* symbol) const {
+    if (!mLibHandle) {
+        return NULL;
+    }
+    // Clear last error before we load the symbol again,
+    // in case the caller didn't retrieve it.
+    (void)dlerror();
+    return dlsym(mLibHandle, symbol);
+}
+
+const char* SharedLibrary::lastError() const {
+    const char* error = dlerror();
+    return error ? error : "No errors or unknown error";
+}
+
+}  // namespace cas
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/cas/aidl/default/SharedLibrary.h b/cas/aidl/default/SharedLibrary.h
new file mode 100755
index 0000000..d367866
--- /dev/null
+++ b/cas/aidl/default/SharedLibrary.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/binder_interface_utils.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/String8.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace cas {
+
+using namespace ::android;
+
+class SharedLibrary : public ndk::SharedRefBase {
+  public:
+    explicit SharedLibrary(const String8& path);
+    ~SharedLibrary();
+
+    bool operator!() const;
+    void* lookup(const char* symbol) const;
+    const char* lastError() const;
+
+  private:
+    void* mLibHandle;
+    DISALLOW_EVIL_CONSTRUCTORS(SharedLibrary);
+};
+
+}  // namespace cas
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/cas/aidl/default/TypeConvert.cpp b/cas/aidl/default/TypeConvert.cpp
new file mode 100755
index 0000000..4f7005f
--- /dev/null
+++ b/cas/aidl/default/TypeConvert.cpp
@@ -0,0 +1,110 @@
+/*
+ * 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 "android.hardware.cas-TypeConvert"
+
+#include <aidl/android/hardware/cas/Status.h>
+#include <utils/Log.h>
+
+#include "TypeConvert.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace cas {
+
+ScopedAStatus toStatus(status_t legacyStatus) {
+    int status;
+    switch (legacyStatus) {
+        case OK:
+            return ndk::ScopedAStatus::ok();
+        case ERROR_CAS_NO_LICENSE:
+            status = Status::ERROR_CAS_NO_LICENSE;
+            break;
+        case ERROR_CAS_LICENSE_EXPIRED:
+            status = Status::ERROR_CAS_LICENSE_EXPIRED;
+            break;
+        case ERROR_CAS_SESSION_NOT_OPENED:
+            status = Status::ERROR_CAS_SESSION_NOT_OPENED;
+            break;
+        case ERROR_CAS_CANNOT_HANDLE:
+            status = Status::ERROR_CAS_CANNOT_HANDLE;
+            break;
+        case ERROR_CAS_TAMPER_DETECTED:
+            status = Status::ERROR_CAS_INVALID_STATE;
+            break;
+        case BAD_VALUE:
+            status = Status::BAD_VALUE;
+            break;
+        case ERROR_CAS_NOT_PROVISIONED:
+            status = Status::ERROR_CAS_NOT_PROVISIONED;
+            break;
+        case ERROR_CAS_RESOURCE_BUSY:
+            status = Status::ERROR_CAS_RESOURCE_BUSY;
+            break;
+        case ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION:
+            status = Status::ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION;
+            break;
+        case ERROR_CAS_DEVICE_REVOKED:
+            status = Status::ERROR_CAS_DEVICE_REVOKED;
+            break;
+        case ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED:
+            status = Status::ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED;
+            break;
+        case ERROR_CAS_DECRYPT:
+            status = Status::ERROR_CAS_DECRYPT;
+            break;
+        case ERROR_CAS_NEED_ACTIVATION:
+            status = Status::ERROR_CAS_NEED_ACTIVATION;
+            break;
+        case ERROR_CAS_NEED_PAIRING:
+            status = Status::ERROR_CAS_NEED_PAIRING;
+            break;
+        case ERROR_CAS_NO_CARD:
+            status = Status::ERROR_CAS_NO_CARD;
+            break;
+        case ERROR_CAS_CARD_MUTE:
+            status = Status::ERROR_CAS_CARD_MUTE;
+            break;
+        case ERROR_CAS_CARD_INVALID:
+            status = Status::ERROR_CAS_CARD_INVALID;
+            break;
+        case ERROR_CAS_BLACKOUT:
+            status = Status::ERROR_CAS_BLACKOUT;
+            break;
+        default:
+            ALOGW("Unable to convert legacy status: %d, defaulting to UNKNOWN", legacyStatus);
+            status = Status::ERROR_CAS_UNKNOWN;
+            break;
+    }
+    return ScopedAStatus::fromServiceSpecificError(status);
+}
+
+String8 sessionIdToString(const std::vector<uint8_t>& sessionId) {
+    String8 result;
+    for (auto it = sessionId.begin(); it != sessionId.end(); it++) {
+        result.appendFormat("%02x ", *it);
+    }
+    if (result.isEmpty()) {
+        result.append("(null)");
+    }
+    return result;
+}
+
+}  // namespace cas
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/cas/aidl/default/TypeConvert.h b/cas/aidl/default/TypeConvert.h
new file mode 100755
index 0000000..ebfa286
--- /dev/null
+++ b/cas/aidl/default/TypeConvert.h
@@ -0,0 +1,36 @@
+/*
+ * 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/binder_interface_utils.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/String8.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace cas {
+
+using namespace ::android;
+using ndk::ScopedAStatus;
+
+ScopedAStatus toStatus(status_t legacyStatus);
+
+String8 sessionIdToString(const std::vector<uint8_t>& sessionId);
+
+}  // namespace cas
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/cas/aidl/default/android.hardware.cas-service.xml b/cas/aidl/default/android.hardware.cas-service.xml
new file mode 100755
index 0000000..6389fe2
--- /dev/null
+++ b/cas/aidl/default/android.hardware.cas-service.xml
@@ -0,0 +1,6 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.cas</name>
+        <fqname>IMediaCasService/default</fqname>
+    </hal>
+</manifest>
diff --git a/cas/aidl/default/cas-default-lazy.rc b/cas/aidl/default/cas-default-lazy.rc
new file mode 100755
index 0000000..60b59ca
--- /dev/null
+++ b/cas/aidl/default/cas-default-lazy.rc
@@ -0,0 +1,9 @@
+service vendor.cas-default-lazy /vendor/bin/hw/android.hardware.cas-service.example-lazy
+    interface aidl android.hardware.cas.IMediaCasService/default
+    class hal
+    user media
+    group mediadrm drmrpc
+    ioprio rt 4
+    task_profiles ProcessCapacityHigh
+    oneshot
+    disabled
diff --git a/cas/aidl/default/cas-default.rc b/cas/aidl/default/cas-default.rc
new file mode 100755
index 0000000..e00b9c5
--- /dev/null
+++ b/cas/aidl/default/cas-default.rc
@@ -0,0 +1,7 @@
+service vendor.cas-default /vendor/bin/hw/android.hardware.cas-service.example
+    interface aidl android.hardware.cas.IMediaCasService/default
+    class hal
+    user media
+    group mediadrm drmrpc
+    ioprio rt 4
+    task_profiles ProcessCapacityHigh
diff --git a/cas/aidl/default/fuzzer.cpp b/cas/aidl/default/fuzzer.cpp
new file mode 100755
index 0000000..db1369e
--- /dev/null
+++ b/cas/aidl/default/fuzzer.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 <MediaCasService.h>

+#include <fuzzbinder/libbinder_ndk_driver.h>

+#include <fuzzer/FuzzedDataProvider.h>

+

+using aidl::android::hardware::cas::MediaCasService;

+using android::fuzzService;

+using ndk::SharedRefBase;

+

+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {

+    auto mediaCasServiceAidl = SharedRefBase::make<MediaCasService>();

+

+    fuzzService(mediaCasServiceAidl->asBinder().get(), FuzzedDataProvider(data, size));

+

+    return 0;

+}
\ No newline at end of file
diff --git a/cas/aidl/default/service.cpp b/cas/aidl/default/service.cpp
new file mode 100755
index 0000000..bed2f01
--- /dev/null
+++ b/cas/aidl/default/service.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 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.
+ */
+
+#ifdef LAZY_SERVICE
+const bool kLazyService = true;
+#define LOG_TAG "android.hardware.cas-service.example-lazy"
+#else
+const bool kLazyService = false;
+#define LOG_TAG "android.hardware.cas-service.example"
+#endif
+
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include "MediaCasService.h"
+
+using aidl::android::hardware::cas::MediaCasService;
+
+int main() {
+    ABinderProcess_setThreadPoolMaxThreadCount(8);
+
+    // Setup hwbinder service
+    std::shared_ptr<MediaCasService> service = ::ndk::SharedRefBase::make<MediaCasService>();
+
+    const std::string instance = std::string() + MediaCasService::descriptor + "/default";
+    binder_status_t status;
+
+    if (kLazyService) {
+        status = AServiceManager_registerLazyService(service->asBinder().get(), instance.c_str());
+    } else {
+        status = AServiceManager_addService(service->asBinder().get(), instance.c_str());
+    }
+    LOG_ALWAYS_FATAL_IF(status != STATUS_OK, "Error while registering cas service: %d", status);
+
+    ABinderProcess_joinThreadPool();
+    return 0;
+}
diff --git a/cas/aidl/vts/functional/Android.bp b/cas/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..295ae7c
--- /dev/null
+++ b/cas/aidl/vts/functional/Android.bp
@@ -0,0 +1,45 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+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"],
+}
+
+cc_test {
+    name: "VtsHalCasAidlTargetTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: ["VtsHalCasAidlTargetTest.cpp"],
+    static_libs: [
+        "android.hardware.cas-V1-ndk",
+        "android.hardware.common-V2-ndk",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+    disable_framework: true,
+}
diff --git a/cas/aidl/vts/functional/OWNERS b/cas/aidl/vts/functional/OWNERS
new file mode 100644
index 0000000..4c55752
--- /dev/null
+++ b/cas/aidl/vts/functional/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 1344
+quxiangfang@google.com
+hgchen@google.com
diff --git a/cas/aidl/vts/functional/VtsHalCasAidlTargetTest.cpp b/cas/aidl/vts/functional/VtsHalCasAidlTargetTest.cpp
new file mode 100644
index 0000000..266b55d
--- /dev/null
+++ b/cas/aidl/vts/functional/VtsHalCasAidlTargetTest.cpp
@@ -0,0 +1,809 @@
+/*
+ * 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 "mediacas_aidl_hal_test"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/cas/BnCasListener.h>
+#include <aidl/android/hardware/cas/IMediaCasService.h>
+#include <aidl/android/hardware/cas/Status.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <binder/ParcelFileDescriptor.h>
+#include <cutils/ashmem.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+
+#define CLEAR_KEY_SYSTEM_ID 0xF6D8
+#define INVALID_SYSTEM_ID 0
+#define WAIT_TIMEOUT 3000000000
+
+#define PROVISION_STR                                      \
+    "{                                                   " \
+    "  \"id\": 21140844,                                 " \
+    "  \"name\": \"Test Title\",                         " \
+    "  \"lowercase_organization_name\": \"Android\",     " \
+    "  \"asset_key\": {                                  " \
+    "  \"encryption_key\": \"nezAr3CHFrmBR9R8Tedotw==\"  " \
+    "  },                                                " \
+    "  \"cas_type\": 1,                                  " \
+    "  \"track_types\": [ ]                              " \
+    "}                                                   "
+
+using aidl::android::hardware::common::Ashmem;
+using android::Mutex;
+using namespace aidl::android::hardware::cas;
+using namespace ndk;
+using namespace std;
+using namespace testing;
+
+const uint8_t kEcmBinaryBuffer[] = {
+        0x00, 0x00, 0x01, 0xf0, 0x00, 0x50, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x46, 0x00,
+        0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x27, 0x10, 0x02, 0x00,
+        0x01, 0x77, 0x01, 0x42, 0x95, 0x6c, 0x0e, 0xe3, 0x91, 0xbc, 0xfd, 0x05, 0xb1, 0x60, 0x4f,
+        0x17, 0x82, 0xa4, 0x86, 0x9b, 0x23, 0x56, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+        0x27, 0x10, 0x02, 0x00, 0x01, 0x77, 0x01, 0x42, 0x95, 0x6c, 0xd7, 0x43, 0x62, 0xf8, 0x1c,
+        0x62, 0x19, 0x05, 0xc7, 0x3a, 0x42, 0xcd, 0xfd, 0xd9, 0x13, 0x48,
+};
+
+const SubSample kSubSamples[] = {{162, 0}, {0, 184}, {0, 184}};
+
+const uint8_t kInBinaryBuffer[] = {
+        0x00, 0x00, 0x00, 0x01, 0x09, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xc0, 0x1e, 0xdb,
+        0x01, 0x40, 0x16, 0xec, 0x04, 0x40, 0x00, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x0f, 0x03,
+        0xc5, 0x8b, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x68, 0xca, 0x8c, 0xb2, 0x00, 0x00, 0x01, 0x06,
+        0x05, 0xff, 0xff, 0x70, 0xdc, 0x45, 0xe9, 0xbd, 0xe6, 0xd9, 0x48, 0xb7, 0x96, 0x2c, 0xd8,
+        0x20, 0xd9, 0x23, 0xee, 0xef, 0x78, 0x32, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x63, 0x6f, 0x72,
+        0x65, 0x20, 0x31, 0x34, 0x32, 0x20, 0x2d, 0x20, 0x48, 0x2e, 0x32, 0x36, 0x34, 0x2f, 0x4d,
+        0x50, 0x45, 0x47, 0x2d, 0x34, 0x20, 0x41, 0x56, 0x43, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x63,
+        0x20, 0x2d, 0x20, 0x43, 0x6f, 0x70, 0x79, 0x6c, 0x65, 0x66, 0x74, 0x20, 0x32, 0x30, 0x30,
+        0x33, 0x2d, 0x32, 0x30, 0x31, 0x34, 0x20, 0x2d, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+        0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x6c, 0x61, 0x6e, 0x2e, 0x6f,
+        0x72, 0x67, 0x2f, 0x78, 0x32, 0x36, 0x34, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x6e, 0x45, 0x21,
+        0x82, 0x38, 0xf0, 0x9d, 0x7d, 0x96, 0xe6, 0x94, 0xae, 0xe2, 0x87, 0x8f, 0x04, 0x49, 0xe5,
+        0xf6, 0x8c, 0x8b, 0x9a, 0x10, 0x18, 0xba, 0x94, 0xe9, 0x22, 0x31, 0x04, 0x7e, 0x60, 0x5b,
+        0xc4, 0x24, 0x00, 0x90, 0x62, 0x0d, 0xdc, 0x85, 0x74, 0x75, 0x78, 0xd0, 0x14, 0x08, 0xcb,
+        0x02, 0x1d, 0x7d, 0x9d, 0x34, 0xe8, 0x81, 0xb9, 0xf7, 0x09, 0x28, 0x79, 0x29, 0x8d, 0xe3,
+        0x14, 0xed, 0x5f, 0xca, 0xaf, 0xf4, 0x1c, 0x49, 0x15, 0xe1, 0x80, 0x29, 0x61, 0x76, 0x80,
+        0x43, 0xf8, 0x58, 0x53, 0x40, 0xd7, 0x31, 0x6d, 0x61, 0x81, 0x41, 0xe9, 0x77, 0x9f, 0x9c,
+        0xe1, 0x6d, 0xf2, 0xee, 0xd9, 0xc8, 0x67, 0xd2, 0x5f, 0x48, 0x73, 0xe3, 0x5c, 0xcd, 0xa7,
+        0x45, 0x58, 0xbb, 0xdd, 0x28, 0x1d, 0x68, 0xfc, 0xb4, 0xc6, 0xf6, 0x92, 0xf6, 0x30, 0x03,
+        0xaa, 0xe4, 0x32, 0xf6, 0x34, 0x51, 0x4b, 0x0f, 0x8c, 0xf9, 0xac, 0x98, 0x22, 0xfb, 0x49,
+        0xc8, 0xbf, 0xca, 0x8c, 0x80, 0x86, 0x5d, 0xd7, 0xa4, 0x52, 0xb1, 0xd9, 0xa6, 0x04, 0x4e,
+        0xb3, 0x2d, 0x1f, 0xb8, 0x35, 0xcc, 0x45, 0x6d, 0x9c, 0x20, 0xa7, 0xa4, 0x34, 0x59, 0x72,
+        0xe3, 0xae, 0xba, 0x49, 0xde, 0xd1, 0xaa, 0xee, 0x3d, 0x77, 0xfc, 0x5d, 0xc6, 0x1f, 0x9d,
+        0xac, 0xc2, 0x15, 0x66, 0xb8, 0xe1, 0x54, 0x4e, 0x74, 0x93, 0xdb, 0x9a, 0x24, 0x15, 0x6e,
+        0x20, 0xa3, 0x67, 0x3e, 0x5a, 0x24, 0x41, 0x5e, 0xb0, 0xe6, 0x35, 0x87, 0x1b, 0xc8, 0x7a,
+        0xf9, 0x77, 0x65, 0xe0, 0x01, 0xf2, 0x4c, 0xe4, 0x2b, 0xa9, 0x64, 0x96, 0x96, 0x0b, 0x46,
+        0xca, 0xea, 0x79, 0x0e, 0x78, 0xa3, 0x5f, 0x43, 0xfc, 0x47, 0x6a, 0x12, 0xfa, 0xc4, 0x33,
+        0x0e, 0x88, 0x1c, 0x19, 0x3a, 0x00, 0xc3, 0x4e, 0xb5, 0xd8, 0xfa, 0x8e, 0xf1, 0xbc, 0x3d,
+        0xb2, 0x7e, 0x50, 0x8d, 0x67, 0xc3, 0x6b, 0xed, 0xe2, 0xea, 0xa6, 0x1f, 0x25, 0x24, 0x7c,
+        0x94, 0x74, 0x50, 0x49, 0xe3, 0xc6, 0x58, 0x2e, 0xfd, 0x28, 0xb4, 0xc6, 0x73, 0xb1, 0x53,
+        0x74, 0x27, 0x94, 0x5c, 0xdf, 0x69, 0xb7, 0xa1, 0xd7, 0xf5, 0xd3, 0x8a, 0x2c, 0x2d, 0xb4,
+        0x5e, 0x8a, 0x16, 0x14, 0x54, 0x64, 0x6e, 0x00, 0x6b, 0x11, 0x59, 0x8a, 0x63, 0x38, 0x80,
+        0x76, 0xc3, 0xd5, 0x59, 0xf7, 0x3f, 0xd2, 0xfa, 0xa5, 0xca, 0x82, 0xff, 0x4a, 0x62, 0xf0,
+        0xe3, 0x42, 0xf9, 0x3b, 0x38, 0x27, 0x8a, 0x89, 0xaa, 0x50, 0x55, 0x4b, 0x29, 0xf1, 0x46,
+        0x7c, 0x75, 0xef, 0x65, 0xaf, 0x9b, 0x0d, 0x6d, 0xda, 0x25, 0x94, 0x14, 0xc1, 0x1b, 0xf0,
+        0xc5, 0x4c, 0x24, 0x0e, 0x65,
+};
+
+const uint8_t kOutRefBinaryBuffer[] = {
+        0x00, 0x00, 0x00, 0x01, 0x09, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xc0, 0x1e, 0xdb,
+        0x01, 0x40, 0x16, 0xec, 0x04, 0x40, 0x00, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x0f, 0x03,
+        0xc5, 0x8b, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x68, 0xca, 0x8c, 0xb2, 0x00, 0x00, 0x01, 0x06,
+        0x05, 0xff, 0xff, 0x70, 0xdc, 0x45, 0xe9, 0xbd, 0xe6, 0xd9, 0x48, 0xb7, 0x96, 0x2c, 0xd8,
+        0x20, 0xd9, 0x23, 0xee, 0xef, 0x78, 0x32, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x63, 0x6f, 0x72,
+        0x65, 0x20, 0x31, 0x34, 0x32, 0x20, 0x2d, 0x20, 0x48, 0x2e, 0x32, 0x36, 0x34, 0x2f, 0x4d,
+        0x50, 0x45, 0x47, 0x2d, 0x34, 0x20, 0x41, 0x56, 0x43, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x63,
+        0x20, 0x2d, 0x20, 0x43, 0x6f, 0x70, 0x79, 0x6c, 0x65, 0x66, 0x74, 0x20, 0x32, 0x30, 0x30,
+        0x33, 0x2d, 0x32, 0x30, 0x31, 0x34, 0x20, 0x2d, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+        0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x6c, 0x61, 0x6e, 0x2e, 0x6f,
+        0x72, 0x67, 0x2f, 0x78, 0x32, 0x36, 0x34, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x20, 0x2d, 0x20,
+        0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3a, 0x20, 0x63, 0x61, 0x62, 0x61, 0x63, 0x3d,
+        0x30, 0x20, 0x72, 0x65, 0x66, 0x3d, 0x32, 0x20, 0x64, 0x65, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
+        0x3d, 0x31, 0x3a, 0x30, 0x3a, 0x30, 0x20, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x65, 0x3d,
+        0x30, 0x78, 0x31, 0x3a, 0x30, 0x78, 0x31, 0x31, 0x31, 0x20, 0x6d, 0x65, 0x3d, 0x68, 0x65,
+        0x78, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x65, 0x3d, 0x37, 0x20, 0x70, 0x73, 0x79, 0x3d, 0x31,
+        0x20, 0x70, 0x73, 0x79, 0x5f, 0x72, 0x64, 0x3d, 0x31, 0x2e, 0x30, 0x30, 0x3a, 0x30, 0x2e,
+        0x30, 0x30, 0x20, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x3d, 0x31, 0x20,
+        0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x3d, 0x31, 0x36, 0x20, 0x63, 0x68, 0x72,
+        0x6f, 0x6d, 0x61, 0x5f, 0x6d, 0x65, 0x3d, 0x31, 0x20, 0x74, 0x72, 0x65, 0x6c, 0x6c, 0x69,
+        0x73, 0x3d, 0x31, 0x20, 0x38, 0x78, 0x38, 0x64, 0x63, 0x74, 0x3d, 0x30, 0x20, 0x63, 0x71,
+        0x6d, 0x3d, 0x30, 0x20, 0x64, 0x65, 0x61, 0x64, 0x7a, 0x6f, 0x6e, 0x65, 0x3d, 0x32, 0x31,
+        0x2c, 0x31, 0x31, 0x20, 0x66, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x73, 0x6b, 0x69, 0x70, 0x3d,
+        0x31, 0x20, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x5f, 0x71, 0x70, 0x5f, 0x6f, 0x66, 0x66,
+        0x73, 0x65, 0x74, 0x3d, 0x2d, 0x32, 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d,
+        0x36, 0x30, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x61, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x74, 0x68,
+        0x72, 0x65, 0x61, 0x64, 0x73, 0x3d, 0x35, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x64, 0x5f,
+        0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d, 0x30, 0x20, 0x6e, 0x72, 0x3d, 0x30, 0x20,
+        0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x3d, 0x31, 0x20, 0x69, 0x6e, 0x74, 0x65,
+        0x72, 0x6c, 0x61, 0x63, 0x65, 0x64, 0x3d, 0x30, 0x20, 0x62, 0x6c, 0x75, 0x72, 0x61, 0x79,
+        0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x3d, 0x30, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74,
+        0x72, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x3d, 0x30, 0x20,
+        0x62, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x3d, 0x30, 0x20, 0x77, 0x65, 0x69, 0x67, 0x68,
+        0x74, 0x70, 0x3d, 0x30, 0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74, 0x3d, 0x32, 0x35, 0x30,
+        0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74, 0x5f, 0x6d, 0x69, 0x6e, 0x3d, 0x32, 0x35, 0x20,
+        0x73, 0x63, 0x65, 0x6e, 0x65,
+};
+
+class MediaCasListener : public BnCasListener {
+  public:
+    virtual ScopedAStatus onEvent(int32_t event, int32_t arg,
+                                  const vector<uint8_t>& data) override {
+        Mutex::Autolock autoLock(mMsgLock);
+        mEvent = event;
+        mEventArg = arg;
+        mEventData = data;
+
+        mEventReceived = true;
+        mMsgCondition.signal();
+        return ScopedAStatus::ok();
+    }
+
+    virtual ScopedAStatus onSessionEvent(const vector<uint8_t>& sessionId, int32_t event,
+                                         int32_t arg, const vector<uint8_t>& data) override {
+        Mutex::Autolock autoLock(mMsgLock);
+        mSessionId = sessionId;
+        mEvent = event;
+        mEventArg = arg;
+        mEventData = data;
+
+        mEventReceived = true;
+        mMsgCondition.signal();
+        return ScopedAStatus::ok();
+    }
+
+    virtual ScopedAStatus onStatusUpdate(StatusEvent event, int32_t arg) override {
+        Mutex::Autolock autoLock(mMsgLock);
+        mStatusEvent = event;
+        mEventArg = arg;
+
+        mEventReceived = true;
+        mMsgCondition.signal();
+        return ScopedAStatus::ok();
+    }
+
+    void testEventEcho(shared_ptr<ICas>& mediaCas, int32_t& event, int32_t& eventArg,
+                       vector<uint8_t>& eventData);
+
+    void testSessionEventEcho(shared_ptr<ICas>& mediaCas, const vector<uint8_t>& sessionId,
+                              int32_t& event, int32_t& eventArg, vector<uint8_t>& eventData);
+
+    void testStatusUpdate(shared_ptr<ICas>& mediaCas, vector<uint8_t>* sessionId,
+                          SessionIntent intent, ScramblingMode mode);
+
+  private:
+    int32_t mEvent = -1;
+    int32_t mEventArg = -1;
+    StatusEvent mStatusEvent;
+    bool mEventReceived = false;
+    vector<uint8_t> mEventData;
+    vector<uint8_t> mSessionId;
+    Mutex mMsgLock;
+    android::Condition mMsgCondition;
+};
+
+void MediaCasListener::testEventEcho(shared_ptr<ICas>& mediaCas, int32_t& event, int32_t& eventArg,
+                                     vector<uint8_t>& eventData) {
+    mEventReceived = false;
+    auto returnStatus = mediaCas->sendEvent(event, eventArg, eventData);
+    EXPECT_TRUE(returnStatus.isOk());
+
+    Mutex::Autolock autoLock(mMsgLock);
+    while (!mEventReceived) {
+        if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+            ADD_FAILURE() << "event not received within timeout";
+            return;
+        }
+    }
+
+    EXPECT_EQ(mEvent, event);
+    EXPECT_EQ(mEventArg, eventArg);
+    EXPECT_TRUE(mEventData == eventData);
+}
+
+void MediaCasListener::testSessionEventEcho(shared_ptr<ICas>& mediaCas,
+                                            const vector<uint8_t>& sessionId, int32_t& event,
+                                            int32_t& eventArg, vector<uint8_t>& eventData) {
+    mEventReceived = false;
+    EXPECT_TRUE(mediaCas->sendSessionEvent(sessionId, event, eventArg, eventData).isOk());
+
+    Mutex::Autolock autoLock(mMsgLock);
+    while (!mEventReceived) {
+        if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+            ADD_FAILURE() << "event not received within timeout";
+            return;
+        }
+    }
+
+    EXPECT_TRUE(mSessionId == sessionId);
+    EXPECT_EQ(mEvent, event);
+    EXPECT_EQ(mEventArg, eventArg);
+    EXPECT_TRUE(mEventData == eventData);
+}
+
+void MediaCasListener::testStatusUpdate(shared_ptr<ICas>& mediaCas, vector<uint8_t>* sessionId,
+                                        SessionIntent intent, ScramblingMode mode) {
+    mEventReceived = false;
+    EXPECT_TRUE(mediaCas->openSession(intent, mode, sessionId).isOk());
+
+    Mutex::Autolock autoLock(mMsgLock);
+    while (!mEventReceived) {
+        if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+            ADD_FAILURE() << "event not received within timeout";
+            return;
+        }
+    }
+    EXPECT_EQ(mStatusEvent, static_cast<StatusEvent>(intent));
+    EXPECT_EQ(mEventArg, static_cast<int32_t>(mode));
+}
+
+class MediaCasAidlTest : public testing::TestWithParam<string> {
+  public:
+    virtual void SetUp() override {
+        if (AServiceManager_isDeclared(GetParam().c_str())) {
+            SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
+            mService = IMediaCasService::fromBinder(binder);
+        } else {
+            mService = nullptr;
+        }
+        ASSERT_NE(mService, nullptr);
+    }
+
+    shared_ptr<IMediaCasService> mService = nullptr;
+
+  protected:
+    static void description(const string& description) {
+        RecordProperty("description", description);
+    }
+
+    shared_ptr<ICas> mMediaCas;
+    shared_ptr<IDescrambler> mDescrambler;
+    shared_ptr<MediaCasListener> mCasListener;
+    typedef struct _OobInputTestParams {
+        const SubSample* subSamples;
+        int32_t numSubSamples;
+        int64_t imemSizeActual;
+        int64_t imemOffset;
+        int64_t imemSize;
+        int64_t srcOffset;
+        int64_t dstOffset;
+    } OobInputTestParams;
+
+    AssertionResult createCasPlugin(int32_t caSystemId);
+    AssertionResult openCasSession(vector<uint8_t>* sessionId, SessionIntent intent,
+                                   ScramblingMode mode);
+    AssertionResult descrambleTestInputBuffer(const shared_ptr<IDescrambler>& descrambler,
+                                              ScopedAStatus& descrambleStatus, uint8_t*& inMemory);
+    AssertionResult descrambleTestOobInput(const shared_ptr<IDescrambler>& descrambler,
+                                           ScopedAStatus& descrambleStatus,
+                                           const OobInputTestParams& params);
+};
+
+AssertionResult MediaCasAidlTest::createCasPlugin(int32_t caSystemId) {
+    bool isSystemIdSupported;
+    auto status = mService->isSystemIdSupported(caSystemId, &isSystemIdSupported);
+    bool skipDescrambler = false;
+    if (!status.isOk() || !isSystemIdSupported) {
+        return AssertionFailure();
+    }
+    bool isDescramblerSupported;
+    status = mService->isDescramblerSupported(caSystemId, &isDescramblerSupported);
+    if (!status.isOk() || !isDescramblerSupported) {
+        ALOGI("Skip Descrambler test since it's not required in cas.");
+        mDescrambler = nullptr;
+        skipDescrambler = true;
+    }
+
+    mCasListener = SharedRefBase::make<MediaCasListener>();
+    status = mService->createPlugin(caSystemId, mCasListener, &mMediaCas);
+    if (!status.isOk()) {
+        return AssertionFailure();
+    }
+    if (mMediaCas == nullptr) {
+        return AssertionFailure();
+    }
+
+    if (skipDescrambler) {
+        return AssertionSuccess();
+    }
+
+    status = mService->createDescrambler(caSystemId, &mDescrambler);
+    if (!status.isOk()) {
+        return AssertionFailure();
+    }
+
+    return AssertionResult(mDescrambler != nullptr);
+}
+
+AssertionResult MediaCasAidlTest::openCasSession(vector<uint8_t>* sessionId, SessionIntent intent,
+                                                 ScramblingMode mode) {
+    return AssertionResult(mMediaCas->openSession(intent, mode, sessionId).isOk());
+}
+
+AssertionResult MediaCasAidlTest::descrambleTestInputBuffer(
+        const shared_ptr<IDescrambler>& descrambler, ScopedAStatus& descrambleStatus,
+        uint8_t*& sharedMemory) {
+    vector<SubSample> subSample(kSubSamples,
+                                kSubSamples + (sizeof(kSubSamples) / sizeof(SubSample)));
+
+    int size = sizeof(kInBinaryBuffer);
+    auto fd = ashmem_create_region("vts-cas", size);
+    if (fd < 0) {
+        ALOGE("ashmem_create_region failed");
+        return AssertionFailure();
+    }
+
+    sharedMemory =
+            static_cast<uint8_t*>(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
+    if (sharedMemory == reinterpret_cast<uint8_t*>(MAP_FAILED)) {
+        ALOGE("mmap failed");
+        return AssertionFailure();
+    }
+
+    memcpy(sharedMemory, kInBinaryBuffer, size);
+
+    auto dupFd = dup(fd);
+
+    SharedBuffer srcBuffer = {.heapBase.fd = ScopedFileDescriptor(std::move(fd)),
+                              .heapBase.size = size,
+                              .offset = 0,
+                              .size = size};
+
+    SharedBuffer dupBuffer = {.heapBase.fd = ScopedFileDescriptor(dupFd),
+                              .heapBase.size = size,
+                              .offset = 0,
+                              .size = size};
+
+    DestinationBuffer dstBuffer;
+    dstBuffer.set<DestinationBuffer::nonsecureMemory>(std::move(dupBuffer));
+
+    int32_t outBytes;
+    descrambleStatus = descrambler->descramble(ScramblingControl::EVENKEY /*2*/, subSample,
+                                               srcBuffer, 0, dstBuffer, 0, &outBytes);
+    if (!descrambleStatus.isOk()) {
+        ALOGI("descramble failed, status=%d, outBytes=%u, error=%s", descrambleStatus.getStatus(),
+              outBytes, descrambleStatus.getDescription().c_str());
+    }
+    return AssertionResult(descrambleStatus.isOk());
+}
+
+AssertionResult MediaCasAidlTest::descrambleTestOobInput(
+        const shared_ptr<IDescrambler>& descrambler, ScopedAStatus& descrambleStatus,
+        const OobInputTestParams& params) {
+    vector<SubSample> subSample(params.subSamples, params.subSamples + params.numSubSamples);
+
+    auto fd = ashmem_create_region("vts-cas", params.imemSizeActual);
+    if (fd < 0) {
+        ALOGE("ashmem_create_region failed");
+        return AssertionFailure();
+    }
+
+    auto dupFd = dup(fd);
+
+    SharedBuffer srcBuffer = {.heapBase.fd = ScopedFileDescriptor(std::move(fd)),
+                              .heapBase.size = params.imemSizeActual,
+                              .offset = params.imemOffset,
+                              .size = params.imemSize};
+
+    SharedBuffer dupBuffer = {.heapBase.fd = ScopedFileDescriptor(dupFd),
+                              .heapBase.size = params.imemSizeActual,
+                              .offset = params.imemOffset,
+                              .size = params.imemSize};
+
+    DestinationBuffer dstBuffer;
+    dstBuffer.set<DestinationBuffer::nonsecureMemory>(std::move(dupBuffer));
+
+    int32_t outBytes;
+    descrambleStatus =
+            descrambler->descramble(ScramblingControl::EVENKEY /*2*/, subSample, srcBuffer,
+                                    params.srcOffset, dstBuffer, params.dstOffset, &outBytes);
+    if (!descrambleStatus.isOk()) {
+        ALOGI("descramble failed, status=%d, outBytes=%u, error=%s", descrambleStatus.getStatus(),
+              outBytes, descrambleStatus.getDescription().c_str());
+    }
+    return AssertionResult(descrambleStatus.isOk());
+}
+
+TEST_P(MediaCasAidlTest, EnumeratePlugins) {
+    description("Test enumerate plugins");
+
+    vector<AidlCasPluginDescriptor> descriptors;
+    EXPECT_TRUE(mService->enumeratePlugins(&descriptors).isOk());
+
+    if (descriptors.size() == 0) {
+        ALOGW("[   WARN   ] enumeratePlugins list empty");
+        return;
+    }
+
+    for (size_t i = 0; i < descriptors.size(); i++) {
+        int32_t caSystemId = descriptors[i].caSystemId;
+
+        ASSERT_TRUE(createCasPlugin(caSystemId));
+    }
+}
+
+TEST_P(MediaCasAidlTest, TestInvalidSystemIdFails) {
+    description("Test failure for invalid system ID");
+
+    bool isSystemIdSupported;
+    auto status = mService->isSystemIdSupported(INVALID_SYSTEM_ID, &isSystemIdSupported);
+
+    EXPECT_TRUE(status.isOk());
+    ASSERT_FALSE(isSystemIdSupported);
+
+    bool isDescramblerSupported;
+    status = mService->isDescramblerSupported(INVALID_SYSTEM_ID, &isDescramblerSupported);
+
+    EXPECT_TRUE(status.isOk());
+    ASSERT_FALSE(isDescramblerSupported);
+
+    shared_ptr<ICas> mediaCas;
+    shared_ptr<MediaCasListener> casListener = SharedRefBase::make<MediaCasListener>();
+    status = mService->createPlugin(INVALID_SYSTEM_ID, casListener, &mediaCas);
+    ASSERT_TRUE(status.isOk());
+    EXPECT_EQ(mediaCas, nullptr);
+
+    shared_ptr<IDescrambler> descrambler;
+    status = mService->createDescrambler(INVALID_SYSTEM_ID, &descrambler);
+    ASSERT_TRUE(status.isOk());
+    EXPECT_EQ(descrambler, nullptr);
+}
+
+TEST_P(MediaCasAidlTest, TestClearKeyPluginInstalled) {
+    description("Test if ClearKey plugin is installed");
+
+    vector<AidlCasPluginDescriptor> descriptors;
+    EXPECT_TRUE(mService->enumeratePlugins(&descriptors).isOk());
+
+    if (descriptors.size() == 0) {
+        ALOGW("[   WARN   ] enumeratePlugins list empty");
+    }
+
+    for (size_t i = 0; i < descriptors.size(); i++) {
+        int32_t caSystemId = descriptors[i].caSystemId;
+        if (CLEAR_KEY_SYSTEM_ID == caSystemId) {
+            return;
+        }
+    }
+
+    ADD_FAILURE() << "ClearKey plugin not installed";
+}
+
+TEST_P(MediaCasAidlTest, TestClearKeySessionClosedAfterRelease) {
+    description("Test that all sessions are closed after a MediaCas object is released");
+
+    ASSERT_TRUE(createCasPlugin(CLEAR_KEY_SYSTEM_ID));
+
+    EXPECT_TRUE(mMediaCas->provision(PROVISION_STR).isOk());
+
+    SessionIntent intent = SessionIntent::LIVE;
+    ScramblingMode mode = ScramblingMode::DVB_CSA1;
+
+    vector<uint8_t> sessionId;
+    ASSERT_TRUE(openCasSession(&sessionId, intent, mode));
+
+    vector<uint8_t> streamSessionId;
+    ASSERT_TRUE(openCasSession(&streamSessionId, intent, mode));
+
+    EXPECT_TRUE(mMediaCas->release().isOk());
+
+    if (mDescrambler != nullptr) {
+        auto status = mDescrambler->setMediaCasSession(sessionId);
+        EXPECT_FALSE(status.isOk());
+        EXPECT_EQ(Status::ERROR_CAS_SESSION_NOT_OPENED, status.getServiceSpecificError());
+
+        status = mDescrambler->setMediaCasSession(streamSessionId);
+        EXPECT_FALSE(status.isOk());
+        EXPECT_EQ(Status::ERROR_CAS_SESSION_NOT_OPENED, status.getServiceSpecificError());
+    }
+}
+
+TEST_P(MediaCasAidlTest, TestClearKeyErrors) {
+    description("Test that invalid call sequences fail with expected error codes");
+
+    ASSERT_TRUE(createCasPlugin(CLEAR_KEY_SYSTEM_ID));
+
+    /*
+     * Test MediaCas error codes
+     */
+    // Provision should fail with an invalid asset string
+    auto returnStatus = mMediaCas->provision("invalid asset string");
+    EXPECT_FALSE(returnStatus.isOk());
+    EXPECT_EQ(Status::ERROR_CAS_NO_LICENSE, returnStatus.getServiceSpecificError());
+
+    SessionIntent intent = SessionIntent::LIVE;
+    ScramblingMode mode = ScramblingMode::DVB_CSA1;
+
+    // Open a session, then close it so that it should become invalid
+    vector<uint8_t> invalidSessionId;
+    ASSERT_TRUE(openCasSession(&invalidSessionId, intent, mode));
+    EXPECT_TRUE(mMediaCas->closeSession(invalidSessionId).isOk());
+
+    // processEcm should fail with an invalid session id
+    vector<uint8_t> ecm(kEcmBinaryBuffer, kEcmBinaryBuffer + sizeof(kEcmBinaryBuffer));
+    returnStatus = mMediaCas->processEcm(invalidSessionId, ecm);
+    EXPECT_FALSE(returnStatus.isOk());
+    EXPECT_EQ(Status::ERROR_CAS_SESSION_NOT_OPENED, returnStatus.getServiceSpecificError());
+
+    vector<uint8_t> sessionId;
+    ASSERT_TRUE(openCasSession(&sessionId, intent, mode));
+
+    // processEcm should fail without provisioning
+    returnStatus = mMediaCas->processEcm(sessionId, ecm);
+    EXPECT_FALSE(returnStatus.isOk());
+    EXPECT_EQ(Status::ERROR_CAS_NOT_PROVISIONED, returnStatus.getServiceSpecificError());
+
+    EXPECT_TRUE(mMediaCas->provision(PROVISION_STR).isOk());
+
+    // processEcm should fail with ecm with bad descriptor count
+    ecm[17] = 0x03;  // change the descriptor count field to 3 (invalid)
+    returnStatus = mMediaCas->processEcm(sessionId, ecm);
+    EXPECT_FALSE(returnStatus.isOk());
+    EXPECT_EQ(Status::ERROR_CAS_UNKNOWN, returnStatus.getServiceSpecificError());
+
+    // processEcm should fail with ecm buffer that's too short
+    ecm.resize(8);
+    returnStatus = mMediaCas->processEcm(sessionId, ecm);
+    EXPECT_FALSE(returnStatus.isOk());
+    EXPECT_EQ(Status::BAD_VALUE, returnStatus.getServiceSpecificError());
+
+    if (mDescrambler != nullptr) {
+        /*
+         * Test MediaDescrambler error codes
+         */
+        // setMediaCasSession should fail with an invalid session id
+        returnStatus = mDescrambler->setMediaCasSession(invalidSessionId);
+        EXPECT_FALSE(returnStatus.isOk());
+        EXPECT_EQ(Status::ERROR_CAS_SESSION_NOT_OPENED, returnStatus.getServiceSpecificError());
+
+        // descramble should fail without a valid session
+        ScopedAStatus descrambleStatus = ScopedAStatus::ok();
+        uint8_t* sharedBuffer = nullptr;
+
+        ASSERT_FALSE(descrambleTestInputBuffer(mDescrambler, descrambleStatus, sharedBuffer));
+        EXPECT_EQ(Status::ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED,
+                  descrambleStatus.getServiceSpecificError());
+
+        // Now set a valid session, should still fail because no valid ecm is processed
+        EXPECT_TRUE(mDescrambler->setMediaCasSession(sessionId).isOk());
+        ASSERT_FALSE(descrambleTestInputBuffer(mDescrambler, descrambleStatus, sharedBuffer));
+        EXPECT_EQ(Status::ERROR_CAS_DECRYPT, descrambleStatus.getServiceSpecificError());
+
+        // Verify that requiresSecureDecoderComponent handles empty mime
+        bool requiresSecureDecoderComponent = true;
+        EXPECT_TRUE(
+                mDescrambler->requiresSecureDecoderComponent("", &requiresSecureDecoderComponent)
+                        .isOk());
+        EXPECT_FALSE(requiresSecureDecoderComponent);
+
+        // Verify that requiresSecureDecoderComponent handles invalid mime
+        requiresSecureDecoderComponent = true;
+        EXPECT_TRUE(
+                mDescrambler->requiresSecureDecoderComponent("bad", &requiresSecureDecoderComponent)
+                        .isOk());
+        EXPECT_FALSE(requiresSecureDecoderComponent);
+    }
+}
+
+TEST_P(MediaCasAidlTest, TestClearKeyApisWithSession) {
+    description("Test that valid call sequences with SessionEvent send and receive");
+
+    ASSERT_TRUE(createCasPlugin(CLEAR_KEY_SYSTEM_ID));
+
+    EXPECT_TRUE(mMediaCas->provision(PROVISION_STR).isOk());
+
+    vector<uint8_t> pvtData;
+    pvtData.resize(256);
+    EXPECT_TRUE(mMediaCas->setPrivateData(pvtData).isOk());
+
+    SessionIntent intent = SessionIntent::LIVE;
+    ScramblingMode mode = ScramblingMode::DVB_CSA1;
+
+    vector<uint8_t> sessionId;
+    ASSERT_TRUE(openCasSession(&sessionId, intent, mode));
+    EXPECT_TRUE(mMediaCas->setSessionPrivateData(sessionId, pvtData).isOk());
+
+    vector<uint8_t> streamSessionId;
+    ASSERT_TRUE(openCasSession(&streamSessionId, intent, mode));
+    EXPECT_TRUE(mMediaCas->setSessionPrivateData(streamSessionId, pvtData).isOk());
+
+    if (mDescrambler != nullptr) {
+        EXPECT_TRUE(mDescrambler->setMediaCasSession(sessionId).isOk());
+        EXPECT_TRUE(mDescrambler->setMediaCasSession(streamSessionId).isOk());
+    }
+
+    vector<uint8_t> nullPtrVector(0);
+    EXPECT_TRUE(mMediaCas->refreshEntitlements(3, nullPtrVector).isOk());
+
+    vector<uint8_t> refreshData{0, 1, 2, 3};
+    EXPECT_TRUE(mMediaCas->refreshEntitlements(10, refreshData).isOk());
+
+    int32_t eventID = 1;
+    int32_t eventArg = 2;
+    mCasListener->testEventEcho(mMediaCas, eventID, eventArg, nullPtrVector);
+    mCasListener->testSessionEventEcho(mMediaCas, sessionId, eventID, eventArg, nullPtrVector);
+
+    eventID = 3;
+    eventArg = 4;
+    vector<uint8_t> eventData{'e', 'v', 'e', 'n', 't', 'd', 'a', 't', 'a'};
+    mCasListener->testEventEcho(mMediaCas, eventID, eventArg, eventData);
+    mCasListener->testSessionEventEcho(mMediaCas, sessionId, eventID, eventArg, eventData);
+
+    mCasListener->testStatusUpdate(mMediaCas, &sessionId, intent, mode);
+
+    vector<uint8_t> clearKeyEmmData{'c', 'l', 'e', 'a', 'r', 'k', 'e', 'y', 'e', 'm', 'm'};
+    EXPECT_TRUE(mMediaCas->processEmm(clearKeyEmmData).isOk());
+
+    vector<uint8_t> ecm(kEcmBinaryBuffer, kEcmBinaryBuffer + sizeof(kEcmBinaryBuffer));
+    EXPECT_TRUE(mMediaCas->processEcm(sessionId, ecm).isOk());
+    EXPECT_TRUE(mMediaCas->processEcm(streamSessionId, ecm).isOk());
+
+    if (mDescrambler != nullptr) {
+        bool requiresSecureDecoderComponent = true;
+        EXPECT_TRUE(mDescrambler
+                            ->requiresSecureDecoderComponent("video/avc",
+                                                             &requiresSecureDecoderComponent)
+                            .isOk());
+        EXPECT_FALSE(requiresSecureDecoderComponent);
+
+        ScopedAStatus descrambleStatus = ScopedAStatus::ok();
+        uint8_t* sharedBuffer = nullptr;
+
+        ASSERT_TRUE(descrambleTestInputBuffer(mDescrambler, descrambleStatus, sharedBuffer));
+
+        int compareResult =
+                memcmp(static_cast<const void*>(sharedBuffer),
+                       static_cast<const void*>(kOutRefBinaryBuffer), sizeof(kOutRefBinaryBuffer));
+        EXPECT_EQ(0, compareResult);
+
+        EXPECT_TRUE(mDescrambler->release().isOk());
+    }
+
+    EXPECT_TRUE(mMediaCas->release().isOk());
+}
+
+TEST_P(MediaCasAidlTest, TestClearKeyOobFails) {
+    description("Test that oob descramble request fails with expected error");
+
+    ASSERT_TRUE(createCasPlugin(CLEAR_KEY_SYSTEM_ID));
+    EXPECT_TRUE(mMediaCas->provision(PROVISION_STR).isOk());
+
+    SessionIntent intent = SessionIntent::LIVE;
+    ScramblingMode mode = ScramblingMode::DVB_CSA1;
+
+    vector<uint8_t> sessionId;
+    ASSERT_TRUE(openCasSession(&sessionId, intent, mode));
+
+    if (mDescrambler != nullptr) {
+        EXPECT_TRUE(mDescrambler->setMediaCasSession(sessionId).isOk());
+    }
+
+    vector<uint8_t> ecm(kEcmBinaryBuffer, kEcmBinaryBuffer + sizeof(kEcmBinaryBuffer));
+    EXPECT_TRUE(mMediaCas->processEcm(sessionId, ecm).isOk());
+
+    if (mDescrambler != nullptr) {
+        ScopedAStatus descrambleStatus = ScopedAStatus::ok();
+
+        // test invalid src buffer offset
+        ASSERT_FALSE(
+                descrambleTestOobInput(mDescrambler, descrambleStatus,
+                                       {.subSamples = kSubSamples,
+                                        .numSubSamples = sizeof(kSubSamples) / sizeof(SubSample),
+                                        .imemSizeActual = sizeof(kInBinaryBuffer),
+                                        .imemOffset = 0xcccccc,
+                                        .imemSize = sizeof(kInBinaryBuffer),
+                                        .srcOffset = 0,
+                                        .dstOffset = 0}));
+        EXPECT_EQ(Status::BAD_VALUE, descrambleStatus.getServiceSpecificError());
+
+        // test invalid src buffer size
+        ASSERT_FALSE(
+                descrambleTestOobInput(mDescrambler, descrambleStatus,
+                                       {.subSamples = kSubSamples,
+                                        .numSubSamples = sizeof(kSubSamples) / sizeof(SubSample),
+                                        .imemSizeActual = sizeof(kInBinaryBuffer),
+                                        .imemOffset = 0,
+                                        .imemSize = 0xcccccc,
+                                        .srcOffset = 0,
+                                        .dstOffset = 0}));
+        EXPECT_EQ(Status::BAD_VALUE, descrambleStatus.getServiceSpecificError());
+
+        // test invalid src buffer size
+        ASSERT_FALSE(
+                descrambleTestOobInput(mDescrambler, descrambleStatus,
+                                       {.subSamples = kSubSamples,
+                                        .numSubSamples = sizeof(kSubSamples) / sizeof(SubSample),
+                                        .imemSizeActual = sizeof(kInBinaryBuffer),
+                                        .imemOffset = 1,
+                                        .imemSize = -1,
+                                        .srcOffset = 0,
+                                        .dstOffset = 0}));
+        EXPECT_EQ(Status::BAD_VALUE, descrambleStatus.getServiceSpecificError());
+
+        // test invalid srcOffset
+        ASSERT_FALSE(
+                descrambleTestOobInput(mDescrambler, descrambleStatus,
+                                       {.subSamples = kSubSamples,
+                                        .numSubSamples = sizeof(kSubSamples) / sizeof(SubSample),
+                                        .imemSizeActual = sizeof(kInBinaryBuffer),
+                                        .imemOffset = 0,
+                                        .imemSize = sizeof(kInBinaryBuffer),
+                                        .srcOffset = 0xcccccc,
+                                        .dstOffset = 0}));
+        EXPECT_EQ(Status::BAD_VALUE, descrambleStatus.getServiceSpecificError());
+
+        // test invalid dstOffset
+        ASSERT_FALSE(
+                descrambleTestOobInput(mDescrambler, descrambleStatus,
+                                       {.subSamples = kSubSamples,
+                                        .numSubSamples = sizeof(kSubSamples) / sizeof(SubSample),
+                                        .imemSizeActual = sizeof(kInBinaryBuffer),
+                                        .imemOffset = 0,
+                                        .imemSize = sizeof(kInBinaryBuffer),
+                                        .srcOffset = 0,
+                                        .dstOffset = 0xcccccc}));
+        EXPECT_EQ(Status::BAD_VALUE, descrambleStatus.getServiceSpecificError());
+
+        // test detection of oob subsample sizes
+        const SubSample invalidSubSamples1[] = {{162, 0}, {0, 184}, {0, 0xdddddd}};
+
+        ASSERT_FALSE(descrambleTestOobInput(
+                mDescrambler, descrambleStatus,
+                {.subSamples = invalidSubSamples1,
+                 .numSubSamples = sizeof(invalidSubSamples1) / sizeof(SubSample),
+                 .imemSizeActual = sizeof(kInBinaryBuffer),
+                 .imemOffset = 0,
+                 .imemSize = sizeof(kInBinaryBuffer),
+                 .srcOffset = 0,
+                 .dstOffset = 0}));
+        EXPECT_EQ(Status::BAD_VALUE, descrambleStatus.getServiceSpecificError());
+
+        // test detection of overflowing subsample sizes
+        const SubSample invalidSubSamples2[] = {{162, 0}, {0, 184}, {2, -1}};
+
+        ASSERT_FALSE(descrambleTestOobInput(
+                mDescrambler, descrambleStatus,
+                {.subSamples = invalidSubSamples2,
+                 .numSubSamples = sizeof(invalidSubSamples2) / sizeof(SubSample),
+                 .imemSizeActual = sizeof(kInBinaryBuffer),
+                 .imemOffset = 0,
+                 .imemSize = sizeof(kInBinaryBuffer),
+                 .srcOffset = 0,
+                 .dstOffset = 0}));
+        EXPECT_EQ(Status::BAD_VALUE, descrambleStatus.getServiceSpecificError());
+
+        EXPECT_TRUE(mDescrambler->release().isOk());
+    }
+    EXPECT_TRUE(mMediaCas->release().isOk());
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MediaCasAidlTest);
+
+INSTANTIATE_TEST_SUITE_P(
+        PerInstance, MediaCasAidlTest,
+        testing::ValuesIn(android::getAidlHalInstanceNames(IMediaCasService::descriptor)),
+        android::PrintInstanceNameToString);
+
+// Start thread pool to receive callbacks from AIDL service.
+int main(int argc, char** argv) {
+    InitGoogleTest(&argc, argv);
+    ABinderProcess_setThreadPoolMaxThreadCount(1);
+    ABinderProcess_startThreadPool();
+    return RUN_ALL_TESTS();
+}
diff --git a/compatibility_matrices/Android.bp b/compatibility_matrices/Android.bp
index 524242f..37c2820 100644
--- a/compatibility_matrices/Android.bp
+++ b/compatibility_matrices/Android.bp
@@ -28,8 +28,6 @@
         "compatibility_matrix.3.xml",
     ],
     kernel_configs: [
-        "kernel_config_p_4.4",
-        "kernel_config_p_4.9",
         "kernel_config_p_4.14",
     ],
 }
@@ -41,7 +39,6 @@
         "compatibility_matrix.4.xml",
     ],
     kernel_configs: [
-        "kernel_config_q_4.9",
         "kernel_config_q_4.14",
         "kernel_config_q_4.19",
     ],
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index ceb5dcf..bbf05cc 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -112,6 +112,13 @@
             <regex-instance>.*</regex-instance>
         </interface>
     </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.automotive.remoteaccess</name>
+        <interface>
+            <name>IRemoteAccess</name>
+            <regex-instance>.*</regex-instance>
+        </interface>
+    </hal>
     <hal format="hidl" optional="true">
         <name>android.hardware.automotive.vehicle</name>
         <version>2.0</version>
@@ -145,6 +152,13 @@
         </interface>
     </hal>
     <hal format="aidl" optional="true">
+        <name>android.hardware.bluetooth</name>
+        <interface>
+            <name>IBluetoothHci</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
         <name>android.hardware.bluetooth.audio</name>
         <version>2</version>
         <interface>
@@ -200,6 +214,13 @@
         </interface>
     </hal>
     <hal format="aidl" optional="true">
+        <name>android.hardware.cas</name>
+        <interface>
+            <name>IMediaCasService</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
         <name>android.hardware.confirmationui</name>
         <version>1</version>
         <interface>
@@ -214,7 +235,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="aidl" optional="true">
+    <hal format="aidl" optional="true" updatable-via-apex="true">
         <name>android.hardware.drm</name>
         <version>1</version>
         <interface>
@@ -352,7 +373,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.identity</name>
-        <version>1-4</version>
+        <version>1-5</version>
         <interface>
             <name>IIdentityCredentialStore</name>
             <instance>default</instance>
@@ -399,7 +420,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.security.keymint</name>
-        <version>1-2</version>
+        <version>1-3</version>
         <interface>
             <name>IKeyMintDevice</name>
             <instance>default</instance>
@@ -408,7 +429,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.security.keymint</name>
-        <version>1-2</version>
+        <version>1-3</version>
         <interface>
             <name>IRemotelyProvisionedComponent</name>
             <instance>default</instance>
@@ -432,18 +453,6 @@
             <regex-instance>vendor[0-9]*_software</regex-instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.media.omx</name>
-        <version>1.0</version>
-        <interface>
-            <name>IOmx</name>
-            <instance>default</instance>
-        </interface>
-        <interface>
-            <name>IOmxStore</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.memtrack</name>
         <version>1</version>
@@ -672,14 +681,20 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="true">
+    <hal format="aidl" optional="true">
         <name>android.hardware.tv.cec</name>
-        <version>1.0-1</version>
         <interface>
             <name>IHdmiCec</name>
             <instance>default</instance>
         </interface>
     </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.tv.hdmi</name>
+        <interface>
+            <name>IHdmi</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
     <hal format="hidl" optional="true">
         <name>android.hardware.tv.input</name>
         <version>1.0</version>
diff --git a/confirmationui/aidl/Android.bp b/confirmationui/aidl/Android.bp
index 4ba4acd..5395101 100644
--- a/confirmationui/aidl/Android.bp
+++ b/confirmationui/aidl/Android.bp
@@ -20,7 +20,7 @@
     name: "android.hardware.confirmationui",
     vendor_available: true,
     imports: [
-        "android.hardware.security.keymint-V2",
+        "android.hardware.security.keymint-V3",
     ],
     srcs: ["android/hardware/confirmationui/*.aidl"],
     stability: "vintf",
diff --git a/dumpstate/aidl/Android.bp b/dumpstate/aidl/Android.bp
index 63670bb..1eb8b32 100644
--- a/dumpstate/aidl/Android.bp
+++ b/dumpstate/aidl/Android.bp
@@ -31,7 +31,8 @@
             enabled: false,
         },
         java: {
-            enabled: false,
+            enabled: true,
+            sdk_version: "module_current",
         },
     },
     versions_with_info: [
diff --git a/gatekeeper/aidl/Android.bp b/gatekeeper/aidl/Android.bp
index 6b1bc7e..770e8ab 100644
--- a/gatekeeper/aidl/Android.bp
+++ b/gatekeeper/aidl/Android.bp
@@ -11,7 +11,7 @@
     name: "android.hardware.gatekeeper",
     vendor_available: true,
     imports: [
-        "android.hardware.security.keymint-V2",
+        "android.hardware.security.keymint-V3",
     ],
     srcs: ["android/hardware/gatekeeper/*.aidl"],
     stability: "vintf",
diff --git a/gnss/aidl/android/hardware/gnss/IGnss.aidl b/gnss/aidl/android/hardware/gnss/IGnss.aidl
index f0b583d..aaafe7f 100644
--- a/gnss/aidl/android/hardware/gnss/IGnss.aidl
+++ b/gnss/aidl/android/hardware/gnss/IGnss.aidl
@@ -104,7 +104,7 @@
      *
      * The framework calls this method to instruct the GPS engine to prepare for serving requests
      * from the framework. The GNSS HAL implementation must respond to all GNSS requests from the
-     * framework upon successful return from this method until cleanup() method is called to
+     * framework upon successful return from this method until close() method is called to
      * close this interface.
      *
      * @param callback Callback interface for IGnss.
diff --git a/gnss/aidl/vts/gnss_hal_test.cpp b/gnss/aidl/vts/gnss_hal_test.cpp
index 0e1218e..3907f57 100644
--- a/gnss/aidl/vts/gnss_hal_test.cpp
+++ b/gnss/aidl/vts/gnss_hal_test.cpp
@@ -107,6 +107,17 @@
     }
 }
 
+void GnssHalTest::TearDown() {
+    GnssHalTestTemplate<IGnss_V2_1>::TearDown();
+    if (aidl_gnss_hal_ != nullptr) {
+        aidl_gnss_hal_->close();
+        aidl_gnss_hal_ = nullptr;
+    }
+
+    // Set to nullptr to destruct the callback event queues and warn of any unprocessed events.
+    aidl_gnss_cb_ = nullptr;
+}
+
 void GnssHalTest::CheckLocation(const GnssLocation& location, bool check_speed) {
     Utils::checkLocation(location, check_speed, /* check_more_accuracies= */ true);
 }
diff --git a/gnss/aidl/vts/gnss_hal_test.h b/gnss/aidl/vts/gnss_hal_test.h
index 645fc82..c49c1b9 100644
--- a/gnss/aidl/vts/gnss_hal_test.h
+++ b/gnss/aidl/vts/gnss_hal_test.h
@@ -64,6 +64,7 @@
 
     virtual void SetUp() override;
     virtual void SetUpGnssCallback() override;
+    virtual void TearDown() override;
 
     void CheckLocation(const android::hardware::gnss::GnssLocation& location,
                        const bool check_speed);
diff --git a/graphics/Android.bp b/graphics/Android.bp
new file mode 100644
index 0000000..b48844d
--- /dev/null
+++ b/graphics/Android.bp
@@ -0,0 +1,59 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_defaults {
+    name: "android.hardware.graphics.allocator-ndk_static",
+    static_libs: [
+        "android.hardware.graphics.allocator-V1-ndk",
+    ],
+}
+
+cc_defaults {
+    name: "android.hardware.graphics.allocator-ndk_shared",
+    shared_libs: [
+        "android.hardware.graphics.allocator-V1-ndk",
+    ],
+}
+
+cc_defaults {
+    name: "android.hardware.graphics.common-ndk_static",
+    static_libs: [
+        "android.hardware.graphics.common-V3-ndk",
+    ],
+}
+
+cc_defaults {
+    name: "android.hardware.graphics.common-ndk_shared",
+    shared_libs: [
+        "android.hardware.graphics.common-V3-ndk",
+    ],
+}
+
+cc_defaults {
+    name: "android.hardware.graphics.composer3-ndk_static",
+    static_libs: [
+        "android.hardware.graphics.composer3-V1-ndk",
+    ],
+}
+
+cc_defaults {
+    name: "android.hardware.graphics.composer3-ndk_shared",
+    shared_libs: [
+        "android.hardware.graphics.composer3-V1-ndk",
+    ],
+}
diff --git a/graphics/OWNERS b/graphics/OWNERS
index 75ceb23..1cb6015 100644
--- a/graphics/OWNERS
+++ b/graphics/OWNERS
@@ -6,4 +6,5 @@
 chrisforbes@google.com
 jreck@google.com
 lpy@google.com
+scroggo@google.com
 sumir@google.com
\ No newline at end of file
diff --git a/graphics/allocator/2.0/default/OWNERS b/graphics/allocator/2.0/default/OWNERS
deleted file mode 100644
index c9f24d0..0000000
--- a/graphics/allocator/2.0/default/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Graphics team
-chrisforbes@google.com
-jreck@google.com
-lpy@google.com
diff --git a/graphics/allocator/2.0/utils/OWNERS b/graphics/allocator/2.0/utils/OWNERS
deleted file mode 100644
index c9f24d0..0000000
--- a/graphics/allocator/2.0/utils/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Graphics team
-chrisforbes@google.com
-jreck@google.com
-lpy@google.com
diff --git a/graphics/allocator/aidl/vts/Android.bp b/graphics/allocator/aidl/vts/Android.bp
index 99ffb24..a38af14 100644
--- a/graphics/allocator/aidl/vts/Android.bp
+++ b/graphics/allocator/aidl/vts/Android.bp
@@ -27,6 +27,7 @@
     name: "VtsHalGraphicsAllocatorAidl_TargetTest",
     defaults: [
         "VtsHalTargetTestDefaults",
+        "android.hardware.graphics.allocator-ndk_shared",
         "use_libaidlvintf_gtest_helper_static",
         "hwui_defaults",
     ],
@@ -35,8 +36,6 @@
     ],
 
     shared_libs: [
-        "android.hardware.graphics.allocator-V1-ndk",
-        "android.hardware.graphics.common-V3-ndk",
         "android.hardware.graphics.mapper@4.0",
         "libEGL",
         "libGLESv2",
diff --git a/graphics/common/OWNERS b/graphics/common/OWNERS
deleted file mode 100644
index 94999ea..0000000
--- a/graphics/common/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-# Bug component: 1075130
-adyabr@google.com
-alecmouri@google.com
-jreck@google.com
-scroggo@google.com
diff --git a/graphics/composer/2.1/default/OWNERS b/graphics/composer/2.1/default/OWNERS
deleted file mode 100644
index 331c80d..0000000
--- a/graphics/composer/2.1/default/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Graphics team
-adyabr@google.com
-alecmouri@google.com
-lpy@google.com
diff --git a/graphics/composer/2.1/utils/OWNERS b/graphics/composer/2.1/utils/OWNERS
deleted file mode 100644
index 83c4f5f..0000000
--- a/graphics/composer/2.1/utils/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-adyabr@google.com
-alecmouri@google.com
-lpy@google.com
diff --git a/graphics/composer/2.1/utils/vts/Android.bp b/graphics/composer/2.1/utils/vts/Android.bp
index c0a0c07..7b6a0e6 100644
--- a/graphics/composer/2.1/utils/vts/Android.bp
+++ b/graphics/composer/2.1/utils/vts/Android.bp
@@ -25,14 +25,16 @@
 
 cc_library_static {
     name: "android.hardware.graphics.composer@2.1-vts",
-    defaults: ["hidl_defaults"],
+    defaults: [
+        "android.hardware.graphics.allocator-ndk_static",
+        "hidl_defaults",
+    ],
     srcs: [
         "ComposerVts.cpp",
         "GraphicsComposerCallback.cpp",
         "TestCommandReader.cpp",
     ],
     static_libs: [
-        "android.hardware.graphics.allocator-V1-ndk",
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.mapper@2.0-vts",
         "android.hardware.graphics.mapper@3.0-vts",
@@ -40,7 +42,6 @@
         "libgtest",
     ],
     export_static_lib_headers: [
-        "android.hardware.graphics.allocator-V1-ndk",
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.mapper@2.0-vts",
         "android.hardware.graphics.mapper@3.0-vts",
diff --git a/graphics/composer/2.1/vts/OWNERS b/graphics/composer/2.1/vts/OWNERS
deleted file mode 100644
index a643bbd..0000000
--- a/graphics/composer/2.1/vts/OWNERS
+++ /dev/null
@@ -1,8 +0,0 @@
-# Graphics team
-adyabr@google.com
-alecmouri@google.com
-lpy@google.com
-
-# VTS team
-yim@google.com
-zhuoyao@google.com
diff --git a/graphics/composer/2.1/vts/functional/Android.bp b/graphics/composer/2.1/vts/functional/Android.bp
index 502036e..0f6d7e8 100644
--- a/graphics/composer/2.1/vts/functional/Android.bp
+++ b/graphics/composer/2.1/vts/functional/Android.bp
@@ -25,7 +25,10 @@
 
 cc_test {
     name: "VtsHalGraphicsComposerV2_1TargetTest",
-    defaults: ["VtsHalTargetTestDefaults"],
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "android.hardware.graphics.allocator-ndk_static",
+    ],
     tidy_timeout_srcs: ["VtsHalGraphicsComposerV2_1TargetTest.cpp"],
     srcs: ["VtsHalGraphicsComposerV2_1TargetTest.cpp"],
 
@@ -42,7 +45,6 @@
         "android.hardware.graphics.mapper@4.0",
     ],
     static_libs: [
-        "android.hardware.graphics.allocator-V1-ndk",
         "android.hardware.graphics.allocator@2.0",
         "android.hardware.graphics.allocator@3.0",
         "android.hardware.graphics.allocator@4.0",
diff --git a/graphics/composer/2.1/vts/functional/OWNERS b/graphics/composer/2.1/vts/functional/OWNERS
deleted file mode 100644
index 3d970d1..0000000
--- a/graphics/composer/2.1/vts/functional/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Bug component: 25423
-adyabr@google.com
-alecmouri@google.com
-sumir@google.com
diff --git a/graphics/composer/2.2/default/OWNERS b/graphics/composer/2.2/default/OWNERS
deleted file mode 100644
index e8f584d..0000000
--- a/graphics/composer/2.2/default/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-# Graphics team
-adyabr@google.com
-alecmouri@google.com
-lpy@google.com
-
diff --git a/graphics/composer/2.2/utils/OWNERS b/graphics/composer/2.2/utils/OWNERS
deleted file mode 100644
index 331c80d..0000000
--- a/graphics/composer/2.2/utils/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Graphics team
-adyabr@google.com
-alecmouri@google.com
-lpy@google.com
diff --git a/graphics/composer/2.2/utils/vts/Android.bp b/graphics/composer/2.2/utils/vts/Android.bp
index cca5323..e383ce2 100644
--- a/graphics/composer/2.2/utils/vts/Android.bp
+++ b/graphics/composer/2.2/utils/vts/Android.bp
@@ -25,7 +25,11 @@
 
 cc_library_static {
     name: "android.hardware.graphics.composer@2.2-vts",
-    defaults: ["hidl_defaults"],
+    defaults: [
+        "android.hardware.graphics.allocator-ndk_static",
+        "android.hardware.graphics.composer3-ndk_static",
+        "hidl_defaults",
+    ],
     srcs: [
         "ComposerVts.cpp",
         "ReadbackVts.cpp",
@@ -35,7 +39,6 @@
         "libui",
     ],
     static_libs: [
-        "android.hardware.graphics.allocator-V1-ndk",
         "android.hardware.graphics.composer@2.1-vts",
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.composer3-V1-ndk",
@@ -53,7 +56,6 @@
         "android.hardware.graphics.mapper@4.0-vts",
     ],
     export_static_lib_headers: [
-        "android.hardware.graphics.allocator-V1-ndk",
         "android.hardware.graphics.composer@2.1-vts",
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.mapper@2.1-vts",
diff --git a/graphics/composer/2.2/vts/functional/Android.bp b/graphics/composer/2.2/vts/functional/Android.bp
index 960b62d..cdc7a93 100644
--- a/graphics/composer/2.2/vts/functional/Android.bp
+++ b/graphics/composer/2.2/vts/functional/Android.bp
@@ -27,6 +27,8 @@
     name: "VtsHalGraphicsComposerV2_2TargetTest",
     defaults: [
         "VtsHalTargetTestDefaults",
+        "android.hardware.graphics.allocator-ndk_static",
+        "android.hardware.graphics.composer3-ndk_static",
         // Needed for librenderengine
         "skia_deps",
     ],
@@ -59,7 +61,6 @@
         "android.hardware.graphics.mapper@4.0",
     ],
     static_libs: [
-        "android.hardware.graphics.allocator-V1-ndk",
         "android.hardware.graphics.allocator@2.0",
         "android.hardware.graphics.allocator@3.0",
         "android.hardware.graphics.allocator@4.0",
diff --git a/graphics/composer/2.2/vts/functional/OWNERS b/graphics/composer/2.2/vts/functional/OWNERS
deleted file mode 100644
index a4eb0ca..0000000
--- a/graphics/composer/2.2/vts/functional/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-# Bug component: 25423
-# Graphics team
-adyabr@google.com
-alecmouri@google.com
-lpy@google.com
-sumir@google.com
diff --git a/graphics/composer/2.3/default/OWNERS b/graphics/composer/2.3/default/OWNERS
deleted file mode 100644
index 331c80d..0000000
--- a/graphics/composer/2.3/default/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Graphics team
-adyabr@google.com
-alecmouri@google.com
-lpy@google.com
diff --git a/graphics/composer/2.3/utils/OWNERS b/graphics/composer/2.3/utils/OWNERS
deleted file mode 100644
index 331c80d..0000000
--- a/graphics/composer/2.3/utils/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Graphics team
-adyabr@google.com
-alecmouri@google.com
-lpy@google.com
diff --git a/graphics/composer/2.3/utils/vts/Android.bp b/graphics/composer/2.3/utils/vts/Android.bp
index 7bc07a4..b372804 100644
--- a/graphics/composer/2.3/utils/vts/Android.bp
+++ b/graphics/composer/2.3/utils/vts/Android.bp
@@ -25,7 +25,10 @@
 
 cc_library_static {
     name: "android.hardware.graphics.composer@2.3-vts",
-    defaults: ["hidl_defaults"],
+    defaults: [
+        "android.hardware.graphics.allocator-ndk_static",
+        "hidl_defaults",
+    ],
     srcs: [
         "ComposerVts.cpp",
     ],
diff --git a/graphics/composer/2.3/vts/functional/Android.bp b/graphics/composer/2.3/vts/functional/Android.bp
index 40b77d5..13f2b11 100644
--- a/graphics/composer/2.3/vts/functional/Android.bp
+++ b/graphics/composer/2.3/vts/functional/Android.bp
@@ -25,7 +25,10 @@
 
 cc_test {
     name: "VtsHalGraphicsComposerV2_3TargetTest",
-    defaults: ["VtsHalTargetTestDefaults"],
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "android.hardware.graphics.allocator-ndk_static",
+    ],
     tidy_timeout_srcs: ["VtsHalGraphicsComposerV2_3TargetTest.cpp"],
     srcs: ["VtsHalGraphicsComposerV2_3TargetTest.cpp"],
 
@@ -43,7 +46,6 @@
         "android.hardware.graphics.mapper@4.0",
     ],
     static_libs: [
-        "android.hardware.graphics.allocator-V1-ndk",
         "android.hardware.graphics.allocator@2.0",
         "android.hardware.graphics.allocator@3.0",
         "android.hardware.graphics.allocator@4.0",
diff --git a/graphics/composer/2.3/vts/functional/OWNERS b/graphics/composer/2.3/vts/functional/OWNERS
deleted file mode 100644
index a4eb0ca..0000000
--- a/graphics/composer/2.3/vts/functional/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-# Bug component: 25423
-# Graphics team
-adyabr@google.com
-alecmouri@google.com
-lpy@google.com
-sumir@google.com
diff --git a/graphics/composer/2.4/default/OWNERS b/graphics/composer/2.4/default/OWNERS
deleted file mode 100644
index 331c80d..0000000
--- a/graphics/composer/2.4/default/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Graphics team
-adyabr@google.com
-alecmouri@google.com
-lpy@google.com
diff --git a/graphics/composer/2.4/utils/OWNERS b/graphics/composer/2.4/utils/OWNERS
deleted file mode 100644
index 331c80d..0000000
--- a/graphics/composer/2.4/utils/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Graphics team
-adyabr@google.com
-alecmouri@google.com
-lpy@google.com
diff --git a/graphics/composer/2.4/utils/vts/Android.bp b/graphics/composer/2.4/utils/vts/Android.bp
index de31975..d2b2ffa 100644
--- a/graphics/composer/2.4/utils/vts/Android.bp
+++ b/graphics/composer/2.4/utils/vts/Android.bp
@@ -25,14 +25,16 @@
 
 cc_library_static {
     name: "android.hardware.graphics.composer@2.4-vts",
-    defaults: ["hidl_defaults"],
+    defaults: [
+        "android.hardware.graphics.allocator-ndk_static",
+        "hidl_defaults",
+    ],
     srcs: [
         "ComposerVts.cpp",
         "GraphicsComposerCallback.cpp",
         "TestCommandReader.cpp",
     ],
     static_libs: [
-        "android.hardware.graphics.allocator-V1-ndk",
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.composer@2.3-vts",
diff --git a/graphics/composer/2.4/vts/functional/Android.bp b/graphics/composer/2.4/vts/functional/Android.bp
index b73ea94..b4ab259 100644
--- a/graphics/composer/2.4/vts/functional/Android.bp
+++ b/graphics/composer/2.4/vts/functional/Android.bp
@@ -25,7 +25,10 @@
 
 cc_test {
     name: "VtsHalGraphicsComposerV2_4TargetTest",
-    defaults: ["VtsHalTargetTestDefaults"],
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "android.hardware.graphics.allocator-ndk_static",
+    ],
     tidy_timeout_srcs: ["VtsHalGraphicsComposerV2_4TargetTest.cpp"],
     srcs: ["VtsHalGraphicsComposerV2_4TargetTest.cpp"],
 
@@ -42,7 +45,6 @@
         "android.hardware.graphics.mapper@4.0",
     ],
     static_libs: [
-        "android.hardware.graphics.allocator-V1-ndk",
         "android.hardware.graphics.allocator@2.0",
         "android.hardware.graphics.allocator@3.0",
         "android.hardware.graphics.allocator@4.0",
diff --git a/graphics/composer/2.4/vts/functional/OWNERS b/graphics/composer/2.4/vts/functional/OWNERS
deleted file mode 100644
index a4eb0ca..0000000
--- a/graphics/composer/2.4/vts/functional/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-# Bug component: 25423
-# Graphics team
-adyabr@google.com
-alecmouri@google.com
-lpy@google.com
-sumir@google.com
diff --git a/graphics/composer/aidl/OWNERS b/graphics/composer/aidl/OWNERS
deleted file mode 100644
index 9028d9d..0000000
--- a/graphics/composer/aidl/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-# Bug component: 1075131
-
-# Graphics team
-adyabr@google.com
-alecmouri@google.com
-sumir@google.com
\ No newline at end of file
diff --git a/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerServiceWriter.h b/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerServiceWriter.h
index 34cda6a..b50b84b 100644
--- a/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerServiceWriter.h
+++ b/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerServiceWriter.h
@@ -18,6 +18,7 @@
 
 #include <aidl/android/hardware/graphics/composer3/CommandResultPayload.h>
 #include <aidl/android/hardware/graphics/composer3/IComposerClient.h>
+#include <android-base/logging.h>
 #include <inttypes.h>
 #include <string.h>
 
@@ -26,8 +27,6 @@
 #include <memory>
 #include <vector>
 
-#include "Util.h"
-
 namespace aidl::android::hardware::graphics::composer3::impl {
 
 class ComposerServiceWriter {
diff --git a/graphics/composer/aidl/vts/OWNERS b/graphics/composer/aidl/vts/OWNERS
deleted file mode 100644
index d95d98d..0000000
--- a/graphics/composer/aidl/vts/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-# Bug component: 199413815
-
-# Graphics team
-adyabr@google.com
-alecmouri@google.com
-ramindani@google.com
diff --git a/graphics/mapper/2.0/default/OWNERS b/graphics/mapper/2.0/default/OWNERS
deleted file mode 100644
index c9f24d0..0000000
--- a/graphics/mapper/2.0/default/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Graphics team
-chrisforbes@google.com
-jreck@google.com
-lpy@google.com
diff --git a/graphics/mapper/2.0/utils/OWNERS b/graphics/mapper/2.0/utils/OWNERS
deleted file mode 100644
index c9f24d0..0000000
--- a/graphics/mapper/2.0/utils/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Graphics team
-chrisforbes@google.com
-jreck@google.com
-lpy@google.com
diff --git a/graphics/mapper/2.0/vts/OWNERS b/graphics/mapper/2.0/vts/OWNERS
deleted file mode 100644
index 62e3f2a..0000000
--- a/graphics/mapper/2.0/vts/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-# Bug component: 25423
-chrisforbes@google.com
-jreck@google.com
-lpy@google.com
-sumir@google.com
diff --git a/graphics/mapper/2.1/default/OWNERS b/graphics/mapper/2.1/default/OWNERS
deleted file mode 100644
index c9f24d0..0000000
--- a/graphics/mapper/2.1/default/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Graphics team
-chrisforbes@google.com
-jreck@google.com
-lpy@google.com
diff --git a/graphics/mapper/2.1/utils/OWNERS b/graphics/mapper/2.1/utils/OWNERS
deleted file mode 100644
index c9f24d0..0000000
--- a/graphics/mapper/2.1/utils/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Graphics team
-chrisforbes@google.com
-jreck@google.com
-lpy@google.com
diff --git a/graphics/mapper/2.1/vts/OWNERS b/graphics/mapper/2.1/vts/OWNERS
deleted file mode 100644
index 43c018a..0000000
--- a/graphics/mapper/2.1/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 25423
-include ../../2.0/vts/OWNERS
diff --git a/graphics/mapper/3.0/utils/OWNERS b/graphics/mapper/3.0/utils/OWNERS
deleted file mode 100644
index c9f24d0..0000000
--- a/graphics/mapper/3.0/utils/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Graphics team
-chrisforbes@google.com
-jreck@google.com
-lpy@google.com
diff --git a/graphics/mapper/3.0/vts/OWNERS b/graphics/mapper/3.0/vts/OWNERS
deleted file mode 100644
index 43c018a..0000000
--- a/graphics/mapper/3.0/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 25423
-include ../../2.0/vts/OWNERS
diff --git a/graphics/mapper/4.0/utils/OWNERS b/graphics/mapper/4.0/utils/OWNERS
deleted file mode 100644
index c9f24d0..0000000
--- a/graphics/mapper/4.0/utils/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Graphics team
-chrisforbes@google.com
-jreck@google.com
-lpy@google.com
diff --git a/graphics/mapper/4.0/utils/vts/Android.bp b/graphics/mapper/4.0/utils/vts/Android.bp
index 7abf5db..269b972 100644
--- a/graphics/mapper/4.0/utils/vts/Android.bp
+++ b/graphics/mapper/4.0/utils/vts/Android.bp
@@ -25,14 +25,17 @@
 
 cc_library_static {
     name: "android.hardware.graphics.mapper@4.0-vts",
-    defaults: ["VtsHalTargetTestDefaults"],
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "android.hardware.graphics.allocator-ndk_static",
+        "android.hardware.graphics.common-ndk_static",
+    ],
     srcs: ["MapperVts.cpp"],
     cflags: [
         "-O0",
         "-g",
     ],
     static_libs: [
-        "android.hardware.graphics.allocator-V1-ndk",
         "android.hardware.graphics.allocator@4.0",
         "android.hardware.graphics.common-V3-ndk",
         "android.hardware.graphics.mapper@4.0",
@@ -44,7 +47,6 @@
         "libvndksupport",
     ],
     export_static_lib_headers: [
-        "android.hardware.graphics.allocator-V1-ndk",
         "android.hardware.graphics.allocator@4.0",
         "android.hardware.graphics.common-V3-ndk",
         "android.hardware.graphics.mapper@4.0",
diff --git a/graphics/mapper/4.0/vts/OWNERS b/graphics/mapper/4.0/vts/OWNERS
deleted file mode 100644
index 43c018a..0000000
--- a/graphics/mapper/4.0/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 25423
-include ../../2.0/vts/OWNERS
diff --git a/graphics/mapper/4.0/vts/functional/Android.bp b/graphics/mapper/4.0/vts/functional/Android.bp
index e830633..8f3e7eb 100644
--- a/graphics/mapper/4.0/vts/functional/Android.bp
+++ b/graphics/mapper/4.0/vts/functional/Android.bp
@@ -27,6 +27,8 @@
     name: "VtsHalGraphicsMapperV4_0TargetTest",
     defaults: [
         "VtsHalTargetTestDefaults",
+        "android.hardware.graphics.allocator-ndk_shared",
+        "android.hardware.graphics.common-ndk_static",
         "use_libaidlvintf_gtest_helper_static",
     ],
     srcs: ["VtsHalGraphicsMapperV4_0TargetTest.cpp"],
@@ -38,7 +40,6 @@
         "libsync",
     ],
     shared_libs: [
-        "android.hardware.graphics.allocator-V1-ndk",
         "android.hardware.graphics.allocator@4.0",
         "android.hardware.graphics.common@1.0",
         "android.hardware.graphics.common@1.1",
diff --git a/identity/aidl/Android.bp b/identity/aidl/Android.bp
index 57451ed..f568f7a 100644
--- a/identity/aidl/Android.bp
+++ b/identity/aidl/Android.bp
@@ -14,10 +14,11 @@
         "android/hardware/identity/*.aidl",
     ],
     imports: [
-        "android.hardware.keymaster",
-        "android.hardware.security.keymint",
+        "android.hardware.keymaster-V3",
+        "android.hardware.security.keymint-V3",
     ],
     stability: "vintf",
+    frozen: false,
     backend: {
         java: {
             platform_apis: true,
@@ -66,20 +67,20 @@
 cc_defaults {
     name: "identity_use_latest_hal_aidl_ndk_static",
     static_libs: [
-        "android.hardware.identity-V4-ndk",
+        "android.hardware.identity-V5-ndk",
     ],
 }
 
 cc_defaults {
     name: "identity_use_latest_hal_aidl_ndk_shared",
     shared_libs: [
-        "android.hardware.identity-V4-ndk",
+        "android.hardware.identity-V5-ndk",
     ],
 }
 
 cc_defaults {
     name: "identity_use_latest_hal_aidl_cpp_static",
     static_libs: [
-        "android.hardware.identity-V4-cpp",
+        "android.hardware.identity-V5-cpp",
     ],
 }
diff --git a/identity/aidl/default/android.hardware.identity_credential.xml b/identity/aidl/default/android.hardware.identity_credential.xml
index 20b2710..a49ddca 100644
--- a/identity/aidl/default/android.hardware.identity_credential.xml
+++ b/identity/aidl/default/android.hardware.identity_credential.xml
@@ -14,5 +14,5 @@
      limitations under the License.
 -->
 <permissions>
-  <feature name="android.hardware.identity_credential" version="202201" />
+  <feature name="android.hardware.identity_credential" version="202301" />
 </permissions>
diff --git a/identity/aidl/vts/Android.bp b/identity/aidl/vts/Android.bp
index dd29819..54bf887 100644
--- a/identity/aidl/vts/Android.bp
+++ b/identity/aidl/vts/Android.bp
@@ -59,3 +59,16 @@
     ],
     require_root: true,
 }
+
+java_test_host {
+    name: "IdentityCredentialImplementedTest",
+    libs: [
+        "tradefed",
+        "vts-core-tradefed-harness",
+    ],
+    srcs: ["src/**/*.java"],
+    test_suites: [
+        "vts",
+    ],
+    test_config: "IdentityCredentialImplementedTest.xml",
+}
diff --git a/identity/aidl/vts/IdentityCredentialImplementedTest.xml b/identity/aidl/vts/IdentityCredentialImplementedTest.xml
new file mode 100644
index 0000000..4276ae6
--- /dev/null
+++ b/identity/aidl/vts/IdentityCredentialImplementedTest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs IdentityCredentialImplementedTest">
+  <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+  <test class="com.android.tradefed.testtype.HostTest" >
+    <option name="jar" value="IdentityCredentialImplementedTest.jar" />
+  </test>
+</configuration>
diff --git a/identity/aidl/vts/src/com/android/tests/security/identity/IdentityCredentialImplementedTest.java b/identity/aidl/vts/src/com/android/tests/security/identity/IdentityCredentialImplementedTest.java
new file mode 100644
index 0000000..4936f8c
--- /dev/null
+++ b/identity/aidl/vts/src/com/android/tests/security/identity/IdentityCredentialImplementedTest.java
@@ -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.
+ */
+
+package com.android.tests.security.identity;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import android.platform.test.annotations.RequiresDevice;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+// This is a host-test which executes shell commands on the device. It would be
+// nicer to have this be a Device test (like CTS) but this is currently not
+// possible, see https://source.android.com/docs/core/tests/vts
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class IdentityCredentialImplementedTest extends BaseHostJUnit4Test {
+    // Returns the ro.vendor.api_level or 0 if not set.
+    //
+    // Throws NumberFormatException if ill-formatted.
+    //
+    // Throws DeviceNotAvailableException if device is not available.
+    //
+    private int getVendorApiLevel() throws NumberFormatException, DeviceNotAvailableException {
+        String vendorApiLevelString =
+                getDevice().executeShellCommand("getprop ro.vendor.api_level").trim();
+        if (vendorApiLevelString.isEmpty()) {
+            return 0;
+        }
+        return Integer.parseInt(vendorApiLevelString);
+    }
+
+    // As of Android 14 VSR (vendor API level 34), Identity Credential is required at feature
+    // version 202301 or later.
+    @RequiresDevice
+    @Test
+    public void testIdentityCredentialIsImplemented() throws Exception {
+        int vendorApiLevel = getVendorApiLevel();
+        assumeTrue(vendorApiLevel >= 34);
+
+        final String minimumFeatureVersionNeeded = "202301";
+
+        String result = getDevice().executeShellCommand(
+                "pm has-feature android.hardware.identity_credential "
+                + minimumFeatureVersionNeeded);
+        if (!result.trim().equals("true")) {
+            fail("Identity Credential feature version " + minimumFeatureVersionNeeded
+                    + " required but not found");
+        }
+    }
+}
diff --git a/ir/aidl/default/Android.bp b/ir/aidl/default/Android.bp
index a4fb439..a8096c2 100644
--- a/ir/aidl/default/Android.bp
+++ b/ir/aidl/default/Android.bp
@@ -36,6 +36,7 @@
         "liblog",
         "libutils",
         "android.hardware.ir-V1-ndk",
+        "libhardware"
     ],
 
     srcs: ["main.cpp"],
diff --git a/ir/aidl/default/android.hardware.ir-service.example.rc b/ir/aidl/default/android.hardware.ir-service.example.rc
index 56def64..d27f282 100644
--- a/ir/aidl/default/android.hardware.ir-service.example.rc
+++ b/ir/aidl/default/android.hardware.ir-service.example.rc
@@ -1,4 +1,4 @@
 service vendor.ir-default /vendor/bin/hw/android.hardware.ir-service.example
     class hal
-    user nobody
-    group nobody
+    user system
+    group system
diff --git a/ir/aidl/default/main.cpp b/ir/aidl/default/main.cpp
index 7c4a816..92376fc 100644
--- a/ir/aidl/default/main.cpp
+++ b/ir/aidl/default/main.cpp
@@ -15,49 +15,77 @@
  */
 
 #include <aidl/android/hardware/ir/BnConsumerIr.h>
+#include <aidl/android/hardware/ir/ConsumerIrFreqRange.h>
 #include <android-base/logging.h>
 #include <android/binder_interface_utils.h>
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
+#include <hardware/consumerir.h>
 #include <numeric>
 
+#include <log/log.h>
+
+using ::aidl::android::hardware::ir::ConsumerIrFreqRange;
+
 namespace aidl::android::hardware::ir {
 
-const std::vector<ConsumerIrFreqRange> kSupportedFreqs = {
-        {2000, 4000},
-        {10000, 30000},
-};
-
 class ConsumerIr : public BnConsumerIr {
+  public:
+    ConsumerIr();
+  private:
     ::ndk::ScopedAStatus getCarrierFreqs(std::vector<ConsumerIrFreqRange>* _aidl_return) override;
     ::ndk::ScopedAStatus transmit(int32_t in_carrierFreqHz,
                                   const std::vector<int32_t>& in_pattern) override;
+    consumerir_device_t *mDevice = nullptr;
 };
 
-::ndk::ScopedAStatus ConsumerIr::getCarrierFreqs(std::vector<ConsumerIrFreqRange>* _aidl_return) {
-    *_aidl_return = kSupportedFreqs;
-    return ::ndk::ScopedAStatus::ok();
+ConsumerIr::ConsumerIr() {
+    const hw_module_t *hw_module = NULL;
+
+    int ret = hw_get_module(CONSUMERIR_HARDWARE_MODULE_ID, &hw_module);
+    if (ret != 0) {
+        ALOGE("hw_get_module %s failed: %d", CONSUMERIR_HARDWARE_MODULE_ID, ret);
+        return;
+    }
+    ret = hw_module->methods->open(hw_module, CONSUMERIR_TRANSMITTER, (hw_device_t **) &mDevice);
+    if (ret < 0) {
+        // note - may want to make this a fatal error - otherwise the service will crash when it's used
+        ALOGE("Can't open consumer IR transmitter, error: %d", ret);
+        // in case it's modified
+        mDevice = nullptr;
+    }
 }
 
-bool isSupportedFreq(int32_t freq) {
-    for (const auto& range : kSupportedFreqs) {
-        if (freq >= range.minHz && freq <= range.maxHz) return true;
+::ndk::ScopedAStatus ConsumerIr::getCarrierFreqs(std::vector<ConsumerIrFreqRange>* _aidl_return) {
+    int32_t len = mDevice->get_num_carrier_freqs(mDevice);
+    if (len < 0) {
+        (*_aidl_return).clear();
+        return ::ndk::ScopedAStatus::ok();
     }
-    return false;
+
+    consumerir_freq_range_t *rangeAr = new consumerir_freq_range_t[len];
+    bool success = (mDevice->get_carrier_freqs(mDevice, len, rangeAr) >= 0);
+    if (!success) {
+        (*_aidl_return).clear();
+        return ::ndk::ScopedAStatus::ok();
+    }
+
+    (*_aidl_return).resize(len);
+    for (int32_t i = 0; i < len; i++) {
+        (*_aidl_return)[i].minHz = static_cast<uint32_t>(rangeAr[i].min);
+        (*_aidl_return)[i].maxHz = static_cast<uint32_t>(rangeAr[i].max);
+    }
+    return ::ndk::ScopedAStatus::ok();
 }
 
 ::ndk::ScopedAStatus ConsumerIr::transmit(int32_t in_carrierFreqHz,
                                           const std::vector<int32_t>& in_pattern) {
-    if (isSupportedFreq(in_carrierFreqHz)) {
-        // trasmit the pattern, each integer is number of microseconds in an
-        // alternating on/off state.
-        usleep(std::accumulate(in_pattern.begin(), in_pattern.end(), 0));
+    if (in_carrierFreqHz > 0) {
+        mDevice->transmit(mDevice, in_carrierFreqHz, in_pattern.data(), in_pattern.size());
         return ::ndk::ScopedAStatus::ok();
     } else {
-        // unsupported operation
         return ::ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
     }
-    return ::ndk::ScopedAStatus::ok();
 }
 
 }  // namespace aidl::android::hardware::ir
diff --git a/keymaster/3.0/default/OWNERS b/keymaster/3.0/default/OWNERS
deleted file mode 100644
index 335660d..0000000
--- a/keymaster/3.0/default/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-jdanis@google.com
-swillden@google.com
diff --git a/keymaster/3.0/vts/OWNERS b/keymaster/3.0/vts/OWNERS
deleted file mode 100644
index 846bb84..0000000
--- a/keymaster/3.0/vts/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-drysdale@google.com
-jdanis@google.com
-swillden@google.com
-yim@google.com
-yuexima@google.com
diff --git a/keymaster/3.0/vts/functional/OWNERS b/keymaster/3.0/vts/functional/OWNERS
deleted file mode 100644
index 2ef9086..0000000
--- a/keymaster/3.0/vts/functional/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 189335
-swillden@google.com
diff --git a/keymaster/4.0/default/OWNERS b/keymaster/4.0/default/OWNERS
deleted file mode 100644
index 335660d..0000000
--- a/keymaster/4.0/default/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-jdanis@google.com
-swillden@google.com
diff --git a/keymaster/4.0/support/OWNERS b/keymaster/4.0/support/OWNERS
deleted file mode 100644
index a9efe66..0000000
--- a/keymaster/4.0/support/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-jdanis@google.com
-swillden@google.com
-jbires@google.com
diff --git a/keymaster/4.0/vts/OWNERS b/keymaster/4.0/vts/OWNERS
deleted file mode 100644
index 0d6fa6c..0000000
--- a/keymaster/4.0/vts/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-drysdale@google.com
-jbires@google.com
-jdanis@google.com
-swillden@google.com
-yim@google.com
-yuexima@google.com
diff --git a/keymaster/4.0/vts/functional/OWNERS b/keymaster/4.0/vts/functional/OWNERS
deleted file mode 100644
index 2ef9086..0000000
--- a/keymaster/4.0/vts/functional/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 189335
-swillden@google.com
diff --git a/keymaster/4.1/default/OWNERS b/keymaster/4.1/default/OWNERS
deleted file mode 100644
index 2b2ad2a..0000000
--- a/keymaster/4.1/default/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-jbires@google.com
-jdanis@google.com
-swillden@google.com
-zeuthen@google.com
\ No newline at end of file
diff --git a/keymaster/4.1/support/OWNERS b/keymaster/4.1/support/OWNERS
deleted file mode 100644
index 2b2ad2a..0000000
--- a/keymaster/4.1/support/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-jbires@google.com
-jdanis@google.com
-swillden@google.com
-zeuthen@google.com
\ No newline at end of file
diff --git a/keymaster/4.1/vts/OWNERS b/keymaster/4.1/vts/OWNERS
deleted file mode 100644
index 24ed042..0000000
--- a/keymaster/4.1/vts/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-drysdale@google.com
-jbires@google.com
-jdanis@google.com
-swillden@google.com
-zeuthen@google.com
\ No newline at end of file
diff --git a/keymaster/4.1/vts/functional/OWNERS b/keymaster/4.1/vts/functional/OWNERS
deleted file mode 100644
index 2ef9086..0000000
--- a/keymaster/4.1/vts/functional/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 189335
-swillden@google.com
diff --git a/keymaster/OWNERS b/keymaster/OWNERS
new file mode 100644
index 0000000..ac8bd83
--- /dev/null
+++ b/keymaster/OWNERS
@@ -0,0 +1,14 @@
+# Bug component: 1084733
+
+# Please assign all bugs related to /hardware/interfaces/keymaster to the team alias:
+#
+#    android-hardware-security@google.com
+#
+# This will get them auto-assigned to the on-call triage engineer, ensuring quickest response.
+
+drysdale@google.com
+eranm@google.com
+hasinitg@google.com
+jbires@google.com
+swillden@google.com
+zeuthen@google.com
diff --git a/keymaster/aidl/Android.bp b/keymaster/aidl/Android.bp
index 0fb6e4c..a3800c1 100644
--- a/keymaster/aidl/Android.bp
+++ b/keymaster/aidl/Android.bp
@@ -19,9 +19,18 @@
             platform_apis: true,
         },
     },
-    versions: [
-        "1",
-        "2",
-        "3",
+    versions_with_info: [
+        {
+            version: "1",
+            imports: [],
+        },
+        {
+            version: "2",
+            imports: [],
+        },
+        {
+            version: "3",
+            imports: [],
+        },
     ],
 }
diff --git a/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/HardwareAuthToken.aidl b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/HardwareAuthToken.aidl
index 4f21cba..6e84e98 100644
--- a/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/HardwareAuthToken.aidl
+++ b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/HardwareAuthToken.aidl
@@ -1,14 +1,30 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 ///////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// 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 changes to the AIDL files built
+// 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
@@ -16,6 +32,7 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.keymaster;
+/* @hide */
 @VintfStability
 parcelable HardwareAuthToken {
   long challenge;
diff --git a/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/HardwareAuthenticatorType.aidl b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/HardwareAuthenticatorType.aidl
index 924567f..0a40549 100644
--- a/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/HardwareAuthenticatorType.aidl
+++ b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/HardwareAuthenticatorType.aidl
@@ -1,14 +1,30 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 ///////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// 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 changes to the AIDL files built
+// 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
@@ -16,6 +32,7 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.keymaster;
+/* @hide */
 @Backing(type="int") @VintfStability
 enum HardwareAuthenticatorType {
   NONE = 0,
diff --git a/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/SecurityLevel.aidl b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/SecurityLevel.aidl
index 127c1bf..d32ef68 100644
--- a/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/SecurityLevel.aidl
+++ b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/SecurityLevel.aidl
@@ -1,14 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 ///////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// 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 changes to the AIDL files built
+// 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
@@ -16,6 +32,7 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.keymaster;
+/* @hide */
 @Backing(type="int") @VintfStability
 enum SecurityLevel {
   SOFTWARE = 0,
diff --git a/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/Timestamp.aidl b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/Timestamp.aidl
index 45fa1ae..5b7b37a 100644
--- a/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/Timestamp.aidl
+++ b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/Timestamp.aidl
@@ -1,14 +1,30 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 ///////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// 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 changes to the AIDL files built
+// 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
@@ -16,6 +32,7 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.keymaster;
+/* @hide */
 @VintfStability
 parcelable Timestamp {
   long milliSeconds;
diff --git a/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/VerificationToken.aidl b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/VerificationToken.aidl
index b116dac..10fa5e1 100644
--- a/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/VerificationToken.aidl
+++ b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/VerificationToken.aidl
@@ -1,14 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 ///////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// 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 changes to the AIDL files built
+// 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
@@ -16,6 +32,7 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.keymaster;
+/* @hide */
 @VintfStability
 parcelable VerificationToken {
   long challenge;
diff --git a/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl b/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl
index 99b036a..427c192 100644
--- a/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl
+++ b/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl
@@ -16,8 +16,8 @@
 
 package android.hardware.keymaster;
 
-import android.hardware.keymaster.Timestamp;
 import android.hardware.keymaster.HardwareAuthenticatorType;
+import android.hardware.keymaster.Timestamp;
 
 /**
  * HardwareAuthToken is used to prove successful user authentication, to unlock the use of a key.
@@ -27,10 +27,10 @@
  * begin(), update(), and finish() to prove that authentication occurred.  See those methods for
  * more details.  It is up to the caller to determine which of the generated auth tokens is
  * appropriate for a given key operation.
+ * @hide
  */
 @VintfStability
 parcelable HardwareAuthToken {
-
     /**
      * challenge is a value that's used to enable authentication tokens to authorize specific
      * events.  The primary use case for challenge is to authorize an IKeymasterDevice cryptographic
@@ -49,7 +49,7 @@
      *  but is created in an authentication application in the secure environment, such as the
      *  Fingerprint application.
      */
-    long authenticatorId;  // Secure authenticator ID.
+    long authenticatorId; // Secure authenticator ID.
 
     /**
      * authenticatorType describes the type of authentication that took place, e.g. password or
diff --git a/keymaster/aidl/android/hardware/keymaster/HardwareAuthenticatorType.aidl b/keymaster/aidl/android/hardware/keymaster/HardwareAuthenticatorType.aidl
index 3141858..7a303a7 100644
--- a/keymaster/aidl/android/hardware/keymaster/HardwareAuthenticatorType.aidl
+++ b/keymaster/aidl/android/hardware/keymaster/HardwareAuthenticatorType.aidl
@@ -20,6 +20,7 @@
  * Hardware authentication type, used by HardwareAuthTokens to specify the mechanism used to
  * authentiate the user, and in KeyCharacteristics to specify the allowable mechanisms for
  * authenticating to activate a key.
+ * @hide
  */
 @VintfStability
 @Backing(type="int")
diff --git a/keymaster/aidl/android/hardware/keymaster/SecurityLevel.aidl b/keymaster/aidl/android/hardware/keymaster/SecurityLevel.aidl
index 00578a4..0b84392 100644
--- a/keymaster/aidl/android/hardware/keymaster/SecurityLevel.aidl
+++ b/keymaster/aidl/android/hardware/keymaster/SecurityLevel.aidl
@@ -18,6 +18,7 @@
 
 /**
  * Device security levels.
+ * @hide
  */
 @VintfStability
 @Backing(type="int")
diff --git a/keymaster/aidl/android/hardware/keymaster/Timestamp.aidl b/keymaster/aidl/android/hardware/keymaster/Timestamp.aidl
index 19ea944..0b0c36f 100644
--- a/keymaster/aidl/android/hardware/keymaster/Timestamp.aidl
+++ b/keymaster/aidl/android/hardware/keymaster/Timestamp.aidl
@@ -16,14 +16,13 @@
 
 package android.hardware.keymaster;
 
-
 /**
  * Time in milliseconds since some arbitrary point in time.  Time must be monotonically increasing,
  * and a secure environment's notion of "current time" must not repeat until the Android device
  * reboots, or until at least 50 million years have elapsed (note that this requirement is satisfied
  * by setting the clock to zero during each boot, and then counting time accurately).
+ * @hide
  */
-
 @VintfStability
 parcelable Timestamp {
     long milliSeconds;
diff --git a/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl b/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl
index 5efd937..ceee941 100644
--- a/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl
+++ b/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl
@@ -24,6 +24,7 @@
  *
  * This version of the parcelable currently don't use the parametersVerified field since it's not
  * needed for time-based verification. This can be added in a later version, if needed.
+ * @hide
  */
 @VintfStability
 parcelable VerificationToken {
@@ -39,7 +40,6 @@
      */
     Timestamp timestamp;
 
-
     /**
      * SecurityLevel of the secure environment that generated the token.
      */
diff --git a/power/TEST_MAPPING b/power/TEST_MAPPING
new file mode 100644
index 0000000..c0963f9
--- /dev/null
+++ b/power/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "VtsHalPowerTargetTest"
+    }
+  ]
+}
diff --git a/power/aidl/default/Android.bp b/power/aidl/default/Android.bp
index 223b9d5..b321394 100644
--- a/power/aidl/default/Android.bp
+++ b/power/aidl/default/Android.bp
@@ -35,6 +35,7 @@
     srcs: [
         "main.cpp",
         "Power.cpp",
+        "PowerHintSession.cpp",
     ],
 }
 
diff --git a/power/aidl/default/Power.cpp b/power/aidl/default/Power.cpp
index 7f08f44..8fe370c 100644
--- a/power/aidl/default/Power.cpp
+++ b/power/aidl/default/Power.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "Power.h"
+#include "PowerHintSession.h"
 
 #include <android-base/logging.h>
 
@@ -25,42 +26,53 @@
 namespace impl {
 namespace example {
 
+using namespace std::chrono_literals;
+
+using ndk::ScopedAStatus;
+
 const std::vector<Boost> BOOST_RANGE{ndk::enum_range<Boost>().begin(),
                                      ndk::enum_range<Boost>().end()};
 const std::vector<Mode> MODE_RANGE{ndk::enum_range<Mode>().begin(), ndk::enum_range<Mode>().end()};
 
-ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) {
+ScopedAStatus Power::setMode(Mode type, bool enabled) {
     LOG(VERBOSE) << "Power setMode: " << static_cast<int32_t>(type) << " to: " << enabled;
-    return ndk::ScopedAStatus::ok();
+    return ScopedAStatus::ok();
 }
 
-ndk::ScopedAStatus Power::isModeSupported(Mode type, bool* _aidl_return) {
+ScopedAStatus Power::isModeSupported(Mode type, bool* _aidl_return) {
     LOG(INFO) << "Power isModeSupported: " << static_cast<int32_t>(type);
     *_aidl_return = type >= MODE_RANGE.front() && type <= MODE_RANGE.back();
-    return ndk::ScopedAStatus::ok();
+    return ScopedAStatus::ok();
 }
 
-ndk::ScopedAStatus Power::setBoost(Boost type, int32_t durationMs) {
+ScopedAStatus Power::setBoost(Boost type, int32_t durationMs) {
     LOG(VERBOSE) << "Power setBoost: " << static_cast<int32_t>(type)
                  << ", duration: " << durationMs;
-    return ndk::ScopedAStatus::ok();
+    return ScopedAStatus::ok();
 }
 
-ndk::ScopedAStatus Power::isBoostSupported(Boost type, bool* _aidl_return) {
+ScopedAStatus Power::isBoostSupported(Boost type, bool* _aidl_return) {
     LOG(INFO) << "Power isBoostSupported: " << static_cast<int32_t>(type);
     *_aidl_return = type >= BOOST_RANGE.front() && type <= BOOST_RANGE.back();
-    return ndk::ScopedAStatus::ok();
+    return ScopedAStatus::ok();
 }
 
-ndk::ScopedAStatus Power::createHintSession(int32_t, int32_t, const std::vector<int32_t>&, int64_t,
-                                            std::shared_ptr<IPowerHintSession>* _aidl_return) {
-    *_aidl_return = nullptr;
-    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ScopedAStatus Power::createHintSession(int32_t, int32_t, const std::vector<int32_t>& tids, int64_t,
+                                       std::shared_ptr<IPowerHintSession>* _aidl_return) {
+    if (tids.size() == 0) {
+        *_aidl_return = nullptr;
+        return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+    std::shared_ptr<IPowerHintSession> powerHintSession =
+            ndk::SharedRefBase::make<PowerHintSession>();
+    mPowerHintSessions.push_back(powerHintSession);
+    *_aidl_return = powerHintSession;
+    return ScopedAStatus::ok();
 }
 
-ndk::ScopedAStatus Power::getHintSessionPreferredRate(int64_t* outNanoseconds) {
-    *outNanoseconds = -1;
-    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ScopedAStatus Power::getHintSessionPreferredRate(int64_t* outNanoseconds) {
+    *outNanoseconds = std::chrono::nanoseconds(1ms).count();
+    return ScopedAStatus::ok();
 }
 
 }  // namespace example
diff --git a/power/aidl/default/Power.h b/power/aidl/default/Power.h
index ef6439d..7f8405e 100644
--- a/power/aidl/default/Power.h
+++ b/power/aidl/default/Power.h
@@ -26,6 +26,7 @@
 namespace example {
 
 class Power : public BnPower {
+  public:
     ndk::ScopedAStatus setMode(Mode type, bool enabled) override;
     ndk::ScopedAStatus isModeSupported(Mode type, bool* _aidl_return) override;
     ndk::ScopedAStatus setBoost(Boost type, int32_t durationMs) override;
@@ -35,6 +36,9 @@
                                          int64_t durationNanos,
                                          std::shared_ptr<IPowerHintSession>* _aidl_return) override;
     ndk::ScopedAStatus getHintSessionPreferredRate(int64_t* outNanoseconds) override;
+
+  private:
+    std::vector<std::shared_ptr<IPowerHintSession>> mPowerHintSessions;
 };
 
 }  // namespace example
diff --git a/power/aidl/default/PowerHintSession.cpp b/power/aidl/default/PowerHintSession.cpp
new file mode 100644
index 0000000..17fd26a
--- /dev/null
+++ b/power/aidl/default/PowerHintSession.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PowerHintSession.h"
+
+#include <android-base/logging.h>
+
+namespace aidl::android::hardware::power::impl::example {
+
+using ndk::ScopedAStatus;
+
+PowerHintSession::PowerHintSession() {}
+
+ScopedAStatus PowerHintSession::updateTargetWorkDuration(int64_t targetDurationNanos) {
+    LOG(VERBOSE) << __func__ << "target duration in nanoseconds: " << targetDurationNanos;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus PowerHintSession::reportActualWorkDuration(
+        const std::vector<WorkDuration>& /* durations */) {
+    LOG(VERBOSE) << __func__;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus PowerHintSession::pause() {
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus PowerHintSession::resume() {
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus PowerHintSession::close() {
+    return ScopedAStatus::ok();
+}
+
+}  // namespace aidl::android::hardware::power::impl::example
diff --git a/power/aidl/default/PowerHintSession.h b/power/aidl/default/PowerHintSession.h
new file mode 100644
index 0000000..b7bf54c
--- /dev/null
+++ b/power/aidl/default/PowerHintSession.h
@@ -0,0 +1,35 @@
+/*
+ * 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/power/BnPowerHintSession.h>
+#include <aidl/android/hardware/power/WorkDuration.h>
+
+namespace aidl::android::hardware::power::impl::example {
+
+class PowerHintSession : public BnPowerHintSession {
+  public:
+    explicit PowerHintSession();
+    ndk::ScopedAStatus updateTargetWorkDuration(int64_t targetDurationNanos) override;
+    ndk::ScopedAStatus reportActualWorkDuration(
+            const std::vector<WorkDuration>& durations) override;
+    ndk::ScopedAStatus pause() override;
+    ndk::ScopedAStatus resume() override;
+    ndk::ScopedAStatus close() override;
+};
+
+}  // namespace aidl::android::hardware::power::impl::example
diff --git a/power/aidl/vts/Android.bp b/power/aidl/vts/Android.bp
index ea398ac..4e8f517 100644
--- a/power/aidl/vts/Android.bp
+++ b/power/aidl/vts/Android.bp
@@ -35,6 +35,7 @@
         "android.hardware.power-V3-ndk",
     ],
     test_suites: [
+        "general-tests",
         "vts",
     ],
 }
diff --git a/power/aidl/vts/VtsHalPowerTargetTest.cpp b/power/aidl/vts/VtsHalPowerTargetTest.cpp
index 2cfa04a..b81dd8f 100644
--- a/power/aidl/vts/VtsHalPowerTargetTest.cpp
+++ b/power/aidl/vts/VtsHalPowerTargetTest.cpp
@@ -83,6 +83,14 @@
         DurationWrapper(1000000000L, 4L),
 };
 
+// DEVICEs launching with Android 11 MUST meet the requirements for the
+// target-level=5 compatibility_matrix file.
+const uint64_t kCompatibilityMatrix5ApiLevel = 30;
+
+// DEVICEs launching with Android 13 MUST meet the requirements for the
+// target-level=7 compatibility_matrix file.
+const uint64_t kCompatibilityMatrix7ApiLevel = 33;
+
 inline bool isUnknownOrUnsupported(const ndk::ScopedAStatus& status) {
     return status.getStatus() == STATUS_UNKNOWN_TRANSACTION ||
            status.getExceptionCode() == EX_UNSUPPORTED_OPERATION;
@@ -94,9 +102,13 @@
         AIBinder* binder = AServiceManager_waitForService(GetParam().c_str());
         ASSERT_NE(binder, nullptr);
         power = IPower::fromBinder(ndk::SpAIBinder(binder));
+
+        mApiLevel = GetUintProperty<uint64_t>("ro.vendor.api_level", 0);
+        ASSERT_NE(mApiLevel, 0);
     }
 
     std::shared_ptr<IPower> power;
+    uint64_t mApiLevel;
 };
 
 TEST_P(PowerAidl, setMode) {
@@ -152,11 +164,11 @@
 TEST_P(PowerAidl, getHintSessionPreferredRate) {
     int64_t rate = -1;
     auto status = power->getHintSessionPreferredRate(&rate);
-    if (!status.isOk()) {
+    if (mApiLevel < kCompatibilityMatrix7ApiLevel && !status.isOk()) {
         EXPECT_TRUE(isUnknownOrUnsupported(status));
-        return;
+        GTEST_SKIP() << "DEVICE not launching with Android 13 and beyond.";
     }
-
+    ASSERT_TRUE(status.isOk());
     // At least 1ms rate limit from HAL
     ASSERT_GE(rate, 1000000);
 }
@@ -164,10 +176,11 @@
 TEST_P(PowerAidl, createAndCloseHintSession) {
     std::shared_ptr<IPowerHintSession> session;
     auto status = power->createHintSession(getpid(), getuid(), kSelfTids, 16666666L, &session);
-    if (!status.isOk()) {
+    if (mApiLevel < kCompatibilityMatrix7ApiLevel && !status.isOk()) {
         EXPECT_TRUE(isUnknownOrUnsupported(status));
-        return;
+        GTEST_SKIP() << "DEVICE not launching with Android 13 and beyond.";
     }
+    ASSERT_TRUE(status.isOk());
     ASSERT_NE(nullptr, session);
     ASSERT_TRUE(session->pause().isOk());
     ASSERT_TRUE(session->resume().isOk());
@@ -175,12 +188,18 @@
     ASSERT_TRUE(session->close().isOk());
     session.reset();
 }
+
 TEST_P(PowerAidl, createHintSessionFailed) {
     std::shared_ptr<IPowerHintSession> session;
     auto status = power->createHintSession(getpid(), getuid(), kEmptyTids, 16666666L, &session);
+
+    // Regardless of whether V2 and beyond is supported, the status is always not STATUS_OK.
     ASSERT_FALSE(status.isOk());
-    if (isUnknownOrUnsupported(status)) {
-        return;
+
+    // If device not launching with Android 13 and beyond, check whether it's supported,
+    // if not, skip the test.
+    if (mApiLevel < kCompatibilityMatrix7ApiLevel && isUnknownOrUnsupported(status)) {
+        GTEST_SKIP() << "DEVICE not launching with Android 13 and beyond.";
     }
     ASSERT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode());
 }
@@ -188,10 +207,11 @@
 TEST_P(PowerAidl, updateAndReportDurations) {
     std::shared_ptr<IPowerHintSession> session;
     auto status = power->createHintSession(getpid(), getuid(), kSelfTids, 16666666L, &session);
-    if (!status.isOk()) {
+    if (mApiLevel < kCompatibilityMatrix7ApiLevel && !status.isOk()) {
         EXPECT_TRUE(isUnknownOrUnsupported(status));
-        return;
+        GTEST_SKIP() << "DEVICE not launching with Android 13 and beyond.";
     }
+    ASSERT_TRUE(status.isOk());
     ASSERT_NE(nullptr, session);
 
     ASSERT_TRUE(session->updateTargetWorkDuration(16666667LL).isOk());
@@ -201,14 +221,13 @@
 // FIXED_PERFORMANCE mode is required for all devices which ship on Android 11
 // or later
 TEST_P(PowerAidl, hasFixedPerformance) {
-    auto apiLevel = GetUintProperty<uint64_t>("ro.vendor.api_level", 0);
-    ASSERT_NE(apiLevel, 0);
-
-    if (apiLevel >= 30) {
-        bool supported;
-        ASSERT_TRUE(power->isModeSupported(Mode::FIXED_PERFORMANCE, &supported).isOk());
-        ASSERT_TRUE(supported);
+    if (mApiLevel < kCompatibilityMatrix5ApiLevel) {
+        GTEST_SKIP() << "FIXED_PERFORMANCE mode is only required for all devices launching Android "
+                        "11 or later.";
     }
+    bool supported;
+    ASSERT_TRUE(power->isModeSupported(Mode::FIXED_PERFORMANCE, &supported).isOk());
+    ASSERT_TRUE(supported);
 }
 
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PowerAidl);
diff --git a/power/stats/aidl/OWNERS b/power/stats/aidl/OWNERS
index b290b49..e0d66d7 100644
--- a/power/stats/aidl/OWNERS
+++ b/power/stats/aidl/OWNERS
@@ -1,3 +1,4 @@
-bsschwar@google.com
+darrenhsu@google.com
+joaodias@google.com
 krossmo@google.com
-tstrudel@google.com
+vincentwang@google.com
diff --git a/radio/aidl/vts/radio_aidl_hal_utils.cpp b/radio/aidl/vts/radio_aidl_hal_utils.cpp
index efc4f26..6ed8e7d 100644
--- a/radio/aidl/vts/radio_aidl_hal_utils.cpp
+++ b/radio/aidl/vts/radio_aidl_hal_utils.cpp
@@ -92,6 +92,10 @@
     return testing::checkSubstringInCommandOutput("getprop persist.radio.multisim.config", "dsds");
 }
 
+bool isDsDaEnabled() {
+    return testing::checkSubstringInCommandOutput("getprop persist.radio.multisim.config", "dsda");
+}
+
 bool isTsTsEnabled() {
     return testing::checkSubstringInCommandOutput("getprop persist.radio.multisim.config", "tsts");
 }
diff --git a/radio/aidl/vts/radio_aidl_hal_utils.h b/radio/aidl/vts/radio_aidl_hal_utils.h
index 47976b9..8170a01 100644
--- a/radio/aidl/vts/radio_aidl_hal_utils.h
+++ b/radio/aidl/vts/radio_aidl_hal_utils.h
@@ -104,6 +104,11 @@
 bool isDsDsEnabled();
 
 /*
+ * Check if device is in DSDA (Dual SIM Dual Active).
+ */
+bool isDsDaEnabled();
+
+/*
  * Check if device is in TSTS (Triple SIM Triple Standby).
  */
 bool isTsTsEnabled();
diff --git a/radio/aidl/vts/radio_config_test.cpp b/radio/aidl/vts/radio_config_test.cpp
index 258b172..c979d28 100644
--- a/radio/aidl/vts/radio_config_test.cpp
+++ b/radio/aidl/vts/radio_config_test.cpp
@@ -176,7 +176,7 @@
         slotPortMapping.physicalSlotId = -1;
         slotPortMapping.portId = -1;
         std::vector<SlotPortMapping> slotPortMappingList = {slotPortMapping};
-        if (isDsDsEnabled()) {
+        if (isDsDsEnabled() || isDsDaEnabled()) {
             slotPortMappingList.push_back(slotPortMapping);
         } else if (isTsTsEnabled()) {
             slotPortMappingList.push_back(slotPortMapping);
@@ -252,7 +252,7 @@
         }
         if (isSsSsEnabled()) {
             EXPECT_EQ(1, simCount);
-        } else if (isDsDsEnabled()) {
+        } else if (isDsDsEnabled() || isDsDaEnabled()) {
             EXPECT_EQ(2, simCount);
         } else if (isTsTsEnabled()) {
             EXPECT_EQ(3, simCount);
diff --git a/security/OWNERS b/security/OWNERS
index f061cd6..fbaf854 100644
--- a/security/OWNERS
+++ b/security/OWNERS
@@ -1,7 +1,14 @@
-# Please assign all bugs related to /hardware/interfaces/security to the team alias,
-# android-hardware-security@google.com.  This will get them auto-assigned to the on-call triage
-# engineer, ensuring quickest response.
+# Bug component: 1084733
+
+# Please assign all bugs related to /hardware/interfaces/security/ to the team alias:
+#
+#    android-hardware-security@google.com
+#
+# This will get them auto-assigned to the on-call triage engineer, ensuring quickest response.
+
 drysdale@google.com
+eranm@google.com
+hasinitg@google.com
 jbires@google.com
-seleneh@google.com
 swillden@google.com
+zeuthen@google.com
diff --git a/security/keymint/TEST_MAPPING b/security/keymint/TEST_MAPPING
new file mode 100644
index 0000000..9ce5e9b
--- /dev/null
+++ b/security/keymint/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "VtsHalRemotelyProvisionedComponentTargetTest"
+    }
+  ]
+}
diff --git a/security/keymint/aidl/Android.bp b/security/keymint/aidl/Android.bp
index 96d44a7..5a76a21 100644
--- a/security/keymint/aidl/Android.bp
+++ b/security/keymint/aidl/Android.bp
@@ -17,6 +17,7 @@
         "android.hardware.security.secureclock-V1",
     ],
     stability: "vintf",
+    frozen: false,
     backend: {
         java: {
             platform_apis: true,
@@ -52,21 +53,21 @@
 cc_defaults {
     name: "keymint_use_latest_hal_aidl_ndk_static",
     static_libs: [
-        "android.hardware.security.keymint-V2-ndk",
+        "android.hardware.security.keymint-V3-ndk",
     ],
 }
 
 cc_defaults {
     name: "keymint_use_latest_hal_aidl_ndk_shared",
     shared_libs: [
-        "android.hardware.security.keymint-V2-ndk",
+        "android.hardware.security.keymint-V3-ndk",
     ],
 }
 
 cc_defaults {
     name: "keymint_use_latest_hal_aidl_cpp_static",
     static_libs: [
-        "android.hardware.security.keymint-V2-cpp",
+        "android.hardware.security.keymint-V3-cpp",
     ],
 }
 
@@ -76,6 +77,6 @@
 rust_defaults {
     name: "keymint_use_latest_hal_aidl_rust",
     rustlibs: [
-        "android.hardware.security.keymint-V2-rust",
+        "android.hardware.security.keymint-V3-rust",
     ],
 }
diff --git a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
index ca517ac..f4c0095 100644
--- a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
+++ b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
@@ -798,7 +798,7 @@
     add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_BRAND, "ro.product.brand");
     add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_DEVICE, "ro.product.device");
     add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_PRODUCT, "ro.product.name");
-    add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serial");
+    add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serialno");
     add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MANUFACTURER,
                       "ro.product.manufacturer");
     add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MODEL, "ro.product.model");
diff --git a/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp b/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
index 1dc5df3..cd140c8 100644
--- a/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
+++ b/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
@@ -252,7 +252,7 @@
     add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_BRAND, "ro.product.brand");
     add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_DEVICE, "ro.product.device");
     add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_PRODUCT, "ro.product.name");
-    add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serial");
+    add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serialno");
     add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MANUFACTURER,
                       "ro.product.manufacturer");
     add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MODEL, "ro.product.model");
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index 4f5d821..69fe434 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -1990,7 +1990,7 @@
     add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_BRAND, "ro.product.brand");
     add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_DEVICE, "ro.product.device");
     add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_PRODUCT, "ro.product.name");
-    add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serial");
+    add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serialno");
     add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_MANUFACTURER, "ro.product.manufacturer");
     add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_MODEL, "ro.product.model");
 
@@ -4946,15 +4946,15 @@
 TEST_P(ImportWrappedKeyTest, WrongDigest) {
     auto wrapping_key_desc = AuthorizationSetBuilder()
                                      .RsaEncryptionKey(2048, 65537)
-                                     .Digest(Digest::SHA_2_512)
                                      .Padding(PaddingMode::RSA_OAEP)
+                                     .Digest(Digest::SHA_2_256)
                                      .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY)
                                      .SetDefaultValidity();
 
     ASSERT_EQ(ErrorCode::INCOMPATIBLE_DIGEST,
               ImportWrappedKey(wrapped_key, wrapping_key, wrapping_key_desc, zero_masking_key,
                                AuthorizationSetBuilder()
-                                       .Digest(Digest::SHA_2_256)
+                                       .Digest(Digest::SHA_2_512)
                                        .Padding(PaddingMode::RSA_OAEP)));
 }
 
diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
index 9b21e4e..4f361bb 100644
--- a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
+++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
@@ -46,6 +46,7 @@
 namespace {
 
 constexpr int32_t VERSION_WITH_UNIQUE_ID_SUPPORT = 2;
+constexpr int32_t VERSION_WITHOUT_TEST_MODE = 3;
 
 #define INSTANTIATE_REM_PROV_AIDL_TEST(name)                                         \
     GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(name);                             \
@@ -180,6 +181,15 @@
         return params;
     }
 
+    void checkMacedPubkeyVersioned(const MacedPublicKey& macedPubKey, bool testMode,
+                                   vector<uint8_t>* payload_value) {
+        if (rpcHardwareInfo.versionNumber >= VERSION_WITHOUT_TEST_MODE) {
+            check_maced_pubkey(macedPubKey, false, payload_value);
+        } else {
+            check_maced_pubkey(macedPubKey, testMode, payload_value);
+        }
+    }
+
   protected:
     std::shared_ptr<IRemotelyProvisionedComponent> provisionable_;
     RpcHardwareInfo rpcHardwareInfo;
@@ -256,7 +266,7 @@
     auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
     ASSERT_TRUE(status.isOk());
     vector<uint8_t> coseKeyData;
-    check_maced_pubkey(macedPubKey, testMode, &coseKeyData);
+    checkMacedPubkeyVersioned(macedPubKey, testMode, &coseKeyData);
 }
 
 /**
@@ -279,7 +289,7 @@
     auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
     ASSERT_TRUE(status.isOk());
     vector<uint8_t> coseKeyData;
-    check_maced_pubkey(macedPubKey, testMode, &coseKeyData);
+    checkMacedPubkeyVersioned(macedPubKey, testMode, &coseKeyData);
 
     AttestationKey attestKey;
     attestKey.keyBlob = std::move(privateKeyBlob);
@@ -334,13 +344,13 @@
     bool testMode = true;
     auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
     ASSERT_TRUE(status.isOk());
-
-    check_maced_pubkey(macedPubKey, testMode, nullptr);
+    checkMacedPubkeyVersioned(macedPubKey, testMode, nullptr);
 }
 
-class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests {
+class CertificateRequestTestBase : public VtsRemotelyProvisionedComponentTests {
   protected:
-    CertificateRequestTest() : eekId_(string_to_bytevec("eekid")), challenge_(randomBytes(64)) {}
+    CertificateRequestTestBase()
+        : eekId_(string_to_bytevec("eekid")), challenge_(randomBytes(64)) {}
 
     void generateTestEekChain(size_t eekLength) {
         auto chain = generateEekChain(rpcHardwareInfo.supportedEekCurve, eekLength, eekId_);
@@ -359,7 +369,7 @@
             ASSERT_TRUE(status.isOk()) << status.getMessage();
 
             vector<uint8_t> payload_value;
-            check_maced_pubkey(key, testMode, &payload_value);
+            checkMacedPubkeyVersioned(key, testMode, &payload_value);
             cborKeysToSign_.add(cppbor::EncodedItem(payload_value));
         }
     }
@@ -372,6 +382,18 @@
     cppbor::Array cborKeysToSign_;
 };
 
+class CertificateRequestTest : public CertificateRequestTestBase {
+  protected:
+    void SetUp() override {
+        CertificateRequestTestBase::SetUp();
+
+        if (rpcHardwareInfo.versionNumber >= VERSION_WITHOUT_TEST_MODE) {
+            GTEST_SKIP() << "This test case only applies to RKP v1 and v2. "
+                         << "RKP version discovered: " << rpcHardwareInfo.versionNumber;
+        }
+    }
+};
+
 /**
  * Generate an empty certificate request in test mode, and decrypt and verify the structure and
  * content.
@@ -638,4 +660,131 @@
 
 INSTANTIATE_REM_PROV_AIDL_TEST(CertificateRequestTest);
 
+class CertificateRequestV2Test : public CertificateRequestTestBase {
+    void SetUp() override {
+        CertificateRequestTestBase::SetUp();
+
+        if (rpcHardwareInfo.versionNumber < VERSION_WITHOUT_TEST_MODE) {
+            GTEST_SKIP() << "This test case only applies to RKP v3 and above. "
+                         << "RKP version discovered: " << rpcHardwareInfo.versionNumber;
+        }
+    }
+};
+
+/**
+ * Generate an empty certificate request, and decrypt and verify the structure and content.
+ */
+TEST_P(CertificateRequestV2Test, EmptyRequest) {
+    bytevec csr;
+
+    auto status =
+            provisionable_->generateCertificateRequestV2({} /* keysToSign */, challenge_, &csr);
+    ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+    auto result = verifyProductionCsr(cppbor::Array(), csr, provisionable_.get(), challenge_);
+    ASSERT_TRUE(result) << result.message();
+}
+
+/**
+ * Generate a non-empty certificate request.  Decrypt, parse and validate the contents.
+ */
+TEST_P(CertificateRequestV2Test, NonEmptyRequest) {
+    generateKeys(false /* testMode */, 1 /* numKeys */);
+
+    bytevec csr;
+
+    auto status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr);
+    ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+    auto result = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_);
+    ASSERT_TRUE(result) << result.message();
+}
+
+/**
+ * Generate a non-empty certificate request.  Make sure contents are reproducible.
+ */
+TEST_P(CertificateRequestV2Test, NonEmptyRequestReproducible) {
+    generateKeys(false /* testMode */, 1 /* numKeys */);
+
+    bytevec csr;
+
+    auto status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr);
+    ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+    auto firstBcc = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_);
+    ASSERT_TRUE(firstBcc) << firstBcc.message();
+
+    status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr);
+    ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+    auto secondBcc = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_);
+    ASSERT_TRUE(secondBcc) << secondBcc.message();
+
+    ASSERT_EQ(firstBcc->size(), secondBcc->size());
+    for (auto i = 0; i < firstBcc->size(); i++) {
+        ASSERT_EQ(firstBcc->at(i).pubKey, secondBcc->at(i).pubKey);
+    }
+}
+
+/**
+ * Generate a non-empty certificate request with multiple keys.
+ */
+TEST_P(CertificateRequestV2Test, NonEmptyRequestMultipleKeys) {
+    // TODO(b/254137722): define a minimum number of keys that must be supported.
+    generateKeys(false /* testMode */, 5 /* numKeys */);
+
+    bytevec csr;
+
+    auto status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr);
+    ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+    auto result = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_);
+    ASSERT_TRUE(result) << result.message();
+}
+
+/**
+ * Generate a non-empty certificate request, but with the MAC corrupted on the keypair.
+ */
+TEST_P(CertificateRequestV2Test, NonEmptyRequestCorruptMac) {
+    generateKeys(false /* testMode */, 1 /* numKeys */);
+    auto result = corrupt_maced_key(keysToSign_[0]);
+    ASSERT_TRUE(result) << result.moveMessage();
+    MacedPublicKey keyWithCorruptMac = result.moveValue();
+
+    bytevec csr;
+    auto status =
+            provisionable_->generateCertificateRequestV2({keyWithCorruptMac}, challenge_, &csr);
+    ASSERT_FALSE(status.isOk()) << status.getMessage();
+    EXPECT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_MAC);
+}
+
+/**
+ * Generate a non-empty certificate request in prod mode, with test keys.  Test mode must be
+ * ignored, i.e. test must pass.
+ */
+TEST_P(CertificateRequestV2Test, NonEmptyRequest_testKeyInProdCert) {
+    generateKeys(true /* testMode */, 1 /* numKeys */);
+
+    bytevec csr;
+    auto status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr);
+    ASSERT_TRUE(status.isOk()) << status.getMessage();
+}
+
+/**
+ * Call generateCertificateRequest(). Make sure it's removed.
+ */
+TEST_P(CertificateRequestV2Test, CertificateRequestV1Removed) {
+    generateTestEekChain(2);
+    bytevec keysToSignMac;
+    DeviceInfo deviceInfo;
+    ProtectedData protectedData;
+    auto status = provisionable_->generateCertificateRequest(
+            true /* testMode */, {} /* keysToSign */, testEekChain_.chain, challenge_, &deviceInfo,
+            &protectedData, &keysToSignMac);
+    ASSERT_FALSE(status.isOk()) << status.getMessage();
+    EXPECT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_REMOVED);
+}
+
+INSTANTIATE_REM_PROV_AIDL_TEST(CertificateRequestV2Test);
+
 }  // namespace aidl::android::hardware::security::keymint::test
diff --git a/security/keymint/support/include/remote_prov/remote_prov_utils.h b/security/keymint/support/include/remote_prov/remote_prov_utils.h
index 9ea20ac..6871e1b 100644
--- a/security/keymint/support/include/remote_prov/remote_prov_utils.h
+++ b/security/keymint/support/include/remote_prov/remote_prov_utils.h
@@ -177,4 +177,19 @@
         const EekChain& eekChain, const std::vector<uint8_t>& eekId, int32_t supportedEekCurve,
         IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge);
 
+/**
+ * Verify the CSR as if the device is still early in the factory process and may not
+ * have all device identifiers provisioned yet.
+ */
+ErrMsgOr<std::vector<BccEntryData>> verifyFactoryCsr(const cppbor::Array& keysToSign,
+                                                     const std::vector<uint8_t>& csr,
+                                                     IRemotelyProvisionedComponent* provisionable,
+                                                     const std::vector<uint8_t>& challenge);
+/**
+ * Verify the CSR as if the device is a final production sample.
+ */
+ErrMsgOr<std::vector<BccEntryData>> verifyProductionCsr(
+        const cppbor::Array& keysToSign, const std::vector<uint8_t>& csr,
+        IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge);
+
 }  // namespace aidl::android::hardware::security::keymint::remote_prov
diff --git a/security/keymint/support/remote_prov_utils.cpp b/security/keymint/support/remote_prov_utils.cpp
index 0651496..f7ab3ac 100644
--- a/security/keymint/support/remote_prov_utils.cpp
+++ b/security/keymint/support/remote_prov_utils.cpp
@@ -32,6 +32,7 @@
 #include <openssl/base64.h>
 #include <openssl/evp.h>
 #include <openssl/rand.h>
+#include <openssl/x509.h>
 #include <remote_prov/remote_prov_utils.h>
 
 namespace aidl::android::hardware::security::keymint::remote_prov {
@@ -45,6 +46,8 @@
 using EC_KEY_Ptr = bssl::UniquePtr<EC_KEY>;
 using EVP_PKEY_Ptr = bssl::UniquePtr<EVP_PKEY>;
 using EVP_PKEY_CTX_Ptr = bssl::UniquePtr<EVP_PKEY_CTX>;
+using X509_Ptr = bssl::UniquePtr<X509>;
+using CRYPTO_BUFFER_Ptr = bssl::UniquePtr<CRYPTO_BUFFER>;
 
 ErrMsgOr<bytevec> ecKeyGetPrivateKey(const EC_KEY* ecKey) {
     // Extract private key.
@@ -527,22 +530,27 @@
     if (parsed->clone()->asMap()->canonicalize().encode() != deviceInfoBytes) {
         return "DeviceInfo ordering is non-canonical.";
     }
-    const std::unique_ptr<cppbor::Item>& version = parsed->get("version");
-    if (!version) {
-        return "Device info is missing version";
-    }
-    if (!version->asUint()) {
-        return "version must be an unsigned integer";
-    }
+
     RpcHardwareInfo info;
     provisionable->getHardwareInfo(&info);
-    if (version->asUint()->value() != info.versionNumber) {
-        return "DeviceInfo version (" + std::to_string(version->asUint()->value()) +
-               ") does not match the remotely provisioned component version (" +
-               std::to_string(info.versionNumber) + ").";
+    if (info.versionNumber < 3) {
+        const std::unique_ptr<cppbor::Item>& version = parsed->get("version");
+        if (!version) {
+            return "Device info is missing version";
+        }
+        if (!version->asUint()) {
+            return "version must be an unsigned integer";
+        }
+        if (version->asUint()->value() != info.versionNumber) {
+            return "DeviceInfo version (" + std::to_string(version->asUint()->value()) +
+                   ") does not match the remotely provisioned component version (" +
+                   std::to_string(info.versionNumber) + ").";
+        }
     }
+
     std::string error;
-    switch (version->asUint()->value()) {
+    switch (info.versionNumber) {
+        case 3:
         case 2:
             for (const auto& entry : kAttestationIdEntrySet) {
                 error += checkMapEntry(isFactory && !entry.alwaysValidate, *parsed, cppbor::TSTR,
@@ -579,7 +587,7 @@
                                    kValidAttIdStates);
             break;
         default:
-            return "Unrecognized version: " + std::to_string(version->asUint()->value());
+            return "Unrecognized version: " + std::to_string(info.versionNumber);
     }
 
     if (!error.empty()) {
@@ -734,4 +742,264 @@
                                /*isFactory=*/false);
 }
 
-}  // namespace aidl::android::hardware::security::keymint::remote_prov
\ No newline at end of file
+ErrMsgOr<X509_Ptr> parseX509Cert(const std::vector<uint8_t>& cert) {
+    CRYPTO_BUFFER_Ptr certBuf(CRYPTO_BUFFER_new(cert.data(), cert.size(), nullptr));
+    if (!certBuf.get()) {
+        return "Failed to create crypto buffer.";
+    }
+    X509_Ptr result(X509_parse_from_buffer(certBuf.get()));
+    if (!result.get()) {
+        return "Failed to parse certificate.";
+    }
+    return result;
+}
+
+std::string getX509IssuerName(const X509_Ptr& cert) {
+    char* name = X509_NAME_oneline(X509_get_issuer_name(cert.get()), nullptr, 0);
+    std::string result(name);
+    OPENSSL_free(name);
+    return result;
+}
+
+std::string getX509SubjectName(const X509_Ptr& cert) {
+    char* name = X509_NAME_oneline(X509_get_subject_name(cert.get()), nullptr, 0);
+    std::string result(name);
+    OPENSSL_free(name);
+    return result;
+}
+
+// Validates the certificate chain and returns the leaf public key.
+ErrMsgOr<bytevec> validateCertChain(const cppbor::Array& chain) {
+    uint8_t rawPubKey[64];
+    size_t rawPubKeySize = sizeof(rawPubKey);
+    for (size_t i = 0; i < chain.size(); ++i) {
+        // Root must be self-signed.
+        size_t signingCertIndex = (i > 1) ? i - 1 : i;
+        auto& keyCertItem = chain[i];
+        auto& signingCertItem = chain[signingCertIndex];
+        if (!keyCertItem || !keyCertItem->asBstr()) {
+            return "Key certificate must be a Bstr.";
+        }
+        if (!signingCertItem || !signingCertItem->asBstr()) {
+            return "Signing certificate must be a Bstr.";
+        }
+
+        auto keyCert = parseX509Cert(keyCertItem->asBstr()->value());
+        if (!keyCert) {
+            return keyCert.message();
+        }
+        auto signingCert = parseX509Cert(keyCertItem->asBstr()->value());
+        if (!signingCert) {
+            return signingCert.message();
+        }
+
+        EVP_PKEY_Ptr pubKey(X509_get_pubkey(keyCert->get()));
+        if (!pubKey.get()) {
+            return "Failed to get public key.";
+        }
+        EVP_PKEY_Ptr signingPubKey(X509_get_pubkey(signingCert->get()));
+        if (!signingPubKey.get()) {
+            return "Failed to get signing public key.";
+        }
+
+        if (!X509_verify(keyCert->get(), signingPubKey.get())) {
+            return "Verification of certificate " + std::to_string(i) +
+                   " faile. OpenSSL error string: " + ERR_error_string(ERR_get_error(), NULL);
+        }
+
+        auto certIssuer = getX509IssuerName(*keyCert);
+        auto signerSubj = getX509SubjectName(*signingCert);
+        if (certIssuer != signerSubj) {
+            return "Certificate " + std::to_string(i) + " has wrong issuer. Signer subject is " +
+                   signerSubj + " Issuer subject is " + certIssuer;
+        }
+
+        rawPubKeySize = sizeof(rawPubKey);
+        if (!EVP_PKEY_get_raw_public_key(pubKey.get(), rawPubKey, &rawPubKeySize)) {
+            return "Failed to get raw public key.";
+        }
+    }
+
+    return bytevec(rawPubKey, rawPubKey + rawPubKeySize);
+}
+
+std::string validateUdsCerts(const cppbor::Map& udsCerts, const bytevec& udsPub) {
+    for (const auto& [signerName, udsCertChain] : udsCerts) {
+        if (!signerName || !signerName->asTstr()) {
+            return "Signer Name must be a Tstr.";
+        }
+        if (!udsCertChain || !udsCertChain->asArray()) {
+            return "UDS certificate chain must be an Array.";
+        }
+        if (udsCertChain->asArray()->size() < 2) {
+            return "UDS certificate chain must have at least two entries: root and leaf.";
+        }
+
+        auto leafPubKey = validateCertChain(*udsCertChain->asArray());
+        if (!leafPubKey) {
+            return leafPubKey.message();
+        }
+        if (*leafPubKey != udsPub) {
+            return "Leaf public key in UDS certificat chain doesn't match UDS public key.";
+        }
+    }
+    return "";
+}
+
+ErrMsgOr<cppbor::Array> parseAndValidateCsrPayload(const cppbor::Array& keysToSign,
+                                                   const std::vector<uint8_t>& csrPayload,
+                                                   IRemotelyProvisionedComponent* provisionable,
+                                                   const std::vector<uint8_t>& challenge,
+                                                   bool isFactory) {
+    auto [parsedCsrPayload, _, errMsg] = cppbor::parse(csrPayload);
+    if (!parsedCsrPayload) {
+        return errMsg;
+    }
+    if (!parsedCsrPayload->asArray()) {
+        return "CSR payload is not a CBOR array.";
+    }
+    if (parsedCsrPayload->asArray()->size() != 5U) {
+        return "CSR payload must contain version, certificate type, device info, challenge, keys. "
+               "However, the parsed CSR payload has " +
+               std::to_string(parsedCsrPayload->asArray()->size()) + " entries.";
+    }
+
+    auto& signedVersion = parsedCsrPayload->asArray()->get(0);
+    auto& signedCertificateType = parsedCsrPayload->asArray()->get(1);
+    auto& signedDeviceInfo = parsedCsrPayload->asArray()->get(2);
+    auto& signedChallenge = parsedCsrPayload->asArray()->get(3);
+    auto& signedKeys = parsedCsrPayload->asArray()->get(4);
+
+    if (!signedVersion || !signedVersion->asUint() || signedVersion->asUint()->value() != 1U) {
+        return "CSR payload version must be an unsigned integer and must be equal to 1.";
+    }
+    if (!signedCertificateType || !signedCertificateType->asTstr()) {
+        // Certificate type is allowed to be extendend by vendor, i.e. we can't
+        // enforce its value.
+        return "Certificate type must be a Tstr.";
+    }
+    if (!signedDeviceInfo || !signedDeviceInfo->asMap()) {
+        return "Device info must be an Map.";
+    }
+    if (!signedChallenge || !signedChallenge->asBstr()) {
+        return "Challenge must be a Bstr.";
+    }
+    if (!signedKeys || !signedKeys->asArray()) {
+        return "Keys must be an Array.";
+    }
+
+    auto result = parseAndValidateDeviceInfo(signedDeviceInfo->asMap()->encode(), provisionable,
+                                             isFactory);
+    if (!result) {
+        return result.message();
+    }
+
+    if (challenge.size() < 32 || challenge.size() > 64) {
+        return "Challenge size must be between 32 and 64 bytes inclusive. "
+               "However, challenge is " +
+               std::to_string(challenge.size()) + " bytes long.";
+    }
+
+    auto challengeBstr = cppbor::Bstr(challenge);
+    if (*signedChallenge->asBstr() != challengeBstr) {
+        return "Signed challenge does not match."
+               "\n  Actual: " +
+               cppbor::prettyPrint(signedChallenge->asBstr(), 64 /* maxBStrSize */) +
+               "\nExpected: " + cppbor::prettyPrint(&challengeBstr, 64 /* maxBStrSize */);
+    }
+
+    if (signedKeys->asArray()->encode() != keysToSign.encode()) {
+        return "Signed keys do not match.";
+    }
+
+    return std::move(*parsedCsrPayload->asArray());
+}
+
+ErrMsgOr<std::vector<BccEntryData>> verifyCsr(const cppbor::Array& keysToSign,
+                                              const std::vector<uint8_t>& csr,
+                                              IRemotelyProvisionedComponent* provisionable,
+                                              const std::vector<uint8_t>& challenge,
+                                              bool isFactory) {
+    auto [parsedCsr, _, csrErrMsg] = cppbor::parse(csr);
+    if (!parsedCsr) {
+        return csrErrMsg;
+    }
+    if (!parsedCsr->asArray()) {
+        return "CSR is not a CBOR array.";
+    }
+    if (parsedCsr->asArray()->size() != 4U) {
+        return "CSR must contain version, UDS certificates, DICE chain, and signed data. "
+               "However, the parsed CSR has " +
+               std::to_string(parsedCsr->asArray()->size()) + " entries.";
+    }
+
+    auto& version = parsedCsr->asArray()->get(0);
+    auto& udsCerts = parsedCsr->asArray()->get(1);
+    auto& diceCertChain = parsedCsr->asArray()->get(2);
+    auto& signedData = parsedCsr->asArray()->get(3);
+
+    if (!version || !version->asUint() || version->asUint()->value() != 3U) {
+        return "Version must be an unsigned integer and must be equal to 3.";
+    }
+    if (!udsCerts || !udsCerts->asMap()) {
+        return "UdsCerts must be an Map.";
+    }
+    if (!diceCertChain || !diceCertChain->asArray()) {
+        return "DiceCertChain must be an Array.";
+    }
+    if (!signedData || !signedData->asArray()) {
+        return "SignedData must be an Array.";
+    }
+
+    RpcHardwareInfo info;
+    provisionable->getHardwareInfo(&info);
+    if (version->asUint()->value() != info.versionNumber) {
+        return "CSR version (" + std::to_string(version->asUint()->value()) +
+               ") does not match the remotely provisioned component version (" +
+               std::to_string(info.versionNumber) + ").";
+    }
+
+    // DICE chain is [ pubkey, + DiceChainEntry ]. Its format is the same as BCC from RKP v1-2.
+    auto diceContents = validateBcc(diceCertChain->asArray());
+    if (!diceContents) {
+        return diceContents.message() + "\n" + prettyPrint(diceCertChain.get());
+    }
+    if (diceContents->size() == 0U) {
+        return "The DICE chain is empty. It must contain at least one entry.";
+    }
+
+    auto& udsPub = diceContents->back().pubKey;
+
+    auto error = validateUdsCerts(*udsCerts->asMap(), udsPub);
+    if (!error.empty()) {
+        return error;
+    }
+
+    auto csrPayload = verifyAndParseCoseSign1(signedData->asArray(), udsPub, {} /* aad */);
+    if (!csrPayload) {
+        return csrPayload.message();
+    }
+
+    auto parsedCsrPayload = parseAndValidateCsrPayload(keysToSign, *csrPayload, provisionable,
+                                                       challenge, isFactory);
+    if (!parsedCsrPayload) {
+        return parsedCsrPayload.message();
+    }
+
+    return *diceContents;
+}
+
+ErrMsgOr<std::vector<BccEntryData>> verifyFactoryCsr(const cppbor::Array& keysToSign,
+                                                     const std::vector<uint8_t>& csr,
+                                                     IRemotelyProvisionedComponent* provisionable,
+                                                     const std::vector<uint8_t>& challenge) {
+    return verifyCsr(keysToSign, csr, provisionable, challenge, /*isFactory=*/true);
+}
+
+ErrMsgOr<std::vector<BccEntryData>> verifyProductionCsr(
+        const cppbor::Array& keysToSign, const std::vector<uint8_t>& csr,
+        IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge) {
+    return verifyCsr(keysToSign, csr, provisionable, challenge, /*isFactory=*/false);
+}
+
+}  // namespace aidl::android::hardware::security::keymint::remote_prov
diff --git a/sensors/common/vts/utils/Android.bp b/sensors/common/vts/utils/Android.bp
index 08b6afa..b35280a 100644
--- a/sensors/common/vts/utils/Android.bp
+++ b/sensors/common/vts/utils/Android.bp
@@ -27,9 +27,11 @@
 // dependencies
 cc_defaults {
     name: "VtsHalSensorsDefaults",
+    defaults: [
+        "android.hardware.graphics.allocator-ndk_shared",
+        "android.hardware.graphics.common-ndk_shared",
+    ],
     shared_libs: [
-        "android.hardware.graphics.allocator-V1-ndk",
-        "android.hardware.graphics.common-V3-ndk",
         "libbinder_ndk",
         "libutils",
         "libvndksupport",
@@ -42,7 +44,11 @@
 
 cc_library_static {
     name: "VtsHalSensorsTargetTestUtils",
-    defaults: ["VtsHalTargetTestDefaults"],
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "android.hardware.graphics.allocator-ndk_shared",
+        "android.hardware.graphics.common-ndk_shared",
+    ],
     cflags: ["-DLOG_TAG=\"sensors_hidl_hal_test\""],
     srcs: [
         "GrallocWrapper.cpp",
@@ -55,8 +61,6 @@
     ],
     // Targets that depend on us need to also include these
     shared_libs: [
-        "android.hardware.graphics.allocator-V1-ndk",
-        "android.hardware.graphics.common-V3-ndk",
         "libbinder_ndk",
         "libutils",
         "libvndksupport",
diff --git a/tv/cec/aidl/Android.bp b/tv/cec/aidl/Android.bp
new file mode 100644
index 0000000..0b0e7cf
--- /dev/null
+++ b/tv/cec/aidl/Android.bp
@@ -0,0 +1,29 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+    name: "android.hardware.tv.cec",
+    vendor_available: true,
+    srcs: ["android/hardware/tv/cec/*.aidl"],
+    stability: "vintf",
+    backend: {
+        java: {
+            sdk_version: "module_current",
+        },
+    },
+}
diff --git a/tv/cec/aidl/OWNERS b/tv/cec/aidl/OWNERS
new file mode 100644
index 0000000..d9c6783
--- /dev/null
+++ b/tv/cec/aidl/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 826094
+include platform/frameworks/base:/core/java/android/hardware/hdmi/OWNERS
\ No newline at end of file
diff --git a/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/AbortReason.aidl b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/AbortReason.aidl
new file mode 100644
index 0000000..7377d81
--- /dev/null
+++ b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/AbortReason.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.tv.cec;
+@Backing(type="int") @VintfStability
+enum AbortReason {
+  UNRECOGNIZED_MODE = 0,
+  NOT_IN_CORRECT_MODE = 1,
+  CANNOT_PROVIDE_SOURCE = 2,
+  INVALID_OPERAND = 3,
+  REFUSED = 4,
+  UNABLE_TO_DETERMINE = 5,
+}
diff --git a/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/CecDeviceType.aidl b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/CecDeviceType.aidl
new file mode 100644
index 0000000..4d991cd
--- /dev/null
+++ b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/CecDeviceType.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.tv.cec;
+@Backing(type="byte") @VintfStability
+enum CecDeviceType {
+  INACTIVE = -1,
+  TV = 0,
+  RECORDER = 1,
+  TUNER = 3,
+  PLAYBACK = 4,
+  AUDIO_SYSTEM = 5,
+}
diff --git a/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/CecLogicalAddress.aidl b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/CecLogicalAddress.aidl
new file mode 100644
index 0000000..a36935b
--- /dev/null
+++ b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/CecLogicalAddress.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.tv.cec;
+@Backing(type="byte") @VintfStability
+enum CecLogicalAddress {
+  TV = 0,
+  RECORDER_1 = 1,
+  RECORDER_2 = 2,
+  TUNER_1 = 3,
+  PLAYBACK_1 = 4,
+  AUDIO_SYSTEM = 5,
+  TUNER_2 = 6,
+  TUNER_3 = 7,
+  PLAYBACK_2 = 8,
+  RECORDER_3 = 9,
+  TUNER_4 = 10,
+  PLAYBACK_3 = 11,
+  BACKUP_1 = 12,
+  BACKUP_2 = 13,
+  FREE_USE = 14,
+  BROADCAST = 15,
+  UNREGISTERED = 15,
+}
diff --git a/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/CecMessage.aidl b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/CecMessage.aidl
new file mode 100644
index 0000000..5ce5ce8
--- /dev/null
+++ b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/CecMessage.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.tv.cec;
+@VintfStability
+parcelable CecMessage {
+  android.hardware.tv.cec.CecLogicalAddress initiator;
+  android.hardware.tv.cec.CecLogicalAddress destination;
+  byte[] body;
+  const int MAX_MESSAGE_BODY_LENGTH = 15;
+}
diff --git a/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/CecMessageType.aidl b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/CecMessageType.aidl
new file mode 100644
index 0000000..61ebb94
--- /dev/null
+++ b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/CecMessageType.aidl
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.tv.cec;
+@Backing(type="int") @VintfStability
+enum CecMessageType {
+  FEATURE_ABORT = 0,
+  IMAGE_VIEW_ON = 4,
+  TUNER_STEP_INCREMENT = 5,
+  TUNER_STEP_DECREMENT = 6,
+  TUNER_DEVICE_STATUS = 7,
+  GIVE_TUNER_DEVICE_STATUS = 8,
+  RECORD_ON = 9,
+  RECORD_STATUS = 10,
+  RECORD_OFF = 11,
+  TEXT_VIEW_ON = 13,
+  RECORD_TV_SCREEN = 15,
+  GIVE_DECK_STATUS = 26,
+  DECK_STATUS = 27,
+  SET_MENU_LANGUAGE = 50,
+  CLEAR_ANALOG_TIMER = 51,
+  SET_ANALOG_TIMER = 52,
+  TIMER_STATUS = 53,
+  STANDBY = 54,
+  PLAY = 65,
+  DECK_CONTROL = 66,
+  TIMER_CLEARED_STATUS = 67,
+  USER_CONTROL_PRESSED = 68,
+  USER_CONTROL_RELEASED = 69,
+  GIVE_OSD_NAME = 70,
+  SET_OSD_NAME = 71,
+  SET_OSD_STRING = 100,
+  SET_TIMER_PROGRAM_TITLE = 103,
+  SYSTEM_AUDIO_MODE_REQUEST = 112,
+  GIVE_AUDIO_STATUS = 113,
+  SET_SYSTEM_AUDIO_MODE = 114,
+  REPORT_AUDIO_STATUS = 122,
+  GIVE_SYSTEM_AUDIO_MODE_STATUS = 125,
+  SYSTEM_AUDIO_MODE_STATUS = 126,
+  ROUTING_CHANGE = 128,
+  ROUTING_INFORMATION = 129,
+  ACTIVE_SOURCE = 130,
+  GIVE_PHYSICAL_ADDRESS = 131,
+  REPORT_PHYSICAL_ADDRESS = 132,
+  REQUEST_ACTIVE_SOURCE = 133,
+  SET_STREAM_PATH = 134,
+  DEVICE_VENDOR_ID = 135,
+  VENDOR_COMMAND = 137,
+  VENDOR_REMOTE_BUTTON_DOWN = 138,
+  VENDOR_REMOTE_BUTTON_UP = 139,
+  GIVE_DEVICE_VENDOR_ID = 140,
+  MENU_REQUEST = 141,
+  MENU_STATUS = 142,
+  GIVE_DEVICE_POWER_STATUS = 143,
+  REPORT_POWER_STATUS = 144,
+  GET_MENU_LANGUAGE = 145,
+  SELECT_ANALOG_SERVICE = 146,
+  SELECT_DIGITAL_SERVICE = 147,
+  SET_DIGITAL_TIMER = 151,
+  CLEAR_DIGITAL_TIMER = 153,
+  SET_AUDIO_RATE = 154,
+  INACTIVE_SOURCE = 157,
+  CEC_VERSION = 158,
+  GET_CEC_VERSION = 159,
+  VENDOR_COMMAND_WITH_ID = 160,
+  CLEAR_EXTERNAL_TIMER = 161,
+  SET_EXTERNAL_TIMER = 162,
+  REPORT_SHORT_AUDIO_DESCRIPTOR = 163,
+  REQUEST_SHORT_AUDIO_DESCRIPTOR = 164,
+  INITIATE_ARC = 192,
+  REPORT_ARC_INITIATED = 193,
+  REPORT_ARC_TERMINATED = 194,
+  REQUEST_ARC_INITIATION = 195,
+  REQUEST_ARC_TERMINATION = 196,
+  TERMINATE_ARC = 197,
+  ABORT = 255,
+  GIVE_FEATURES = 165,
+  REPORT_FEATURES = 166,
+  REQUEST_CURRENT_LATENCY = 167,
+  REPORT_CURRENT_LATENCY = 168,
+}
diff --git a/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/IHdmiCec.aidl b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/IHdmiCec.aidl
new file mode 100644
index 0000000..cf8425e
--- /dev/null
+++ b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/IHdmiCec.aidl
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.tv.cec;
+@VintfStability
+interface IHdmiCec {
+  android.hardware.tv.cec.Result addLogicalAddress(in android.hardware.tv.cec.CecLogicalAddress addr);
+  void clearLogicalAddress();
+  void enableAudioReturnChannel(in int portId, in boolean enable);
+  int getCecVersion();
+  int getPhysicalAddress();
+  int getVendorId();
+  android.hardware.tv.cec.SendMessageResult sendMessage(in android.hardware.tv.cec.CecMessage message);
+  void setCallback(in android.hardware.tv.cec.IHdmiCecCallback callback);
+  void setLanguage(in String language);
+  void enableWakeupByOtp(in boolean value);
+  void enableCec(in boolean value);
+  void enableSystemCecControl(in boolean value);
+}
diff --git a/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/IHdmiCecCallback.aidl b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/IHdmiCecCallback.aidl
new file mode 100644
index 0000000..1918765
--- /dev/null
+++ b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/IHdmiCecCallback.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.tv.cec;
+@VintfStability
+interface IHdmiCecCallback {
+  oneway void onCecMessage(in android.hardware.tv.cec.CecMessage message);
+}
diff --git a/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/Result.aidl b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/Result.aidl
new file mode 100644
index 0000000..a5ba276
--- /dev/null
+++ b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/Result.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.tv.cec;
+@Backing(type="byte") @VintfStability
+enum Result {
+  SUCCESS = 0,
+  FAILURE_UNKNOWN = 1,
+  FAILURE_INVALID_ARGS = 2,
+  FAILURE_INVALID_STATE = 3,
+  FAILURE_NOT_SUPPORTED = 4,
+  FAILURE_BUSY = 5,
+}
diff --git a/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/SendMessageResult.aidl b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/SendMessageResult.aidl
new file mode 100644
index 0000000..58206c8
--- /dev/null
+++ b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/SendMessageResult.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.tv.cec;
+@Backing(type="byte") @VintfStability
+enum SendMessageResult {
+  SUCCESS = 0,
+  NACK = 1,
+  BUSY = 2,
+  FAIL = 3,
+}
diff --git a/tv/cec/aidl/android/hardware/tv/cec/AbortReason.aidl b/tv/cec/aidl/android/hardware/tv/cec/AbortReason.aidl
new file mode 100644
index 0000000..3ae23ec
--- /dev/null
+++ b/tv/cec/aidl/android/hardware/tv/cec/AbortReason.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.tv.cec;
+
+/**
+ * Operand description [Abort Reason]
+ */
+@VintfStability
+@Backing(type="int")
+enum AbortReason {
+    UNRECOGNIZED_MODE = 0,
+    NOT_IN_CORRECT_MODE = 1,
+    CANNOT_PROVIDE_SOURCE = 2,
+    INVALID_OPERAND = 3,
+    REFUSED = 4,
+    UNABLE_TO_DETERMINE = 5,
+}
diff --git a/tv/cec/aidl/android/hardware/tv/cec/CecDeviceType.aidl b/tv/cec/aidl/android/hardware/tv/cec/CecDeviceType.aidl
new file mode 100644
index 0000000..16dfbec
--- /dev/null
+++ b/tv/cec/aidl/android/hardware/tv/cec/CecDeviceType.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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.tv.cec;
+
+@VintfStability
+@Backing(type="byte")
+enum CecDeviceType {
+    INACTIVE = -1,
+    TV = 0,
+    RECORDER = 1,
+    TUNER = 3,
+    PLAYBACK = 4,
+    AUDIO_SYSTEM = 5,
+}
diff --git a/tv/cec/aidl/android/hardware/tv/cec/CecLogicalAddress.aidl b/tv/cec/aidl/android/hardware/tv/cec/CecLogicalAddress.aidl
new file mode 100644
index 0000000..fbf5328
--- /dev/null
+++ b/tv/cec/aidl/android/hardware/tv/cec/CecLogicalAddress.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.tv.cec;
+
+@VintfStability
+@Backing(type="byte")
+enum CecLogicalAddress {
+    TV = 0,
+    RECORDER_1 = 1,
+    RECORDER_2 = 2,
+    TUNER_1 = 3,
+    PLAYBACK_1 = 4,
+    AUDIO_SYSTEM = 5,
+    TUNER_2 = 6,
+    TUNER_3 = 7,
+    PLAYBACK_2 = 8,
+    RECORDER_3 = 9,
+    TUNER_4 = 10,
+    PLAYBACK_3 = 11,
+    BACKUP_1 = 12,
+    BACKUP_2 = 13,
+    FREE_USE = 14,
+    BROADCAST = 15,
+    UNREGISTERED = 15,
+}
diff --git a/tv/cec/aidl/android/hardware/tv/cec/CecMessage.aidl b/tv/cec/aidl/android/hardware/tv/cec/CecMessage.aidl
new file mode 100644
index 0000000..b126045
--- /dev/null
+++ b/tv/cec/aidl/android/hardware/tv/cec/CecMessage.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.tv.cec;
+
+import android.hardware.tv.cec.CecLogicalAddress;
+
+@VintfStability
+parcelable CecMessage {
+    /**
+     * Maximum length of the message body
+     */
+    const int MAX_MESSAGE_BODY_LENGTH = 15;
+    /**
+     * logical address of the initiator
+     */
+    CecLogicalAddress initiator;
+    /**
+     * logical address of destination
+     */
+    CecLogicalAddress destination;
+    /**
+     * The maximum size of body is 15 (MAX_MESSAGE_BODY_LENGTH) as specified in
+     * the section 6 of the CEC Spec 1.4b. Overflowed data must be ignored.
+     */
+    byte[] body;
+}
diff --git a/tv/cec/aidl/android/hardware/tv/cec/CecMessageType.aidl b/tv/cec/aidl/android/hardware/tv/cec/CecMessageType.aidl
new file mode 100644
index 0000000..b544a91
--- /dev/null
+++ b/tv/cec/aidl/android/hardware/tv/cec/CecMessageType.aidl
@@ -0,0 +1,96 @@
+/*
+ * 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.tv.cec;
+
+@VintfStability
+@Backing(type="int")
+enum CecMessageType {
+    FEATURE_ABORT = 0x00,
+    IMAGE_VIEW_ON = 0x04,
+    TUNER_STEP_INCREMENT = 0x05,
+    TUNER_STEP_DECREMENT = 0x06,
+    TUNER_DEVICE_STATUS = 0x07,
+    GIVE_TUNER_DEVICE_STATUS = 0x08,
+    RECORD_ON = 0x09,
+    RECORD_STATUS = 0x0A,
+    RECORD_OFF = 0x0B,
+    TEXT_VIEW_ON = 0x0D,
+    RECORD_TV_SCREEN = 0x0F,
+    GIVE_DECK_STATUS = 0x1A,
+    DECK_STATUS = 0x1B,
+    SET_MENU_LANGUAGE = 0x32,
+    CLEAR_ANALOG_TIMER = 0x33,
+    SET_ANALOG_TIMER = 0x34,
+    TIMER_STATUS = 0x35,
+    STANDBY = 0x36,
+    PLAY = 0x41,
+    DECK_CONTROL = 0x42,
+    TIMER_CLEARED_STATUS = 0x43,
+    USER_CONTROL_PRESSED = 0x44,
+    USER_CONTROL_RELEASED = 0x45,
+    GIVE_OSD_NAME = 0x46,
+    SET_OSD_NAME = 0x47,
+    SET_OSD_STRING = 0x64,
+    SET_TIMER_PROGRAM_TITLE = 0x67,
+    SYSTEM_AUDIO_MODE_REQUEST = 0x70,
+    GIVE_AUDIO_STATUS = 0x71,
+    SET_SYSTEM_AUDIO_MODE = 0x72,
+    REPORT_AUDIO_STATUS = 0x7A,
+    GIVE_SYSTEM_AUDIO_MODE_STATUS = 0x7D,
+    SYSTEM_AUDIO_MODE_STATUS = 0x7E,
+    ROUTING_CHANGE = 0x80,
+    ROUTING_INFORMATION = 0x81,
+    ACTIVE_SOURCE = 0x82,
+    GIVE_PHYSICAL_ADDRESS = 0x83,
+    REPORT_PHYSICAL_ADDRESS = 0x84,
+    REQUEST_ACTIVE_SOURCE = 0x85,
+    SET_STREAM_PATH = 0x86,
+    DEVICE_VENDOR_ID = 0x87,
+    VENDOR_COMMAND = 0x89,
+    VENDOR_REMOTE_BUTTON_DOWN = 0x8A,
+    VENDOR_REMOTE_BUTTON_UP = 0x8B,
+    GIVE_DEVICE_VENDOR_ID = 0x8C,
+    MENU_REQUEST = 0x8D,
+    MENU_STATUS = 0x8E,
+    GIVE_DEVICE_POWER_STATUS = 0x8F,
+    REPORT_POWER_STATUS = 0x90,
+    GET_MENU_LANGUAGE = 0x91,
+    SELECT_ANALOG_SERVICE = 0x92,
+    SELECT_DIGITAL_SERVICE = 0x93,
+    SET_DIGITAL_TIMER = 0x97,
+    CLEAR_DIGITAL_TIMER = 0x99,
+    SET_AUDIO_RATE = 0x9A,
+    INACTIVE_SOURCE = 0x9D,
+    CEC_VERSION = 0x9E,
+    GET_CEC_VERSION = 0x9F,
+    VENDOR_COMMAND_WITH_ID = 0xA0,
+    CLEAR_EXTERNAL_TIMER = 0xA1,
+    SET_EXTERNAL_TIMER = 0xA2,
+    REPORT_SHORT_AUDIO_DESCRIPTOR = 0xA3,
+    REQUEST_SHORT_AUDIO_DESCRIPTOR = 0xA4,
+    INITIATE_ARC = 0xC0,
+    REPORT_ARC_INITIATED = 0xC1,
+    REPORT_ARC_TERMINATED = 0xC2,
+    REQUEST_ARC_INITIATION = 0xC3,
+    REQUEST_ARC_TERMINATION = 0xC4,
+    TERMINATE_ARC = 0xC5,
+    ABORT = 0xFF,
+    GIVE_FEATURES = 0xA5,
+    REPORT_FEATURES = 0xA6,
+    REQUEST_CURRENT_LATENCY = 0xA7,
+    REPORT_CURRENT_LATENCY = 0xA8,
+}
diff --git a/tv/cec/aidl/android/hardware/tv/cec/IHdmiCec.aidl b/tv/cec/aidl/android/hardware/tv/cec/IHdmiCec.aidl
new file mode 100644
index 0000000..dbf7139
--- /dev/null
+++ b/tv/cec/aidl/android/hardware/tv/cec/IHdmiCec.aidl
@@ -0,0 +1,156 @@
+/*
+ * 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.tv.cec;
+
+import android.hardware.tv.cec.CecLogicalAddress;
+import android.hardware.tv.cec.CecMessage;
+import android.hardware.tv.cec.IHdmiCecCallback;
+import android.hardware.tv.cec.Result;
+import android.hardware.tv.cec.SendMessageResult;
+
+/**
+ * HDMI-CEC HAL interface definition.
+ */
+@VintfStability
+interface IHdmiCec {
+    /**
+     * Passes the logical address that must be used in this system.
+     *
+     * HAL must use it to configure the hardware so that the CEC commands
+     * addressed the given logical address can be filtered in. This method must
+     * be able to be called as many times as necessary in order to support
+     * multiple logical devices.
+     *
+     * @param addr Logical address that must be used in this system. It must be
+     *        in the range of valid logical addresses for the call to succeed.
+     * @return Result status of the operation. SUCCESS if successful,
+     *         FAILURE_INVALID_ARGS if the given logical address is invalid,
+     *         FAILURE_BUSY if device or resource is busy
+     */
+    Result addLogicalAddress(in CecLogicalAddress addr);
+
+    /**
+     * Clears all the logical addresses.
+     *
+     * It is used when the system doesn't need to process CEC command any more,
+     * hence to tell HAL to stop receiving commands from the CEC bus, and change
+     * the state back to the beginning.
+     */
+    void clearLogicalAddress();
+
+    /**
+     * Configures ARC circuit in the hardware logic to start or stop the
+     * feature.
+     *
+     * @param portId Port id to be configured.
+     * @param enable Flag must be either true to start the feature or false to
+     *        stop it.
+     */
+    void enableAudioReturnChannel(in int portId, in boolean enable);
+
+    /**
+     * Returns the CEC version supported by underlying hardware.
+     *
+     * @return the CEC version supported by underlying hardware.
+     */
+    int getCecVersion();
+
+    /**
+     * Gets the CEC physical address.
+     *
+     * The physical address depends on the topology of the network formed by
+     * connected HDMI devices. It is therefore likely to change if the cable is
+     * plugged off and on again. It is advised to call getPhysicalAddress to get
+     * the updated address when hot plug event takes place.
+     *
+     * @param out addr Physical address of this device.
+     */
+    int getPhysicalAddress();
+
+    /**
+     * Gets the identifier of the vendor.
+     *
+     * @return Identifier of the vendor that is the 24-bit unique
+     *         company ID obtained from the IEEE Registration Authority
+     *         Committee (RAC). The upper 8 bits must be 0.
+     */
+    int getVendorId();
+
+    /**
+     * Transmits HDMI-CEC message to other HDMI device.
+     *
+     * The method must be designed to return in a certain amount of time and not
+     * hanging forever which may happen if CEC signal line is pulled low for
+     * some reason.
+     *
+     * It must try retransmission at least once as specified in the section '7.1
+     * Frame Re-transmissions' of the CEC Spec 1.4b.
+     *
+     * @param message CEC message to be sent to other HDMI device.
+     * @return Result status of the operation. SUCCESS if successful,
+     *         NACK if the sent message is not acknowledged,
+     *         BUSY if the CEC bus is busy,
+     *         FAIL if the message could not be sent.
+     */
+    SendMessageResult sendMessage(in CecMessage message);
+
+    /**
+     * Sets a callback that HDMI-CEC HAL must later use for incoming CEC
+     * messages.
+     *
+     * @param callback Callback object to pass hdmi events to the system. The
+     *        previously registered callback must be replaced with this one.
+     *        setCallback(null) should deregister the callback.
+     */
+    void setCallback(in IHdmiCecCallback callback);
+
+    /**
+     * Passes the updated language information of Android system. Contains
+     * three-letter code as defined in ISO/FDIS 639-2. Must be used for HAL to
+     * respond to <Get Menu Language> while in standby mode.
+     *
+     * @param language Three-letter code defined in ISO/FDIS 639-2. Must be
+     *        lowercase letters. (e.g., eng for English)
+     */
+    void setLanguage(in String language);
+
+    /**
+     * Determines whether a TV panel device in standby mode should wake up when
+     * it receives an OTP (One Touch Play) from a source device.
+     *
+     * @param value If true, the TV device will wake up when OTP is received
+     *              and if false, the TV device will not wake up for an OTP.
+     */
+    void enableWakeupByOtp(in boolean value);
+
+    /**
+     * Switch to enable or disable CEC on the device.
+     *
+     * @param value If true, the device will have all CEC functionalities
+     *              and if false, the device will not perform any CEC functions.
+     */
+    void enableCec(in boolean value);
+
+    /**
+     * Determines which module processes CEC messages - the Android framework or
+     * the HAL.
+     *
+     * @param value If true, the Android framework will actively process CEC
+     *        messages and if false, only the HAL will process the CEC messages.
+     */
+    void enableSystemCecControl(in boolean value);
+}
diff --git a/tv/cec/aidl/android/hardware/tv/cec/IHdmiCecCallback.aidl b/tv/cec/aidl/android/hardware/tv/cec/IHdmiCecCallback.aidl
new file mode 100644
index 0000000..4934a64
--- /dev/null
+++ b/tv/cec/aidl/android/hardware/tv/cec/IHdmiCecCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.tv.cec;
+
+import android.hardware.tv.cec.CecMessage;
+
+/**
+ * Callbacks from the HAL implementation to notify the system of new events.
+ */
+@VintfStability
+oneway interface IHdmiCecCallback {
+    /**
+     * The callback function that must be called by HAL implementation to notify
+     * the system of new CEC message arrival.
+     */
+    void onCecMessage(in CecMessage message);
+}
diff --git a/tv/cec/aidl/android/hardware/tv/cec/Result.aidl b/tv/cec/aidl/android/hardware/tv/cec/Result.aidl
new file mode 100644
index 0000000..3184c46
--- /dev/null
+++ b/tv/cec/aidl/android/hardware/tv/cec/Result.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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.tv.cec;
+
+@VintfStability
+@Backing(type="byte")
+enum Result {
+    SUCCESS = 0,
+    FAILURE_UNKNOWN = 1,
+    FAILURE_INVALID_ARGS = 2,
+    FAILURE_INVALID_STATE = 3,
+    FAILURE_NOT_SUPPORTED = 4,
+    FAILURE_BUSY = 5,
+}
diff --git a/tv/cec/aidl/android/hardware/tv/cec/SendMessageResult.aidl b/tv/cec/aidl/android/hardware/tv/cec/SendMessageResult.aidl
new file mode 100644
index 0000000..8cb98bc
--- /dev/null
+++ b/tv/cec/aidl/android/hardware/tv/cec/SendMessageResult.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.tv.cec;
+
+/**
+ * error code used for send_message.
+ */
+@VintfStability
+@Backing(type="byte")
+enum SendMessageResult {
+    SUCCESS = 0,
+    NACK = 1,
+    BUSY = 2,
+    FAIL = 3,
+}
diff --git a/tv/cec/aidl/default/Android.bp b/tv/cec/aidl/default/Android.bp
new file mode 100644
index 0000000..5479601
--- /dev/null
+++ b/tv/cec/aidl/default/Android.bp
@@ -0,0 +1,58 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_binary {
+    name: "android.hardware.tv.cec-service",
+    vintf_fragments: ["android.hardware.tv.cec-service.xml"],
+    relative_install_path: "hw",
+    vendor: true,
+    cflags: [
+        "-Wall",
+        "-Wextra",
+    ],
+    init_rc: ["android.hardware.tv.cec-service.rc"],
+    srcs: [
+        "serviceMock.cpp",
+        "HdmiCecMock.cpp",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+        "liblog",
+        "libbase",
+        "libutils",
+        "libhardware",
+        "libhidlbase",
+        "android.hardware.tv.cec-V1-ndk",
+    ],
+}
+
+cc_fuzz {
+    name: "android.hardware.tv.cec-service_fuzzer",
+    defaults: ["service_fuzzer_defaults"],
+    static_libs: [
+        "android.hardware.tv.cec-V1-ndk",
+        "liblog",
+    ],
+    srcs: [
+        "fuzzer.cpp",
+        "HdmiCecMock.cpp",
+    ],
+    fuzz_config: {
+        componentid: 826094,
+    },
+}
diff --git a/tv/cec/aidl/default/HdmiCecMock.cpp b/tv/cec/aidl/default/HdmiCecMock.cpp
new file mode 100644
index 0000000..d8d655b
--- /dev/null
+++ b/tv/cec/aidl/default/HdmiCecMock.cpp
@@ -0,0 +1,266 @@
+/*
+ * 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 "android.hardware.tv.cec"
+#include <android-base/logging.h>
+#include <fcntl.h>
+#include <utils/Log.h>
+
+#include <hardware/hardware.h>
+#include <hardware/hdmi_cec.h>
+#include "HdmiCecMock.h"
+
+using ndk::ScopedAStatus;
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace cec {
+namespace implementation {
+
+void HdmiCecMock::serviceDied(void* cookie) {
+    ALOGE("HdmiCecMock died");
+    auto hdmiCecMock = static_cast<HdmiCecMock*>(cookie);
+    hdmiCecMock->mCecThreadRun = false;
+}
+
+ScopedAStatus HdmiCecMock::addLogicalAddress(CecLogicalAddress addr, Result* _aidl_return) {
+    // Have a list to maintain logical addresses
+    mLogicalAddresses.push_back(addr);
+    *_aidl_return = Result::SUCCESS;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiCecMock::clearLogicalAddress() {
+    // Remove logical address from the list
+    mLogicalAddresses = {};
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiCecMock::enableAudioReturnChannel(int32_t portId __unused, bool enable __unused) {
+    // Maintain ARC status
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiCecMock::getCecVersion(int32_t* _aidl_return) {
+    // Maintain a cec version and return it
+    *_aidl_return = mCecVersion;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiCecMock::getPhysicalAddress(int32_t* _aidl_return) {
+    // Maintain a physical address and return it
+    // Default 0xFFFF, update on hotplug event
+    *_aidl_return = mPhysicalAddress;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiCecMock::getVendorId(int32_t* _aidl_return) {
+    *_aidl_return = mCecVendorId;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiCecMock::sendMessage(const CecMessage& message, SendMessageResult* _aidl_return) {
+    if (message.body.size() == 0) {
+        *_aidl_return = SendMessageResult::NACK;
+    } else {
+        sendMessageToFifo(message);
+        *_aidl_return = SendMessageResult::SUCCESS;
+    }
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiCecMock::setCallback(const std::shared_ptr<IHdmiCecCallback>& callback) {
+    // If callback is null, mCallback is also set to null so we do not call the old callback.
+    mCallback = callback;
+
+    if (callback != nullptr) {
+        AIBinder_linkToDeath(this->asBinder().get(), mDeathRecipient.get(), 0 /* cookie */);
+
+        mInputFile = open(CEC_MSG_IN_FIFO, O_RDWR | O_CLOEXEC);
+        mOutputFile = open(CEC_MSG_OUT_FIFO, O_RDWR | O_CLOEXEC);
+        pthread_create(&mThreadId, NULL, __threadLoop, this);
+        pthread_setname_np(mThreadId, "hdmi_cec_loop");
+    }
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiCecMock::setLanguage(const std::string& language) {
+    if (language.size() != 3) {
+        LOG(ERROR) << "Wrong language code: expected 3 letters, but it was " << language.size()
+                   << ".";
+        return ScopedAStatus::ok();
+    }
+    // TODO Validate if language is a valid language code
+    const char* languageStr = language.c_str();
+    int convertedLanguage = ((languageStr[0] & 0xFF) << 16) | ((languageStr[1] & 0xFF) << 8) |
+                            (languageStr[2] & 0xFF);
+    mOptionLanguage = convertedLanguage;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiCecMock::enableWakeupByOtp(bool value) {
+    mOptionWakeUp = value;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiCecMock::enableCec(bool value) {
+    mOptionEnableCec = value;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiCecMock::enableSystemCecControl(bool value) {
+    mOptionSystemCecControl = value;
+    return ScopedAStatus::ok();
+}
+
+void* HdmiCecMock::__threadLoop(void* user) {
+    HdmiCecMock* const self = static_cast<HdmiCecMock*>(user);
+    self->threadLoop();
+    return 0;
+}
+
+int HdmiCecMock::readMessageFromFifo(unsigned char* buf, int msgCount) {
+    if (msgCount <= 0 || !buf) {
+        return 0;
+    }
+
+    int ret = -1;
+    // Maybe blocked at driver
+    ret = read(mInputFile, buf, msgCount);
+    if (ret < 0) {
+        ALOGE("[halimp_aidl] read :%s failed, ret:%d\n", CEC_MSG_IN_FIFO, ret);
+        return -1;
+    }
+
+    return ret;
+}
+
+int HdmiCecMock::sendMessageToFifo(const CecMessage& message) {
+    unsigned char msgBuf[CEC_MESSAGE_BODY_MAX_LENGTH + 1] = {0};
+    int ret = -1;
+
+    msgBuf[0] = ((static_cast<uint8_t>(message.initiator) & 0xf) << 4) |
+                (static_cast<uint8_t>(message.destination) & 0xf);
+
+    size_t length = std::min(static_cast<size_t>(message.body.size()),
+                             static_cast<size_t>(CEC_MESSAGE_BODY_MAX_LENGTH));
+    for (size_t i = 0; i < length; ++i) {
+        msgBuf[i + 1] = static_cast<unsigned char>(message.body[i]);
+    }
+
+    // Open the output pipe for writing outgoing cec message
+    mOutputFile = open(CEC_MSG_OUT_FIFO, O_WRONLY | O_CLOEXEC);
+    if (mOutputFile < 0) {
+        ALOGD("[halimp_aidl] file open failed for writing");
+        return -1;
+    }
+
+    // Write message into the output pipe
+    ret = write(mOutputFile, msgBuf, length + 1);
+    close(mOutputFile);
+    if (ret < 0) {
+        ALOGE("[halimp_aidl] write :%s failed, ret:%d\n", CEC_MSG_OUT_FIFO, ret);
+        return -1;
+    }
+    return ret;
+}
+
+void HdmiCecMock::printCecMsgBuf(const char* msg_buf, int len) {
+    int i, size = 0;
+    const int bufSize = CEC_MESSAGE_BODY_MAX_LENGTH * 3;
+    // Use 2 characters for each byte in the message plus 1 space
+    char buf[bufSize] = {0};
+
+    // Messages longer than max length will be truncated.
+    for (i = 0; i < len && size < bufSize; i++) {
+        size += sprintf(buf + size, " %02x", msg_buf[i]);
+    }
+    ALOGD("[halimp_aidl] %s, msg:%.*s", __FUNCTION__, size, buf);
+}
+
+void HdmiCecMock::handleCecMessage(unsigned char* msgBuf, int msgSize) {
+    CecMessage message;
+    size_t length = std::min(static_cast<size_t>(msgSize - 1),
+                             static_cast<size_t>(CEC_MESSAGE_BODY_MAX_LENGTH));
+    message.body.resize(length);
+
+    for (size_t i = 0; i < length; ++i) {
+        message.body[i] = static_cast<uint8_t>(msgBuf[i + 1]);
+        ALOGD("[halimp_aidl] msg body %x", message.body[i]);
+    }
+
+    message.initiator = static_cast<CecLogicalAddress>((msgBuf[0] >> 4) & 0xf);
+    ALOGD("[halimp_aidl] msg init %hhd", message.initiator);
+    message.destination = static_cast<CecLogicalAddress>((msgBuf[0] >> 0) & 0xf);
+    ALOGD("[halimp_aidl] msg dest %hhd", message.destination);
+
+    if (mCallback != nullptr) {
+        mCallback->onCecMessage(message);
+    }
+}
+
+void HdmiCecMock::threadLoop() {
+    ALOGD("[halimp_aidl] threadLoop start.");
+    unsigned char msgBuf[CEC_MESSAGE_BODY_MAX_LENGTH];
+    int r = -1;
+
+    // Open the input pipe
+    while (mInputFile < 0) {
+        usleep(1000 * 1000);
+        mInputFile = open(CEC_MSG_IN_FIFO, O_RDONLY | O_CLOEXEC);
+    }
+    ALOGD("[halimp_aidl] file open ok, fd = %d.", mInputFile);
+
+    while (mCecThreadRun) {
+        if (!mOptionSystemCecControl) {
+            usleep(1000 * 1000);
+            continue;
+        }
+
+        memset(msgBuf, 0, sizeof(msgBuf));
+        // Try to get a message from dev.
+        // echo -n -e '\x04\x83' >> /dev/cec
+        r = readMessageFromFifo(msgBuf, CEC_MESSAGE_BODY_MAX_LENGTH);
+        if (r <= 1) {
+            // Ignore received ping messages
+            continue;
+        }
+
+        printCecMsgBuf((const char*)msgBuf, r);
+
+        if (((msgBuf[0] >> 4) & 0xf) == 0xf) {
+            // The message is a hotplug event, handled by HDMI HAL.
+            continue;
+        }
+
+        handleCecMessage(msgBuf, r);
+    }
+
+    ALOGD("[halimp_aidl] thread end.");
+}
+
+HdmiCecMock::HdmiCecMock() {
+    ALOGE("[halimp_aidl] Opening a virtual CEC HAL for testing and virtual machine.");
+    mCallback = nullptr;
+    mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient_new(serviceDied));
+}
+
+}  // namespace implementation
+}  // namespace cec
+}  // namespace tv
+}  // namespace hardware
+}  // namespace android
diff --git a/tv/cec/aidl/default/HdmiCecMock.h b/tv/cec/aidl/default/HdmiCecMock.h
new file mode 100644
index 0000000..08f4d6f
--- /dev/null
+++ b/tv/cec/aidl/default/HdmiCecMock.h
@@ -0,0 +1,94 @@
+/*
+ * 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 <aidl/android/hardware/tv/cec/BnHdmiCec.h>
+#include <algorithm>
+#include <vector>
+
+using namespace std;
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace cec {
+namespace implementation {
+
+using ::aidl::android::hardware::tv::cec::BnHdmiCec;
+using ::aidl::android::hardware::tv::cec::CecLogicalAddress;
+using ::aidl::android::hardware::tv::cec::CecMessage;
+using ::aidl::android::hardware::tv::cec::IHdmiCec;
+using ::aidl::android::hardware::tv::cec::IHdmiCecCallback;
+using ::aidl::android::hardware::tv::cec::Result;
+using ::aidl::android::hardware::tv::cec::SendMessageResult;
+
+#define CEC_MSG_IN_FIFO "/dev/cec_aidl_in_pipe"
+#define CEC_MSG_OUT_FIFO "/dev/cec_aidl_out_pipe"
+
+struct HdmiCecMock : public BnHdmiCec {
+    HdmiCecMock();
+    ::ndk::ScopedAStatus addLogicalAddress(CecLogicalAddress addr, Result* _aidl_return) override;
+    ::ndk::ScopedAStatus clearLogicalAddress() override;
+    ::ndk::ScopedAStatus enableAudioReturnChannel(int32_t portId, bool enable) override;
+    ::ndk::ScopedAStatus getCecVersion(int32_t* _aidl_return) override;
+    ::ndk::ScopedAStatus getPhysicalAddress(int32_t* _aidl_return) override;
+    ::ndk::ScopedAStatus getVendorId(int32_t* _aidl_return) override;
+    ::ndk::ScopedAStatus sendMessage(const CecMessage& message,
+                                     SendMessageResult* _aidl_return) override;
+    ::ndk::ScopedAStatus setCallback(const std::shared_ptr<IHdmiCecCallback>& callback) override;
+    ::ndk::ScopedAStatus setLanguage(const std::string& language) override;
+    ::ndk::ScopedAStatus enableWakeupByOtp(bool value) override;
+    ::ndk::ScopedAStatus enableCec(bool value) override;
+    ::ndk::ScopedAStatus enableSystemCecControl(bool value) override;
+    void printCecMsgBuf(const char* msg_buf, int len);
+
+  private:
+    static void* __threadLoop(void* data);
+    void threadLoop();
+    int readMessageFromFifo(unsigned char* buf, int msgCount);
+    int sendMessageToFifo(const CecMessage& message);
+    void handleCecMessage(unsigned char* msgBuf, int length);
+
+  private:
+    static void serviceDied(void* cookie);
+    std::shared_ptr<IHdmiCecCallback> mCallback;
+
+    // Variables for the virtual cec hal impl
+    uint16_t mPhysicalAddress = 0xFFFF;
+    vector<CecLogicalAddress> mLogicalAddresses;
+    int32_t mCecVersion = 0x06;
+    uint32_t mCecVendorId = 0x01;
+
+    // CEC Option value
+    bool mOptionWakeUp = 0;
+    bool mOptionEnableCec = 0;
+    bool mOptionSystemCecControl = 0;
+    int mOptionLanguage;
+
+    // Testing variables
+    // Input file descriptor
+    int mInputFile;
+    // Output file descriptor
+    int mOutputFile;
+    bool mCecThreadRun = true;
+    pthread_t mThreadId = 0;
+
+    ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
+};
+}  // namespace implementation
+}  // namespace cec
+}  // namespace tv
+}  // namespace hardware
+}  // namespace android
diff --git a/tv/cec/aidl/default/android.hardware.tv.cec-service.rc b/tv/cec/aidl/default/android.hardware.tv.cec-service.rc
new file mode 100644
index 0000000..c79520c
--- /dev/null
+++ b/tv/cec/aidl/default/android.hardware.tv.cec-service.rc
@@ -0,0 +1,5 @@
+service vendor.cec-default /vendor/bin/hw/android.hardware.tv.cec-service
+    interface aidl android.hardware.tv.cec.IHdmiCec/default
+    class hal
+    user system
+    group system
diff --git a/tv/cec/aidl/default/android.hardware.tv.cec-service.xml b/tv/cec/aidl/default/android.hardware.tv.cec-service.xml
new file mode 100644
index 0000000..e68450d
--- /dev/null
+++ b/tv/cec/aidl/default/android.hardware.tv.cec-service.xml
@@ -0,0 +1,10 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.tv.cec</name>
+        <version>1</version>
+        <interface>
+            <name>IHdmiCec</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/tv/cec/aidl/default/fuzzer.cpp b/tv/cec/aidl/default/fuzzer.cpp
new file mode 100644
index 0000000..9f6a9ac
--- /dev/null
+++ b/tv/cec/aidl/default/fuzzer.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 <HdmiCecMock.h>
+#include <fuzzbinder/libbinder_ndk_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+using android::fuzzService;
+using android::hardware::tv::cec::implementation::HdmiCecMock;
+using ndk::SharedRefBase;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    auto hdmiCecAidl = SharedRefBase::make<HdmiCecMock>();
+
+    fuzzService(hdmiCecAidl->asBinder().get(), FuzzedDataProvider(data, size));
+
+    return 0;
+}
diff --git a/tv/cec/aidl/default/serviceMock.cpp b/tv/cec/aidl/default/serviceMock.cpp
new file mode 100644
index 0000000..ab86c3f
--- /dev/null
+++ b/tv/cec/aidl/default/serviceMock.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.tv.cec-service-shim"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <hidl/HidlTransportSupport.h>
+#include <utils/Log.h>
+#include "HdmiCecMock.h"
+
+using android::hardware::tv::cec::implementation::HdmiCecMock;
+
+int main() {
+    ABinderProcess_setThreadPoolMaxThreadCount(1);
+    ABinderProcess_startThreadPool();
+
+    std::shared_ptr<HdmiCecMock> hdmiCecAidl = ndk::SharedRefBase::make<HdmiCecMock>();
+    const std::string instance = std::string() + HdmiCecMock::descriptor + "/default";
+    binder_status_t status =
+            AServiceManager_addService(hdmiCecAidl->asBinder().get(), instance.c_str());
+    CHECK_EQ(status, STATUS_OK);
+
+    ABinderProcess_joinThreadPool();
+    return 0;
+}
diff --git a/tv/cec/aidl/vts/functional/Android.bp b/tv/cec/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..37fbaf0
--- /dev/null
+++ b/tv/cec/aidl/vts/functional/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.
+
+package {
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+    name: "VtsHalTvCecAidlTargetTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: ["VtsHalTvCecAidlTargetTest.cpp"],
+    static_libs: [
+        "android.hardware.tv.cec-V1-ndk",
+        "android.hardware.tv.hdmi-V1-ndk",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+    disable_framework: true,
+}
diff --git a/tv/cec/aidl/vts/functional/VtsHalTvCecAidlTargetTest.cpp b/tv/cec/aidl/vts/functional/VtsHalTvCecAidlTargetTest.cpp
new file mode 100644
index 0000000..69c209f
--- /dev/null
+++ b/tv/cec/aidl/vts/functional/VtsHalTvCecAidlTargetTest.cpp
@@ -0,0 +1,170 @@
+/*
+ * 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 "HdmiCec_hal_test"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/tv/cec/BnHdmiCec.h>
+#include <aidl/android/hardware/tv/cec/BnHdmiCecCallback.h>
+#include <aidl/android/hardware/tv/cec/CecDeviceType.h>
+#include <aidl/android/hardware/tv/hdmi/BnHdmi.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <sstream>
+#include <vector>
+
+using ::aidl::android::hardware::tv::cec::BnHdmiCecCallback;
+using ::aidl::android::hardware::tv::cec::CecDeviceType;
+using ::aidl::android::hardware::tv::cec::CecLogicalAddress;
+using ::aidl::android::hardware::tv::cec::CecMessage;
+using ::aidl::android::hardware::tv::cec::IHdmiCec;
+using ::aidl::android::hardware::tv::cec::IHdmiCecCallback;
+using ::aidl::android::hardware::tv::cec::Result;
+using ::aidl::android::hardware::tv::cec::SendMessageResult;
+using ::aidl::android::hardware::tv::hdmi::HdmiPortInfo;
+using ::ndk::SpAIBinder;
+
+#define CEC_VERSION 0x05
+#define INCORRECT_VENDOR_ID 0x00
+#define TV_PHYSICAL_ADDRESS 0x0000
+
+// The main test class for TV CEC HAL.
+class HdmiCecTest : public ::testing::TestWithParam<std::string> {
+    static void serviceDied(void* /* cookie */) { ALOGE("VtsHalTvCecAidlTargetTest died"); }
+
+  public:
+    void SetUp() override {
+        hdmiCec = IHdmiCec::fromBinder(
+                SpAIBinder(AServiceManager_waitForService(GetParam().c_str())));
+        ASSERT_NE(hdmiCec, nullptr);
+        ALOGI("%s: getService() for hdmiCec is %s", __func__,
+              hdmiCec->isRemote() ? "remote" : "local");
+
+        hdmiCecCallback = ::ndk::SharedRefBase::make<CecCallback>();
+        ASSERT_NE(hdmiCecCallback, nullptr);
+        hdmiCecDeathRecipient =
+                ndk::ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient_new(&serviceDied));
+        ASSERT_EQ(AIBinder_linkToDeath(hdmiCec->asBinder().get(), hdmiCecDeathRecipient.get(), 0),
+                  STATUS_OK);
+    }
+
+    std::vector<int> getDeviceTypes() {
+        std::vector<int> deviceTypes;
+        FILE* p = popen("getprop ro.hdmi.device_type", "re");
+        if (p) {
+            char* line = NULL;
+            size_t len = 0;
+            if (getline(&line, &len, p) > 0) {
+                std::istringstream stream(line);
+                std::string number{};
+                while (std::getline(stream, number, ',')) {
+                    deviceTypes.push_back(stoi(number));
+                }
+            }
+            pclose(p);
+        }
+        return deviceTypes;
+    }
+
+    bool hasDeviceType(CecDeviceType type) {
+        std::vector<int> deviceTypes = getDeviceTypes();
+        return std::find(deviceTypes.begin(), deviceTypes.end(), (int)type) != deviceTypes.end();
+    }
+
+    class CecCallback : public BnHdmiCecCallback {
+      public:
+        ::ndk::ScopedAStatus onCecMessage(const CecMessage& message __unused) {
+            return ::ndk::ScopedAStatus::ok();
+        };
+    };
+
+    std::shared_ptr<IHdmiCec> hdmiCec;
+    std::shared_ptr<IHdmiCecCallback> hdmiCecCallback;
+    ::ndk::ScopedAIBinder_DeathRecipient hdmiCecDeathRecipient;
+};
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HdmiCecTest);
+INSTANTIATE_TEST_SUITE_P(PerInstance, HdmiCecTest,
+                         testing::ValuesIn(android::getAidlHalInstanceNames(IHdmiCec::descriptor)),
+                         android::PrintInstanceNameToString);
+
+TEST_P(HdmiCecTest, ClearAddLogicalAddress) {
+    Result addLaResult;
+    ASSERT_TRUE(hdmiCec->clearLogicalAddress().isOk());
+    ASSERT_TRUE(hdmiCec->addLogicalAddress(CecLogicalAddress::PLAYBACK_3, &addLaResult).isOk());
+    EXPECT_EQ(addLaResult, Result::SUCCESS);
+}
+
+TEST_P(HdmiCecTest, PhysicalAddress) {
+    int32_t addr;
+    ASSERT_TRUE(hdmiCec->getPhysicalAddress(&addr).isOk());
+    if (!hasDeviceType(CecDeviceType::TV)) {
+        EXPECT_NE(addr, TV_PHYSICAL_ADDRESS);
+    }
+}
+
+TEST_P(HdmiCecTest, SendMessage) {
+    CecMessage message;
+    message.initiator = CecLogicalAddress::PLAYBACK_1;
+    message.destination = CecLogicalAddress::BROADCAST;
+    message.body.resize(1);
+    message.body[0] = 131;
+    SendMessageResult result;
+    ASSERT_TRUE(hdmiCec->sendMessage(message, &result).isOk());
+    EXPECT_EQ(result, SendMessageResult::SUCCESS);
+}
+
+TEST_P(HdmiCecTest, CecVersion) {
+    int32_t version;
+    ASSERT_TRUE(hdmiCec->getCecVersion(&version).isOk());
+    EXPECT_GE(version, CEC_VERSION);
+}
+
+TEST_P(HdmiCecTest, SetCallback) {
+    ASSERT_TRUE(hdmiCec->setCallback(::ndk::SharedRefBase::make<CecCallback>()).isOk());
+}
+
+TEST_P(HdmiCecTest, VendorId) {
+    int32_t vendorId;
+    ASSERT_TRUE(hdmiCec->getVendorId(&vendorId).isOk());
+    EXPECT_NE(vendorId, INCORRECT_VENDOR_ID);
+}
+
+TEST_P(HdmiCecTest, EnableWakeupByOtp) {
+    ASSERT_TRUE(hdmiCec->enableWakeupByOtp(false).isOk());
+    // Restore option to its default value
+    ASSERT_TRUE(hdmiCec->enableWakeupByOtp(true).isOk());
+}
+
+TEST_P(HdmiCecTest, EnableCec) {
+    ASSERT_TRUE(hdmiCec->enableCec(false).isOk());
+    // Restore option to its default value
+    ASSERT_TRUE(hdmiCec->enableCec(true).isOk());
+}
+
+TEST_P(HdmiCecTest, EnableSystemCecControl) {
+    ASSERT_TRUE(hdmiCec->enableSystemCecControl(true).isOk());
+    // Restore option to its default value
+    ASSERT_TRUE(hdmiCec->enableSystemCecControl(false).isOk());
+}
+
+TEST_P(HdmiCecTest, SetLanguage) {
+    ASSERT_TRUE(hdmiCec->setLanguage("eng").isOk());
+}
diff --git a/tv/hdmi/aidl/Android.bp b/tv/hdmi/aidl/Android.bp
new file mode 100644
index 0000000..d8c6e5f
--- /dev/null
+++ b/tv/hdmi/aidl/Android.bp
@@ -0,0 +1,29 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+    name: "android.hardware.tv.hdmi",
+    vendor_available: true,
+    srcs: ["android/hardware/tv/hdmi/*.aidl"],
+    stability: "vintf",
+    backend: {
+        java: {
+            sdk_version: "module_current",
+        },
+    },
+}
diff --git a/tv/hdmi/aidl/OWNERS b/tv/hdmi/aidl/OWNERS
new file mode 100644
index 0000000..d9c6783
--- /dev/null
+++ b/tv/hdmi/aidl/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 826094
+include platform/frameworks/base:/core/java/android/hardware/hdmi/OWNERS
\ No newline at end of file
diff --git a/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/HdmiPortInfo.aidl b/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/HdmiPortInfo.aidl
new file mode 100644
index 0000000..a5e3a2a
--- /dev/null
+++ b/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/HdmiPortInfo.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.tv.hdmi;
+@VintfStability
+parcelable HdmiPortInfo {
+  android.hardware.tv.hdmi.HdmiPortType type;
+  int portId;
+  boolean cecSupported;
+  boolean arcSupported;
+  int physicalAddress;
+}
diff --git a/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/HdmiPortType.aidl b/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/HdmiPortType.aidl
new file mode 100644
index 0000000..af5f0f7
--- /dev/null
+++ b/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/HdmiPortType.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.tv.hdmi;
+@Backing(type="byte") @VintfStability
+enum HdmiPortType {
+  INPUT = 0,
+  OUTPUT = 1,
+}
diff --git a/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/IHdmi.aidl b/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/IHdmi.aidl
new file mode 100644
index 0000000..3fc7f41
--- /dev/null
+++ b/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/IHdmi.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.tv.hdmi;
+@VintfStability
+interface IHdmi {
+  android.hardware.tv.hdmi.HdmiPortInfo[] getPortInfo();
+  boolean isConnected(in int portId);
+  void setCallback(in android.hardware.tv.hdmi.IHdmiCallback callback);
+}
diff --git a/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/IHdmiCallback.aidl b/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/IHdmiCallback.aidl
new file mode 100644
index 0000000..05fe623
--- /dev/null
+++ b/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/IHdmiCallback.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.tv.hdmi;
+@VintfStability
+interface IHdmiCallback {
+  oneway void onHotplugEvent(in boolean connected, in int portId);
+}
diff --git a/tv/hdmi/aidl/android/hardware/tv/hdmi/HdmiPortInfo.aidl b/tv/hdmi/aidl/android/hardware/tv/hdmi/HdmiPortInfo.aidl
new file mode 100644
index 0000000..1d6f27d
--- /dev/null
+++ b/tv/hdmi/aidl/android/hardware/tv/hdmi/HdmiPortInfo.aidl
@@ -0,0 +1,33 @@
+/*
+ * 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.tv.hdmi;
+
+import android.hardware.tv.hdmi.HdmiPortType;
+
+/**
+ * HDMI port descriptor
+ */
+@VintfStability
+parcelable HdmiPortInfo {
+    HdmiPortType type;
+    int portId; // Should start from 1 which corresponds to HDMI "port 1".
+    boolean cecSupported;
+    boolean arcSupported;
+    // The physical address of the device connected to this port, valid range is 0x0000 to 0xFFFF
+    // (ref Sec 8.7.2 of HDMI 1.4b).
+    int physicalAddress;
+}
diff --git a/tv/hdmi/aidl/android/hardware/tv/hdmi/HdmiPortType.aidl b/tv/hdmi/aidl/android/hardware/tv/hdmi/HdmiPortType.aidl
new file mode 100644
index 0000000..59c0d42
--- /dev/null
+++ b/tv/hdmi/aidl/android/hardware/tv/hdmi/HdmiPortType.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.tv.hdmi;
+
+/**
+ * HDMI port type.
+ */
+@VintfStability
+@Backing(type="byte")
+enum HdmiPortType {
+    INPUT = 0,
+    OUTPUT = 1,
+}
diff --git a/tv/hdmi/aidl/android/hardware/tv/hdmi/IHdmi.aidl b/tv/hdmi/aidl/android/hardware/tv/hdmi/IHdmi.aidl
new file mode 100644
index 0000000..5536846
--- /dev/null
+++ b/tv/hdmi/aidl/android/hardware/tv/hdmi/IHdmi.aidl
@@ -0,0 +1,51 @@
+/*
+ * 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.tv.hdmi;
+
+import android.hardware.tv.hdmi.HdmiPortInfo;
+import android.hardware.tv.hdmi.IHdmiCallback;
+
+/**
+ * HDMI HAL interface definition.
+ */
+@VintfStability
+interface IHdmi {
+    /**
+     * Gets the hdmi port information of underlying hardware.
+     *
+     * @return The list of HDMI port information
+     */
+    HdmiPortInfo[] getPortInfo();
+
+    /**
+     * Gets the connection status of the specified port.
+     *
+     * @param portId Port id to be inspected for the connection status.
+     * @return True if a device is connected, otherwise false. The HAL
+     *         must watch for +5V power signal to determine the status.
+     */
+    boolean isConnected(in int portId);
+
+    /**
+     * Sets a callback that HDMI HAL must later use for internal HDMI events
+     *
+     * @param callback Callback object to pass hdmi events to the system. The
+     *        previously registered callback must be replaced with this one.
+     *        setCallback(null) should deregister the callback.
+     */
+    void setCallback(in IHdmiCallback callback);
+}
diff --git a/tv/hdmi/aidl/android/hardware/tv/hdmi/IHdmiCallback.aidl b/tv/hdmi/aidl/android/hardware/tv/hdmi/IHdmiCallback.aidl
new file mode 100644
index 0000000..51275b0
--- /dev/null
+++ b/tv/hdmi/aidl/android/hardware/tv/hdmi/IHdmiCallback.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.tv.hdmi;
+
+/**
+ * Callbacks from the HDMI HAL implementation to notify the system of new events.
+ */
+@VintfStability
+oneway interface IHdmiCallback {
+    /**
+     * The callback function that must be called by HAL implementation to notify
+     * the system of new hotplug event.
+     */
+    void onHotplugEvent(in boolean connected, in int portId);
+}
diff --git a/tv/hdmi/aidl/default/Android.bp b/tv/hdmi/aidl/default/Android.bp
new file mode 100644
index 0000000..3e466a0
--- /dev/null
+++ b/tv/hdmi/aidl/default/Android.bp
@@ -0,0 +1,58 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_binary {
+    name: "android.hardware.tv.hdmi-service",
+    vintf_fragments: ["android.hardware.tv.hdmi-service.xml"],
+    relative_install_path: "hw",
+    vendor: true,
+    cflags: [
+        "-Wall",
+        "-Wextra",
+    ],
+    init_rc: ["android.hardware.tv.hdmi-service.rc"],
+    srcs: [
+        "serviceMock.cpp",
+        "HdmiMock.cpp",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+        "liblog",
+        "libbase",
+        "libutils",
+        "libhardware",
+        "libhidlbase",
+        "android.hardware.tv.hdmi-V1-ndk",
+    ],
+}
+
+cc_fuzz {
+    name: "android.hardware.tv.hdmi-service_fuzzer",
+    defaults: ["service_fuzzer_defaults"],
+    static_libs: [
+        "android.hardware.tv.hdmi-V1-ndk",
+        "liblog",
+    ],
+    srcs: [
+        "fuzzer.cpp",
+        "HdmiMock.cpp",
+    ],
+    fuzz_config: {
+        componentid: 826094,
+    },
+}
diff --git a/tv/hdmi/aidl/default/HdmiMock.cpp b/tv/hdmi/aidl/default/HdmiMock.cpp
new file mode 100644
index 0000000..0cf5118
--- /dev/null
+++ b/tv/hdmi/aidl/default/HdmiMock.cpp
@@ -0,0 +1,178 @@
+/*
+ * 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 "android.hardware.tv.hdmi"
+#include <android-base/logging.h>
+#include <fcntl.h>
+#include <utils/Log.h>
+
+#include "HdmiMock.h"
+
+using ndk::ScopedAStatus;
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace hdmi {
+namespace implementation {
+
+void HdmiMock::serviceDied(void* cookie) {
+    ALOGE("HdmiMock died");
+    auto hdmi = static_cast<HdmiMock*>(cookie);
+    hdmi->mHdmiThreadRun = false;
+}
+
+ScopedAStatus HdmiMock::getPortInfo(std::vector<HdmiPortInfo>* _aidl_return) {
+    *_aidl_return = mPortInfos;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiMock::isConnected(int32_t portId, bool* _aidl_return) {
+    // Maintain port connection status and update on hotplug event
+    if (portId <= mTotalPorts && portId >= 1) {
+        *_aidl_return = mPortConnectionStatus[portId];
+    } else {
+        *_aidl_return = false;
+    }
+
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiMock::setCallback(const std::shared_ptr<IHdmiCallback>& callback) {
+    if (mCallback != nullptr) {
+        mCallback = nullptr;
+    }
+
+    if (callback != nullptr) {
+        mCallback = callback;
+        AIBinder_linkToDeath(this->asBinder().get(), mDeathRecipient.get(), 0 /* cookie */);
+
+        mInputFile = open(HDMI_MSG_IN_FIFO, O_RDWR | O_CLOEXEC);
+        pthread_create(&mThreadId, NULL, __threadLoop, this);
+        pthread_setname_np(mThreadId, "hdmi_loop");
+    }
+    return ScopedAStatus::ok();
+}
+
+void* HdmiMock::__threadLoop(void* user) {
+    HdmiMock* const self = static_cast<HdmiMock*>(user);
+    self->threadLoop();
+    return 0;
+}
+
+int HdmiMock::readMessageFromFifo(unsigned char* buf, int msgCount) {
+    if (msgCount <= 0 || !buf) {
+        return 0;
+    }
+
+    int ret = -1;
+    // Maybe blocked at driver
+    ret = read(mInputFile, buf, msgCount);
+    if (ret < 0) {
+        ALOGE("[halimp_aidl] read :%s failed, ret:%d\n", HDMI_MSG_IN_FIFO, ret);
+        return -1;
+    }
+
+    return ret;
+}
+
+void HdmiMock::printEventBuf(const char* msg_buf, int len) {
+    int i, size = 0;
+    const int bufSize = MESSAGE_BODY_MAX_LENGTH * 3;
+    // Use 2 characters for each byte in the message plus 1 space
+    char buf[bufSize] = {0};
+
+    // Messages longer than max length will be truncated.
+    for (i = 0; i < len && size < bufSize; i++) {
+        size += sprintf(buf + size, " %02x", msg_buf[i]);
+    }
+    ALOGD("[halimp_aidl] %s, msg:%.*s", __FUNCTION__, size, buf);
+}
+
+void HdmiMock::handleHotplugMessage(unsigned char* msgBuf) {
+    bool connected = ((msgBuf[3]) & 0xf) > 0;
+    int32_t portId = static_cast<uint32_t>(msgBuf[0] & 0xf);
+
+    if (portId > static_cast<int32_t>(mPortInfos.size())) {
+        ALOGD("[halimp_aidl] ignore hot plug message, id %x does not exist", portId);
+        return;
+    }
+
+    ALOGD("[halimp_aidl] hot plug port id %x, is connected %x", (msgBuf[0] & 0xf),
+          (msgBuf[3] & 0xf));
+    mPortConnectionStatus[portId] = connected;
+    if (mPortInfos[portId].type == HdmiPortType::OUTPUT) {
+        mPhysicalAddress = (connected ? 0xffff : ((msgBuf[1] << 8) | (msgBuf[2])));
+        mPortInfos[portId].physicalAddress = mPhysicalAddress;
+        ALOGD("[halimp_aidl] hot plug physical address %x", mPhysicalAddress);
+    }
+
+    if (mCallback != nullptr) {
+        mCallback->onHotplugEvent(connected, portId);
+    }
+}
+
+void HdmiMock::threadLoop() {
+    ALOGD("[halimp_aidl] threadLoop start.");
+    unsigned char msgBuf[MESSAGE_BODY_MAX_LENGTH];
+    int r = -1;
+
+    // Open the input pipe
+    while (mInputFile < 0) {
+        usleep(1000 * 1000);
+        mInputFile = open(HDMI_MSG_IN_FIFO, O_RDONLY | O_CLOEXEC);
+    }
+    ALOGD("[halimp_aidl] file open ok, fd = %d.", mInputFile);
+
+    while (mHdmiThreadRun) {
+        memset(msgBuf, 0, sizeof(msgBuf));
+        // Try to get a message from dev.
+        // echo -n -e '\x04\x83' >> /dev/cec
+        r = readMessageFromFifo(msgBuf, MESSAGE_BODY_MAX_LENGTH);
+        if (r <= 1) {
+            // Ignore received ping messages
+            continue;
+        }
+
+        printEventBuf((const char*)msgBuf, r);
+
+        if (((msgBuf[0] >> 4) & 0xf) == 0xf) {
+            handleHotplugMessage(msgBuf);
+        }
+    }
+
+    ALOGD("[halimp_aidl] thread end.");
+}
+
+HdmiMock::HdmiMock() {
+    ALOGE("[halimp_aidl] Opening a virtual HDMI HAL for testing and virtual machine.");
+    mCallback = nullptr;
+    mPortInfos.resize(mTotalPorts);
+    mPortConnectionStatus.resize(mTotalPorts);
+    mPortInfos[0] = {.type = HdmiPortType::OUTPUT,
+                     .portId = static_cast<uint32_t>(1),
+                     .cecSupported = true,
+                     .arcSupported = false,
+                     .physicalAddress = mPhysicalAddress};
+    mPortConnectionStatus[0] = false;
+    mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient_new(serviceDied));
+}
+
+}  // namespace implementation
+}  // namespace hdmi
+}  // namespace tv
+}  // namespace hardware
+}  // namespace android
diff --git a/tv/hdmi/aidl/default/HdmiMock.h b/tv/hdmi/aidl/default/HdmiMock.h
new file mode 100644
index 0000000..05795dd
--- /dev/null
+++ b/tv/hdmi/aidl/default/HdmiMock.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <aidl/android/hardware/tv/hdmi/BnHdmi.h>
+#include <algorithm>
+#include <vector>
+
+using namespace std;
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace hdmi {
+namespace implementation {
+
+using ::aidl::android::hardware::tv::hdmi::BnHdmi;
+using ::aidl::android::hardware::tv::hdmi::HdmiPortInfo;
+using ::aidl::android::hardware::tv::hdmi::HdmiPortType;
+using ::aidl::android::hardware::tv::hdmi::IHdmi;
+using ::aidl::android::hardware::tv::hdmi::IHdmiCallback;
+
+#define HDMI_MSG_IN_FIFO "/dev/hdmi_in_pipe"
+#define MESSAGE_BODY_MAX_LENGTH 4
+
+struct HdmiMock : public BnHdmi {
+    HdmiMock();
+
+    ::ndk::ScopedAStatus getPortInfo(std::vector<HdmiPortInfo>* _aidl_return) override;
+    ::ndk::ScopedAStatus isConnected(int32_t portId, bool* _aidl_return) override;
+    ::ndk::ScopedAStatus setCallback(const std::shared_ptr<IHdmiCallback>& callback) override;
+
+    void printEventBuf(const char* msg_buf, int len);
+
+  private:
+    static void* __threadLoop(void* data);
+    void threadLoop();
+    int readMessageFromFifo(unsigned char* buf, int msgCount);
+    void handleHotplugMessage(unsigned char* msgBuf);
+
+  private:
+    static void serviceDied(void* cookie);
+    std::shared_ptr<IHdmiCallback> mCallback;
+
+    // Variables for the virtual HDMI hal impl
+    std::vector<HdmiPortInfo> mPortInfos;
+    std::vector<bool> mPortConnectionStatus;
+
+    // Port configuration
+    uint16_t mPhysicalAddress = 0xFFFF;
+    int mTotalPorts = 1;
+
+    // Testing variables
+    // Input file descriptor
+    int mInputFile;
+    bool mHdmiThreadRun = true;
+    pthread_t mThreadId = 0;
+
+    ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
+};
+}  // namespace implementation
+}  // namespace hdmi
+}  // Namespace tv
+}  // namespace hardware
+}  // namespace android
diff --git a/tv/hdmi/aidl/default/android.hardware.tv.hdmi-service.rc b/tv/hdmi/aidl/default/android.hardware.tv.hdmi-service.rc
new file mode 100644
index 0000000..c926221
--- /dev/null
+++ b/tv/hdmi/aidl/default/android.hardware.tv.hdmi-service.rc
@@ -0,0 +1,5 @@
+service vendor.hdmi-default /vendor/bin/hw/android.hardware.tv.hdmi-service
+    interface aidl android.hardware.tv.hdmi.IHdmi/default
+    class hal
+    user system
+    group system
diff --git a/tv/hdmi/aidl/default/android.hardware.tv.hdmi-service.xml b/tv/hdmi/aidl/default/android.hardware.tv.hdmi-service.xml
new file mode 100644
index 0000000..a03c199
--- /dev/null
+++ b/tv/hdmi/aidl/default/android.hardware.tv.hdmi-service.xml
@@ -0,0 +1,10 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.tv.hdmi</name>
+        <version>1</version>
+        <interface>
+            <name>IHdmi</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/tv/hdmi/aidl/default/fuzzer.cpp b/tv/hdmi/aidl/default/fuzzer.cpp
new file mode 100644
index 0000000..06a2bc0
--- /dev/null
+++ b/tv/hdmi/aidl/default/fuzzer.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 <HdmiMock.h>
+#include <fuzzbinder/libbinder_ndk_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+using android::fuzzService;
+using android::hardware::tv::hdmi::implementation::HdmiMock;
+using ndk::SharedRefBase;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    auto hdmiAidl = SharedRefBase::make<HdmiMock>();
+
+    fuzzService(hdmiAidl->asBinder().get(), FuzzedDataProvider(data, size));
+
+    return 0;
+}
diff --git a/tv/hdmi/aidl/default/serviceMock.cpp b/tv/hdmi/aidl/default/serviceMock.cpp
new file mode 100644
index 0000000..1d8bf51
--- /dev/null
+++ b/tv/hdmi/aidl/default/serviceMock.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.tv.hdmi-service-shim"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <hidl/HidlTransportSupport.h>
+#include <utils/Log.h>
+#include "HdmiMock.h"
+
+using android::hardware::tv::hdmi::implementation::HdmiMock;
+
+int main() {
+    ABinderProcess_setThreadPoolMaxThreadCount(1);
+    ABinderProcess_startThreadPool();
+
+    std::shared_ptr<HdmiMock> hdmiAidl = ndk::SharedRefBase::make<HdmiMock>();
+    const std::string instance = std::string() + HdmiMock::descriptor + "/default";
+    binder_status_t status =
+            AServiceManager_addService(hdmiAidl->asBinder().get(), instance.c_str());
+    CHECK_EQ(status, STATUS_OK);
+
+    ABinderProcess_joinThreadPool();
+    return 0;
+}
diff --git a/tv/hdmi/aidl/vts/functional/Android.bp b/tv/hdmi/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..f9af58d
--- /dev/null
+++ b/tv/hdmi/aidl/vts/functional/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+    name: "VtsHalTvHdmiAidlTargetTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: ["VtsHalTvHdmiAidlTargetTest.cpp"],
+    static_libs: [
+        "android.hardware.tv.hdmi-V1-ndk",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+    disable_framework: true,
+}
diff --git a/tv/hdmi/aidl/vts/functional/VtsHalTvHdmiAidlTargetTest.cpp b/tv/hdmi/aidl/vts/functional/VtsHalTvHdmiAidlTargetTest.cpp
new file mode 100644
index 0000000..78c2590
--- /dev/null
+++ b/tv/hdmi/aidl/vts/functional/VtsHalTvHdmiAidlTargetTest.cpp
@@ -0,0 +1,103 @@
+/*
+ * 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 "Hdmi_hal_test"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/tv/hdmi/BnHdmi.h>
+#include <aidl/android/hardware/tv/hdmi/BnHdmiCallback.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <sstream>
+#include <vector>
+
+using ::aidl::android::hardware::tv::hdmi::BnHdmiCallback;
+using ::aidl::android::hardware::tv::hdmi::HdmiPortInfo;
+using ::aidl::android::hardware::tv::hdmi::HdmiPortType;
+using ::aidl::android::hardware::tv::hdmi::IHdmi;
+using ::aidl::android::hardware::tv::hdmi::IHdmiCallback;
+using ::ndk::SpAIBinder;
+
+#define INCORRECT_VENDOR_ID 0x00
+#define TV_PHYSICAL_ADDRESS 0x0000
+
+// The main test class for TV HDMI HAL.
+class HdmiTest : public ::testing::TestWithParam<std::string> {
+    static void serviceDied(void* /* cookie */) { ALOGE("VtsHalTvCecAidlTargetTest died"); }
+
+  public:
+    void SetUp() override {
+        hdmi = IHdmi::fromBinder(SpAIBinder(AServiceManager_waitForService(GetParam().c_str())));
+        ASSERT_NE(hdmi, nullptr);
+        ALOGI("%s: getService() for hdmi is %s", __func__, hdmi->isRemote() ? "remote" : "local");
+
+        hdmiCallback = ::ndk::SharedRefBase::make<HdmiCallback>();
+        ASSERT_NE(hdmiCallback, nullptr);
+        hdmiDeathRecipient =
+                ndk::ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient_new(&serviceDied));
+        ASSERT_EQ(AIBinder_linkToDeath(hdmi->asBinder().get(), hdmiDeathRecipient.get(), 0),
+                  STATUS_OK);
+    }
+
+    class HdmiCallback : public BnHdmiCallback {
+      public:
+        ::ndk::ScopedAStatus onHotplugEvent(bool connected __unused, int32_t portId __unused) {
+            return ::ndk::ScopedAStatus::ok();
+        };
+    };
+
+    std::shared_ptr<IHdmi> hdmi;
+    std::shared_ptr<IHdmiCallback> hdmiCallback;
+    ::ndk::ScopedAIBinder_DeathRecipient hdmiDeathRecipient;
+};
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HdmiTest);
+INSTANTIATE_TEST_SUITE_P(PerInstance, HdmiTest,
+                         testing::ValuesIn(android::getAidlHalInstanceNames(IHdmi::descriptor)),
+                         android::PrintInstanceNameToString);
+
+TEST_P(HdmiTest, SetCallback) {
+    ASSERT_TRUE(hdmi->setCallback(::ndk::SharedRefBase::make<HdmiCallback>()).isOk());
+}
+
+TEST_P(HdmiTest, GetPortInfo) {
+    std::vector<HdmiPortInfo> ports;
+    ASSERT_TRUE(hdmi->getPortInfo(&ports).isOk());
+
+    bool cecSupportedOnDevice = false;
+    for (size_t i = 0; i < ports.size(); ++i) {
+        EXPECT_TRUE((ports[i].type == HdmiPortType::OUTPUT) ||
+                    (ports[i].type == HdmiPortType::INPUT));
+        if (ports[i].portId == 0) {
+            ALOGW("%s: Port id should start from 1", __func__);
+        }
+        cecSupportedOnDevice = cecSupportedOnDevice | ports[i].cecSupported;
+    }
+    EXPECT_NE(cecSupportedOnDevice, false) << "At least one port should support CEC";
+}
+
+TEST_P(HdmiTest, IsConnected) {
+    std::vector<HdmiPortInfo> ports;
+    ASSERT_TRUE(hdmi->getPortInfo(&ports).isOk());
+    for (size_t i = 0; i < ports.size(); ++i) {
+        bool connected;
+        ASSERT_TRUE(hdmi->isConnected(ports[i].portId, &connected).isOk());
+    }
+}
diff --git a/usb/gadget/1.2/default/lib/UsbGadgetUtils.cpp b/usb/gadget/1.2/default/lib/UsbGadgetUtils.cpp
index fa50821..0924da7 100644
--- a/usb/gadget/1.2/default/lib/UsbGadgetUtils.cpp
+++ b/usb/gadget/1.2/default/lib/UsbGadgetUtils.cpp
@@ -170,7 +170,6 @@
 
     if ((functions & GadgetFunction::RNDIS) != 0) {
         ALOGI("setCurrentUsbFunctions rndis");
-        if (linkFunction("gsi.rndis", (*functionCount)++)) return Status::ERROR;
         std::string rndisFunction = GetProperty(kVendorRndisConfig, "");
         if (rndisFunction != "") {
             if (linkFunction(rndisFunction.c_str(), (*functionCount)++)) return Status::ERROR;
diff --git a/uwb/aidl/Android.bp b/uwb/aidl/Android.bp
index 52f0605..7dc2b7f 100755
--- a/uwb/aidl/Android.bp
+++ b/uwb/aidl/Android.bp
@@ -47,6 +47,7 @@
 
 aidl_interface {
     name: "android.hardware.uwb.fira_android",
+    owner: "google",
     vendor_available: true,
     srcs: ["android/hardware/uwb/fira_android/*.aidl"],
     stability: "vintf",
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
index cbe2068..39bb5d9 100644
--- a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
@@ -44,4 +44,8 @@
   CCC_SUPPORTED_PULSE_SHAPE_COMBOS = 166,
   CCC_SUPPORTED_RAN_MULTIPLIER = 167,
   SUPPORTED_AOA_RESULT_REQ_ANTENNA_INTERLEAVING = 227,
+  SUPPORTED_MIN_RANGING_INTERVAL_MS = 228,
+  SUPPORTED_RANGE_DATA_NTF_CONFIG = 229,
+  SUPPORTED_RSSI_REPORTING = 230,
+  SUPPORTED_DIAGNOSTICS = 231,
 }
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
index cd2e122..fbcfbff 100644
--- a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
@@ -36,4 +36,5 @@
 enum UwbVendorGidAndroidOids {
   ANDROID_GET_POWER_STATS = 0,
   ANDROID_SET_COUNTRY_CODE = 1,
+  ANDROID_RANGE_DIAGNOSTICS = 2,
 }
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
index d35728f..8413f06 100644
--- a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
@@ -43,4 +43,6 @@
   NB_OF_RANGE_MEASUREMENTS = 227,
   NB_OF_AZIMUTH_MEASUREMENTS = 228,
   NB_OF_ELEVATION_MEASUREMENTS = 229,
+  ENABLE_DIAGNOSTICS = 232,
+  DIAGRAMS_FRAME_REPORTS_FIELDS = 233,
 }
diff --git a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
index 0140fdd..86479fb 100644
--- a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
+++ b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
@@ -150,4 +150,33 @@
      *  0 - Feature not supported.
      */
     SUPPORTED_AOA_RESULT_REQ_ANTENNA_INTERLEAVING = 0xE3,
+
+    /**
+     * 4 byte value to indicate supported min ranging interval in ms.
+     */
+    SUPPORTED_MIN_RANGING_INTERVAL_MS = 0xE4,
+
+    /**
+     * 4 byte bitmask to indicate the supported RANGE_DATA_NTF_CONFIG values
+     *
+     * Bitmask where each bit corresponds to values used in
+     * RANGE_DATA_NTF_CONFIG in SET_APP_CFG_CMD
+     */
+    SUPPORTED_RANGE_DATA_NTF_CONFIG = 0xE5,
+
+    /**
+     * 1 byte bitmask to indicate the supported RSSI_REPORTING values
+     * Values:
+     * 1 - Feature supported.
+     * 0 - Feature not supported.
+     */
+    SUPPORTED_RSSI_REPORTING = 0xE6,
+
+    /**
+     * 1 byte value to indicate support for diagnostics feature.
+     * Values:
+     *  1 - Feature supported.
+     *  0 - Feature not supported.
+     */
+    SUPPORTED_DIAGNOSTICS = 0xE7,
 }
diff --git a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
index e389a2d..4768f55 100644
--- a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
+++ b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
@@ -30,5 +30,11 @@
     ANDROID_GET_POWER_STATS = 0x0,
     // Used to set the current regulatory country code (determined usinag
     // SIM or hardcoded by OEM).
+    // Country code is sent as a 2 byte value corresponding to ISO-3166 country code.
+    // Note: "00" is used to indicate that the country code is unknown.
     ANDROID_SET_COUNTRY_CODE = 0x1,
+    // Used by the notification to get UWB ranging diagnostics stats.
+    // Supported only if the UwbVendorCapabilityTlvTypes.SUPPORTED_DIAGNOSTICS set
+    // to 1.
+    ANDROID_RANGE_DIAGNOSTICS = 0x2,
 }
diff --git a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
index f43b249..f303ed9 100644
--- a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
+++ b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
@@ -66,4 +66,23 @@
     NB_OF_AZIMUTH_MEASUREMENTS = 0xE4,
     /** 1 byte data */
     NB_OF_ELEVATION_MEASUREMENTS = 0xE5,
+
+    /**
+     * Supported only if the UwbVendorCapabilityTlvTypes
+     * .SUPPORTED_DIAGNOSTICS set to 1.
+     */
+    /**
+     * 1 byte data
+     * 1 - Enable,
+     * 0 - Disable
+     */
+    ENABLE_DIAGNOSTICS = 0xE8,
+    /**
+     * 1 byte bitmask
+     * b0: Activate RSSIs field,
+     * b1: Activate AoAs field,
+     * b2: Activate CIRs field,
+     * b3 - b7: RFU
+     */
+    DIAGRAMS_FRAME_REPORTS_FIELDS = 0xE9,
 }
diff --git a/wifi/1.0/vts/OWNERS b/wifi/1.0/vts/OWNERS
deleted file mode 100644
index 287152d..0000000
--- a/wifi/1.0/vts/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 33618
-arabawy@google.com
-etancohen@google.com
diff --git a/wifi/1.0/vts/functional/wifi_hidl_test_utils.cpp b/wifi/1.0/vts/functional/wifi_hidl_test_utils.cpp
index e6e61cf..02f8209 100644
--- a/wifi/1.0/vts/functional/wifi_hidl_test_utils.cpp
+++ b/wifi/1.0/vts/functional/wifi_hidl_test_utils.cpp
@@ -66,7 +66,7 @@
 bool configureChipToSupportIfaceTypeInternal(const sp<IWifiChip>& wifi_chip,
                                              IfaceType type,
                                              ChipModeId* configured_mode_id) {
-    if (!configured_mode_id) {
+    if (!configured_mode_id || !wifi_chip.get()) {
         return false;
     }
     const auto& status_and_modes = HIDL_INVOKE(wifi_chip, getAvailableModes);
diff --git a/wifi/1.1/vts/OWNERS b/wifi/1.1/vts/OWNERS
deleted file mode 100644
index 294fc82..0000000
--- a/wifi/1.1/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.0/vts/OWNERS
diff --git a/wifi/1.2/vts/OWNERS b/wifi/1.2/vts/OWNERS
deleted file mode 100644
index 294fc82..0000000
--- a/wifi/1.2/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.0/vts/OWNERS
diff --git a/wifi/1.3/vts/OWNERS b/wifi/1.3/vts/OWNERS
deleted file mode 100644
index 294fc82..0000000
--- a/wifi/1.3/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.0/vts/OWNERS
diff --git a/wifi/1.4/vts/OWNERS b/wifi/1.4/vts/OWNERS
deleted file mode 100644
index 294fc82..0000000
--- a/wifi/1.4/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.0/vts/OWNERS
diff --git a/wifi/1.5/vts/OWNERS b/wifi/1.5/vts/OWNERS
deleted file mode 100644
index 294fc82..0000000
--- a/wifi/1.5/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.0/vts/OWNERS
diff --git a/wifi/1.6/default/OWNERS b/wifi/1.6/default/OWNERS
deleted file mode 100644
index cf81c79..0000000
--- a/wifi/1.6/default/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-arabawy@google.com
-etancohen@google.com
diff --git a/wifi/1.6/vts/OWNERS b/wifi/1.6/vts/OWNERS
deleted file mode 100644
index 294fc82..0000000
--- a/wifi/1.6/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.0/vts/OWNERS
diff --git a/wifi/OWNERS b/wifi/OWNERS
new file mode 100644
index 0000000..c10bbab
--- /dev/null
+++ b/wifi/OWNERS
@@ -0,0 +1,10 @@
+# Bug component: 33618
+
+# Please assign all bugs related to /hardware/interfaces/wifi to the team alias:
+#
+#    android-wifi-team@google.com
+#
+# This will get them auto-assigned to the on-call triage engineer, ensuring quickest response.
+#
+arabawy@google.com
+etancohen@google.com
diff --git a/wifi/hostapd/1.0/vts/OWNERS b/wifi/hostapd/1.0/vts/OWNERS
deleted file mode 100644
index 287152d..0000000
--- a/wifi/hostapd/1.0/vts/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 33618
-arabawy@google.com
-etancohen@google.com
diff --git a/wifi/hostapd/1.1/vts/OWNERS b/wifi/hostapd/1.1/vts/OWNERS
deleted file mode 100644
index 294fc82..0000000
--- a/wifi/hostapd/1.1/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.0/vts/OWNERS
diff --git a/wifi/hostapd/1.2/vts/OWNERS b/wifi/hostapd/1.2/vts/OWNERS
deleted file mode 100644
index 294fc82..0000000
--- a/wifi/hostapd/1.2/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.0/vts/OWNERS
diff --git a/wifi/hostapd/1.3/vts/OWNERS b/wifi/hostapd/1.3/vts/OWNERS
deleted file mode 100644
index 294fc82..0000000
--- a/wifi/hostapd/1.3/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.0/vts/OWNERS
diff --git a/wifi/hostapd/aidl/vts/OWNERS b/wifi/hostapd/aidl/vts/OWNERS
deleted file mode 100644
index 2a7a7b0..0000000
--- a/wifi/hostapd/aidl/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-etancohen@google.com
-lzye@google.com
diff --git a/wifi/netlinkinterceptor/aidl/default/OWNERS b/wifi/netlinkinterceptor/aidl/default/OWNERS
deleted file mode 100644
index b738dac..0000000
--- a/wifi/netlinkinterceptor/aidl/default/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-chrisweir@google.com
-twasilczyk@google.com
diff --git a/wifi/netlinkinterceptor/vts/OWNERS b/wifi/netlinkinterceptor/vts/OWNERS
deleted file mode 100644
index b738dac..0000000
--- a/wifi/netlinkinterceptor/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-chrisweir@google.com
-twasilczyk@google.com
diff --git a/wifi/offload/1.0/vts/OWNERS b/wifi/offload/1.0/vts/OWNERS
deleted file mode 100644
index 287152d..0000000
--- a/wifi/offload/1.0/vts/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 33618
-arabawy@google.com
-etancohen@google.com
diff --git a/wifi/supplicant/1.0/vts/OWNERS b/wifi/supplicant/1.0/vts/OWNERS
deleted file mode 100644
index b16dc11..0000000
--- a/wifi/supplicant/1.0/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.3/vts/OWNERS
diff --git a/wifi/supplicant/1.1/vts/OWNERS b/wifi/supplicant/1.1/vts/OWNERS
deleted file mode 100644
index b16dc11..0000000
--- a/wifi/supplicant/1.1/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.3/vts/OWNERS
diff --git a/wifi/supplicant/1.2/vts/OWNERS b/wifi/supplicant/1.2/vts/OWNERS
deleted file mode 100644
index b16dc11..0000000
--- a/wifi/supplicant/1.2/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.3/vts/OWNERS
diff --git a/wifi/supplicant/1.4/vts/OWNERS b/wifi/supplicant/1.4/vts/OWNERS
deleted file mode 100644
index b16dc11..0000000
--- a/wifi/supplicant/1.4/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.3/vts/OWNERS
diff --git a/wifi/supplicant/OWNERS b/wifi/supplicant/OWNERS
deleted file mode 100644
index 7febd60..0000000
--- a/wifi/supplicant/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-# Bug component: 33618
-arabawy@google.com
-etancohen@google.com
-gbiren@google.com
-lzye@google.com