Merge "Add triggerEpsFallback to IRadioIms" into main-ims-dev
diff --git a/OWNERS b/OWNERS
index 3a1a038..a53d83e 100644
--- a/OWNERS
+++ b/OWNERS
@@ -9,6 +9,3 @@
 
 # historical/backup
 maco@google.com
-
-# vts tests
-guangzhu@google.com
diff --git a/audio/aidl/Android.bp b/audio/aidl/Android.bp
index 7db50d5..d6e6f50 100644
--- a/audio/aidl/Android.bp
+++ b/audio/aidl/Android.bp
@@ -33,7 +33,7 @@
         "android/hardware/audio/common/SourceMetadata.aidl",
     ],
     imports: [
-        "android.media.audio.common.types-V1",
+        "android.media.audio.common.types-V2",
     ],
     stability: "vintf",
     backend: {
@@ -51,7 +51,7 @@
         ndk: {
             apex_available: [
                 "//apex_available:platform",
-                "com.android.bluetooth",
+                "com.android.btservices",
             ],
             min_sdk_version: "31",
         },
@@ -59,12 +59,34 @@
     versions_with_info: [
         {
             version: "1",
-            imports: ["android.media.audio.common.types-V1"],
+            imports: ["android.media.audio.common.types-V2"],
         },
+        // IMPORTANT: Update latest_android_hardware_audio_common every time you
+        // add the latest frozen version to versions_with_info
     ],
 
 }
 
+// Note: This should always be one version ahead of the last frozen version
+latest_android_hardware_audio_common = "android.hardware.audio.common-V1"
+
+// Modules that depend on android.hardware.audio.common directly can include
+// the following cc_defaults to avoid explicitly managing dependency versions
+// across many scattered files.
+cc_defaults {
+    name: "latest_android_hardware_audio_common_cpp_static",
+    static_libs: [
+        latest_android_hardware_audio_common + "-cpp",
+    ],
+}
+
+cc_defaults {
+    name: "latest_android_hardware_audio_common_ndk_static",
+    static_libs: [
+        latest_android_hardware_audio_common + "-ndk",
+    ],
+}
+
 aidl_interface {
     name: "android.hardware.audio.core",
     vendor_available: true,
@@ -75,11 +97,15 @@
         "android/hardware/audio/core/IModule.aidl",
         "android/hardware/audio/core/IStreamIn.aidl",
         "android/hardware/audio/core/IStreamOut.aidl",
+        "android/hardware/audio/core/MmapBufferDescriptor.aidl",
         "android/hardware/audio/core/ModuleDebug.aidl",
+        "android/hardware/audio/core/StreamDescriptor.aidl",
     ],
     imports: [
+        "android.hardware.common-V2",
+        "android.hardware.common.fmq-V1",
         "android.hardware.audio.common-V1",
-        "android.media.audio.common.types-V1",
+        "android.media.audio.common.types-V2",
     ],
     stability: "vintf",
     backend: {
@@ -88,7 +114,71 @@
             enabled: false,
         },
         java: {
-            platform_apis: true,
+            sdk_version: "module_current",
         },
     },
+    versions_with_info: [
+        // IMPORTANT: Update latest_android_hardware_audio_core every time you
+        // add the latest frozen version to versions_with_info
+    ],
+}
+
+// Note: This should always be one version ahead of the last frozen version
+latest_android_hardware_audio_core = "android.hardware.audio.core-V1"
+
+// Modules that depend on android.hardware.audio.core directly can include
+// the following cc_defaults to avoid explicitly managing dependency versions
+// across many scattered files.
+cc_defaults {
+    name: "latest_android_hardware_audio_core_ndk_shared",
+    shared_libs: [
+        latest_android_hardware_audio_core + "-ndk",
+    ],
+}
+
+cc_defaults {
+    name: "latest_android_hardware_audio_core_ndk_static",
+    static_libs: [
+        latest_android_hardware_audio_core + "-ndk",
+    ],
+}
+
+aidl_interface {
+    name: "android.hardware.audio.effect",
+    vendor_available: true,
+    srcs: [
+        "android/hardware/audio/effect/Descriptor.aidl",
+        "android/hardware/audio/effect/IEffect.aidl",
+        "android/hardware/audio/effect/IFactory.aidl",
+    ],
+    imports: [
+        "android.hardware.audio.common-V1",
+        "android.media.audio.common.types-V2",
+    ],
+    stability: "vintf",
+    backend: {
+        // The C++ backend is disabled transitively due to use of FMQ.
+        cpp: {
+            enabled: false,
+        },
+        java: {
+            sdk_version: "module_current",
+        },
+    },
+}
+
+latest_android_hardware_audio_effect = "android.hardware.audio.effect-V1"
+
+cc_defaults {
+    name: "latest_android_hardware_audio_effect_ndk_shared",
+    shared_libs: [
+        latest_android_hardware_audio_effect + "-ndk",
+    ],
+}
+
+cc_defaults {
+    name: "latest_android_hardware_audio_effect_ndk_static",
+    static_libs: [
+        latest_android_hardware_audio_effect + "-ndk",
+    ],
 }
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioPatch.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioPatch.aidl
index 1cef4cd..078b5ea 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioPatch.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioPatch.aidl
@@ -37,4 +37,6 @@
   int id;
   int[] sourcePortConfigIds;
   int[] sinkPortConfigIds;
+  int minimumStreamBufferSizeFrames;
+  int[] latenciesMs;
 }
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl
index f8bc2c7..a8bbb15 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl
@@ -43,10 +43,33 @@
   android.media.audio.common.AudioPort[] getAudioPorts();
   android.hardware.audio.core.AudioRoute[] getAudioRoutes();
   android.hardware.audio.core.AudioRoute[] getAudioRoutesForAudioPort(int portId);
-  android.hardware.audio.core.IStreamIn openInputStream(int portConfigId, in android.hardware.audio.common.SinkMetadata sinkMetadata);
-  android.hardware.audio.core.IStreamOut openOutputStream(int portConfigId, in android.hardware.audio.common.SourceMetadata sourceMetadata, in @nullable android.media.audio.common.AudioOffloadInfo offloadInfo);
+  android.hardware.audio.core.IModule.OpenInputStreamReturn openInputStream(in android.hardware.audio.core.IModule.OpenInputStreamArguments args);
+  android.hardware.audio.core.IModule.OpenOutputStreamReturn openOutputStream(in android.hardware.audio.core.IModule.OpenOutputStreamArguments args);
   android.hardware.audio.core.AudioPatch setAudioPatch(in android.hardware.audio.core.AudioPatch requested);
   boolean setAudioPortConfig(in android.media.audio.common.AudioPortConfig requested, out android.media.audio.common.AudioPortConfig suggested);
   void resetAudioPatch(int patchId);
   void resetAudioPortConfig(int portConfigId);
+  @VintfStability
+  parcelable OpenInputStreamArguments {
+    int portConfigId;
+    android.hardware.audio.common.SinkMetadata sinkMetadata;
+    long bufferSizeFrames;
+  }
+  @VintfStability
+  parcelable OpenInputStreamReturn {
+    android.hardware.audio.core.IStreamIn stream;
+    android.hardware.audio.core.StreamDescriptor desc;
+  }
+  @VintfStability
+  parcelable OpenOutputStreamArguments {
+    int portConfigId;
+    android.hardware.audio.common.SourceMetadata sourceMetadata;
+    @nullable android.media.audio.common.AudioOffloadInfo offloadInfo;
+    long bufferSizeFrames;
+  }
+  @VintfStability
+  parcelable OpenOutputStreamReturn {
+    android.hardware.audio.core.IStreamOut stream;
+    android.hardware.audio.core.StreamDescriptor desc;
+  }
 }
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/MmapBufferDescriptor.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/MmapBufferDescriptor.aidl
new file mode 100644
index 0000000..6ea1c69
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/MmapBufferDescriptor.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.audio.core;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable MmapBufferDescriptor {
+  android.hardware.common.Ashmem sharedMemory;
+  long burstSizeFrames;
+  int flags;
+  const int FLAG_INDEX_APPLICATION_SHAREABLE = 0;
+}
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
new file mode 100644
index 0000000..db1ac22
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/StreamDescriptor.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.audio.core;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable StreamDescriptor {
+  android.hardware.common.fmq.MQDescriptor<android.hardware.audio.core.StreamDescriptor.Command,android.hardware.common.fmq.SynchronizedReadWrite> command;
+  android.hardware.common.fmq.MQDescriptor<android.hardware.audio.core.StreamDescriptor.Reply,android.hardware.common.fmq.SynchronizedReadWrite> reply;
+  int frameSizeBytes;
+  long bufferSizeFrames;
+  android.hardware.audio.core.StreamDescriptor.AudioBuffer audio;
+  const int COMMAND_BURST = 1;
+  @FixedSize @VintfStability
+  parcelable Position {
+    long frames;
+    long timeNs;
+  }
+  @FixedSize @VintfStability
+  parcelable Command {
+    int code;
+    int fmqByteCount;
+  }
+  @FixedSize @VintfStability
+  parcelable Reply {
+    int status;
+    int fmqByteCount;
+    android.hardware.audio.core.StreamDescriptor.Position observable;
+    android.hardware.audio.core.StreamDescriptor.Position hardware;
+    int latencyMs;
+  }
+  @VintfStability
+  union AudioBuffer {
+    android.hardware.common.fmq.MQDescriptor<byte,android.hardware.common.fmq.SynchronizedReadWrite> fmq;
+    android.hardware.audio.core.MmapBufferDescriptor mmap;
+  }
+}
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
new file mode 100644
index 0000000..94cacd9
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Descriptor.aidl
@@ -0,0 +1,60 @@
+/*
+ * 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 Descriptor {
+  android.hardware.audio.effect.Descriptor.Common common;
+  const String EFFECT_TYPE_UUID_ENV_REVERB = "c2e5d5f0-94bd-4763-9cac-4e234d06839e";
+  const String EFFECT_TYPE_UUID_PRESET_REVERB = "47382d60-ddd8-11db-bf3a-0002a5d5c51b";
+  const String EFFECT_TYPE_UUID_EQUALIZER = "0bed4300-ddd6-11db-8f34-0002a5d5c51b";
+  const String EFFECT_TYPE_UUID_BASS_BOOST = "0634f220-ddd4-11db-a0fc-0002a5d5c51b";
+  const String EFFECT_TYPE_UUID_VIRTUALIZER = "37cc2c00-dddd-11db-8577-0002a5d5c51b";
+  const String EFFECT_TYPE_UUID_AGC = "0a8abfe0-654c-11e0-ba26-0002a5d5c51b";
+  const String EFFECT_TYPE_UUID_AEC = "7b491460-8d4d-11e0-bd61-0002a5d5c51b";
+  const String EFFECT_TYPE_UUID_NS = "58b4b260-8e06-11e0-aa8e-0002a5d5c51b";
+  const String EFFECT_TYPE_UUID_LOUDNESS_ENHANCER = "fe3199be-aed0-413f-87bb-11260eb63cf1";
+  const String EFFECT_TYPE_UUID_DYNAMICS_PROCESSING = "7261676f-6d75-7369-6364-28e2fd3ac39e";
+  const String EFFECT_TYPE_UUID_HAPTIC_GENERATOR = "1411e6d6-aecd-4021-a1cf-a6aceb0d71e5";
+  const String EFFECT_TYPE_UUID_SPATIALIZER = "ccd4cf09-a79d-46c2-9aae-06a1698d6c8f";
+  const String EFFECT_TYPE_UUID_VOLUME = "09e8ede0-ddde-11db-b4f6-0002a5d5c51b";
+  @VintfStability
+  parcelable Identity {
+    android.media.audio.common.AudioUuid type;
+    android.media.audio.common.AudioUuid uuid;
+  }
+  @VintfStability
+  parcelable Common {
+    android.hardware.audio.effect.Descriptor.Identity id;
+  }
+}
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
new file mode 100644
index 0000000..7f868ad
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/IEffect.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.audio.effect;
+@VintfStability
+interface IEffect {
+  void open();
+  void close();
+  android.hardware.audio.effect.Descriptor getDescriptor();
+}
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
new file mode 100644
index 0000000..db06475
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/IFactory.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.audio.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.IEffect createEffect(in android.media.audio.common.AudioUuid implUuid);
+  void destroyEffect(in android.hardware.audio.effect.IEffect handle);
+}
diff --git a/audio/aidl/android/hardware/audio/core/AudioPatch.aidl b/audio/aidl/android/hardware/audio/core/AudioPatch.aidl
index 48ca214..005d4c0 100644
--- a/audio/aidl/android/hardware/audio/core/AudioPatch.aidl
+++ b/audio/aidl/android/hardware/audio/core/AudioPatch.aidl
@@ -37,4 +37,18 @@
      * unique.
      */
     int[] sinkPortConfigIds;
+    /**
+     * The minimum buffer size, in frames, which streams must use for
+     * this connection configuration. This field is filled out by the
+     * HAL module on creation of the patch and must be a positive number.
+     */
+    int minimumStreamBufferSizeFrames;
+    /**
+     * Latencies, in milliseconds, associated with each sink port config from
+     * the 'sinkPortConfigIds' field. This field is filled out by the HAL module
+     * on creation or updating of the patch and must be a positive number. This
+     * is a nominal value. The current value of latency is provided via
+     * 'StreamDescriptor' command exchange on each audio I/O operation.
+     */
+    int[] latenciesMs;
 }
diff --git a/audio/aidl/android/hardware/audio/core/IModule.aidl b/audio/aidl/android/hardware/audio/core/IModule.aidl
index 802cb2f..735f87f 100644
--- a/audio/aidl/android/hardware/audio/core/IModule.aidl
+++ b/audio/aidl/android/hardware/audio/core/IModule.aidl
@@ -23,6 +23,7 @@
 import android.hardware.audio.core.IStreamIn;
 import android.hardware.audio.core.IStreamOut;
 import android.hardware.audio.core.ModuleDebug;
+import android.hardware.audio.core.StreamDescriptor;
 import android.media.audio.common.AudioOffloadInfo;
 import android.media.audio.common.AudioPort;
 import android.media.audio.common.AudioPortConfig;
@@ -241,22 +242,54 @@
      * 'setAudioPortConfig' method. Existence of an audio patch involving this
      * port configuration is not required for successful opening of a stream.
      *
+     * The requested buffer size is expressed in frames, thus the actual size
+     * in bytes depends on the audio port configuration. Also, the HAL module
+     * may end up providing a larger buffer, thus the requested size is treated
+     * as the minimum size that the client needs. The minimum buffer size
+     * suggested by the HAL is in the 'AudioPatch.minimumStreamBufferSizeFrames'
+     * field, returned as a result of calling the 'setAudioPatch' method.
+     *
      * Only one stream is allowed per audio port configuration. HAL module can
      * also set a limit on how many output streams can be opened for a particular
      * mix port by using its 'AudioPortMixExt.maxOpenStreamCount' field.
      *
-     * @return An opened input stream.
-     * @param portConfigId The ID of the audio mix port config.
-     * @param sinkMetadata Description of the audio that will be recorded.
+     * Note that although it's not prohibited to open a stream on a mix port
+     * configuration which is not connected (using a patch) to any device port,
+     * and set up a patch afterwards, this sequence of calls is not recommended,
+     * because setting up of a patch might fail due to an insufficient stream
+     * buffer size. Another consequence of having a stream on an unconnected mix
+     * port is that capture positions can not be determined because there is no
+     * "external observer," thus read operations done via StreamDescriptor will
+     * be completing with an error, although data (zero filled) will still be
+     * provided.
+     *
+     * @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:
      *                             - If the port config can not be found by the ID.
      *                             - If the port config is not of an input mix port.
+     *                             - If a buffer of the requested size can not be provided.
      * @throws EX_ILLEGAL_STATE In the following cases:
      *                          - If the port config already has a stream opened on it.
      *                          - If the limit on the open stream count for the port has
      *                            been reached.
+     *                          - If the HAL module failed to initialize the stream.
      */
-    IStreamIn openInputStream(int portConfigId, in SinkMetadata sinkMetadata);
+    @VintfStability
+    parcelable OpenInputStreamArguments {
+        /** The ID of the audio mix port config. */
+        int portConfigId;
+        /** Description of the audio that will be recorded. */
+        SinkMetadata sinkMetadata;
+        /** Requested audio I/O buffer minimum size, in frames. */
+        long bufferSizeFrames;
+    }
+    @VintfStability
+    parcelable OpenInputStreamReturn {
+        IStreamIn stream;
+        StreamDescriptor desc;
+    }
+    OpenInputStreamReturn openInputStream(in OpenInputStreamArguments args);
 
     /**
      * Open an output stream using an existing audio mix port configuration.
@@ -269,30 +302,62 @@
      * the framework must provide additional information about the encoded
      * audio stream in 'offloadInfo' argument.
      *
+     * The requested buffer size is expressed in frames, thus the actual size
+     * in bytes depends on the audio port configuration. Also, the HAL module
+     * may end up providing a larger buffer, thus the requested size is treated
+     * as the minimum size that the client needs. The minimum buffer size
+     * suggested by the HAL is in the 'AudioPatch.minimumStreamBufferSizeFrames'
+     * field, returned as a result of calling the 'setAudioPatch' method.
+     *
      * Only one stream is allowed per audio port configuration. HAL module can
      * also set a limit on how many output streams can be opened for a particular
      * mix port by using its 'AudioPortMixExt.maxOpenStreamCount' field.
      * Only one stream can be opened on the audio port with 'PRIMARY' output
      * flag. This rule can not be overridden with 'maxOpenStreamCount' field.
      *
-     * @return An opened output stream.
-     * @param portConfigId The ID of the audio mix port config.
-     * @param sourceMetadata Description of the audio that will be played.
-     * @param offloadInfo Additional information for offloaded playback.
+     * Note that although it's not prohibited to open a stream on a mix port
+     * configuration which is not connected (using a patch) to any device port,
+     * and set up a patch afterwards, this sequence of calls is not recommended,
+     * because setting up of a patch might fail due to an insufficient stream
+     * buffer size. Another consequence of having a stream on an unconnected mix
+     * port is that presentation positions can not be determined because there
+     * is no "external observer," thus write operations done via
+     * StreamDescriptor will be completing with an error, although the data
+     * will still be accepted and immediately discarded.
+     *
+     * @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:
      *                             - If the port config can not be found by the ID.
      *                             - If the port config is not of an output mix port.
      *                             - If the offload info is not provided for an offload
      *                               port configuration.
+     *                             - If a buffer of the requested size can not be provided.
      * @throws EX_ILLEGAL_STATE In the following cases:
      *                          - If the port config already has a stream opened on it.
      *                          - If the limit on the open stream count for the port has
      *                            been reached.
      *                          - If another opened stream already exists for the 'PRIMARY'
      *                            output port.
+     *                          - If the HAL module failed to initialize the stream.
      */
-    IStreamOut openOutputStream(int portConfigId, in SourceMetadata sourceMetadata,
-            in @nullable AudioOffloadInfo offloadInfo);
+    @VintfStability
+    parcelable OpenOutputStreamArguments {
+        /** The ID of the audio mix port config. */
+        int portConfigId;
+        /** Description of the audio that will be played. */
+        SourceMetadata sourceMetadata;
+        /** Additional information used for offloaded playback only. */
+        @nullable AudioOffloadInfo offloadInfo;
+        /** Requested audio I/O buffer minimum size, in frames. */
+        long bufferSizeFrames;
+    }
+    @VintfStability
+    parcelable OpenOutputStreamReturn {
+        IStreamOut stream;
+        StreamDescriptor desc;
+    }
+    OpenOutputStreamReturn openOutputStream(in OpenOutputStreamArguments args);
 
     /**
      * Set an audio patch.
@@ -300,19 +365,27 @@
      * This method creates new or updates an existing audio patch. If the
      * requested audio patch does not have a specified id, then a new patch is
      * created and an ID is allocated for it by the HAL module. Otherwise an
-     * attempt to update an existing patch is made. It is recommended that
-     * updating of an existing audio patch should be performed by the HAL module
-     * in a way that does not interrupt active audio streams involving audio
-     * port configurations of the patch. If the HAL module is unable to avoid
-     * interruption when updating a certain patch, it is permitted to allocate a
-     * new patch ID for the result. The returned audio patch contains all the
-     * information about the new or updated audio patch.
+     * attempt to update an existing patch is made.
+     *
+     * The operation of updating an existing audio patch must not change
+     * playback state of audio streams opened on the audio port configurations
+     * of the patch. That is, the HAL module must still be able to consume or
+     * to provide data from / to streams continuously during the patch
+     * switching. Natural intermittent audible loss of some audio frames due to
+     * switching between device ports which does not affect stream playback is
+     * allowed. If the HAL module is unable to avoid playback or recording
+     * state change when updating a certain patch, it must return an error. In
+     * that case, the client must take care of changing port configurations,
+     * patches, and recreating streams in a way which provides an acceptable
+     * user experience.
      *
      * Audio port configurations specified in the patch must be obtained by
      * calling 'setAudioPortConfig' method. There must be an audio route which
      * allows connection between the audio ports whose configurations are used.
-     * An audio patch may be created before or after an audio steam is created
-     * for this configuration.
+     *
+     * When updating an existing audio patch, nominal latency values may change
+     * and must be provided by the HAL module in the returned 'AudioPatch'
+     * structure.
      *
      * @return Resulting audio patch.
      * @param requested Requested audio patch.
@@ -324,6 +397,9 @@
      * @throws EX_ILLEGAL_STATE In the following cases:
      *                          - If application of the patch can only use a route with an
      *                            exclusive use the sink port, and it is already patched.
+     *                          - If updating an existing patch will cause interruption
+     *                            of audio, or requires re-opening of streams due to
+     *                            change of minimum audio I/O buffer size.
      * @throws EX_UNSUPPORTED_OPERATION If the patch can not be established because
      *                                  the HAL module does not support this otherwise valid
      *                                  patch configuration. For example, if it's a patch
diff --git a/audio/aidl/android/hardware/audio/core/IStreamIn.aidl b/audio/aidl/android/hardware/audio/core/IStreamIn.aidl
index b770449..0c3e3d1 100644
--- a/audio/aidl/android/hardware/audio/core/IStreamIn.aidl
+++ b/audio/aidl/android/hardware/audio/core/IStreamIn.aidl
@@ -27,8 +27,12 @@
      * Close the stream.
      *
      * Releases any resources allocated for this stream on the HAL module side.
-     * The stream can not be operated after it has been closed. Methods of this
-     * interface throw EX_ILLEGAL_STATE in for a closed stream.
+     * This includes the fast message queues and shared memories returned via
+     * the StreamDescriptor. Thus, the stream can not be operated anymore after
+     * it has been closed. The client needs to release the audio data I/O
+     * objects after the call to this method returns.
+     *
+     * Methods of this interface throw EX_ILLEGAL_STATE for a closed stream.
      *
      * @throws EX_ILLEGAL_STATE If the stream has already been closed.
      */
diff --git a/audio/aidl/android/hardware/audio/core/IStreamOut.aidl b/audio/aidl/android/hardware/audio/core/IStreamOut.aidl
index 60212fc..9fdb37d 100644
--- a/audio/aidl/android/hardware/audio/core/IStreamOut.aidl
+++ b/audio/aidl/android/hardware/audio/core/IStreamOut.aidl
@@ -27,8 +27,12 @@
      * Close the stream.
      *
      * Releases any resources allocated for this stream on the HAL module side.
-     * The stream can not be operated after it has been closed. Methods of this
-     * interface throw EX_ILLEGAL_STATE in for a closed stream.
+     * This includes the fast message queues and shared memories returned via
+     * the StreamDescriptor. Thus, the stream can not be operated anymore after
+     * it has been closed. The client needs to release the audio data I/O
+     * objects after the call to this method returns.
+     *
+     * Methods of this interface throw EX_ILLEGAL_STATE for a closed stream.
      *
      * @throws EX_ILLEGAL_STATE If the stream has already been closed.
      */
diff --git a/audio/aidl/android/hardware/audio/core/MmapBufferDescriptor.aidl b/audio/aidl/android/hardware/audio/core/MmapBufferDescriptor.aidl
new file mode 100644
index 0000000..108bcbe
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/core/MmapBufferDescriptor.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.audio.core;
+
+import android.hardware.common.Ashmem;
+
+/**
+ * MMap buffer descriptor is used by streams opened in MMap No IRQ mode.
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable MmapBufferDescriptor {
+    /**
+     * MMap memory buffer.
+     */
+    Ashmem sharedMemory;
+    /**
+     * Transfer size granularity in frames.
+     */
+    long burstSizeFrames;
+    /**
+     * Attributes describing the buffer. Bitmask indexed by FLAG_INDEX_*
+     * constants.
+     */
+    int flags;
+
+    /**
+     * Whether the buffer can be securely shared to untrusted applications
+     * through the AAudio exclusive mode.
+     *
+     * Only set this flag if applications are restricted from accessing the
+     * memory surrounding the audio data buffer by a kernel mechanism.
+     * See Linux kernel's dma-buf
+     * (https://www.kernel.org/doc/html/v4.16/driver-api/dma-buf.html).
+     */
+    const int FLAG_INDEX_APPLICATION_SHAREABLE = 0;
+}
diff --git a/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl b/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl
new file mode 100644
index 0000000..2b1ed8c
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.audio.core;
+
+import android.hardware.audio.core.MmapBufferDescriptor;
+import android.hardware.common.fmq.MQDescriptor;
+import android.hardware.common.fmq.SynchronizedReadWrite;
+
+/**
+ * Stream descriptor contains fast message queues and buffers used for sending
+ * and receiving audio data. The descriptor complements IStream* interfaces by
+ * providing communication channels that serve as an alternative to Binder
+ * transactions.
+ *
+ * Handling of audio data and commands must be done by the HAL module on a
+ * dedicated thread with high priority, for all modes, including MMap No
+ * IRQ. The HAL module is responsible for creating this thread and setting its
+ * priority. The HAL module is also responsible for serializing access to the
+ * 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.
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable StreamDescriptor {
+    /**
+     * Position binds together a position within the stream and time.
+     *
+     * The timestamp must use "monotonic" clock.
+     *
+     * The frame count must advance between consecutive I/O operations, and stop
+     * advancing when the stream was put into the 'standby' mode. On exiting the
+     * 'standby' mode, the frame count must not reset, but continue counting.
+     */
+    @VintfStability
+    @FixedSize
+    parcelable Position {
+        /** Frame count. */
+        long frames;
+        /** Timestamp in nanoseconds. */
+        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;
+
+    /**
+     * Used for sending commands to the HAL module. The client writes into
+     * the queue, the HAL module reads. The queue can only contain a single
+     * command.
+     */
+    @VintfStability
+    @FixedSize
+    parcelable Command {
+        /**
+         * One of COMMAND_* codes.
+         */
+        int code;
+        /**
+         * For output streams: the amount of bytes that the client requests the
+         *   HAL module to read from 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.
+         *
+         * In both cases it is allowed for this field to contain any
+         * non-negative number. The value 0 can be used if the client only needs
+         * to retrieve current positions and latency. Any sufficiently big value
+         * which exceeds the size of the queue's area which is currently
+         * available for reading or writing by the HAL module must be trimmed by
+         * the HAL module to the available size. Note that the HAL module is
+         * allowed to consume or provide less data than requested, and it must
+         * return the amount of actually read or written data via the
+         * 'Reply.fmqByteCount' field. Thus, only attempts to pass a negative
+         * number must be constituted as a client's error.
+         */
+        int fmqByteCount;
+    }
+    MQDescriptor<Command, SynchronizedReadWrite> command;
+
+    /**
+     * 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.
+     */
+    @VintfStability
+    @FixedSize
+    parcelable Reply {
+        /**
+         * 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_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.
+         * For input streams: the amount of bytes actually provided by the HAL
+         *   in the 'audio.fmq' queue.
+         *
+         * The returned value must not exceed the value passed in the
+         * 'fmqByteCount' field of the corresponding command or be negative.
+         */
+        int fmqByteCount;
+        /**
+         * 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).
+         */
+        Position observable;
+        /**
+         * Used only for MMap streams to provide the hardware read / write
+         * position for audio data in the shared memory buffer 'audio.mmap'.
+         */
+        Position hardware;
+        /**
+         * Current latency reported by the hardware.
+         */
+        int latencyMs;
+    }
+    MQDescriptor<Reply, SynchronizedReadWrite> reply;
+
+    /**
+     * The size of one frame of audio data in bytes. For PCM formats this is
+     * usually equal to the size of a sample multiplied by the number of
+     * channels used. For encoded bitstreams encapsulated into PCM the sample
+     * size of the underlying PCM stream is used. For encoded bitstreams that
+     * are passed without encapsulation, the frame size is usually 1 byte.
+     */
+    int frameSizeBytes;
+    /**
+     * Total buffer size in frames. This applies both to the size of the 'audio.fmq'
+     * queue and to the size of the shared memory buffer for MMap No IRQ streams.
+     * Note that this size may end up being slightly larger than the size requested
+     * in a call to 'IModule.openInputStream' or 'openOutputStream' due to memory
+     * alignment requirements.
+     */
+    long bufferSizeFrames;
+
+    /**
+     * Used for sending or receiving audio data to/from the stream. In the case
+     * of MMap No IRQ streams this structure only contains the information about
+     * the shared memory buffer. Audio data is sent via the shared buffer
+     * directly.
+     */
+    @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.
+         *
+         * 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,
+         *     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.
+         *  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,
+         *     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.
+         *  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.
+         */
+        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.
+         */
+        MmapBufferDescriptor mmap;
+    }
+    AudioBuffer audio;
+}
diff --git a/audio/aidl/android/hardware/audio/effect/Descriptor.aidl b/audio/aidl/android/hardware/audio/effect/Descriptor.aidl
new file mode 100644
index 0000000..51b31c2
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/effect/Descriptor.aidl
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.audio.effect;
+
+import android.media.audio.common.AudioUuid;
+
+/**
+ * Effect descriptor contains all information (capabilities, attributes, and ownership) for an
+ * effect implemented in the Audio Effect HAL. Framework uses this information to decide when and
+ * how to apply the effect.
+ */
+@VintfStability
+parcelable Descriptor {
+    /**
+     * UUID for effect types, these definitions are in sync with SDK, see @c AudioEffect.java.
+     */
+    // UUID for environmental reverberation effect type.
+    const String EFFECT_TYPE_UUID_ENV_REVERB = "c2e5d5f0-94bd-4763-9cac-4e234d06839e";
+    // UUID for preset reverberation effect type.
+    const String EFFECT_TYPE_UUID_PRESET_REVERB = "47382d60-ddd8-11db-bf3a-0002a5d5c51b";
+    // UUID for equalizer effect type.
+    const String EFFECT_TYPE_UUID_EQUALIZER = "0bed4300-ddd6-11db-8f34-0002a5d5c51b";
+    // UUID for bass boost effect type.
+    const String EFFECT_TYPE_UUID_BASS_BOOST = "0634f220-ddd4-11db-a0fc-0002a5d5c51b";
+    // UUID for virtualizer effect type.
+    const String EFFECT_TYPE_UUID_VIRTUALIZER = "37cc2c00-dddd-11db-8577-0002a5d5c51b";
+    // 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.
+    const String EFFECT_TYPE_UUID_AEC = "7b491460-8d4d-11e0-bd61-0002a5d5c51b";
+    // UUID for Noise Suppressor (NS) type.
+    const String EFFECT_TYPE_UUID_NS = "58b4b260-8e06-11e0-aa8e-0002a5d5c51b";
+    // UUID for Loudness Enhancer type.
+    const String EFFECT_TYPE_UUID_LOUDNESS_ENHANCER = "fe3199be-aed0-413f-87bb-11260eb63cf1";
+    // UUID for Dynamics Processing type.
+    const String EFFECT_TYPE_UUID_DYNAMICS_PROCESSING = "7261676f-6d75-7369-6364-28e2fd3ac39e";
+    // UUID for Haptic Generator type.
+    const String EFFECT_TYPE_UUID_HAPTIC_GENERATOR = "1411e6d6-aecd-4021-a1cf-a6aceb0d71e5";
+    // 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.
+    const String EFFECT_TYPE_UUID_VOLUME = "09e8ede0-ddde-11db-b4f6-0002a5d5c51b";
+
+    /**
+     * This structure completely identifies an effect implementation.
+     */
+    @VintfStability
+    parcelable Identity {
+        /**
+         * UUID for the type of effect.
+         */
+        AudioUuid type;
+        /**
+         * UUID for this particular implementation.
+         */
+        AudioUuid uuid;
+    }
+
+    // Common attributes of all effect implementation.
+    @VintfStability
+    parcelable Common {
+        /**
+         * Identity of effect implementation.
+         */
+        Identity id;
+    }
+    Common common;
+}
diff --git a/audio/aidl/android/hardware/audio/effect/IEffect.aidl b/audio/aidl/android/hardware/audio/effect/IEffect.aidl
new file mode 100644
index 0000000..44e916b
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/effect/IEffect.aidl
@@ -0,0 +1,63 @@
+/*
+ * 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;
+
+/**
+ * Effect interfaces definitions to configure and control the effect instance.
+ */
+@VintfStability
+interface IEffect {
+    /**
+     * Open an effect instance, effect should not start processing data before receive START
+     * command. All necessary information should be allocated and instance should transfer to IDLE
+     * state after open() call has been handled successfully.
+     * After open, the effect instance should be able to handle all IEffect interface calls.
+     *
+     * @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 not result in throw error.
+     */
+    void open();
+
+    /**
+     * Called by the client to close the effect instance, instance context will be kept after
+     * close, but processing thread should be destroyed and consume no CPU. It is recommended to
+     * close the effect on the client side as soon as it becomes unused, it's client responsibility
+     * to make sure all parameter/buffer is correct if client wants to reopen a closed instance.
+     *
+     * Effect instance close interface should always success unless:
+     * 1. The effect instance is not in a proper state to be closed, for example it's still in
+     * processing state.
+     * 2. There is system/hardware related failure when close.
+     *
+     * @throws EX_ILLEGAL_STATE if the effect instance is not in a proper state to be closed.
+     * @throws EX_UNSUPPORTED_OPERATION if the effect instance failed to close for any other reason.
+     * @note Close an already-closed effect should do nothing and not result in throw error.
+     */
+    void close();
+
+    /**
+     * Return the @c Descriptor of this effect instance.
+     *
+     * Must be available for the effect instance at anytime and should always succeed.
+     *
+     * @return Descriptor The @c Descriptor of this effect instance.
+     */
+    Descriptor getDescriptor();
+}
diff --git a/audio/aidl/android/hardware/audio/effect/IFactory.aidl b/audio/aidl/android/hardware/audio/effect/IFactory.aidl
new file mode 100644
index 0000000..4873d3a
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/effect/IFactory.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.Descriptor;
+import android.hardware.audio.effect.IEffect;
+import android.media.audio.common.AudioUuid;
+
+/**
+ * Provides system-wide effect factory interfaces.
+ *
+ * An android.hardware.audio.effect.IFactory platform service is registered with ServiceManager, and
+ * is always available on the device.
+ *
+ */
+@VintfStability
+interface IFactory {
+    /**
+     * Return a list of effect identities supported by this device, with the optional
+     * filter by type and/or by instance UUID.
+     *
+     * @param type UUID identifying the effect 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.
+     * @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.
+     * @return List of effect identities supported and filtered by type/implementation UUID.
+     */
+    Descriptor.Identity[] queryEffects(
+            in @nullable AudioUuid type, in @nullable AudioUuid implementation);
+
+    /**
+     * Called by the audio framework to create the effect (identified by the implementation UUID
+     * parameter).
+     *
+     * The effect instance should be able to maintain its own context and parameters after creation.
+     *
+     * @param implUuid UUID for the effect implementation which instance will be created based on.
+     * @return The effect instance handle created.
+     * @throws EX_ILLEGAL_ARGUMENT if the implUuid is not valid.
+     * @throws EX_TRANSACTION_FAILED if device capability/resource is not enough to create the
+     *         effect instance.
+     */
+    IEffect createEffect(in AudioUuid implUuid);
+
+    /**
+     * Called by the framework to destroy the effect and free up all currently allocated resources.
+     * It is recommended to destroy the effect from the client side as soon as it is becomes unused.
+     *
+     * The client must ensure effect instance is closed before destroy.
+     *
+     * @param handle The handle of effect instance to be destroyed.
+     * @throws EX_ILLEGAL_ARGUMENT if the effect handle is not valid.
+     * @throws EX_ILLEGAL_STATE if the effect instance is not in a proper state to be destroyed.
+     */
+    void destroyEffect(in IEffect handle);
+}
diff --git a/audio/aidl/common/Android.bp b/audio/aidl/common/Android.bp
new file mode 100644
index 0000000..a3f7f0b
--- /dev/null
+++ b/audio/aidl/common/Android.bp
@@ -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 {
+    // 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 {
+    name: "libaudioaidlcommon",
+    host_supported: true,
+    vendor_available: true,
+    export_include_dirs: ["include"],
+    header_libs: [
+        "libbase_headers",
+        "libsystem_headers",
+    ],
+    export_header_lib_headers: [
+        "libbase_headers",
+        "libsystem_headers",
+    ],
+    srcs: [
+        "StreamWorker.cpp",
+    ],
+}
+
+cc_test {
+    name: "libaudioaidlcommon_test",
+    host_supported: true,
+    vendor_available: true,
+    static_libs: [
+        "android.media.audio.common.types-V1-ndk",
+        "libaudioaidlcommon",
+    ],
+    shared_libs: [
+        "liblog",
+    ],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wthread-safety",
+    ],
+    srcs: [
+        "tests/streamworker_tests.cpp",
+        "tests/utils_tests.cpp",
+    ],
+    test_suites: [
+        "general-tests",
+    ],
+}
diff --git a/audio/aidl/common/StreamWorker.cpp b/audio/aidl/common/StreamWorker.cpp
new file mode 100644
index 0000000..9bca760
--- /dev/null
+++ b/audio/aidl/common/StreamWorker.cpp
@@ -0,0 +1,160 @@
+/*
+ * 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 <pthread.h>
+#include <sched.h>
+#include <sys/resource.h>
+
+#include "include/StreamWorker.h"
+
+namespace android::hardware::audio::common::internal {
+
+bool ThreadController::start(const std::string& name, int priority) {
+    mThreadName = name;
+    mThreadPriority = priority;
+    mWorker = std::thread(&ThreadController::workerThread, this);
+    std::unique_lock<std::mutex> lock(mWorkerLock);
+    android::base::ScopedLockAssertion lock_assertion(mWorkerLock);
+    mWorkerCv.wait(lock, [&]() {
+        android::base::ScopedLockAssertion lock_assertion(mWorkerLock);
+        return mWorkerState == WorkerState::RUNNING || !mError.empty();
+    });
+    mWorkerStateChangeRequest = false;
+    return mWorkerState == WorkerState::RUNNING;
+}
+
+void ThreadController::stop() {
+    {
+        std::lock_guard<std::mutex> lock(mWorkerLock);
+        if (mWorkerState != WorkerState::STOPPED) {
+            mWorkerState = WorkerState::STOPPED;
+            mWorkerStateChangeRequest = true;
+        }
+    }
+    if (mWorker.joinable()) {
+        mWorker.join();
+    }
+}
+
+bool ThreadController::waitForAtLeastOneCycle() {
+    WorkerState newState;
+    switchWorkerStateSync(WorkerState::RUNNING, WorkerState::PAUSE_REQUESTED, &newState);
+    if (newState != WorkerState::PAUSED) return false;
+    switchWorkerStateSync(newState, WorkerState::RESUME_REQUESTED, &newState);
+    return newState == WorkerState::RUNNING;
+}
+
+void ThreadController::switchWorkerStateSync(WorkerState oldState, WorkerState newState,
+                                             WorkerState* finalState) {
+    std::unique_lock<std::mutex> lock(mWorkerLock);
+    android::base::ScopedLockAssertion lock_assertion(mWorkerLock);
+    if (mWorkerState != oldState) {
+        if (finalState) *finalState = mWorkerState;
+        return;
+    }
+    mWorkerState = newState;
+    mWorkerStateChangeRequest = true;
+    mWorkerCv.wait(lock, [&]() {
+        android::base::ScopedLockAssertion lock_assertion(mWorkerLock);
+        return mWorkerState != newState;
+    });
+    if (finalState) *finalState = mWorkerState;
+}
+
+void ThreadController::workerThread() {
+    using Status = StreamLogic::Status;
+
+    std::string error = mLogic->init();
+    if (error.empty() && !mThreadName.empty()) {
+        std::string compliantName(mThreadName.substr(0, 15));
+        if (int errCode = pthread_setname_np(pthread_self(), compliantName.c_str()); errCode != 0) {
+            error.append("Failed to set thread name: ").append(strerror(errCode));
+        }
+    }
+    if (error.empty() && mThreadPriority != ANDROID_PRIORITY_DEFAULT) {
+        if (int result = setpriority(PRIO_PROCESS, 0, mThreadPriority); result != 0) {
+            int errCode = errno;
+            error.append("Failed to set thread priority: ").append(strerror(errCode));
+        }
+    }
+    {
+        std::lock_guard<std::mutex> lock(mWorkerLock);
+        mWorkerState = error.empty() ? WorkerState::RUNNING : WorkerState::STOPPED;
+        mError = error;
+    }
+    mWorkerCv.notify_one();
+    if (!error.empty()) return;
+
+    for (WorkerState state = WorkerState::RUNNING; state != WorkerState::STOPPED;) {
+        bool needToNotify = false;
+        if (Status status = state != WorkerState::PAUSED ? mLogic->cycle()
+                                                         : (sched_yield(), Status::CONTINUE);
+            status == Status::CONTINUE) {
+            {
+                // See https://developer.android.com/training/articles/smp#nonracing
+                android::base::ScopedLockAssertion lock_assertion(mWorkerLock);
+                if (!mWorkerStateChangeRequest.load(std::memory_order_relaxed)) continue;
+            }
+            //
+            // Pause and resume are synchronous. One worker cycle must complete
+            // before the worker indicates a state change. This is how 'mWorkerState' and
+            // 'state' interact:
+            //
+            // mWorkerState == RUNNING
+            // client sets mWorkerState := PAUSE_REQUESTED
+            // last workerCycle gets executed, state := mWorkerState := PAUSED by us
+            //   (or the workers enters the 'error' state if workerCycle fails)
+            // client gets notified about state change in any case
+            // thread is doing a busy wait while 'state == PAUSED'
+            // client sets mWorkerState := RESUME_REQUESTED
+            // state := mWorkerState (RESUME_REQUESTED)
+            // mWorkerState := RUNNING, but we don't notify the client yet
+            // first workerCycle gets executed, the code below triggers a client notification
+            //   (or if workerCycle fails, worker enters 'error' state and also notifies)
+            // state := mWorkerState (RUNNING)
+            std::lock_guard<std::mutex> lock(mWorkerLock);
+            if (state == WorkerState::RESUME_REQUESTED) {
+                needToNotify = true;
+            }
+            state = mWorkerState;
+            if (mWorkerState == WorkerState::PAUSE_REQUESTED) {
+                state = mWorkerState = WorkerState::PAUSED;
+                needToNotify = true;
+            } else if (mWorkerState == WorkerState::RESUME_REQUESTED) {
+                mWorkerState = WorkerState::RUNNING;
+            }
+        } else {
+            std::lock_guard<std::mutex> lock(mWorkerLock);
+            if (state == WorkerState::RESUME_REQUESTED ||
+                mWorkerState == WorkerState::PAUSE_REQUESTED) {
+                needToNotify = true;
+            }
+            state = mWorkerState = WorkerState::STOPPED;
+            if (status == Status::ABORT) {
+                mError = "Received ABORT from the logic cycle";
+            }
+        }
+        if (needToNotify) {
+            {
+                std::lock_guard<std::mutex> lock(mWorkerLock);
+                mWorkerStateChangeRequest = false;
+            }
+            mWorkerCv.notify_one();
+        }
+    }
+}
+
+}  // namespace android::hardware::audio::common::internal
diff --git a/audio/aidl/common/TEST_MAPPING b/audio/aidl/common/TEST_MAPPING
new file mode 100644
index 0000000..9dcf44e
--- /dev/null
+++ b/audio/aidl/common/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "libaudioaidlcommon_test"
+    }
+  ]
+}
diff --git a/audio/aidl/common/include/StreamWorker.h b/audio/aidl/common/include/StreamWorker.h
new file mode 100644
index 0000000..6260eca
--- /dev/null
+++ b/audio/aidl/common/include/StreamWorker.h
@@ -0,0 +1,153 @@
+/*
+ * 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 <atomic>
+#include <condition_variable>
+#include <mutex>
+#include <string>
+#include <thread>
+
+#include <android-base/thread_annotations.h>
+#include <system/thread_defs.h>
+
+namespace android::hardware::audio::common {
+
+class StreamLogic;
+
+namespace internal {
+
+class ThreadController {
+    enum class WorkerState { STOPPED, RUNNING, PAUSE_REQUESTED, PAUSED, RESUME_REQUESTED };
+
+  public:
+    explicit ThreadController(StreamLogic* logic) : mLogic(logic) {}
+    ~ThreadController() { stop(); }
+
+    bool start(const std::string& name, int priority);
+    void pause() { switchWorkerStateSync(WorkerState::RUNNING, WorkerState::PAUSE_REQUESTED); }
+    void resume() { switchWorkerStateSync(WorkerState::PAUSED, WorkerState::RESUME_REQUESTED); }
+    bool hasError() {
+        std::lock_guard<std::mutex> lock(mWorkerLock);
+        return !mError.empty();
+    }
+    std::string getError() {
+        std::lock_guard<std::mutex> lock(mWorkerLock);
+        return mError;
+    }
+    void stop();
+    bool waitForAtLeastOneCycle();
+
+    // Only used by unit tests.
+    void lockUnlockMutex(bool lock) NO_THREAD_SAFETY_ANALYSIS {
+        lock ? mWorkerLock.lock() : mWorkerLock.unlock();
+    }
+    std::thread::native_handle_type getThreadNativeHandle() { return mWorker.native_handle(); }
+
+  private:
+    void switchWorkerStateSync(WorkerState oldState, WorkerState newState,
+                               WorkerState* finalState = nullptr);
+    void workerThread();
+
+    StreamLogic* const mLogic;
+    std::string mThreadName;
+    int mThreadPriority = ANDROID_PRIORITY_DEFAULT;
+    std::thread mWorker;
+    std::mutex mWorkerLock;
+    std::condition_variable mWorkerCv;
+    WorkerState mWorkerState GUARDED_BY(mWorkerLock) = WorkerState::STOPPED;
+    std::string mError GUARDED_BY(mWorkerLock);
+    // The atomic lock-free variable is used to prevent priority inversions
+    // that can occur when a high priority worker tries to acquire the lock
+    // which has been taken by a lower priority control thread which in its turn
+    // got preempted. To prevent a PI under normal operating conditions, that is,
+    // when there are no errors or state changes, the worker does not attempt
+    // taking `mWorkerLock` unless `mWorkerStateChangeRequest` is set.
+    // To make sure that updates to `mWorkerState` and `mWorkerStateChangeRequest`
+    // are serialized, they are always made under a lock.
+    static_assert(std::atomic<bool>::is_always_lock_free);
+    std::atomic<bool> mWorkerStateChangeRequest GUARDED_BY(mWorkerLock) = false;
+};
+
+}  // namespace internal
+
+class StreamLogic {
+  public:
+    friend class internal::ThreadController;
+
+    virtual ~StreamLogic() = default;
+
+  protected:
+    enum class Status { ABORT, CONTINUE, EXIT };
+
+    /* Called once at the beginning of the thread loop. Must return
+     * an empty string to enter the thread loop, otherwise the thread loop
+     * exits and the worker switches into the 'error' state, setting
+     * the error to the returned value.
+     */
+    virtual std::string init() = 0;
+
+    /* Called for each thread loop unless the thread is in 'paused' state.
+     * Must return 'CONTINUE' to continue running, otherwise the thread loop
+     * exits. If the result from worker cycle is 'ABORT' then the worker switches
+     * into the 'error' state with a generic error message. It is recommended that
+     * the subclass reports any problems via logging facilities. Returning the 'EXIT'
+     * status is equivalent to calling 'stop()' method. This is just a way of
+     * of stopping the worker by its own initiative.
+     */
+    virtual Status cycle() = 0;
+};
+
+template <class LogicImpl>
+class StreamWorker : public LogicImpl {
+  public:
+    template <class... Args>
+    explicit StreamWorker(Args&&... args) : LogicImpl(std::forward<Args>(args)...), mThread(this) {}
+
+    // Methods of LogicImpl are available via inheritance.
+    // Forwarded methods of ThreadController follow.
+
+    // Note that 'priority' here is what is known as the 'nice number' in *nix systems.
+    // The nice number is used with the default scheduler. For threads that
+    // need to use a specialized scheduler (e.g. SCHED_FIFO) and set the priority within it,
+    // it is recommended to implement an appropriate configuration sequence within
+    // 'LogicImpl' or 'StreamLogic::init'.
+    bool start(const std::string& name = "", int priority = ANDROID_PRIORITY_DEFAULT) {
+        return mThread.start(name, priority);
+    }
+    void pause() { mThread.pause(); }
+    void resume() { mThread.resume(); }
+    bool hasError() { return mThread.hasError(); }
+    std::string getError() { return mThread.getError(); }
+    void stop() { return mThread.stop(); }
+    bool waitForAtLeastOneCycle() { return mThread.waitForAtLeastOneCycle(); }
+
+    // Only used by unit tests.
+    void testLockUnlockMutex(bool lock) { mThread.lockUnlockMutex(lock); }
+    std::thread::native_handle_type testGetThreadNativeHandle() {
+        return mThread.getThreadNativeHandle();
+    }
+
+  private:
+    // The ThreadController gets destroyed before LogicImpl.
+    // After the controller has been destroyed, it is guaranteed that
+    // the thread was joined, thus the 'cycle' method of LogicImpl
+    // will not be called anymore, and it is safe to destroy LogicImpl.
+    internal::ThreadController mThread;
+};
+
+}  // namespace android::hardware::audio::common
diff --git a/audio/aidl/common/include/Utils.h b/audio/aidl/common/include/Utils.h
new file mode 100644
index 0000000..1026134
--- /dev/null
+++ b/audio/aidl/common/include/Utils.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 <initializer_list>
+#include <type_traits>
+
+#include <aidl/android/media/audio/common/AudioChannelLayout.h>
+#include <aidl/android/media/audio/common/AudioFormatDescription.h>
+#include <aidl/android/media/audio/common/AudioInputFlags.h>
+#include <aidl/android/media/audio/common/AudioOutputFlags.h>
+#include <aidl/android/media/audio/common/PcmType.h>
+
+namespace android::hardware::audio::common {
+
+constexpr size_t getPcmSampleSizeInBytes(::aidl::android::media::audio::common::PcmType pcm) {
+    using ::aidl::android::media::audio::common::PcmType;
+    switch (pcm) {
+        case PcmType::UINT_8_BIT:
+            return 1;
+        case PcmType::INT_16_BIT:
+            return 2;
+        case PcmType::INT_32_BIT:
+            return 4;
+        case PcmType::FIXED_Q_8_24:
+            return 4;
+        case PcmType::FLOAT_32_BIT:
+            return 4;
+        case PcmType::INT_24_BIT:
+            return 3;
+    }
+    return 0;
+}
+
+constexpr size_t getChannelCount(
+        const ::aidl::android::media::audio::common::AudioChannelLayout& layout) {
+    using Tag = ::aidl::android::media::audio::common::AudioChannelLayout::Tag;
+    switch (layout.getTag()) {
+        case Tag::none:
+            return 0;
+        case Tag::invalid:
+            return 0;
+        case Tag::indexMask:
+            return __builtin_popcount(layout.get<Tag::indexMask>());
+        case Tag::layoutMask:
+            return __builtin_popcount(layout.get<Tag::layoutMask>());
+        case Tag::voiceMask:
+            return __builtin_popcount(layout.get<Tag::voiceMask>());
+    }
+    return 0;
+}
+
+constexpr size_t getFrameSizeInBytes(
+        const ::aidl::android::media::audio::common::AudioFormatDescription& format,
+        const ::aidl::android::media::audio::common::AudioChannelLayout& layout) {
+    if (format == ::aidl::android::media::audio::common::AudioFormatDescription{}) {
+        // Unspecified format.
+        return 0;
+    }
+    using ::aidl::android::media::audio::common::AudioFormatType;
+    if (format.type == AudioFormatType::PCM) {
+        return getPcmSampleSizeInBytes(format.pcm) * getChannelCount(layout);
+    } else if (format.type == AudioFormatType::NON_PCM) {
+        // For non-PCM formats always use the underlying PCM size. The default value for
+        // PCM is "UINT_8_BIT", thus non-encapsulated streams have the frame size of 1.
+        return getPcmSampleSizeInBytes(format.pcm);
+    }
+    // Something unexpected.
+    return 0;
+}
+
+// The helper functions defined below are only applicable to the case when an enum type
+// specifies zero-based bit positions, not bit masks themselves. This is why instantiation
+// is restricted to certain enum types.
+template <typename E>
+using is_bit_position_enum = std::integral_constant<
+        bool, std::is_same_v<E, ::aidl::android::media::audio::common::AudioInputFlags> ||
+                      std::is_same_v<E, ::aidl::android::media::audio::common::AudioOutputFlags>>;
+
+template <typename E, typename U = std::underlying_type_t<E>,
+          typename = std::enable_if_t<is_bit_position_enum<E>::value>>
+constexpr U makeBitPositionFlagMask(E flag) {
+    return 1 << static_cast<U>(flag);
+}
+
+template <typename E, typename U = std::underlying_type_t<E>,
+          typename = std::enable_if_t<is_bit_position_enum<E>::value>>
+constexpr bool isBitPositionFlagSet(U mask, E flag) {
+    return (mask & makeBitPositionFlagMask(flag)) != 0;
+}
+
+template <typename E, typename U = std::underlying_type_t<E>,
+          typename = std::enable_if_t<is_bit_position_enum<E>::value>>
+constexpr U makeBitPositionFlagMask(std::initializer_list<E> flags) {
+    U result = 0;
+    for (const auto flag : flags) {
+        result |= makeBitPositionFlagMask(flag);
+    }
+    return result;
+}
+
+}  // namespace android::hardware::audio::common
diff --git a/audio/aidl/common/tests/streamworker_tests.cpp b/audio/aidl/common/tests/streamworker_tests.cpp
new file mode 100644
index 0000000..e3e484d
--- /dev/null
+++ b/audio/aidl/common/tests/streamworker_tests.cpp
@@ -0,0 +1,278 @@
+/*
+ * 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 <pthread.h>
+#include <sched.h>
+#include <sys/resource.h>
+#include <unistd.h>
+
+#include <atomic>
+
+#include <StreamWorker.h>
+
+#include <gtest/gtest.h>
+#define LOG_TAG "StreamWorker_Test"
+#include <log/log.h>
+
+using android::hardware::audio::common::StreamLogic;
+using android::hardware::audio::common::StreamWorker;
+
+class TestWorkerLogic : public StreamLogic {
+  public:
+    struct Stream {
+        void setErrorStatus() { status = Status::ABORT; }
+        void setStopStatus() { status = Status::EXIT; }
+        std::atomic<Status> status = Status::CONTINUE;
+    };
+
+    // Use nullptr to test error reporting from the worker thread.
+    explicit TestWorkerLogic(Stream* stream) : mStream(stream) {}
+
+    size_t getWorkerCycles() const { return mWorkerCycles; }
+    int getPriority() const { return mPriority; }
+    bool hasWorkerCycleCalled() const { return mWorkerCycles != 0; }
+    bool hasNoWorkerCycleCalled(useconds_t usec) {
+        const size_t cyclesBefore = mWorkerCycles;
+        usleep(usec);
+        return mWorkerCycles == cyclesBefore;
+    }
+
+  protected:
+    // StreamLogic implementation
+    std::string init() override { return mStream != nullptr ? "" : "Expected error"; }
+    Status cycle() override {
+        mPriority = getpriority(PRIO_PROCESS, 0);
+        do {
+            mWorkerCycles++;
+        } while (mWorkerCycles == 0);
+        return mStream->status;
+    }
+
+  private:
+    Stream* const mStream;
+    std::atomic<size_t> mWorkerCycles = 0;
+    std::atomic<int> mPriority = ANDROID_PRIORITY_DEFAULT;
+};
+using TestWorker = StreamWorker<TestWorkerLogic>;
+
+// The parameter specifies whether an extra call to 'stop' is made at the end.
+class StreamWorkerInvalidTest : public testing::TestWithParam<bool> {
+  public:
+    StreamWorkerInvalidTest() : StreamWorkerInvalidTest(nullptr) {}
+    void TearDown() override {
+        if (GetParam()) {
+            worker.stop();
+        }
+    }
+
+  protected:
+    StreamWorkerInvalidTest(TestWorker::Stream* stream)
+        : testing::TestWithParam<bool>(), worker(stream) {}
+    TestWorker worker;
+};
+
+TEST_P(StreamWorkerInvalidTest, Uninitialized) {
+    EXPECT_FALSE(worker.hasWorkerCycleCalled());
+    EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerInvalidTest, UninitializedPauseIgnored) {
+    EXPECT_FALSE(worker.hasError());
+    worker.pause();
+    EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerInvalidTest, UninitializedResumeIgnored) {
+    EXPECT_FALSE(worker.hasError());
+    worker.resume();
+    EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerInvalidTest, Start) {
+    EXPECT_FALSE(worker.start());
+    EXPECT_FALSE(worker.hasWorkerCycleCalled());
+    EXPECT_TRUE(worker.hasError());
+}
+
+TEST_P(StreamWorkerInvalidTest, PauseIgnored) {
+    EXPECT_FALSE(worker.start());
+    EXPECT_TRUE(worker.hasError());
+    worker.pause();
+    EXPECT_TRUE(worker.hasError());
+}
+
+TEST_P(StreamWorkerInvalidTest, ResumeIgnored) {
+    EXPECT_FALSE(worker.start());
+    EXPECT_TRUE(worker.hasError());
+    worker.resume();
+    EXPECT_TRUE(worker.hasError());
+}
+
+INSTANTIATE_TEST_SUITE_P(StreamWorkerInvalid, StreamWorkerInvalidTest, testing::Bool());
+
+class StreamWorkerTest : public StreamWorkerInvalidTest {
+  public:
+    StreamWorkerTest() : StreamWorkerInvalidTest(&stream) {}
+
+  protected:
+    TestWorker::Stream stream;
+};
+
+static constexpr unsigned kWorkerIdleCheckTime = 50 * 1000;
+
+TEST_P(StreamWorkerTest, Uninitialized) {
+    EXPECT_FALSE(worker.hasWorkerCycleCalled());
+    EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, Start) {
+    ASSERT_TRUE(worker.start());
+    EXPECT_TRUE(worker.waitForAtLeastOneCycle());
+    EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, StartStop) {
+    ASSERT_TRUE(worker.start());
+    EXPECT_TRUE(worker.waitForAtLeastOneCycle());
+    EXPECT_FALSE(worker.hasError());
+    worker.stop();
+    EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, WorkerExit) {
+    ASSERT_TRUE(worker.start());
+    stream.setStopStatus();
+    worker.waitForAtLeastOneCycle();
+    EXPECT_FALSE(worker.hasError());
+    EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+}
+
+TEST_P(StreamWorkerTest, WorkerError) {
+    ASSERT_TRUE(worker.start());
+    stream.setErrorStatus();
+    worker.waitForAtLeastOneCycle();
+    EXPECT_TRUE(worker.hasError());
+    EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+}
+
+TEST_P(StreamWorkerTest, StopAfterError) {
+    ASSERT_TRUE(worker.start());
+    stream.setErrorStatus();
+    worker.waitForAtLeastOneCycle();
+    EXPECT_TRUE(worker.hasError());
+    EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+    worker.stop();
+    EXPECT_TRUE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, PauseResume) {
+    ASSERT_TRUE(worker.start());
+    EXPECT_TRUE(worker.waitForAtLeastOneCycle());
+    EXPECT_FALSE(worker.hasError());
+    worker.pause();
+    EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+    EXPECT_FALSE(worker.hasError());
+    const size_t workerCyclesBefore = worker.getWorkerCycles();
+    worker.resume();
+    // 'resume' is synchronous and returns after the worker has looped at least once.
+    EXPECT_GT(worker.getWorkerCycles(), workerCyclesBefore);
+    EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, StopPaused) {
+    ASSERT_TRUE(worker.start());
+    EXPECT_TRUE(worker.waitForAtLeastOneCycle());
+    EXPECT_FALSE(worker.hasError());
+    worker.pause();
+    worker.stop();
+    EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, PauseAfterErrorIgnored) {
+    ASSERT_TRUE(worker.start());
+    stream.setErrorStatus();
+    worker.waitForAtLeastOneCycle();
+    EXPECT_TRUE(worker.hasError());
+    worker.pause();
+    EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+    EXPECT_TRUE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, ResumeAfterErrorIgnored) {
+    ASSERT_TRUE(worker.start());
+    stream.setErrorStatus();
+    worker.waitForAtLeastOneCycle();
+    EXPECT_TRUE(worker.hasError());
+    worker.resume();
+    EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+    EXPECT_TRUE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, WorkerErrorOnResume) {
+    ASSERT_TRUE(worker.start());
+    EXPECT_TRUE(worker.waitForAtLeastOneCycle());
+    EXPECT_FALSE(worker.hasError());
+    worker.pause();
+    EXPECT_FALSE(worker.hasError());
+    stream.setErrorStatus();
+    EXPECT_FALSE(worker.hasError());
+    worker.resume();
+    worker.waitForAtLeastOneCycle();
+    EXPECT_TRUE(worker.hasError());
+    EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+}
+
+TEST_P(StreamWorkerTest, WaitForAtLeastOneCycle) {
+    ASSERT_TRUE(worker.start());
+    const size_t workerCyclesBefore = worker.getWorkerCycles();
+    EXPECT_TRUE(worker.waitForAtLeastOneCycle());
+    EXPECT_GT(worker.getWorkerCycles(), workerCyclesBefore);
+}
+
+TEST_P(StreamWorkerTest, WaitForAtLeastOneCycleError) {
+    ASSERT_TRUE(worker.start());
+    stream.setErrorStatus();
+    EXPECT_FALSE(worker.waitForAtLeastOneCycle());
+}
+
+TEST_P(StreamWorkerTest, MutexDoesNotBlockWorker) {
+    ASSERT_TRUE(worker.start());
+    const size_t workerCyclesBefore = worker.getWorkerCycles();
+    worker.testLockUnlockMutex(true);
+    while (worker.getWorkerCycles() == workerCyclesBefore) {
+        usleep(kWorkerIdleCheckTime);
+    }
+    worker.testLockUnlockMutex(false);
+    EXPECT_TRUE(worker.waitForAtLeastOneCycle());
+    EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, ThreadName) {
+    const std::string workerName = "TestWorker";
+    ASSERT_TRUE(worker.start(workerName)) << worker.getError();
+    char nameBuf[128];
+    ASSERT_EQ(0, pthread_getname_np(worker.testGetThreadNativeHandle(), nameBuf, sizeof(nameBuf)));
+    EXPECT_EQ(workerName, nameBuf);
+}
+
+TEST_P(StreamWorkerTest, ThreadPriority) {
+    const int priority = ANDROID_PRIORITY_LOWEST;
+    ASSERT_TRUE(worker.start("", priority)) << worker.getError();
+    EXPECT_TRUE(worker.waitForAtLeastOneCycle());
+    EXPECT_EQ(priority, worker.getPriority());
+}
+
+INSTANTIATE_TEST_SUITE_P(StreamWorker, StreamWorkerTest, testing::Bool());
diff --git a/audio/aidl/common/tests/utils_tests.cpp b/audio/aidl/common/tests/utils_tests.cpp
new file mode 100644
index 0000000..d7f1a5d
--- /dev/null
+++ b/audio/aidl/common/tests/utils_tests.cpp
@@ -0,0 +1,190 @@
+/*
+ * 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 <cstdint>
+#include <limits>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include <Utils.h>
+
+#include <gtest/gtest.h>
+#define LOG_TAG "Utils_Test"
+#include <log/log.h>
+
+using aidl::android::media::audio::common::AudioChannelLayout;
+using aidl::android::media::audio::common::AudioFormatDescription;
+using aidl::android::media::audio::common::AudioFormatType;
+using aidl::android::media::audio::common::PcmType;
+using android::hardware::audio::common::getChannelCount;
+using android::hardware::audio::common::getFrameSizeInBytes;
+using android::hardware::audio::common::getPcmSampleSizeInBytes;
+
+TEST(UtilsTest, ChannelCountOddCases) {
+    using Tag = AudioChannelLayout::Tag;
+    EXPECT_EQ(0UL, getChannelCount(AudioChannelLayout{}));
+    EXPECT_EQ(0UL, getChannelCount(AudioChannelLayout::make<Tag::invalid>(0)));
+    EXPECT_EQ(0UL, getChannelCount(AudioChannelLayout::make<Tag::invalid>(-1)));
+}
+
+TEST(UtilsTest, ChannelCountForIndexMask) {
+    using Tag = AudioChannelLayout::Tag;
+    EXPECT_EQ(0UL, getChannelCount(AudioChannelLayout::make<Tag::indexMask>(0)));
+#define VERIFY_INDEX_MASK(N)                                                                  \
+    {                                                                                         \
+        const auto l =                                                                        \
+                AudioChannelLayout::make<Tag::indexMask>(AudioChannelLayout::INDEX_MASK_##N); \
+        EXPECT_EQ(N##UL, getChannelCount(l)) << l.toString();                                 \
+    }
+    VERIFY_INDEX_MASK(1);
+    VERIFY_INDEX_MASK(2);
+    VERIFY_INDEX_MASK(3);
+    VERIFY_INDEX_MASK(4);
+    VERIFY_INDEX_MASK(5);
+    VERIFY_INDEX_MASK(6);
+    VERIFY_INDEX_MASK(7);
+    VERIFY_INDEX_MASK(8);
+    VERIFY_INDEX_MASK(9);
+    VERIFY_INDEX_MASK(10);
+    VERIFY_INDEX_MASK(11);
+    VERIFY_INDEX_MASK(12);
+    VERIFY_INDEX_MASK(13);
+    VERIFY_INDEX_MASK(14);
+    VERIFY_INDEX_MASK(15);
+    VERIFY_INDEX_MASK(16);
+    VERIFY_INDEX_MASK(17);
+    VERIFY_INDEX_MASK(18);
+    VERIFY_INDEX_MASK(19);
+    VERIFY_INDEX_MASK(20);
+    VERIFY_INDEX_MASK(21);
+    VERIFY_INDEX_MASK(22);
+    VERIFY_INDEX_MASK(23);
+    VERIFY_INDEX_MASK(24);
+#undef VERIFY_INDEX_MASK
+}
+
+TEST(UtilsTest, ChannelCountForLayoutMask) {
+    using Tag = AudioChannelLayout::Tag;
+    const std::vector<std::pair<size_t, int32_t>> kTestLayouts = {
+            std::make_pair(0UL, 0),
+            std::make_pair(1UL, AudioChannelLayout::LAYOUT_MONO),
+            std::make_pair(2UL, AudioChannelLayout::LAYOUT_STEREO),
+            std::make_pair(6UL, AudioChannelLayout::LAYOUT_5POINT1),
+            std::make_pair(8UL, AudioChannelLayout::LAYOUT_7POINT1),
+            std::make_pair(16UL, AudioChannelLayout::LAYOUT_9POINT1POINT6),
+            std::make_pair(13UL, AudioChannelLayout::LAYOUT_13POINT_360RA),
+            std::make_pair(24UL, AudioChannelLayout::LAYOUT_22POINT2),
+            std::make_pair(3UL, AudioChannelLayout::LAYOUT_STEREO_HAPTIC_A),
+            std::make_pair(4UL, AudioChannelLayout::LAYOUT_STEREO_HAPTIC_AB)};
+    for (const auto& [expected_count, layout] : kTestLayouts) {
+        const auto l = AudioChannelLayout::make<Tag::layoutMask>(layout);
+        EXPECT_EQ(expected_count, getChannelCount(l)) << l.toString();
+    }
+}
+
+TEST(UtilsTest, ChannelCountForVoiceMask) {
+    using Tag = AudioChannelLayout::Tag;
+    // clang-format off
+    const std::vector<std::pair<size_t, int32_t>> kTestLayouts = {
+            std::make_pair(0UL, 0),
+            std::make_pair(1UL, AudioChannelLayout::VOICE_UPLINK_MONO),
+            std::make_pair(1UL, AudioChannelLayout::VOICE_DNLINK_MONO),
+            std::make_pair(2UL, AudioChannelLayout::VOICE_CALL_MONO)};
+    // clang-format on
+    for (const auto& [expected_count, layout] : kTestLayouts) {
+        const auto l = AudioChannelLayout::make<Tag::voiceMask>(layout);
+        EXPECT_EQ(expected_count, getChannelCount(l)) << l.toString();
+    }
+}
+
+namespace {
+
+AudioChannelLayout make_AudioChannelLayout_Mono() {
+    return AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+            AudioChannelLayout::LAYOUT_MONO);
+}
+
+AudioChannelLayout make_AudioChannelLayout_Stereo() {
+    return AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+            AudioChannelLayout::LAYOUT_STEREO);
+}
+
+AudioFormatDescription make_AudioFormatDescription(AudioFormatType type) {
+    AudioFormatDescription result;
+    result.type = type;
+    return result;
+}
+
+AudioFormatDescription make_AudioFormatDescription(PcmType pcm) {
+    auto result = make_AudioFormatDescription(AudioFormatType::PCM);
+    result.pcm = pcm;
+    return result;
+}
+
+AudioFormatDescription make_AudioFormatDescription(const std::string& encoding) {
+    AudioFormatDescription result;
+    result.encoding = encoding;
+    return result;
+}
+
+AudioFormatDescription make_AudioFormatDescription(PcmType transport, const std::string& encoding) {
+    auto result = make_AudioFormatDescription(encoding);
+    result.pcm = transport;
+    return result;
+}
+
+}  // namespace
+
+TEST(UtilsTest, FrameSize) {
+    EXPECT_EQ(0UL, getFrameSizeInBytes(AudioFormatDescription{}, AudioChannelLayout{}));
+    EXPECT_EQ(sizeof(int16_t), getFrameSizeInBytes(make_AudioFormatDescription(PcmType::INT_16_BIT),
+                                                   make_AudioChannelLayout_Mono()));
+    EXPECT_EQ(2 * sizeof(int16_t),
+              getFrameSizeInBytes(make_AudioFormatDescription(PcmType::INT_16_BIT),
+                                  make_AudioChannelLayout_Stereo()));
+    EXPECT_EQ(sizeof(int32_t), getFrameSizeInBytes(make_AudioFormatDescription(PcmType::INT_32_BIT),
+                                                   make_AudioChannelLayout_Mono()));
+    EXPECT_EQ(2 * sizeof(int32_t),
+              getFrameSizeInBytes(make_AudioFormatDescription(PcmType::INT_32_BIT),
+                                  make_AudioChannelLayout_Stereo()));
+    EXPECT_EQ(sizeof(float), getFrameSizeInBytes(make_AudioFormatDescription(PcmType::FLOAT_32_BIT),
+                                                 make_AudioChannelLayout_Mono()));
+    EXPECT_EQ(2 * sizeof(float),
+              getFrameSizeInBytes(make_AudioFormatDescription(PcmType::FLOAT_32_BIT),
+                                  make_AudioChannelLayout_Stereo()));
+    EXPECT_EQ(sizeof(uint8_t),
+              getFrameSizeInBytes(make_AudioFormatDescription("bitstream"), AudioChannelLayout{}));
+    EXPECT_EQ(sizeof(int16_t),
+              getFrameSizeInBytes(make_AudioFormatDescription(PcmType::INT_16_BIT, "encapsulated"),
+                                  AudioChannelLayout{}));
+}
+
+TEST(UtilsTest, PcmSampleSize) {
+    EXPECT_EQ(1UL, getPcmSampleSizeInBytes(PcmType{}));
+    EXPECT_EQ(sizeof(uint8_t), getPcmSampleSizeInBytes(PcmType::UINT_8_BIT));
+    EXPECT_EQ(sizeof(int16_t), getPcmSampleSizeInBytes(PcmType::INT_16_BIT));
+    EXPECT_EQ(sizeof(int32_t), getPcmSampleSizeInBytes(PcmType::INT_32_BIT));
+    EXPECT_EQ(sizeof(int32_t), getPcmSampleSizeInBytes(PcmType::FIXED_Q_8_24));
+    EXPECT_EQ(sizeof(float), getPcmSampleSizeInBytes(PcmType::FLOAT_32_BIT));
+    EXPECT_EQ(3UL, getPcmSampleSizeInBytes(PcmType::INT_24_BIT));
+    EXPECT_EQ(0UL, getPcmSampleSizeInBytes(PcmType(-1)));
+    using PcmTypeUnderlyingType = std::underlying_type_t<PcmType>;
+    EXPECT_EQ(0UL,
+              getPcmSampleSizeInBytes(PcmType(std::numeric_limits<PcmTypeUnderlyingType>::min())));
+    EXPECT_EQ(0UL,
+              getPcmSampleSizeInBytes(PcmType(std::numeric_limits<PcmTypeUnderlyingType>::max())));
+}
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index ad1d9c7..53ed908 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -7,15 +7,28 @@
     default_applicable_licenses: ["hardware_interfaces_license"],
 }
 
-cc_library_static {
-    name: "libaudioserviceexampleimpl",
+cc_defaults {
+    name: "aidlaudioservice_defaults",
     vendor: true,
     shared_libs: [
+        "libaudioaidlcommon",
         "libbase",
         "libbinder_ndk",
+        "libcutils",
+        "libfmq",
         "libstagefright_foundation",
-        "android.media.audio.common.types-V1-ndk",
-        "android.hardware.audio.core-V1-ndk",
+        "libutils",
+        "android.hardware.common-V2-ndk",
+        "android.hardware.common.fmq-V1-ndk",
+    ],
+}
+
+cc_library_static {
+    name: "libaudioserviceexampleimpl",
+    defaults: [
+        "aidlaudioservice_defaults",
+        "latest_android_media_audio_common_types_ndk_shared",
+        "latest_android_hardware_audio_core_ndk_shared",
     ],
     export_include_dirs: ["include"],
     srcs: [
@@ -34,16 +47,60 @@
     relative_install_path: "hw",
     init_rc: ["android.hardware.audio.service-aidl.example.rc"],
     vintf_fragments: ["android.hardware.audio.service-aidl.xml"],
-    vendor: true,
-    shared_libs: [
-        "libbase",
-        "libbinder_ndk",
-        "libstagefright_foundation",
-        "android.media.audio.common.types-V1-ndk",
-        "android.hardware.audio.core-V1-ndk",
+    defaults: [
+        "aidlaudioservice_defaults",
+        "latest_android_media_audio_common_types_ndk_shared",
+        "latest_android_hardware_audio_core_ndk_shared",
     ],
     static_libs: [
         "libaudioserviceexampleimpl",
     ],
     srcs: ["main.cpp"],
 }
+
+cc_defaults {
+    name: "aidlaudioeffectservice_defaults",
+    defaults: [
+        "latest_android_media_audio_common_types_ndk_shared",
+    ],
+    vendor: true,
+    shared_libs: [
+        "libbase",
+        "libbinder_ndk",
+        "android.hardware.audio.effect-V1-ndk",
+        "libequalizer",
+    ],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wthread-safety",
+    ],
+}
+
+cc_library_static {
+    name: "libaudioeffectserviceexampleimpl",
+    defaults: ["aidlaudioeffectservice_defaults"],
+    export_include_dirs: [
+        "include",
+        "include/equalizer-impl/",
+    ],
+    srcs: [
+        "EffectFactory.cpp",
+    ],
+    visibility: [
+        ":__subpackages__",
+    ],
+}
+
+cc_binary {
+    name: "android.hardware.audio.effect.service-aidl.example",
+    relative_install_path: "hw",
+    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",
+    ],
+    srcs: ["EffectMain.cpp"],
+}
diff --git a/audio/aidl/default/Configuration.cpp b/audio/aidl/default/Configuration.cpp
index f5d679b..a3e5ff7 100644
--- a/audio/aidl/default/Configuration.cpp
+++ b/audio/aidl/default/Configuration.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <Utils.h>
 #include <aidl/android/media/audio/common/AudioChannelLayout.h>
 #include <aidl/android/media/audio/common/AudioDeviceType.h>
 #include <aidl/android/media/audio/common/AudioFormatDescription.h>
@@ -40,6 +41,7 @@
 using aidl::android::media::audio::common::AudioProfile;
 using aidl::android::media::audio::common::Int;
 using aidl::android::media::audio::common::PcmType;
+using android::hardware::audio::common::makeBitPositionFlagMask;
 
 namespace aidl::android::hardware::audio::core::internal {
 
@@ -193,7 +195,7 @@
                                  createDeviceExt(AudioDeviceType::OUT_SPEAKER, 0)));
 
         AudioPort primaryOutMix = createPort(c.nextPortId++, "primary output",
-                                             1 << static_cast<int32_t>(AudioOutputFlags::PRIMARY),
+                                             makeBitPositionFlagMask(AudioOutputFlags::PRIMARY),
                                              false, createPortMixExt(1, 1));
         primaryOutMix.profiles.insert(primaryOutMix.profiles.begin(),
                                       standardPcmAudioProfiles.begin(),
@@ -202,9 +204,9 @@
 
         AudioPort compressedOffloadOutMix =
                 createPort(c.nextPortId++, "compressed offload",
-                           1 << static_cast<int32_t>(AudioOutputFlags::DIRECT) |
-                                   1 << static_cast<int32_t>(AudioOutputFlags::COMPRESS_OFFLOAD) |
-                                   1 << static_cast<int32_t>(AudioOutputFlags::NON_BLOCKING),
+                           makeBitPositionFlagMask({AudioOutputFlags::DIRECT,
+                                                    AudioOutputFlags::COMPRESS_OFFLOAD,
+                                                    AudioOutputFlags::NON_BLOCKING}),
                            false, createPortMixExt(1, 1));
         compressedOffloadOutMix.profiles.push_back(
                 createProfile(::android::MEDIA_MIMETYPE_AUDIO_MPEG,
diff --git a/audio/aidl/default/EffectFactory.cpp b/audio/aidl/default/EffectFactory.cpp
new file mode 100644
index 0000000..ea9d470
--- /dev/null
+++ b/audio/aidl/default/EffectFactory.cpp
@@ -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.
+ */
+
+#define LOG_TAG "AHAL_EffectFactory"
+#include <android-base/logging.h>
+
+#include "effectFactory-impl/EffectFactory.h"
+#include "equalizer-impl/Equalizer.h"
+#include "visualizer-impl/Visualizer.h"
+
+using aidl::android::media::audio::common::AudioUuid;
+
+namespace aidl::android::hardware::audio::effect {
+
+Factory::Factory() {
+    // TODO: implement this with xml parser on audio_effect.xml, and filter with optional
+    // parameters.
+    Descriptor::Identity id;
+    id.type = {static_cast<int32_t>(0x0bed4300),
+               0xddd6,
+               0x11db,
+               0x8f34,
+               {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
+    id.uuid = EqualizerUUID;
+    mIdentityList.push_back(id);
+    // TODO: Add visualizer with default implementation later
+#if 0
+    id.type = {static_cast<int32_t>(0xd3467faa),
+               0xacc7,
+               0x4d34,
+               0xacaf,
+               {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
+    id.uuid = VisualizerUUID;
+    mIdentityList.push_back(id);
+#endif
+}
+
+ndk::ScopedAStatus Factory::queryEffects(const std::optional<AudioUuid>& in_type,
+                                         const std::optional<AudioUuid>& in_instance,
+                                         std::vector<Descriptor::Identity>* _aidl_return) {
+    std::copy_if(mIdentityList.begin(), mIdentityList.end(), std::back_inserter(*_aidl_return),
+                 [&](auto& desc) {
+                     return (!in_type.has_value() || in_type.value() == desc.type) &&
+                            (!in_instance.has_value() || in_instance.value() == desc.uuid);
+                 });
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Factory::createEffect(
+        const AudioUuid& in_impl_uuid,
+        std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>* _aidl_return) {
+    LOG(DEBUG) << __func__ << ": UUID " << in_impl_uuid.toString();
+    if (in_impl_uuid == EqualizerUUID) {
+        *_aidl_return = ndk::SharedRefBase::make<Equalizer>();
+    } else {
+        LOG(ERROR) << __func__ << ": UUID "
+                   << " not supported";
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Factory::destroyEffect(
+        const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_handle) {
+    if (in_handle) {
+        // TODO: b/245393900 need check the instance state with IEffect.getState before destroy.
+        return ndk::ScopedAStatus::ok();
+    } else {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+}
+
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/EffectMain.cpp b/audio/aidl/default/EffectMain.cpp
new file mode 100644
index 0000000..b30f2e7
--- /dev/null
+++ b/audio/aidl/default/EffectMain.cpp
@@ -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.
+ */
+
+#include "effectFactory-impl/EffectFactory.h"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+int main() {
+    // This is a debug implementation, always enable debug logging.
+    android::base::SetMinimumLogSeverity(::android::base::DEBUG);
+    ABinderProcess_setThreadPoolMaxThreadCount(1);
+
+    auto effectFactory =
+            ndk::SharedRefBase::make<aidl::android::hardware::audio::effect::Factory>();
+
+    std::string serviceName = std::string() + effectFactory->descriptor + "/default";
+    binder_status_t status =
+            AServiceManager_addService(effectFactory->asBinder().get(), serviceName.c_str());
+    CHECK_EQ(STATUS_OK, status);
+    LOG(DEBUG) << __func__ << ": effectFactoryName:" << serviceName;
+
+    ABinderProcess_joinThreadPool();
+    return EXIT_FAILURE;  // should not reach
+}
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index 5b4d48a..deaca49 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -20,6 +20,8 @@
 #define LOG_TAG "AHAL_Module"
 #include <android-base/logging.h>
 
+#include <Utils.h>
+#include <aidl/android/media/audio/common/AudioInputFlags.h>
 #include <aidl/android/media/audio/common/AudioOutputFlags.h>
 
 #include "core-impl/Module.h"
@@ -27,7 +29,10 @@
 
 using aidl::android::hardware::audio::common::SinkMetadata;
 using aidl::android::hardware::audio::common::SourceMetadata;
+using aidl::android::media::audio::common::AudioChannelLayout;
 using aidl::android::media::audio::common::AudioFormatDescription;
+using aidl::android::media::audio::common::AudioFormatType;
+using aidl::android::media::audio::common::AudioInputFlags;
 using aidl::android::media::audio::common::AudioIoFlags;
 using aidl::android::media::audio::common::AudioOffloadInfo;
 using aidl::android::media::audio::common::AudioOutputFlags;
@@ -36,6 +41,9 @@
 using aidl::android::media::audio::common::AudioPortExt;
 using aidl::android::media::audio::common::AudioProfile;
 using aidl::android::media::audio::common::Int;
+using aidl::android::media::audio::common::PcmType;
+using android::hardware::audio::common::getFrameSizeInBytes;
+using android::hardware::audio::common::isBitPositionFlagSet;
 
 namespace aidl::android::hardware::audio::core {
 
@@ -87,28 +95,94 @@
     erase_all_values(mPatches, std::set<int32_t>{patchId});
 }
 
-void Module::cleanUpPatches(int32_t portConfigId) {
-    auto& patches = getConfig().patches;
-    if (patches.size() == 0) return;
-    auto range = mPatches.equal_range(portConfigId);
-    for (auto it = range.first; it != range.second; ++it) {
-        auto patchIt = findById<AudioPatch>(patches, it->second);
-        if (patchIt != patches.end()) {
-            erase_if(patchIt->sourcePortConfigIds,
-                     [portConfigId](auto e) { return e == portConfigId; });
-            erase_if(patchIt->sinkPortConfigIds,
-                     [portConfigId](auto e) { return e == portConfigId; });
-        }
+ndk::ScopedAStatus Module::createStreamContext(int32_t in_portConfigId, int64_t in_bufferSizeFrames,
+                                               StreamContext* out_context) {
+    if (in_bufferSizeFrames <= 0) {
+        LOG(ERROR) << __func__ << ": non-positive buffer size " << in_bufferSizeFrames;
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
-    std::set<int32_t> erasedPatches;
-    for (size_t i = patches.size() - 1; i != 0; --i) {
-        const auto& patch = patches[i];
-        if (patch.sourcePortConfigIds.empty() || patch.sinkPortConfigIds.empty()) {
-            erasedPatches.insert(patch.id);
-            patches.erase(patches.begin() + i);
-        }
+    if (in_bufferSizeFrames < kMinimumStreamBufferSizeFrames) {
+        LOG(ERROR) << __func__ << ": insufficient buffer size " << in_bufferSizeFrames
+                   << ", must be at least " << kMinimumStreamBufferSizeFrames;
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
-    erase_all_values(mPatches, erasedPatches);
+    auto& configs = getConfig().portConfigs;
+    auto portConfigIt = findById<AudioPortConfig>(configs, in_portConfigId);
+    // Since this is a private method, it is assumed that
+    // validity of the portConfigId has already been checked.
+    const size_t frameSize =
+            getFrameSizeInBytes(portConfigIt->format.value(), portConfigIt->channelMask.value());
+    if (frameSize == 0) {
+        LOG(ERROR) << __func__ << ": could not calculate frame size for port config "
+                   << portConfigIt->toString();
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+    LOG(DEBUG) << __func__ << ": frame size " << frameSize << " bytes";
+    if (frameSize > kMaximumStreamBufferSizeBytes / in_bufferSizeFrames) {
+        LOG(ERROR) << __func__ << ": buffer size " << in_bufferSizeFrames
+                   << " frames is too large, maximum size is "
+                   << kMaximumStreamBufferSizeBytes / frameSize;
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+    const auto& flags = portConfigIt->flags.value();
+    if ((flags.getTag() == AudioIoFlags::Tag::input &&
+         !isBitPositionFlagSet(flags.get<AudioIoFlags::Tag::input>(),
+                               AudioInputFlags::MMAP_NOIRQ)) ||
+        (flags.getTag() == AudioIoFlags::Tag::output &&
+         !isBitPositionFlagSet(flags.get<AudioIoFlags::Tag::output>(),
+                               AudioOutputFlags::MMAP_NOIRQ))) {
+        StreamContext temp(
+                std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
+                std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
+                frameSize,
+                std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames));
+        if (temp.isValid()) {
+            *out_context = std::move(temp);
+        } else {
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+        }
+    } else {
+        // TODO: Implement simulation of MMAP buffer allocation
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Module::findPortIdForNewStream(int32_t in_portConfigId, AudioPort** port) {
+    auto& configs = getConfig().portConfigs;
+    auto portConfigIt = findById<AudioPortConfig>(configs, in_portConfigId);
+    if (portConfigIt == configs.end()) {
+        LOG(ERROR) << __func__ << ": existing port config id " << in_portConfigId << " not found";
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+    const int32_t portId = portConfigIt->portId;
+    // In our implementation, configs of mix ports always have unique IDs.
+    CHECK(portId != in_portConfigId);
+    auto& ports = getConfig().ports;
+    auto portIt = findById<AudioPort>(ports, portId);
+    if (portIt == ports.end()) {
+        LOG(ERROR) << __func__ << ": port id " << portId << " used by port config id "
+                   << in_portConfigId << " not found";
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+    if (mStreams.count(in_portConfigId) != 0) {
+        LOG(ERROR) << __func__ << ": port config id " << in_portConfigId
+                   << " already has a stream opened on it";
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    if (portIt->ext.getTag() != AudioPortExt::Tag::mix) {
+        LOG(ERROR) << __func__ << ": port config id " << in_portConfigId
+                   << " does not correspond to a mix port";
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+    const int32_t maxOpenStreamCount = portIt->ext.get<AudioPortExt::Tag::mix>().maxOpenStreamCount;
+    if (maxOpenStreamCount != 0 && mStreams.count(portId) >= maxOpenStreamCount) {
+        LOG(ERROR) << __func__ << ": port id " << portId
+                   << " has already reached maximum allowed opened stream count: "
+                   << maxOpenStreamCount;
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    *port = &(*portIt);
+    return ndk::ScopedAStatus::ok();
 }
 
 internal::Configuration& Module::getConfig() {
@@ -135,6 +209,28 @@
     do_insert(patch.sinkPortConfigIds);
 }
 
+void Module::updateStreamsConnectedState(const AudioPatch& oldPatch, const AudioPatch& newPatch) {
+    // Streams from the old patch need to be disconnected, streams from the new
+    // patch need to be connected. If the stream belongs to both patches, no need
+    // to update it.
+    std::set<int32_t> idsToDisconnect, idsToConnect;
+    idsToDisconnect.insert(oldPatch.sourcePortConfigIds.begin(),
+                           oldPatch.sourcePortConfigIds.end());
+    idsToDisconnect.insert(oldPatch.sinkPortConfigIds.begin(), oldPatch.sinkPortConfigIds.end());
+    idsToConnect.insert(newPatch.sourcePortConfigIds.begin(), newPatch.sourcePortConfigIds.end());
+    idsToConnect.insert(newPatch.sinkPortConfigIds.begin(), newPatch.sinkPortConfigIds.end());
+    std::for_each(idsToDisconnect.begin(), idsToDisconnect.end(), [&](const auto& portConfigId) {
+        if (idsToConnect.count(portConfigId) == 0) {
+            mStreams.setStreamIsConnected(portConfigId, false);
+        }
+    });
+    std::for_each(idsToConnect.begin(), idsToConnect.end(), [&](const auto& portConfigId) {
+        if (idsToDisconnect.count(portConfigId) == 0) {
+            mStreams.setStreamIsConnected(portConfigId, true);
+        }
+    });
+}
+
 ndk::ScopedAStatus Module::setModuleDebug(
         const ::aidl::android::hardware::audio::core::ModuleDebug& in_debug) {
     LOG(DEBUG) << __func__ << ": old flags:" << mDebug.toString()
@@ -336,98 +432,78 @@
     return ndk::ScopedAStatus::ok();
 }
 
-ndk::ScopedAStatus Module::openInputStream(int32_t in_portConfigId,
-                                           const SinkMetadata& in_sinkMetadata,
-                                           std::shared_ptr<IStreamIn>* _aidl_return) {
-    auto& configs = getConfig().portConfigs;
-    auto portConfigIt = findById<AudioPortConfig>(configs, in_portConfigId);
-    if (portConfigIt == configs.end()) {
-        LOG(ERROR) << __func__ << ": existing port config id " << in_portConfigId << " not found";
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ndk::ScopedAStatus Module::openInputStream(const OpenInputStreamArguments& in_args,
+                                           OpenInputStreamReturn* _aidl_return) {
+    LOG(DEBUG) << __func__ << ": port config id " << in_args.portConfigId << ", buffer size "
+               << in_args.bufferSizeFrames << " frames";
+    AudioPort* port = nullptr;
+    if (auto status = findPortIdForNewStream(in_args.portConfigId, &port); !status.isOk()) {
+        return status;
     }
-    const int32_t portId = portConfigIt->portId;
-    // In our implementation, configs of mix ports always have unique IDs.
-    CHECK(portId != in_portConfigId);
-    auto& ports = getConfig().ports;
-    auto portIt = findById<AudioPort>(ports, portId);
-    if (portIt == ports.end()) {
-        LOG(ERROR) << __func__ << ": port id " << portId << " used by port config id "
-                   << in_portConfigId << " not found";
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
-    }
-    if (portIt->flags.getTag() != AudioIoFlags::Tag::input ||
-        portIt->ext.getTag() != AudioPortExt::Tag::mix) {
-        LOG(ERROR) << __func__ << ": port config id " << in_portConfigId
+    if (port->flags.getTag() != AudioIoFlags::Tag::input) {
+        LOG(ERROR) << __func__ << ": port config id " << in_args.portConfigId
                    << " does not correspond to an input mix port";
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
-    if (mStreams.count(in_portConfigId) != 0) {
-        LOG(ERROR) << __func__ << ": port config id " << in_portConfigId
-                   << " already has a stream opened on it";
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    StreamContext context;
+    if (auto status = createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames, &context);
+        !status.isOk()) {
+        return status;
     }
-    const int32_t maxOpenStreamCount = portIt->ext.get<AudioPortExt::Tag::mix>().maxOpenStreamCount;
-    if (maxOpenStreamCount != 0 && mStreams.count(portId) >= maxOpenStreamCount) {
-        LOG(ERROR) << __func__ << ": port id " << portId
-                   << " has already reached maximum allowed opened stream count: "
-                   << maxOpenStreamCount;
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    context.fillDescriptor(&_aidl_return->desc);
+    auto stream = ndk::SharedRefBase::make<StreamIn>(in_args.sinkMetadata, std::move(context));
+    if (auto status = stream->init(); !status.isOk()) {
+        return status;
     }
-    auto stream = ndk::SharedRefBase::make<StreamIn>(in_sinkMetadata);
-    mStreams.insert(portId, in_portConfigId, StreamWrapper(stream));
-    *_aidl_return = std::move(stream);
+    StreamWrapper streamWrapper(stream);
+    auto patchIt = mPatches.find(in_args.portConfigId);
+    if (patchIt != mPatches.end()) {
+        streamWrapper.setStreamIsConnected(true);
+    }
+    mStreams.insert(port->id, in_args.portConfigId, std::move(streamWrapper));
+    _aidl_return->stream = std::move(stream);
     return ndk::ScopedAStatus::ok();
 }
 
-ndk::ScopedAStatus Module::openOutputStream(int32_t in_portConfigId,
-                                            const SourceMetadata& in_sourceMetadata,
-                                            const std::optional<AudioOffloadInfo>& in_offloadInfo,
-                                            std::shared_ptr<IStreamOut>* _aidl_return) {
-    auto& configs = getConfig().portConfigs;
-    auto portConfigIt = findById<AudioPortConfig>(configs, in_portConfigId);
-    if (portConfigIt == configs.end()) {
-        LOG(ERROR) << __func__ << ": existing port config id " << in_portConfigId << " not found";
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ndk::ScopedAStatus Module::openOutputStream(const OpenOutputStreamArguments& in_args,
+                                            OpenOutputStreamReturn* _aidl_return) {
+    LOG(DEBUG) << __func__ << ": port config id " << in_args.portConfigId << ", has offload info? "
+               << (in_args.offloadInfo.has_value()) << ", buffer size " << in_args.bufferSizeFrames
+               << " frames";
+    AudioPort* port = nullptr;
+    if (auto status = findPortIdForNewStream(in_args.portConfigId, &port); !status.isOk()) {
+        return status;
     }
-    const int32_t portId = portConfigIt->portId;
-    // In our implementation, configs of mix ports always have unique IDs.
-    CHECK(portId != in_portConfigId);
-    auto& ports = getConfig().ports;
-    auto portIt = findById<AudioPort>(ports, portId);
-    if (portIt == ports.end()) {
-        LOG(ERROR) << __func__ << ": port id " << portId << " used by port config id "
-                   << in_portConfigId << " not found";
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
-    }
-    if (portIt->flags.getTag() != AudioIoFlags::Tag::output ||
-        portIt->ext.getTag() != AudioPortExt::Tag::mix) {
-        LOG(ERROR) << __func__ << ": port config id " << in_portConfigId
+    if (port->flags.getTag() != AudioIoFlags::Tag::output) {
+        LOG(ERROR) << __func__ << ": port config id " << in_args.portConfigId
                    << " does not correspond to an output mix port";
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
-    if (portConfigIt->flags.has_value() &&
-        ((portConfigIt->flags.value().get<AudioIoFlags::Tag::output>() &
-          1 << static_cast<int32_t>(AudioOutputFlags::COMPRESS_OFFLOAD)) != 0) &&
-        !in_offloadInfo.has_value()) {
-        LOG(ERROR) << __func__ << ": port config id " << in_portConfigId
+    const bool isOffload = isBitPositionFlagSet(port->flags.get<AudioIoFlags::Tag::output>(),
+                                                AudioOutputFlags::COMPRESS_OFFLOAD);
+    if (isOffload && !in_args.offloadInfo.has_value()) {
+        LOG(ERROR) << __func__ << ": port id " << port->id
                    << " has COMPRESS_OFFLOAD flag set, requires offload info";
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
-    if (mStreams.count(in_portConfigId) != 0) {
-        LOG(ERROR) << __func__ << ": port config id " << in_portConfigId
-                   << " already has a stream opened on it";
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    StreamContext context;
+    if (auto status = createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames, &context);
+        !status.isOk()) {
+        return status;
     }
-    const int32_t maxOpenStreamCount = portIt->ext.get<AudioPortExt::Tag::mix>().maxOpenStreamCount;
-    if (maxOpenStreamCount != 0 && mStreams.count(portId) >= maxOpenStreamCount) {
-        LOG(ERROR) << __func__ << ": port id " << portId
-                   << " has already reached maximum allowed opened stream count: "
-                   << maxOpenStreamCount;
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    context.fillDescriptor(&_aidl_return->desc);
+    auto stream = ndk::SharedRefBase::make<StreamOut>(in_args.sourceMetadata, std::move(context),
+                                                      in_args.offloadInfo);
+    if (auto status = stream->init(); !status.isOk()) {
+        return status;
     }
-    auto stream = ndk::SharedRefBase::make<StreamOut>(in_sourceMetadata, in_offloadInfo);
-    mStreams.insert(portId, in_portConfigId, StreamWrapper(stream));
-    *_aidl_return = std::move(stream);
+    StreamWrapper streamWrapper(stream);
+    auto patchIt = mPatches.find(in_args.portConfigId);
+    if (patchIt != mPatches.end()) {
+        streamWrapper.setStreamIsConnected(true);
+    }
+    mStreams.insert(port->id, in_args.portConfigId, std::move(streamWrapper));
+    _aidl_return->stream = std::move(stream);
     return ndk::ScopedAStatus::ok();
 }
 
@@ -512,15 +588,24 @@
         }
     }
     *_aidl_return = in_requested;
+    _aidl_return->minimumStreamBufferSizeFrames = kMinimumStreamBufferSizeFrames;
+    _aidl_return->latenciesMs.clear();
+    _aidl_return->latenciesMs.insert(_aidl_return->latenciesMs.end(),
+                                     _aidl_return->sinkPortConfigIds.size(), kLatencyMs);
+    AudioPatch oldPatch{};
     if (existing == patches.end()) {
         _aidl_return->id = getConfig().nextPatchId++;
         patches.push_back(*_aidl_return);
         existing = patches.begin() + (patches.size() - 1);
     } else {
+        oldPatch = *existing;
         *existing = *_aidl_return;
     }
     registerPatch(*existing);
-    LOG(DEBUG) << __func__ << ": created or updated patch id " << _aidl_return->id;
+    updateStreamsConnectedState(oldPatch, *_aidl_return);
+
+    LOG(DEBUG) << __func__ << ": " << (oldPatch.id == 0 ? "created" : "updated") << " patch "
+               << _aidl_return->toString();
     return ndk::ScopedAStatus::ok();
 }
 
@@ -655,6 +740,7 @@
     auto patchIt = findById<AudioPatch>(patches, in_patchId);
     if (patchIt != patches.end()) {
         cleanUpPatch(patchIt->id);
+        updateStreamsConnectedState(*patchIt, AudioPatch{});
         patches.erase(patchIt);
         LOG(DEBUG) << __func__ << ": erased patch " << in_patchId;
         return ndk::ScopedAStatus::ok();
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index e16b2c6..312df72 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -15,9 +15,10 @@
  */
 
 #define LOG_TAG "AHAL_Stream"
-#define LOG_NDEBUG 0
 #include <android-base/logging.h>
+#include <utils/SystemClock.h>
 
+#include "core-impl/Module.h"
 #include "core-impl/Stream.h"
 
 using aidl::android::hardware::audio::common::SinkMetadata;
@@ -26,11 +27,199 @@
 
 namespace aidl::android::hardware::audio::core {
 
-StreamIn::StreamIn(const SinkMetadata& sinkMetadata) : mMetadata(sinkMetadata) {}
+void StreamContext::fillDescriptor(StreamDescriptor* desc) {
+    if (mCommandMQ) {
+        desc->command = mCommandMQ->dupeDesc();
+    }
+    if (mReplyMQ) {
+        desc->reply = mReplyMQ->dupeDesc();
+    }
+    if (mDataMQ) {
+        desc->frameSizeBytes = mFrameSize;
+        desc->bufferSizeFrames =
+                mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize() / mFrameSize;
+        desc->audio.set<StreamDescriptor::AudioBuffer::Tag::fmq>(mDataMQ->dupeDesc());
+    }
+}
 
-ndk::ScopedAStatus StreamIn::close() {
+bool StreamContext::isValid() const {
+    if (mCommandMQ && !mCommandMQ->isValid()) {
+        LOG(ERROR) << "command FMQ is invalid";
+        return false;
+    }
+    if (mReplyMQ && !mReplyMQ->isValid()) {
+        LOG(ERROR) << "reply FMQ is invalid";
+        return false;
+    }
+    if (mFrameSize == 0) {
+        LOG(ERROR) << "frame size is not set";
+        return false;
+    }
+    if (mDataMQ && !mDataMQ->isValid()) {
+        LOG(ERROR) << "data FMQ is invalid";
+        return false;
+    }
+    return true;
+}
+
+void StreamContext::reset() {
+    mCommandMQ.reset();
+    mReplyMQ.reset();
+    mDataMQ.reset();
+}
+
+std::string StreamWorkerCommonLogic::init() {
+    if (mCommandMQ == nullptr) return "Command MQ is null";
+    if (mReplyMQ == nullptr) return "Reply MQ is null";
+    if (mDataMQ == nullptr) return "Data MQ is null";
+    if (sizeof(decltype(mDataBuffer)::element_type) != mDataMQ->getQuantumSize()) {
+        return "Unexpected Data MQ quantum size: " + std::to_string(mDataMQ->getQuantumSize());
+    }
+    mDataBufferSize = mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize();
+    mDataBuffer.reset(new (std::nothrow) int8_t[mDataBufferSize]);
+    if (mDataBuffer == nullptr) {
+        return "Failed to allocate data buffer for element count " +
+               std::to_string(mDataMQ->getQuantumCount()) +
+               ", size in bytes: " + std::to_string(mDataBufferSize);
+    }
+    return "";
+}
+
+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";
+        return Status::ABORT;
+    }
+    StreamDescriptor::Reply reply{};
+    if (command.code == StreamContext::COMMAND_EXIT &&
+        command.fmqByteCount == mInternalCommandCookie) {
+        LOG(DEBUG) << __func__ << ": received EXIT command";
+        // This is an internal command, no need to reply.
+        return Status::EXIT;
+    } else if (command.code == StreamDescriptor::COMMAND_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;
+            }
+        } else {
+            LOG(WARNING) << __func__ << ": writing of " << byteCount
+                         << " bytes of data to MQ failed";
+            reply.status = STATUS_NOT_ENOUGH_DATA;
+        }
+        reply.latencyMs = Module::kLatencyMs;
+    } else {
+        LOG(WARNING) << __func__ << ": invalid command (" << command.toString()
+                     << ") or count: " << command.fmqByteCount;
+        reply.status = STATUS_BAD_VALUE;
+    }
+    LOG(DEBUG) << __func__ << ": writing reply " << reply.toString();
+    if (!mReplyMQ->writeBlocking(&reply, 1)) {
+        LOG(ERROR) << __func__ << ": writing of reply " << reply.toString() << " to MQ failed";
+        return Status::ABORT;
+    }
+    return Status::CONTINUE;
+}
+
+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";
+        return Status::ABORT;
+    }
+    StreamDescriptor::Reply reply{};
+    if (command.code == StreamContext::COMMAND_EXIT &&
+        command.fmqByteCount == mInternalCommandCookie) {
+        LOG(DEBUG) << __func__ << ": received EXIT command";
+        // This is an internal command, no need to reply.
+        return Status::EXIT;
+    } else if (command.code == StreamDescriptor::COMMAND_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;
+            }
+            usleep(3000);  // Simulate a blocking call into the driver.
+        } else {
+            LOG(WARNING) << __func__ << ": reading of " << byteCount
+                         << " bytes of data from MQ failed";
+            reply.status = STATUS_NOT_ENOUGH_DATA;
+        }
+        reply.latencyMs = Module::kLatencyMs;
+    } else {
+        LOG(WARNING) << __func__ << ": invalid command (" << command.toString()
+                     << ") or count: " << command.fmqByteCount;
+        reply.status = STATUS_BAD_VALUE;
+    }
+    LOG(DEBUG) << __func__ << ": writing reply " << reply.toString();
+    if (!mReplyMQ->writeBlocking(&reply, 1)) {
+        LOG(ERROR) << __func__ << ": writing of reply " << reply.toString() << " to MQ failed";
+        return Status::ABORT;
+    }
+    return Status::CONTINUE;
+}
+
+template <class Metadata, class StreamWorker>
+StreamCommon<Metadata, StreamWorker>::~StreamCommon() {
+    if (!mIsClosed) {
+        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.
+    }
+}
+
+template <class Metadata, class StreamWorker>
+ndk::ScopedAStatus StreamCommon<Metadata, StreamWorker>::close() {
     LOG(DEBUG) << __func__;
     if (!mIsClosed) {
+        stopWorker();
+        LOG(DEBUG) << __func__ << ": joining the worker thread...";
+        mWorker.stop();
+        LOG(DEBUG) << __func__ << ": worker thread joined";
+        mContext.reset();
         mIsClosed = true;
         return ndk::ScopedAStatus::ok();
     } else {
@@ -39,38 +228,44 @@
     }
 }
 
-ndk::ScopedAStatus StreamIn::updateMetadata(const SinkMetadata& in_sinkMetadata) {
+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...";
+        StreamDescriptor::Command cmd;
+        cmd.code = 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.
+        if (!commandMQ->writeBlocking(&cmd, 1)) {
+            LOG(ERROR) << __func__ << ": failed to write exit command to the MQ";
+        }
+        LOG(DEBUG) << __func__ << ": done";
+    }
+}
+
+template <class Metadata, class StreamWorker>
+ndk::ScopedAStatus StreamCommon<Metadata, StreamWorker>::updateMetadata(const Metadata& metadata) {
     LOG(DEBUG) << __func__;
     if (!mIsClosed) {
-        mMetadata = in_sinkMetadata;
+        mMetadata = metadata;
         return ndk::ScopedAStatus::ok();
     }
     LOG(ERROR) << __func__ << ": stream was closed";
     return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
 }
 
-StreamOut::StreamOut(const SourceMetadata& sourceMetadata,
+StreamIn::StreamIn(const SinkMetadata& sinkMetadata, StreamContext context)
+    : StreamCommon<SinkMetadata, StreamInWorker>(sinkMetadata, std::move(context)) {
+    LOG(DEBUG) << __func__;
+}
+
+StreamOut::StreamOut(const SourceMetadata& sourceMetadata, StreamContext context,
                      const std::optional<AudioOffloadInfo>& offloadInfo)
-    : mMetadata(sourceMetadata), mOffloadInfo(offloadInfo) {}
-
-ndk::ScopedAStatus StreamOut::close() {
+    : StreamCommon<SourceMetadata, StreamOutWorker>(sourceMetadata, std::move(context)),
+      mOffloadInfo(offloadInfo) {
     LOG(DEBUG) << __func__;
-    if (!mIsClosed) {
-        mIsClosed = true;
-        return ndk::ScopedAStatus::ok();
-    }
-    LOG(ERROR) << __func__ << ": stream was already closed";
-    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-}
-
-ndk::ScopedAStatus StreamOut::updateMetadata(const SourceMetadata& in_sourceMetadata) {
-    LOG(DEBUG) << __func__;
-    if (!mIsClosed) {
-        mMetadata = in_sourceMetadata;
-        return ndk::ScopedAStatus::ok();
-    }
-    LOG(ERROR) << __func__ << ": stream was closed";
-    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
 }
 
 }  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/android.hardware.audio.effect.service-aidl.example.rc b/audio/aidl/default/android.hardware.audio.effect.service-aidl.example.rc
new file mode 100644
index 0000000..68bbf5b
--- /dev/null
+++ b/audio/aidl/default/android.hardware.audio.effect.service-aidl.example.rc
@@ -0,0 +1,9 @@
+service vendor.audio-effect-hal-aidl /vendor/bin/hw/android.hardware.audio.effect.service-aidl.example
+    class hal
+    user audioserver
+    # media gid needed for /dev/fm (radio) and for /data/misc/media (tee)
+    group audio media
+    capabilities BLOCK_SUSPEND
+    ioprio rt 4
+    task_profiles ProcessCapacityHigh HighPerformance
+    onrestart restart audioserver
diff --git a/audio/aidl/default/android.hardware.audio.effect.service-aidl.xml b/audio/aidl/default/android.hardware.audio.effect.service-aidl.xml
new file mode 100644
index 0000000..fdc53a3
--- /dev/null
+++ b/audio/aidl/default/android.hardware.audio.effect.service-aidl.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+  <hal format="aidl">
+    <name>android.hardware.audio.effect</name>
+    <version>1</version>
+    <fqname>IFactory/default</fqname>
+  </hal>
+</manifest>
diff --git a/audio/aidl/default/equalizer/Android.bp b/audio/aidl/default/equalizer/Android.bp
new file mode 100644
index 0000000..b842149
--- /dev/null
+++ b/audio/aidl/default/equalizer/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_library_shared {
+    name: "libequalizer",
+    vendor: true,
+    shared_libs: [
+        "libbase",
+        "libbinder_ndk",
+        "libstagefright_foundation",
+    ],
+    defaults: [
+        "latest_android_media_audio_common_types_ndk_shared",
+        "latest_android_hardware_audio_effect_ndk_shared",
+    ],
+    include_dirs: ["hardware/interfaces/audio/aidl/default/include/equalizer-impl"],
+    srcs: [
+        "Equalizer.cpp",
+    ],
+    visibility: [
+        "//hardware/interfaces/audio/aidl/default",
+    ],
+}
diff --git a/audio/aidl/default/equalizer/Equalizer.cpp b/audio/aidl/default/equalizer/Equalizer.cpp
new file mode 100644
index 0000000..dae3ab7
--- /dev/null
+++ b/audio/aidl/default/equalizer/Equalizer.cpp
@@ -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.
+ */
+
+#define LOG_TAG "AHAL_Equalizer"
+#include <android-base/logging.h>
+
+#include "Equalizer.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+Equalizer::Equalizer() {
+    // Implementation UUID
+    mDesc.common.id.uuid = {static_cast<int32_t>(0xce772f20),
+                            0x847d,
+                            0x11df,
+                            0xbb17,
+                            {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
+}
+
+ndk::ScopedAStatus Equalizer::open() {
+    LOG(DEBUG) << __func__;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Equalizer::close() {
+    LOG(DEBUG) << __func__;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Equalizer::getDescriptor(Descriptor* _aidl_return) {
+    LOG(DEBUG) << __func__ << "descriptor " << mDesc.toString();
+    *_aidl_return = mDesc;
+    return ndk::ScopedAStatus::ok();
+}
+
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index 81a02ba..61516b2 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -28,6 +28,11 @@
 namespace aidl::android::hardware::audio::core {
 
 class Module : public BnModule {
+  public:
+    // This value is used for all AudioPatches and reported by all streams.
+    static constexpr int32_t kLatencyMs = 10;
+
+  private:
     ndk::ScopedAStatus setModuleDebug(
             const ::aidl::android::hardware::audio::core::ModuleDebug& in_debug) override;
     ndk::ScopedAStatus connectExternalDevice(
@@ -48,15 +53,15 @@
             int32_t in_portId,
             std::vector<::aidl::android::hardware::audio::core::AudioRoute>* _aidl_return) override;
     ndk::ScopedAStatus openInputStream(
-            int32_t in_portConfigId,
-            const ::aidl::android::hardware::audio::common::SinkMetadata& in_sinkMetadata,
-            std::shared_ptr<IStreamIn>* _aidl_return) override;
+            const ::aidl::android::hardware::audio::core::IModule::OpenInputStreamArguments&
+                    in_args,
+            ::aidl::android::hardware::audio::core::IModule::OpenInputStreamReturn* _aidl_return)
+            override;
     ndk::ScopedAStatus openOutputStream(
-            int32_t in_portConfigId,
-            const ::aidl::android::hardware::audio::common::SourceMetadata& in_sourceMetadata,
-            const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
-                    in_offloadInfo,
-            std::shared_ptr<IStreamOut>* _aidl_return) override;
+            const ::aidl::android::hardware::audio::core::IModule::OpenOutputStreamArguments&
+                    in_args,
+            ::aidl::android::hardware::audio::core::IModule::OpenOutputStreamReturn* _aidl_return)
+            override;
     ndk::ScopedAStatus setAudioPatch(const AudioPatch& in_requested,
                                      AudioPatch* _aidl_return) override;
     ndk::ScopedAStatus setAudioPortConfig(
@@ -66,11 +71,20 @@
     ndk::ScopedAStatus resetAudioPatch(int32_t in_patchId) override;
     ndk::ScopedAStatus resetAudioPortConfig(int32_t in_portConfigId) override;
 
-  private:
     void cleanUpPatch(int32_t patchId);
-    void cleanUpPatches(int32_t portConfigId);
+    ndk::ScopedAStatus createStreamContext(
+            int32_t in_portConfigId, int64_t in_bufferSizeFrames,
+            ::aidl::android::hardware::audio::core::StreamContext* out_context);
+    ndk::ScopedAStatus findPortIdForNewStream(
+            int32_t in_portConfigId, ::aidl::android::media::audio::common::AudioPort** port);
     internal::Configuration& getConfig();
     void registerPatch(const AudioPatch& patch);
+    void updateStreamsConnectedState(const AudioPatch& oldPatch, const AudioPatch& newPatch);
+
+    // This value is used for all AudioPatches.
+    static constexpr int32_t kMinimumStreamBufferSizeFrames = 16;
+    // The maximum stream buffer size is 1 GiB = 2 ** 30 bytes;
+    static constexpr int32_t kMaximumStreamBufferSizeBytes = 1 << 30;
 
     std::unique_ptr<internal::Configuration> mConfig;
     ModuleDebug mDebug;
diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h
index 87104dd..488edf1 100644
--- a/audio/aidl/default/include/core-impl/Stream.h
+++ b/audio/aidl/default/include/core-impl/Stream.h
@@ -16,50 +16,203 @@
 
 #pragma once
 
+#include <atomic>
+#include <cstdlib>
 #include <map>
+#include <memory>
 #include <optional>
 #include <variant>
 
+#include <StreamWorker.h>
 #include <aidl/android/hardware/audio/common/SinkMetadata.h>
 #include <aidl/android/hardware/audio/common/SourceMetadata.h>
 #include <aidl/android/hardware/audio/core/BnStreamIn.h>
 #include <aidl/android/hardware/audio/core/BnStreamOut.h>
+#include <aidl/android/hardware/audio/core/StreamDescriptor.h>
 #include <aidl/android/media/audio/common/AudioOffloadInfo.h>
+#include <fmq/AidlMessageQueue.h>
+#include <system/thread_defs.h>
 
 #include "core-impl/utils.h"
 
 namespace aidl::android::hardware::audio::core {
 
-class StreamIn : public BnStreamIn {
-    ndk::ScopedAStatus close() override;
-    ndk::ScopedAStatus updateMetadata(
-            const ::aidl::android::hardware::audio::common::SinkMetadata& in_sinkMetadata) override;
-
+// This class is similar to StreamDescriptor, but unlike
+// the descriptor, it actually owns the objects implementing
+// data exchange: FMQs etc, whereas StreamDescriptor only
+// contains their descriptors.
+class StreamContext {
   public:
-    explicit StreamIn(const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata);
-    bool isClosed() const { return mIsClosed; }
+    typedef ::android::AidlMessageQueue<
+            StreamDescriptor::Command,
+            ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
+            CommandMQ;
+    typedef ::android::AidlMessageQueue<
+            StreamDescriptor::Reply, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
+            ReplyMQ;
+    typedef ::android::AidlMessageQueue<
+            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;
+
+    StreamContext() = default;
+    StreamContext(std::unique_ptr<CommandMQ> commandMQ, std::unique_ptr<ReplyMQ> replyMQ,
+                  size_t frameSize, std::unique_ptr<DataMQ> dataMQ)
+        : mCommandMQ(std::move(commandMQ)),
+          mInternalCommandCookie(std::rand()),
+          mReplyMQ(std::move(replyMQ)),
+          mFrameSize(frameSize),
+          mDataMQ(std::move(dataMQ)) {}
+    StreamContext(StreamContext&& other)
+        : mCommandMQ(std::move(other.mCommandMQ)),
+          mInternalCommandCookie(other.mInternalCommandCookie),
+          mReplyMQ(std::move(other.mReplyMQ)),
+          mFrameSize(other.mFrameSize),
+          mDataMQ(std::move(other.mDataMQ)) {}
+    StreamContext& operator=(StreamContext&& other) {
+        mCommandMQ = std::move(other.mCommandMQ);
+        mInternalCommandCookie = other.mInternalCommandCookie;
+        mReplyMQ = std::move(other.mReplyMQ);
+        mFrameSize = other.mFrameSize;
+        mDataMQ = std::move(other.mDataMQ);
+        return *this;
+    }
+
+    void fillDescriptor(StreamDescriptor* desc);
+    CommandMQ* getCommandMQ() const { return mCommandMQ.get(); }
+    DataMQ* getDataMQ() const { return mDataMQ.get(); }
+    size_t getFrameSize() const { return mFrameSize; }
+    int getInternalCommandCookie() const { return mInternalCommandCookie; }
+    ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
+    bool isValid() const;
+    void reset();
 
   private:
-    ::aidl::android::hardware::audio::common::SinkMetadata mMetadata;
-    bool mIsClosed = false;
+    std::unique_ptr<CommandMQ> mCommandMQ;
+    int mInternalCommandCookie;  // The value used to confirm that the command was posted internally
+    std::unique_ptr<ReplyMQ> mReplyMQ;
+    size_t mFrameSize;
+    std::unique_ptr<DataMQ> mDataMQ;
 };
 
-class StreamOut : public BnStreamOut {
-    ndk::ScopedAStatus close() override;
+class StreamWorkerCommonLogic : public ::android::hardware::audio::common::StreamLogic {
+  public:
+    void setIsConnected(bool connected) { mIsConnected = connected; }
+
+  protected:
+    explicit StreamWorkerCommonLogic(const StreamContext& context)
+        : mInternalCommandCookie(context.getInternalCommandCookie()),
+          mFrameSize(context.getFrameSize()),
+          mCommandMQ(context.getCommandMQ()),
+          mReplyMQ(context.getReplyMQ()),
+          mDataMQ(context.getDataMQ()) {}
+    std::string init() override;
+
+    // Used both by the main and worker threads.
+    std::atomic<bool> mIsConnected = false;
+    // All fields are used on the worker thread only.
+    const int mInternalCommandCookie;
+    const size_t mFrameSize;
+    StreamContext::CommandMQ* mCommandMQ;
+    StreamContext::ReplyMQ* mReplyMQ;
+    StreamContext::DataMQ* mDataMQ;
+    // We use an array and the "size" field instead of a vector to be able to detect
+    // memory allocation issues.
+    std::unique_ptr<int8_t[]> mDataBuffer;
+    size_t mDataBufferSize;
+    long mFrameCount = 0;
+};
+
+class StreamInWorkerLogic : public StreamWorkerCommonLogic {
+  public:
+    static const std::string kThreadName;
+    explicit StreamInWorkerLogic(const StreamContext& context) : StreamWorkerCommonLogic(context) {}
+
+  protected:
+    Status cycle() override;
+};
+using StreamInWorker = ::android::hardware::audio::common::StreamWorker<StreamInWorkerLogic>;
+
+class StreamOutWorkerLogic : public StreamWorkerCommonLogic {
+  public:
+    static const std::string kThreadName;
+    explicit StreamOutWorkerLogic(const StreamContext& context)
+        : StreamWorkerCommonLogic(context) {}
+
+  protected:
+    Status cycle() override;
+};
+using StreamOutWorker = ::android::hardware::audio::common::StreamWorker<StreamOutWorkerLogic>;
+
+template <class Metadata, class StreamWorker>
+class StreamCommon {
+  public:
+    ndk::ScopedAStatus close();
+    ndk::ScopedAStatus init() {
+        return mWorker.start(StreamWorker::kThreadName, ANDROID_PRIORITY_AUDIO)
+                       ? ndk::ScopedAStatus::ok()
+                       : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    bool isClosed() const { return mIsClosed; }
+    void setIsConnected(bool connected) { mWorker.setIsConnected(connected); }
+    ndk::ScopedAStatus updateMetadata(const Metadata& metadata);
+
+  protected:
+    StreamCommon(const Metadata& metadata, StreamContext context)
+        : mMetadata(metadata), mContext(std::move(context)), mWorker(mContext) {}
+    ~StreamCommon();
+    void stopWorker();
+
+    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
+    : public StreamCommon<::aidl::android::hardware::audio::common::SinkMetadata, StreamInWorker>,
+      public BnStreamIn {
+    ndk::ScopedAStatus close() override {
+        return StreamCommon<::aidl::android::hardware::audio::common::SinkMetadata,
+                            StreamInWorker>::close();
+    }
+    ndk::ScopedAStatus updateMetadata(const ::aidl::android::hardware::audio::common::SinkMetadata&
+                                              in_sinkMetadata) override {
+        return StreamCommon<::aidl::android::hardware::audio::common::SinkMetadata,
+                            StreamInWorker>::updateMetadata(in_sinkMetadata);
+    }
+
+  public:
+    StreamIn(const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
+             StreamContext context);
+};
+
+class StreamOut : public StreamCommon<::aidl::android::hardware::audio::common::SourceMetadata,
+                                      StreamOutWorker>,
+                  public BnStreamOut {
+    ndk::ScopedAStatus close() override {
+        return StreamCommon<::aidl::android::hardware::audio::common::SourceMetadata,
+                            StreamOutWorker>::close();
+    }
     ndk::ScopedAStatus updateMetadata(
             const ::aidl::android::hardware::audio::common::SourceMetadata& in_sourceMetadata)
-            override;
+            override {
+        return StreamCommon<::aidl::android::hardware::audio::common::SourceMetadata,
+                            StreamOutWorker>::updateMetadata(in_sourceMetadata);
+    }
 
   public:
     StreamOut(const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
+              StreamContext context,
               const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
                       offloadInfo);
-    bool isClosed() const { return mIsClosed; }
 
   private:
-    ::aidl::android::hardware::audio::common::SourceMetadata mMetadata;
     std::optional<::aidl::android::media::audio::common::AudioOffloadInfo> mOffloadInfo;
-    bool mIsClosed = false;
 };
 
 class StreamWrapper {
@@ -74,6 +227,14 @@
                 },
                 mStream);
     }
+    void setStreamIsConnected(bool connected) {
+        std::visit(
+                [&](auto&& ws) {
+                    auto s = ws.lock();
+                    if (s) s->setIsConnected(connected);
+                },
+                mStream);
+    }
 
   private:
     std::variant<std::weak_ptr<StreamIn>, std::weak_ptr<StreamOut>> mStream;
@@ -91,7 +252,12 @@
     }
     void insert(int32_t portId, int32_t portConfigId, StreamWrapper sw) {
         mStreams.insert(std::pair{portConfigId, sw});
-        mStreams.insert(std::pair{portId, sw});
+        mStreams.insert(std::pair{portId, std::move(sw)});
+    }
+    void setStreamIsConnected(int32_t portConfigId, bool connected) {
+        if (auto it = mStreams.find(portConfigId); it != mStreams.end()) {
+            it->second.setStreamIsConnected(connected);
+        }
     }
 
   private:
diff --git a/audio/aidl/default/include/effectFactory-impl/EffectFactory.h b/audio/aidl/default/include/effectFactory-impl/EffectFactory.h
new file mode 100644
index 0000000..8da5525
--- /dev/null
+++ b/audio/aidl/default/include/effectFactory-impl/EffectFactory.h
@@ -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.
+ */
+
+#pragma once
+
+#include <optional>
+#include <vector>
+
+#include <aidl/android/hardware/audio/effect/BnFactory.h>
+
+namespace aidl::android::hardware::audio::effect {
+
+class Factory : public BnFactory {
+  public:
+    Factory();
+    /**
+     * @brief Get identity of all effects supported by the device, with the optional filter by type
+     * and/or by instance UUID.
+     *
+     * @param in_type Type UUID.
+     * @param in_instance Instance 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,
+            std::vector<Descriptor::Identity>* out_descriptor) override;
+
+    /**
+     * @brief Create an effect instance for a certain implementation (identified by UUID).
+     *
+     * @param in_impl_uuid Effect implementation UUID.
+     * @param _aidl_return A pointer to created effect instance.
+     * @return ndk::ScopedAStatus
+     */
+    ndk::ScopedAStatus createEffect(
+            const ::aidl::android::media::audio::common::AudioUuid& in_impl_uuid,
+            std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>* _aidl_return)
+            override;
+
+    /**
+     * @brief Destroy an effect instance.
+     *
+     * @param in_handle Effect instance handle.
+     * @return ndk::ScopedAStatus
+     */
+    ndk::ScopedAStatus destroyEffect(
+            const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_handle)
+            override;
+
+  private:
+    // List of effect descriptors supported by the devices.
+    std::vector<Descriptor::Identity> mIdentityList;
+};
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/equalizer-impl/Equalizer.h b/audio/aidl/default/include/equalizer-impl/Equalizer.h
new file mode 100644
index 0000000..44b1d6d
--- /dev/null
+++ b/audio/aidl/default/include/equalizer-impl/Equalizer.h
@@ -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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/audio/effect/BnEffect.h>
+#include <cstdlib>
+
+namespace aidl::android::hardware::audio::effect {
+
+// Equalizer implementation UUID.
+static const ::aidl::android::media::audio::common::AudioUuid EqualizerUUID = {
+        static_cast<int32_t>(0x0bed4300),
+        0xddd6,
+        0x11db,
+        0x8f34,
+        {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
+
+class Equalizer : public BnEffect {
+  public:
+    Equalizer();
+    ndk::ScopedAStatus open() override;
+    ndk::ScopedAStatus close() override;
+    ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
+
+  private:
+    // Effect descriptor.
+    Descriptor mDesc = {.common.id.type = EqualizerUUID};
+};
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/visualizer-impl/Visualizer.h b/audio/aidl/default/include/visualizer-impl/Visualizer.h
new file mode 100644
index 0000000..4b82dd0
--- /dev/null
+++ b/audio/aidl/default/include/visualizer-impl/Visualizer.h
@@ -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.
+ */
+
+#pragma once
+
+#include <cstdlib>
+
+namespace aidl::android::hardware::audio::effect {
+
+// Visualizer implementation UUID.
+static const ::aidl::android::media::audio::common::AudioUuid VisualizerUUID = {
+        static_cast<int32_t>(0x1d4033c0),
+        0x8557,
+        0x11df,
+        0x9f2d,
+        {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
+
+}  // namespace aidl::android::hardware::audio::effect
\ No newline at end of file
diff --git a/audio/aidl/default/main.cpp b/audio/aidl/default/main.cpp
index aeb9983..15874a0 100644
--- a/audio/aidl/default/main.cpp
+++ b/audio/aidl/default/main.cpp
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+#include <cstdlib>
+#include <ctime>
+
 #include "core-impl/Config.h"
 #include "core-impl/Module.h"
 
@@ -25,6 +28,9 @@
 using aidl::android::hardware::audio::core::Module;
 
 int main() {
+    // Random values are used in the implementation.
+    std::srand(std::time(nullptr));
+
     // This is a debug implementation, always enable debug logging.
     android::base::SetMinimumLogSeverity(::android::base::DEBUG);
     ABinderProcess_setThreadPoolMaxThreadCount(16);
diff --git a/audio/aidl/vts/Android.bp b/audio/aidl/vts/Android.bp
index cd5915b..6ea7cef 100644
--- a/audio/aidl/vts/Android.bp
+++ b/audio/aidl/vts/Android.bp
@@ -12,18 +12,57 @@
     defaults: [
         "VtsHalTargetTestDefaults",
         "use_libaidlvintf_gtest_helper_static",
+        "latest_android_hardware_audio_common_ndk_static",
+        "latest_android_hardware_audio_core_ndk_static",
+        "latest_android_media_audio_common_types_ndk_static",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+        "libcutils",
+        "libfmq",
+    ],
+    static_libs: [
+        "android.hardware.common-V2-ndk",
+        "android.hardware.common.fmq-V1-ndk",
+        "libaudioaidlcommon",
+    ],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wthread-safety",
     ],
     srcs: [
         "ModuleConfig.cpp",
         "VtsHalAudioCoreTargetTest.cpp",
     ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
+
+cc_test {
+    name: "VtsHalAudioEffectTargetTest",
+    defaults: [
+        "latest_android_media_audio_common_types_ndk_static",
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: [
+        "VtsHalAudioEffectTargetTest.cpp",
+    ],
     shared_libs: [
         "libbinder_ndk",
     ],
     static_libs: [
-        "android.hardware.audio.common-V1-ndk",
-        "android.hardware.audio.core-V1-ndk",
-        "android.media.audio.common.types-V1-ndk",
+        "android.hardware.audio.effect-V1-ndk",
+    ],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wthread-safety",
     ],
     test_suites: [
         "general-tests",
diff --git a/audio/aidl/vts/AudioHalBinderServiceUtil.h b/audio/aidl/vts/AudioHalBinderServiceUtil.h
new file mode 100644
index 0000000..e928286
--- /dev/null
+++ b/audio/aidl/vts/AudioHalBinderServiceUtil.h
@@ -0,0 +1,99 @@
+/*
+ * 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 <condition_variable>
+#include <memory>
+#include <mutex>
+
+#include <android-base/properties.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include <android-base/logging.h>
+
+class AudioHalBinderServiceUtil {
+  public:
+    ndk::SpAIBinder connectToService(const std::string& serviceName) {
+        mServiceName = serviceName;
+        mBinder = ndk::SpAIBinder(AServiceManager_getService(serviceName.c_str()));
+        if (mBinder == nullptr) {
+            LOG(ERROR) << "Failed to get service " << serviceName;
+        } else {
+            LOG(DEBUG) << "succeed to get service " << serviceName;
+        }
+        return mBinder;
+    }
+
+    ndk::SpAIBinder restartService(
+            std::chrono::milliseconds timeoutMs = std::chrono::milliseconds(3000)) {
+        mDeathHandler.reset(new AidlDeathRecipient(mBinder));
+        if (STATUS_OK != mDeathHandler->linkToDeath()) {
+            LOG(ERROR) << "linkToDeath failed";
+            return nullptr;
+        }
+        if (!android::base::SetProperty("sys.audio.restart.hal", "1")) {
+            LOG(ERROR) << "SetProperty failed";
+            return nullptr;
+        }
+        if (!mDeathHandler->waitForFired(timeoutMs)) {
+            LOG(ERROR) << "Timeout wait for death";
+            return nullptr;
+        }
+        mDeathHandler.reset();
+        return connectToService(mServiceName);
+    }
+
+  private:
+    class AidlDeathRecipient {
+      public:
+        explicit AidlDeathRecipient(const ndk::SpAIBinder& binder)
+            : binder(binder), recipient(AIBinder_DeathRecipient_new(&binderDiedCallbackAidl)) {}
+
+        binder_status_t linkToDeath() {
+            return AIBinder_linkToDeath(binder.get(), recipient.get(), this);
+        }
+
+        bool waitForFired(std::chrono::milliseconds timeoutMs) {
+            std::unique_lock<std::mutex> lock(mutex);
+            condition.wait_for(lock, timeoutMs, [this]() { return fired; });
+            return fired;
+        }
+
+      private:
+        const ndk::SpAIBinder binder;
+        const ndk::ScopedAIBinder_DeathRecipient recipient;
+        std::mutex mutex;
+        std::condition_variable condition;
+        bool fired = false;
+
+        void binderDied() {
+            std::unique_lock<std::mutex> lock(mutex);
+            fired = true;
+            condition.notify_one();
+        };
+
+        static void binderDiedCallbackAidl(void* cookie) {
+            AidlDeathRecipient* self = static_cast<AidlDeathRecipient*>(cookie);
+            self->binderDied();
+        }
+    };
+
+    std::string mServiceName;
+    ndk::SpAIBinder mBinder;
+    std::unique_ptr<AidlDeathRecipient> mDeathHandler;
+};
diff --git a/audio/aidl/vts/ModuleConfig.cpp b/audio/aidl/vts/ModuleConfig.cpp
index 969b0e9..33c5b72 100644
--- a/audio/aidl/vts/ModuleConfig.cpp
+++ b/audio/aidl/vts/ModuleConfig.cpp
@@ -17,6 +17,7 @@
 #include <algorithm>
 #include <chrono>
 
+#include <Utils.h>
 #include <aidl/android/media/audio/common/AudioIoFlags.h>
 #include <aidl/android/media/audio/common/AudioOutputFlags.h>
 
@@ -39,14 +40,15 @@
 using aidl::android::media::audio::common::AudioProfile;
 using aidl::android::media::audio::common::AudioUsage;
 using aidl::android::media::audio::common::Int;
+using android::hardware::audio::common::isBitPositionFlagSet;
 
 // static
 std::optional<AudioOffloadInfo> ModuleConfig::generateOffloadInfoIfNeeded(
         const AudioPortConfig& portConfig) {
     if (portConfig.flags.has_value() &&
         portConfig.flags.value().getTag() == AudioIoFlags::Tag::output &&
-        (portConfig.flags.value().get<AudioIoFlags::Tag::output>() &
-         1 << static_cast<int>(AudioOutputFlags::COMPRESS_OFFLOAD)) != 0) {
+        isBitPositionFlagSet(portConfig.flags.value().get<AudioIoFlags::Tag::output>(),
+                             AudioOutputFlags::COMPRESS_OFFLOAD)) {
         AudioOffloadInfo offloadInfo;
         offloadInfo.base.sampleRate = portConfig.sampleRate.value().value;
         offloadInfo.base.channelMask = portConfig.channelMask.value();
@@ -123,6 +125,32 @@
     return result;
 }
 
+std::vector<AudioPort> ModuleConfig::getOffloadMixPorts(bool attachedOnly, bool singlePort) const {
+    std::vector<AudioPort> result;
+    const auto mixPorts = getMixPorts(false /*isInput*/);
+    auto offloadPortIt = mixPorts.begin();
+    while (offloadPortIt != mixPorts.end()) {
+        offloadPortIt = std::find_if(offloadPortIt, mixPorts.end(), [&](const AudioPort& port) {
+            return isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
+                                        AudioOutputFlags::COMPRESS_OFFLOAD) &&
+                   (!attachedOnly || !getAttachedSinkDevicesPortsForMixPort(port).empty());
+        });
+        if (offloadPortIt == mixPorts.end()) break;
+        result.push_back(*offloadPortIt++);
+        if (singlePort) break;
+    }
+    return result;
+}
+
+std::vector<AudioPort> ModuleConfig::getAttachedDevicesPortsForMixPort(
+        bool isInput, const AudioPortConfig& mixPortConfig) const {
+    const auto mixPortIt = findById<AudioPort>(mPorts, mixPortConfig.portId);
+    if (mixPortIt != mPorts.end()) {
+        return getAttachedDevicesPortsForMixPort(isInput, *mixPortIt);
+    }
+    return {};
+}
+
 std::vector<AudioPort> ModuleConfig::getAttachedSinkDevicesPortsForMixPort(
         const AudioPort& mixPort) const {
     std::vector<AudioPort> result;
diff --git a/audio/aidl/vts/ModuleConfig.h b/audio/aidl/vts/ModuleConfig.h
index df13430..dc109a7 100644
--- a/audio/aidl/vts/ModuleConfig.h
+++ b/audio/aidl/vts/ModuleConfig.h
@@ -48,12 +48,17 @@
     std::vector<aidl::android::media::audio::common::AudioPort> getMixPorts(bool isInput) const {
         return isInput ? getInputMixPorts() : getOutputMixPorts();
     }
+    std::vector<aidl::android::media::audio::common::AudioPort> getOffloadMixPorts(
+            bool attachedOnly, bool singlePort) const;
 
     std::vector<aidl::android::media::audio::common::AudioPort> getAttachedDevicesPortsForMixPort(
             bool isInput, const aidl::android::media::audio::common::AudioPort& mixPort) const {
         return isInput ? getAttachedSourceDevicesPortsForMixPort(mixPort)
                        : getAttachedSinkDevicesPortsForMixPort(mixPort);
     }
+    std::vector<aidl::android::media::audio::common::AudioPort> getAttachedDevicesPortsForMixPort(
+            bool isInput,
+            const aidl::android::media::audio::common::AudioPortConfig& mixPortConfig) const;
     std::vector<aidl::android::media::audio::common::AudioPort>
     getAttachedSinkDevicesPortsForMixPort(
             const aidl::android::media::audio::common::AudioPort& mixPort) const;
diff --git a/audio/aidl/vts/TestUtils.h b/audio/aidl/vts/TestUtils.h
new file mode 100644
index 0000000..2fc109a
--- /dev/null
+++ b/audio/aidl/vts/TestUtils.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 <iostream>
+
+#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 {
+
+inline ::testing::AssertionResult assertIsOk(const char* expr, const ::ndk::ScopedAStatus& status) {
+    if (status.isOk()) {
+        return ::testing::AssertionSuccess();
+    }
+    return ::testing::AssertionFailure()
+           << "Expected the transaction \'" << expr << "\' to succeed\n"
+           << "  but it has failed with: " << status;
+}
+
+inline ::testing::AssertionResult assertResult(const char* exp_expr, const char* act_expr,
+                                               int32_t expected,
+                                               const ::ndk::ScopedAStatus& status) {
+    if (status.getExceptionCode() == expected) {
+        return ::testing::AssertionSuccess();
+    }
+    return ::testing::AssertionFailure()
+           << "Expected the transaction \'" << act_expr << "\' to fail with " << exp_expr
+           << "\n  but is has completed with: " << status;
+}
+
+}  // namespace detail
+
+}  // namespace android::hardware::audio::common::testing
+
+// Test that the transaction status 'isOk'
+#define ASSERT_IS_OK(ret) \
+    ASSERT_PRED_FORMAT1(::android::hardware::audio::common::testing::detail::assertIsOk, ret)
+#define EXPECT_IS_OK(ret) \
+    EXPECT_PRED_FORMAT1(::android::hardware::audio::common::testing::detail::assertIsOk, ret)
+
+// Test that the transaction status is as expected.
+#define ASSERT_STATUS(expected, ret)                                                       \
+    ASSERT_PRED_FORMAT2(::android::hardware::audio::common::testing::detail::assertResult, \
+                        expected, ret)
+#define EXPECT_STATUS(expected, ret)                                                       \
+    EXPECT_PRED_FORMAT2(::android::hardware::audio::common::testing::detail::assertResult, \
+                        expected, ret)
diff --git a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
index bb24365..2381200 100644
--- a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
@@ -15,27 +15,30 @@
  */
 
 #include <algorithm>
-#include <condition_variable>
+#include <limits>
 #include <memory>
-#include <mutex>
 #include <optional>
 #include <set>
 #include <string>
+#include <vector>
 
 #define LOG_TAG "VtsHalAudioCore"
 #include <android-base/logging.h>
 
+#include <StreamWorker.h>
+#include <Utils.h>
 #include <aidl/Gtest.h>
 #include <aidl/Vintf.h>
 #include <aidl/android/hardware/audio/core/IConfig.h>
 #include <aidl/android/hardware/audio/core/IModule.h>
 #include <aidl/android/media/audio/common/AudioIoFlags.h>
 #include <aidl/android/media/audio/common/AudioOutputFlags.h>
-#include <android-base/properties.h>
-#include <android/binder_manager.h>
-#include <android/binder_process.h>
+#include <android-base/chrono_utils.h>
+#include <fmq/AidlMessageQueue.h>
 
+#include "AudioHalBinderServiceUtil.h"
 #include "ModuleConfig.h"
+#include "TestUtils.h"
 
 using namespace android;
 using aidl::android::hardware::audio::common::PlaybackTrackMetadata;
@@ -48,6 +51,8 @@
 using aidl::android::hardware::audio::core::IStreamIn;
 using aidl::android::hardware::audio::core::IStreamOut;
 using aidl::android::hardware::audio::core::ModuleDebug;
+using aidl::android::hardware::audio::core::StreamDescriptor;
+using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
 using aidl::android::media::audio::common::AudioContentType;
 using aidl::android::media::audio::common::AudioDevice;
 using aidl::android::media::audio::common::AudioDeviceAddress;
@@ -61,15 +66,11 @@
 using aidl::android::media::audio::common::AudioPortExt;
 using aidl::android::media::audio::common::AudioSource;
 using aidl::android::media::audio::common::AudioUsage;
+using android::hardware::audio::common::isBitPositionFlagSet;
+using android::hardware::audio::common::StreamLogic;
+using android::hardware::audio::common::StreamWorker;
 using ndk::ScopedAStatus;
 
-namespace ndk {
-std::ostream& operator<<(std::ostream& str, const ScopedAStatus& status) {
-    str << status.getDescription();
-    return str;
-}
-}  // namespace ndk
-
 template <typename T>
 auto findById(std::vector<T>& v, int32_t id) {
     return std::find_if(v.begin(), v.end(), [&](const auto& e) { return e.id == id; });
@@ -92,52 +93,6 @@
     return AudioDeviceAddress::make<AudioDeviceAddress::Tag::id>(std::to_string(++nextId));
 }
 
-struct AidlDeathRecipient {
-    const ndk::SpAIBinder binder;
-    const ndk::ScopedAIBinder_DeathRecipient recipient;
-    std::mutex mutex;
-    std::condition_variable condition;
-    bool fired = false;
-
-    explicit AidlDeathRecipient(const ndk::SpAIBinder& binder)
-        : binder(binder), recipient(AIBinder_DeathRecipient_new(&binderDiedCallbackAidl)) {}
-
-    binder_status_t linkToDeath() {
-        return AIBinder_linkToDeath(binder.get(), recipient.get(), this);
-    }
-
-    void binderDied() {
-        std::unique_lock<std::mutex> lock(mutex);
-        fired = true;
-        condition.notify_one();
-    };
-
-    bool waitForFired(int timeoutMs) {
-        std::unique_lock<std::mutex> lock(mutex);
-        condition.wait_for(lock, std::chrono::milliseconds(timeoutMs), [this]() { return fired; });
-        return fired;
-    }
-
-    static void binderDiedCallbackAidl(void* cookie) {
-        AidlDeathRecipient* self = static_cast<AidlDeathRecipient*>(cookie);
-        self->binderDied();
-    }
-};
-
-template <typename T>
-struct IsInput {
-    constexpr operator bool() const;
-};
-
-template <>
-constexpr IsInput<IStreamIn>::operator bool() const {
-    return true;
-}
-template <>
-constexpr IsInput<IStreamOut>::operator bool() const {
-    return false;
-}
-
 // All 'With*' classes are move-only because they are associated with some
 // resource or state of a HAL module.
 class WithDebugFlags {
@@ -152,14 +107,10 @@
     WithDebugFlags& operator=(const WithDebugFlags&) = delete;
     ~WithDebugFlags() {
         if (mModule != nullptr) {
-            ScopedAStatus status = mModule->setModuleDebug(mInitial);
-            EXPECT_EQ(EX_NONE, status.getExceptionCode()) << status;
+            EXPECT_IS_OK(mModule->setModuleDebug(mInitial));
         }
     }
-    void SetUp(IModule* module) {
-        ScopedAStatus status = module->setModuleDebug(mFlags);
-        ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
-    }
+    void SetUp(IModule* module) { ASSERT_IS_OK(module->setModuleDebug(mFlags)); }
     ModuleDebug& flags() { return mFlags; }
 
   private:
@@ -180,9 +131,7 @@
     WithAudioPortConfig& operator=(const WithAudioPortConfig&) = delete;
     ~WithAudioPortConfig() {
         if (mModule != nullptr) {
-            ScopedAStatus status = mModule->resetAudioPortConfig(getId());
-            EXPECT_EQ(EX_NONE, status.getExceptionCode())
-                    << status << "; port config id " << getId();
+            EXPECT_IS_OK(mModule->resetAudioPortConfig(getId())) << "port config id " << getId();
         }
     }
     void SetUp(IModule* module) {
@@ -201,9 +150,8 @@
         if (mInitialConfig.id == 0) {
             AudioPortConfig suggested;
             bool applied = false;
-            ScopedAStatus status = module->setAudioPortConfig(mInitialConfig, &suggested, &applied);
-            ASSERT_EQ(EX_NONE, status.getExceptionCode())
-                    << status << "; Config: " << mInitialConfig.toString();
+            ASSERT_IS_OK(module->setAudioPortConfig(mInitialConfig, &suggested, &applied))
+                    << "Config: " << mInitialConfig.toString();
             if (!applied && negotiate) {
                 mInitialConfig = suggested;
                 ASSERT_NO_FATAL_FAILURE(SetUpImpl(module, false))
@@ -225,6 +173,9 @@
 
 class AudioCoreModule : public testing::TestWithParam<std::string> {
   public:
+    // The default buffer size is used mostly for negative tests.
+    static constexpr int kDefaultBufferSizeFrames = 256;
+
     void SetUp() override {
         ASSERT_NO_FATAL_FAILURE(ConnectToService());
         debug.flags().simulateDeviceConnections = true;
@@ -233,27 +184,20 @@
 
     void TearDown() override {
         if (module != nullptr) {
-            ScopedAStatus status = module->setModuleDebug(ModuleDebug{});
-            EXPECT_EQ(EX_NONE, status.getExceptionCode())
-                    << status << " returned when resetting debug flags";
+            EXPECT_IS_OK(module->setModuleDebug(ModuleDebug{}));
         }
     }
 
     void ConnectToService() {
-        module = IModule::fromBinder(
-                ndk::SpAIBinder(AServiceManager_getService(GetParam().c_str())));
+        module = IModule::fromBinder(binderUtil.connectToService(GetParam()));
         ASSERT_NE(module, nullptr);
     }
 
     void RestartService() {
         ASSERT_NE(module, nullptr);
         moduleConfig.reset();
-        deathHandler.reset(new AidlDeathRecipient(module->asBinder()));
-        ASSERT_EQ(STATUS_OK, deathHandler->linkToDeath());
-        ASSERT_TRUE(base::SetProperty("sys.audio.restart.hal", "1"));
-        EXPECT_TRUE(deathHandler->waitForFired(3000));
-        deathHandler.reset();
-        ASSERT_NO_FATAL_FAILURE(ConnectToService());
+        module = IModule::fromBinder(binderUtil.restartService());
+        ASSERT_NE(module, nullptr);
     }
 
     void ApplyEveryConfig(const std::vector<AudioPortConfig>& configs) {
@@ -263,8 +207,7 @@
             ASSERT_NO_FATAL_FAILURE(portConfig.SetUp(module.get()));  // calls setAudioPortConfig
             EXPECT_EQ(config.portId, portConfig.get().portId);
             std::vector<AudioPortConfig> retrievedPortConfigs;
-            ScopedAStatus status = module->getAudioPortConfigs(&retrievedPortConfigs);
-            ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
+            ASSERT_IS_OK(module->getAudioPortConfigs(&retrievedPortConfigs));
             const int32_t portConfigId = portConfig.getId();
             auto configIt = std::find_if(
                     retrievedPortConfigs.begin(), retrievedPortConfigs.end(),
@@ -287,10 +230,7 @@
                          ScopedAStatus (IModule::*getter)(std::vector<Entity>*),
                          const std::string& errorMessage) {
         std::vector<Entity> entities;
-        {
-            ScopedAStatus status = (module.get()->*getter)(&entities);
-            ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
-        }
+        { ASSERT_IS_OK((module.get()->*getter)(&entities)); }
         std::transform(entities.begin(), entities.end(),
                        std::inserter(*entityIds, entityIds->begin()),
                        [](const auto& entity) { return entity.id; });
@@ -324,8 +264,8 @@
     }
 
     std::shared_ptr<IModule> module;
-    std::unique_ptr<AidlDeathRecipient> deathHandler;
     std::unique_ptr<ModuleConfig> moduleConfig;
+    AudioHalBinderServiceUtil binderUtil;
     WithDebugFlags debug;
 };
 
@@ -338,16 +278,13 @@
     WithDevicePortConnectedState& operator=(const WithDevicePortConnectedState&) = delete;
     ~WithDevicePortConnectedState() {
         if (mModule != nullptr) {
-            ScopedAStatus status = mModule->disconnectExternalDevice(getId());
-            EXPECT_EQ(EX_NONE, status.getExceptionCode())
-                    << status << " returned when disconnecting device port ID " << getId();
+            EXPECT_IS_OK(mModule->disconnectExternalDevice(getId()))
+                    << "when disconnecting device port ID " << getId();
         }
     }
     void SetUp(IModule* module) {
-        ScopedAStatus status = module->connectExternalDevice(mIdAndData, &mConnectedPort);
-        ASSERT_EQ(EX_NONE, status.getExceptionCode())
-                << status << " returned when connecting device port ID & data "
-                << mIdAndData.toString();
+        ASSERT_IS_OK(module->connectExternalDevice(mIdAndData, &mConnectedPort))
+                << "when connecting device port ID & data " << mIdAndData.toString();
         ASSERT_NE(mIdAndData.id, getId())
                 << "ID of the connected port must not be the same as the ID of the template port";
         mModule = module;
@@ -367,6 +304,214 @@
     AudioPort mConnectedPort;
 };
 
+class StreamContext {
+  public:
+    typedef AidlMessageQueue<StreamDescriptor::Command, SynchronizedReadWrite> CommandMQ;
+    typedef AidlMessageQueue<StreamDescriptor::Reply, SynchronizedReadWrite> ReplyMQ;
+    typedef AidlMessageQueue<int8_t, SynchronizedReadWrite> DataMQ;
+
+    explicit StreamContext(const StreamDescriptor& descriptor)
+        : mFrameSizeBytes(descriptor.frameSizeBytes),
+          mCommandMQ(new CommandMQ(descriptor.command)),
+          mReplyMQ(new ReplyMQ(descriptor.reply)),
+          mBufferSizeFrames(descriptor.bufferSizeFrames),
+          mDataMQ(maybeCreateDataMQ(descriptor)) {}
+    void checkIsValid() const {
+        EXPECT_NE(0UL, mFrameSizeBytes);
+        ASSERT_NE(nullptr, mCommandMQ);
+        EXPECT_TRUE(mCommandMQ->isValid());
+        ASSERT_NE(nullptr, mReplyMQ);
+        EXPECT_TRUE(mReplyMQ->isValid());
+        if (mDataMQ != nullptr) {
+            EXPECT_TRUE(mDataMQ->isValid());
+            EXPECT_GE(mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize(),
+                      mFrameSizeBytes * mBufferSizeFrames)
+                    << "Data MQ actual buffer size is "
+                       "less than the buffer size as specified by the descriptor";
+        }
+    }
+    size_t getBufferSizeBytes() const { return mFrameSizeBytes * mBufferSizeFrames; }
+    size_t getBufferSizeFrames() const { return mBufferSizeFrames; }
+    CommandMQ* getCommandMQ() const { return mCommandMQ.get(); }
+    DataMQ* getDataMQ() const { return mDataMQ.get(); }
+    ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
+
+  private:
+    static std::unique_ptr<DataMQ> maybeCreateDataMQ(const StreamDescriptor& descriptor) {
+        using Tag = StreamDescriptor::AudioBuffer::Tag;
+        if (descriptor.audio.getTag() == Tag::fmq) {
+            return std::make_unique<DataMQ>(descriptor.audio.get<Tag::fmq>());
+        }
+        return nullptr;
+    }
+
+    const size_t mFrameSizeBytes;
+    std::unique_ptr<CommandMQ> mCommandMQ;
+    std::unique_ptr<ReplyMQ> mReplyMQ;
+    const size_t mBufferSizeFrames;
+    std::unique_ptr<DataMQ> mDataMQ;
+};
+
+class StreamCommonLogic : public StreamLogic {
+  public:
+    StreamDescriptor::Position getLastObservablePosition() {
+        std::lock_guard<std::mutex> lock(mLock);
+        return mLastReply.observable;
+    }
+
+  protected:
+    explicit StreamCommonLogic(const StreamContext& context)
+        : mCommandMQ(context.getCommandMQ()),
+          mReplyMQ(context.getReplyMQ()),
+          mDataMQ(context.getDataMQ()),
+          mData(context.getBufferSizeBytes()) {}
+    StreamContext::CommandMQ* getCommandMQ() const { return mCommandMQ; }
+    StreamContext::ReplyMQ* getReplyMQ() const { return mReplyMQ; }
+
+    std::string init() override { return ""; }
+
+    StreamContext::CommandMQ* mCommandMQ;
+    StreamContext::ReplyMQ* mReplyMQ;
+    StreamContext::DataMQ* mDataMQ;
+    std::vector<int8_t> mData;
+    std::mutex mLock;
+    StreamDescriptor::Reply mLastReply GUARDED_BY(mLock);
+};
+
+class StreamReaderLogic : public StreamCommonLogic {
+  public:
+    explicit StreamReaderLogic(const StreamContext& context) : StreamCommonLogic(context) {}
+
+  protected:
+    Status cycle() override {
+        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;
+        }
+        StreamDescriptor::Reply reply{};
+        if (!mReplyMQ->readBlocking(&reply, 1)) {
+            LOG(ERROR) << __func__ << ": reading of reply from MQ failed";
+            return Status::ABORT;
+        }
+        if (reply.status != STATUS_OK) {
+            LOG(ERROR) << __func__ << ": received error status: " << statusToString(reply.status);
+            return Status::ABORT;
+        }
+        if (reply.fmqByteCount < 0 || reply.fmqByteCount > command.fmqByteCount) {
+            LOG(ERROR) << __func__
+                       << ": received invalid byte count in the reply: " << reply.fmqByteCount;
+            return Status::ABORT;
+        }
+        {
+            std::lock_guard<std::mutex> lock(mLock);
+            mLastReply = reply;
+        }
+        const size_t readCount = std::min({mDataMQ->availableToRead(),
+                                           static_cast<size_t>(reply.fmqByteCount), mData.size()});
+        if (readCount == 0 || mDataMQ->read(mData.data(), readCount)) {
+            return Status::CONTINUE;
+        }
+        LOG(ERROR) << __func__ << ": reading of " << readCount << " data bytes from MQ failed";
+        return Status::ABORT;
+    }
+};
+using StreamReader = StreamWorker<StreamReaderLogic>;
+
+class StreamWriterLogic : public StreamCommonLogic {
+  public:
+    explicit StreamWriterLogic(const StreamContext& context) : StreamCommonLogic(context) {}
+
+  protected:
+    Status cycle() override {
+        if (!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;
+        }
+        StreamDescriptor::Reply reply{};
+        if (!mReplyMQ->readBlocking(&reply, 1)) {
+            LOG(ERROR) << __func__ << ": reading of reply from MQ failed";
+            return Status::ABORT;
+        }
+        if (reply.status != STATUS_OK) {
+            LOG(ERROR) << __func__ << ": received error status: " << statusToString(reply.status);
+            return Status::ABORT;
+        }
+        if (reply.fmqByteCount < 0 || reply.fmqByteCount > command.fmqByteCount) {
+            LOG(ERROR) << __func__
+                       << ": received invalid byte count in the reply: " << reply.fmqByteCount;
+            return Status::ABORT;
+        }
+        {
+            std::lock_guard<std::mutex> lock(mLock);
+            mLastReply = reply;
+        }
+        return Status::CONTINUE;
+    }
+};
+using StreamWriter = StreamWorker<StreamWriterLogic>;
+
+template <typename T>
+struct IOTraits {
+    static constexpr bool is_input = std::is_same_v<T, IStreamIn>;
+    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:
@@ -376,24 +521,27 @@
     WithStream& operator=(const WithStream&) = delete;
     ~WithStream() {
         if (mStream != nullptr) {
-            ScopedAStatus status = mStream->close();
-            EXPECT_EQ(EX_NONE, status.getExceptionCode())
-                    << status << "; port config id " << getPortId();
+            mContext.reset();
+            EXPECT_IS_OK(mStream->close()) << "port config id " << getPortId();
         }
     }
     void SetUpPortConfig(IModule* module) { ASSERT_NO_FATAL_FAILURE(mPortConfig.SetUp(module)); }
-    ScopedAStatus SetUpNoChecks(IModule* module) {
-        return SetUpNoChecks(module, mPortConfig.get());
+    ScopedAStatus SetUpNoChecks(IModule* module, long bufferSizeFrames) {
+        return SetUpNoChecks(module, mPortConfig.get(), bufferSizeFrames);
     }
-    ScopedAStatus SetUpNoChecks(IModule* module, const AudioPortConfig& portConfig);
-    void SetUp(IModule* module) {
+    ScopedAStatus SetUpNoChecks(IModule* module, const AudioPortConfig& portConfig,
+                                long bufferSizeFrames);
+    void SetUp(IModule* module, long bufferSizeFrames) {
         ASSERT_NO_FATAL_FAILURE(SetUpPortConfig(module));
-        ScopedAStatus status = SetUpNoChecks(module);
-        ASSERT_EQ(EX_NONE, status.getExceptionCode())
-                << status << "; port config id " << getPortId();
-        ASSERT_NE(nullptr, mStream) << "; port config id " << getPortId();
+        ASSERT_IS_OK(SetUpNoChecks(module, bufferSizeFrames)) << "port config id " << getPortId();
+        ASSERT_NE(nullptr, mStream) << "port config id " << getPortId();
+        EXPECT_GE(mDescriptor.bufferSizeFrames, bufferSizeFrames)
+                << "actual buffer size must be no less than requested";
+        mContext.emplace(mDescriptor);
+        ASSERT_NO_FATAL_FAILURE(mContext.value().checkIsValid());
     }
     Stream* get() const { return mStream.get(); }
+    const StreamContext* getContext() const { return mContext ? &(mContext.value()) : nullptr; }
     std::shared_ptr<Stream> getSharedPointer() const { return mStream; }
     const AudioPortConfig& getPortConfig() const { return mPortConfig.get(); }
     int32_t getPortId() const { return mPortConfig.getId(); }
@@ -401,6 +549,8 @@
   private:
     WithAudioPortConfig mPortConfig;
     std::shared_ptr<Stream> mStream;
+    StreamDescriptor mDescriptor;
+    std::optional<StreamContext> mContext;
 };
 
 SinkMetadata GenerateSinkMetadata(const AudioPortConfig& portConfig) {
@@ -415,8 +565,19 @@
 
 template <>
 ScopedAStatus WithStream<IStreamIn>::SetUpNoChecks(IModule* module,
-                                                   const AudioPortConfig& portConfig) {
-    return module->openInputStream(portConfig.id, GenerateSinkMetadata(portConfig), &mStream);
+                                                   const AudioPortConfig& portConfig,
+                                                   long bufferSizeFrames) {
+    aidl::android::hardware::audio::core::IModule::OpenInputStreamArguments args;
+    args.portConfigId = portConfig.id;
+    args.sinkMetadata = GenerateSinkMetadata(portConfig);
+    args.bufferSizeFrames = bufferSizeFrames;
+    aidl::android::hardware::audio::core::IModule::OpenInputStreamReturn ret;
+    ScopedAStatus status = module->openInputStream(args, &ret);
+    if (status.isOk()) {
+        mStream = std::move(ret.stream);
+        mDescriptor = std::move(ret.desc);
+    }
+    return status;
 }
 
 SourceMetadata GenerateSourceMetadata(const AudioPortConfig& portConfig) {
@@ -432,10 +593,20 @@
 
 template <>
 ScopedAStatus WithStream<IStreamOut>::SetUpNoChecks(IModule* module,
-                                                    const AudioPortConfig& portConfig) {
-    return module->openOutputStream(portConfig.id, GenerateSourceMetadata(portConfig),
-                                    ModuleConfig::generateOffloadInfoIfNeeded(portConfig),
-                                    &mStream);
+                                                    const AudioPortConfig& portConfig,
+                                                    long bufferSizeFrames) {
+    aidl::android::hardware::audio::core::IModule::OpenOutputStreamArguments args;
+    args.portConfigId = portConfig.id;
+    args.sourceMetadata = GenerateSourceMetadata(portConfig);
+    args.offloadInfo = ModuleConfig::generateOffloadInfoIfNeeded(portConfig);
+    args.bufferSizeFrames = bufferSizeFrames;
+    aidl::android::hardware::audio::core::IModule::OpenOutputStreamReturn ret;
+    ScopedAStatus status = module->openOutputStream(args, &ret);
+    if (status.isOk()) {
+        mStream = std::move(ret.stream);
+        mDescriptor = std::move(ret.desc);
+    }
+    return status;
 }
 
 class WithAudioPatch {
@@ -443,12 +614,15 @@
     WithAudioPatch() {}
     WithAudioPatch(const AudioPortConfig& srcPortConfig, const AudioPortConfig& sinkPortConfig)
         : mSrcPortConfig(srcPortConfig), mSinkPortConfig(sinkPortConfig) {}
+    WithAudioPatch(bool sinkIsCfg1, const AudioPortConfig& portConfig1,
+                   const AudioPortConfig& portConfig2)
+        : mSrcPortConfig(sinkIsCfg1 ? portConfig2 : portConfig1),
+          mSinkPortConfig(sinkIsCfg1 ? portConfig1 : portConfig2) {}
     WithAudioPatch(const WithAudioPatch&) = delete;
     WithAudioPatch& operator=(const WithAudioPatch&) = delete;
     ~WithAudioPatch() {
         if (mModule != nullptr && mPatch.id != 0) {
-            ScopedAStatus status = mModule->resetAudioPatch(mPatch.id);
-            EXPECT_EQ(EX_NONE, status.getExceptionCode()) << status << "; patch id " << getId();
+            EXPECT_IS_OK(mModule->resetAudioPatch(mPatch.id)) << "patch id " << getId();
         }
     }
     void SetUpPortConfigs(IModule* module) {
@@ -463,13 +637,20 @@
     }
     void SetUp(IModule* module) {
         ASSERT_NO_FATAL_FAILURE(SetUpPortConfigs(module));
-        ScopedAStatus status = SetUpNoChecks(module);
-        ASSERT_EQ(EX_NONE, status.getExceptionCode())
-                << status << "; source port config id " << mSrcPortConfig.getId()
-                << "; sink port config id " << mSinkPortConfig.getId();
+        ASSERT_IS_OK(SetUpNoChecks(module)) << "source port config id " << mSrcPortConfig.getId()
+                                            << "; sink port config id " << mSinkPortConfig.getId();
+        EXPECT_GT(mPatch.minimumStreamBufferSizeFrames, 0) << "patch id " << getId();
+        for (auto latencyMs : mPatch.latenciesMs) {
+            EXPECT_GT(latencyMs, 0) << "patch id " << getId();
+        }
     }
     int32_t getId() const { return mPatch.id; }
     const AudioPatch& get() const { return mPatch; }
+    const AudioPortConfig& getSinkPortConfig() const { return mSinkPortConfig.get(); }
+    const AudioPortConfig& getSrcPortConfig() const { return mSrcPortConfig.get(); }
+    const AudioPortConfig& getPortConfig(bool getSink) const {
+        return getSink ? getSinkPortConfig() : getSrcPortConfig();
+    }
 
   private:
     WithAudioPortConfig mSrcPortConfig;
@@ -493,15 +674,9 @@
 
 TEST_P(AudioCoreModule, GetAudioPortsIsStable) {
     std::vector<AudioPort> ports1;
-    {
-        ScopedAStatus status = module->getAudioPorts(&ports1);
-        ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
-    }
+    ASSERT_IS_OK(module->getAudioPorts(&ports1));
     std::vector<AudioPort> ports2;
-    {
-        ScopedAStatus status = module->getAudioPorts(&ports2);
-        ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
-    }
+    ASSERT_IS_OK(module->getAudioPorts(&ports2));
     ASSERT_EQ(ports1.size(), ports2.size())
             << "Sizes of audio port arrays do not match across consequent calls to getAudioPorts";
     std::sort(ports1.begin(), ports1.end());
@@ -511,15 +686,9 @@
 
 TEST_P(AudioCoreModule, GetAudioRoutesIsStable) {
     std::vector<AudioRoute> routes1;
-    {
-        ScopedAStatus status = module->getAudioRoutes(&routes1);
-        ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
-    }
+    ASSERT_IS_OK(module->getAudioRoutes(&routes1));
     std::vector<AudioRoute> routes2;
-    {
-        ScopedAStatus status = module->getAudioRoutes(&routes2);
-        ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
-    }
+    ASSERT_IS_OK(module->getAudioRoutes(&routes2));
     ASSERT_EQ(routes1.size(), routes2.size())
             << "Sizes of audio route arrays do not match across consequent calls to getAudioRoutes";
     std::sort(routes1.begin(), routes1.end());
@@ -529,13 +698,10 @@
 
 TEST_P(AudioCoreModule, GetAudioRoutesAreValid) {
     std::vector<AudioRoute> routes;
-    {
-        ScopedAStatus status = module->getAudioRoutes(&routes);
-        ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
-    }
+    ASSERT_IS_OK(module->getAudioRoutes(&routes));
     for (const auto& route : routes) {
         std::set<int32_t> sources(route.sourcePortIds.begin(), route.sourcePortIds.end());
-        EXPECT_NE(0, sources.size())
+        EXPECT_NE(0UL, sources.size())
                 << "empty audio port sinks in the audio route: " << route.toString();
         EXPECT_EQ(sources.size(), route.sourcePortIds.size())
                 << "IDs of audio port sinks are not unique in the audio route: "
@@ -547,15 +713,12 @@
     std::set<int32_t> portIds;
     ASSERT_NO_FATAL_FAILURE(GetAllPortIds(&portIds));
     std::vector<AudioRoute> routes;
-    {
-        ScopedAStatus status = module->getAudioRoutes(&routes);
-        ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
-    }
+    ASSERT_IS_OK(module->getAudioRoutes(&routes));
     for (const auto& route : routes) {
-        EXPECT_EQ(1, portIds.count(route.sinkPortId))
+        EXPECT_EQ(1UL, portIds.count(route.sinkPortId))
                 << route.sinkPortId << " sink port id is unknown";
         for (const auto& source : route.sourcePortIds) {
-            EXPECT_EQ(1, portIds.count(source)) << source << " source port id is unknown";
+            EXPECT_EQ(1UL, portIds.count(source)) << source << " source port id is unknown";
         }
     }
 }
@@ -568,8 +731,7 @@
     }
     for (const auto portId : portIds) {
         std::vector<AudioRoute> routes;
-        ScopedAStatus status = module->getAudioRoutesForAudioPort(portId, &routes);
-        EXPECT_EQ(EX_NONE, status.getExceptionCode()) << status;
+        EXPECT_IS_OK(module->getAudioRoutesForAudioPort(portId, &routes));
         for (const auto& r : routes) {
             if (r.sinkPortId != portId) {
                 const auto& srcs = r.sourcePortIds;
@@ -580,18 +742,14 @@
     }
     for (const auto portId : GetNonExistentIds(portIds)) {
         std::vector<AudioRoute> routes;
-        ScopedAStatus status = module->getAudioRoutesForAudioPort(portId, &routes);
-        EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
-                << status << " returned for port ID " << portId;
+        EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->getAudioRoutesForAudioPort(portId, &routes))
+                << "port ID " << portId;
     }
 }
 
 TEST_P(AudioCoreModule, CheckDevicePorts) {
     std::vector<AudioPort> ports;
-    {
-        ScopedAStatus status = module->getAudioPorts(&ports);
-        ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
-    }
+    ASSERT_IS_OK(module->getAudioPorts(&ports));
     std::optional<int32_t> defaultOutput, defaultInput;
     std::set<AudioDevice> inputs, outputs;
     const int defaultDeviceFlag = 1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE;
@@ -617,7 +775,7 @@
                         << "At least two output device ports are declared as default: "
                         << defaultOutput.value() << " and " << port.id;
                 defaultOutput = port.id;
-                EXPECT_EQ(0, outputs.count(devicePort.device))
+                EXPECT_EQ(0UL, outputs.count(devicePort.device))
                         << "Non-unique output device: " << devicePort.device.toString();
                 outputs.insert(devicePort.device);
             } else if (port.flags.getTag() == AudioIoFlags::Tag::input) {
@@ -625,7 +783,7 @@
                         << "At least two input device ports are declared as default: "
                         << defaultInput.value() << " and " << port.id;
                 defaultInput = port.id;
-                EXPECT_EQ(0, inputs.count(devicePort.device))
+                EXPECT_EQ(0UL, inputs.count(devicePort.device))
                         << "Non-unique input device: " << devicePort.device.toString();
                 inputs.insert(devicePort.device);
             } else {
@@ -637,17 +795,14 @@
 
 TEST_P(AudioCoreModule, CheckMixPorts) {
     std::vector<AudioPort> ports;
-    {
-        ScopedAStatus status = module->getAudioPorts(&ports);
-        ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
-    }
+    ASSERT_IS_OK(module->getAudioPorts(&ports));
     std::optional<int32_t> primaryMixPort;
-    constexpr int primaryOutputFlag = 1 << static_cast<int>(AudioOutputFlags::PRIMARY);
     for (const auto& port : ports) {
         if (port.ext.getTag() != AudioPortExt::Tag::mix) continue;
         const auto& mixPort = port.ext.get<AudioPortExt::Tag::mix>();
         if (port.flags.getTag() == AudioIoFlags::Tag::output &&
-            ((port.flags.get<AudioIoFlags::Tag::output>() & primaryOutputFlag) != 0)) {
+            isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
+                                 AudioOutputFlags::PRIMARY)) {
             EXPECT_FALSE(primaryMixPort.has_value())
                     << "At least two mix ports have PRIMARY flag set: " << primaryMixPort.value()
                     << " and " << port.id;
@@ -667,15 +822,13 @@
     }
     for (const auto portId : portIds) {
         AudioPort port;
-        ScopedAStatus status = module->getAudioPort(portId, &port);
-        EXPECT_EQ(EX_NONE, status.getExceptionCode()) << status;
+        EXPECT_IS_OK(module->getAudioPort(portId, &port));
         EXPECT_EQ(portId, port.id);
     }
     for (const auto portId : GetNonExistentIds(portIds)) {
         AudioPort port;
-        ScopedAStatus status = module->getAudioPort(portId, &port);
-        EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
-                << status << " returned for port ID " << portId;
+        EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->getAudioPort(portId, &port))
+                << "port ID " << portId;
     }
 }
 
@@ -707,12 +860,11 @@
                   portConnected.get().ext.get<AudioPortExt::Tag::device>().device);
         // Verify that 'getAudioPort' and 'getAudioPorts' return the same connected port.
         AudioPort connectedPort;
-        ScopedAStatus status = module->getAudioPort(connectedPortId, &connectedPort);
-        EXPECT_EQ(EX_NONE, status.getExceptionCode())
-                << status << " returned for getAudioPort port ID " << connectedPortId;
+        EXPECT_IS_OK(module->getAudioPort(connectedPortId, &connectedPort))
+                << "port ID " << connectedPortId;
         EXPECT_EQ(portConnected.get(), connectedPort);
         const auto& portProfiles = connectedPort.profiles;
-        EXPECT_NE(0, portProfiles.size())
+        EXPECT_NE(0UL, portProfiles.size())
                 << "Connected port has no profiles: " << connectedPort.toString();
         const auto dynamicProfileIt =
                 std::find_if(portProfiles.begin(), portProfiles.end(), [](const auto& profile) {
@@ -722,10 +874,7 @@
                                                         << "profiles: " << connectedPort.toString();
 
         std::vector<AudioPort> allPorts;
-        {
-            ScopedAStatus status = module->getAudioPorts(&allPorts);
-            ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
-        }
+        ASSERT_IS_OK(module->getAudioPorts(&allPorts));
         const auto allPortsIt = findById(allPorts, connectedPortId);
         EXPECT_NE(allPorts.end(), allPortsIt);
         if (allPortsIt != allPorts.end()) {
@@ -739,18 +888,22 @@
     ASSERT_NO_FATAL_FAILURE(GetAllPortConfigIds(&portConfigIds));
     for (const auto portConfigId : GetNonExistentIds(portConfigIds)) {
         {
-            std::shared_ptr<IStreamIn> stream;
-            ScopedAStatus status = module->openInputStream(portConfigId, {}, &stream);
-            EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
-                    << status << " openInputStream returned for port config ID " << portConfigId;
-            EXPECT_EQ(nullptr, stream);
+            aidl::android::hardware::audio::core::IModule::OpenInputStreamArguments args;
+            args.portConfigId = portConfigId;
+            args.bufferSizeFrames = kDefaultBufferSizeFrames;
+            aidl::android::hardware::audio::core::IModule::OpenInputStreamReturn ret;
+            EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->openInputStream(args, &ret))
+                    << "port config ID " << portConfigId;
+            EXPECT_EQ(nullptr, ret.stream);
         }
         {
-            std::shared_ptr<IStreamOut> stream;
-            ScopedAStatus status = module->openOutputStream(portConfigId, {}, {}, &stream);
-            EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
-                    << status << " openOutputStream returned for port config ID " << portConfigId;
-            EXPECT_EQ(nullptr, stream);
+            aidl::android::hardware::audio::core::IModule::OpenOutputStreamArguments args;
+            args.portConfigId = portConfigId;
+            args.bufferSizeFrames = kDefaultBufferSizeFrames;
+            aidl::android::hardware::audio::core::IModule::OpenOutputStreamReturn ret;
+            EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->openOutputStream(args, &ret))
+                    << "port config ID " << portConfigId;
+            EXPECT_EQ(nullptr, ret.stream);
         }
     }
 }
@@ -764,12 +917,9 @@
     std::set<int32_t> portIds;
     ASSERT_NO_FATAL_FAILURE(GetAllPortIds(&portIds));
     std::vector<AudioPortConfig> portConfigs;
-    {
-        ScopedAStatus status = module->getAudioPortConfigs(&portConfigs);
-        ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
-    }
+    ASSERT_IS_OK(module->getAudioPortConfigs(&portConfigs));
     for (const auto& config : portConfigs) {
-        EXPECT_EQ(1, portIds.count(config.portId))
+        EXPECT_EQ(1UL, portIds.count(config.portId))
                 << config.portId << " port id is unknown, config id " << config.id;
     }
 }
@@ -778,9 +928,8 @@
     std::set<int32_t> portConfigIds;
     ASSERT_NO_FATAL_FAILURE(GetAllPortConfigIds(&portConfigIds));
     for (const auto portConfigId : GetNonExistentIds(portConfigIds)) {
-        ScopedAStatus status = module->resetAudioPortConfig(portConfigId);
-        EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
-                << status << " returned for port config ID " << portConfigId;
+        EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->resetAudioPortConfig(portConfigId))
+                << "port config ID " << portConfigId;
     }
 }
 
@@ -788,21 +937,13 @@
 // the config does not delete it, but brings it back to the initial config.
 TEST_P(AudioCoreModule, ResetAudioPortConfigToInitialValue) {
     std::vector<AudioPortConfig> portConfigsBefore;
-    {
-        ScopedAStatus status = module->getAudioPortConfigs(&portConfigsBefore);
-        ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
-    }
+    ASSERT_IS_OK(module->getAudioPortConfigs(&portConfigsBefore));
     // TODO: Change port configs according to port profiles.
     for (const auto& c : portConfigsBefore) {
-        ScopedAStatus status = module->resetAudioPortConfig(c.id);
-        EXPECT_EQ(EX_NONE, status.getExceptionCode())
-                << status << " returned for port config ID " << c.id;
+        EXPECT_IS_OK(module->resetAudioPortConfig(c.id)) << "port config ID " << c.id;
     }
     std::vector<AudioPortConfig> portConfigsAfter;
-    {
-        ScopedAStatus status = module->getAudioPortConfigs(&portConfigsAfter);
-        ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
-    }
+    ASSERT_IS_OK(module->getAudioPortConfigs(&portConfigsAfter));
     for (const auto& c : portConfigsBefore) {
         auto afterIt = findById<AudioPortConfig>(portConfigsAfter, c.id);
         EXPECT_NE(portConfigsAfter.end(), afterIt)
@@ -824,9 +965,8 @@
     portConfig.portId = srcMixPort.value().id;
     {
         bool applied = true;
-        ScopedAStatus status = module->setAudioPortConfig(portConfig, &suggestedConfig, &applied);
-        ASSERT_EQ(EX_NONE, status.getExceptionCode())
-                << status << "; Config: " << portConfig.toString();
+        ASSERT_IS_OK(module->setAudioPortConfig(portConfig, &suggestedConfig, &applied))
+                << "Config: " << portConfig.toString();
         EXPECT_FALSE(applied);
     }
     EXPECT_EQ(0, suggestedConfig.id);
@@ -880,9 +1020,9 @@
         AudioPortConfig portConfig, suggestedConfig;
         bool applied;
         portConfig.portId = portId;
-        ScopedAStatus status = module->setAudioPortConfig(portConfig, &suggestedConfig, &applied);
-        EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
-                << status << " returned for port ID " << portId;
+        EXPECT_STATUS(EX_ILLEGAL_ARGUMENT,
+                      module->setAudioPortConfig(portConfig, &suggestedConfig, &applied))
+                << "port ID " << portId;
         EXPECT_FALSE(suggestedConfig.format.has_value());
         EXPECT_FALSE(suggestedConfig.channelMask.has_value());
         EXPECT_FALSE(suggestedConfig.sampleRate.has_value());
@@ -896,9 +1036,9 @@
         AudioPortConfig portConfig, suggestedConfig;
         bool applied;
         portConfig.id = portConfigId;
-        ScopedAStatus status = module->setAudioPortConfig(portConfig, &suggestedConfig, &applied);
-        EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
-                << status << " returned for port config ID " << portConfigId;
+        EXPECT_STATUS(EX_ILLEGAL_ARGUMENT,
+                      module->setAudioPortConfig(portConfig, &suggestedConfig, &applied))
+                << "port config ID " << portConfigId;
         EXPECT_FALSE(suggestedConfig.format.has_value());
         EXPECT_FALSE(suggestedConfig.channelMask.has_value());
         EXPECT_FALSE(suggestedConfig.sampleRate.has_value());
@@ -919,9 +1059,8 @@
         AudioPort portWithData = port;
         portWithData.ext.get<AudioPortExt::Tag::device>().device.address =
                 GenerateUniqueDeviceAddress();
-        ScopedAStatus status = module->connectExternalDevice(portWithData, &ignored);
-        EXPECT_EQ(EX_ILLEGAL_STATE, status.getExceptionCode())
-                << status << " returned for static port " << portWithData.toString();
+        EXPECT_STATUS(EX_ILLEGAL_STATE, module->connectExternalDevice(portWithData, &ignored))
+                << "static port " << portWithData.toString();
     }
 }
 
@@ -935,10 +1074,8 @@
     ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get()));
     ModuleDebug midwayDebugChange = debug.flags();
     midwayDebugChange.simulateDeviceConnections = false;
-    ScopedAStatus status = module->setModuleDebug(midwayDebugChange);
-    EXPECT_EQ(EX_ILLEGAL_STATE, status.getExceptionCode())
-            << status << " returned when trying to disable connections simulation "
-            << "while having a connected device";
+    EXPECT_STATUS(EX_ILLEGAL_STATE, module->setModuleDebug(midwayDebugChange))
+            << "when trying to disable connections simulation while having a connected device";
 }
 
 TEST_P(AudioCoreModule, ConnectDisconnectExternalDeviceInvalidPorts) {
@@ -948,40 +1085,28 @@
     for (const auto portId : GetNonExistentIds(portIds)) {
         AudioPort invalidPort;
         invalidPort.id = portId;
-        ScopedAStatus status = module->connectExternalDevice(invalidPort, &ignored);
-        EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
-                << status << " returned for port ID " << portId << " when setting CONNECTED state";
-        status = module->disconnectExternalDevice(portId);
-        EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
-                << status << " returned for port ID " << portId
-                << " when setting DISCONNECTED state";
+        EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->connectExternalDevice(invalidPort, &ignored))
+                << "port ID " << portId << ", when setting CONNECTED state";
+        EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->disconnectExternalDevice(portId))
+                << "port ID " << portId << ", when setting DISCONNECTED state";
     }
 
     std::vector<AudioPort> ports;
-    {
-        ScopedAStatus status = module->getAudioPorts(&ports);
-        ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
-    }
+    ASSERT_IS_OK(module->getAudioPorts(&ports));
     for (const auto& port : ports) {
         if (port.ext.getTag() != AudioPortExt::Tag::device) {
-            ScopedAStatus status = module->connectExternalDevice(port, &ignored);
-            EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
-                    << status << " returned for non-device port ID " << port.id
-                    << " when setting CONNECTED state";
-            status = module->disconnectExternalDevice(port.id);
-            EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
-                    << status << " returned for non-device port ID " << port.id
-                    << " when setting DISCONNECTED state";
+            EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->connectExternalDevice(port, &ignored))
+                    << "non-device port ID " << port.id << " when setting CONNECTED state";
+            EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->disconnectExternalDevice(port.id))
+                    << "non-device port ID " << port.id << " when setting DISCONNECTED state";
         } else {
             const auto& devicePort = port.ext.get<AudioPortExt::Tag::device>();
             if (devicePort.device.type.connection.empty()) {
-                ScopedAStatus status = module->connectExternalDevice(port, &ignored);
-                EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
-                        << status << " returned for permanently attached device port ID " << port.id
+                EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->connectExternalDevice(port, &ignored))
+                        << "for a permanently attached device port ID " << port.id
                         << " when setting CONNECTED state";
-                status = module->disconnectExternalDevice(port.id);
-                EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
-                        << status << " returned for permanently attached device port ID " << port.id
+                EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->disconnectExternalDevice(port.id))
+                        << "for a permanently attached device port ID " << port.id
                         << " when setting DISCONNECTED state";
             }
         }
@@ -997,27 +1122,22 @@
         GTEST_SKIP() << "No external devices in the module.";
     }
     for (const auto& port : ports) {
-        ScopedAStatus status = module->disconnectExternalDevice(port.id);
-        EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
-                << status << " returned when disconnecting already disconnected device port ID "
-                << port.id;
+        EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->disconnectExternalDevice(port.id))
+                << "when disconnecting already disconnected device port ID " << port.id;
         AudioPort portWithData = port;
         portWithData.ext.get<AudioPortExt::Tag::device>().device.address =
                 GenerateUniqueDeviceAddress();
         WithDevicePortConnectedState portConnected(portWithData);
         ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get()));
-        status = module->connectExternalDevice(portConnected.get(), &ignored);
-        EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
-                << status << " returned when trying to connect a connected device port "
+        EXPECT_STATUS(EX_ILLEGAL_ARGUMENT,
+                      module->connectExternalDevice(portConnected.get(), &ignored))
+                << "when trying to connect a connected device port "
                 << portConnected.get().toString();
-        status = module->connectExternalDevice(portWithData, &ignored);
-        EXPECT_EQ(EX_ILLEGAL_STATE, status.getExceptionCode())
-                << status << " returned when connecting again the external device "
-                << portWithData.ext.get<AudioPortExt::Tag::device>().device.toString();
-        if (status.getExceptionCode() == EX_NONE) {
-            ADD_FAILURE() << "Returned connected port " << ignored.toString() << " for template "
-                          << portWithData.toString();
-        }
+        EXPECT_STATUS(EX_ILLEGAL_STATE, module->connectExternalDevice(portWithData, &ignored))
+                << "when connecting again the external device "
+                << portWithData.ext.get<AudioPortExt::Tag::device>().device.toString()
+                << "; Returned connected port " << ignored.toString() << " for template "
+                << portWithData.toString();
     }
 }
 
@@ -1038,9 +1158,8 @@
             // Our test assumes that 'getAudioPort' returns at least one profile, and it
             // is not a dynamic profile.
             ASSERT_NO_FATAL_FAILURE(config.SetUp(module.get()));
-            ScopedAStatus status = module->disconnectExternalDevice(portConnected.getId());
-            EXPECT_EQ(EX_ILLEGAL_STATE, status.getExceptionCode())
-                    << status << " returned when trying to disconnect device port ID " << port.id
+            EXPECT_STATUS(EX_ILLEGAL_STATE, module->disconnectExternalDevice(portConnected.getId()))
+                    << "when trying to disconnect device port ID " << port.id
                     << " with active configuration " << config.getId();
         }
     }
@@ -1054,10 +1173,7 @@
     }
     for (const auto& port : ports) {
         std::vector<AudioRoute> routesBefore;
-        {
-            ScopedAStatus status = module->getAudioRoutes(&routesBefore);
-            ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
-        }
+        ASSERT_IS_OK(module->getAudioRoutes(&routesBefore));
 
         int32_t connectedPortId;
         {
@@ -1065,34 +1181,24 @@
             ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get()));
             connectedPortId = portConnected.getId();
             std::vector<AudioRoute> connectedPortRoutes;
-            {
-                ScopedAStatus status =
-                        module->getAudioRoutesForAudioPort(connectedPortId, &connectedPortRoutes);
-                ASSERT_EQ(EX_NONE, status.getExceptionCode())
-                        << status << " returned when retrieving routes for connected port id "
-                        << connectedPortId;
-            }
+            ASSERT_IS_OK(module->getAudioRoutesForAudioPort(connectedPortId, &connectedPortRoutes))
+                    << "when retrieving routes for connected port id " << connectedPortId;
             // There must be routes for the port to be useful.
             if (connectedPortRoutes.empty()) {
                 std::vector<AudioRoute> allRoutes;
-                ScopedAStatus status = module->getAudioRoutes(&allRoutes);
-                ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
+                ASSERT_IS_OK(module->getAudioRoutes(&allRoutes));
                 ADD_FAILURE() << " no routes returned for the connected port "
                               << portConnected.get().toString()
                               << "; all routes: " << android::internal::ToString(allRoutes);
             }
         }
         std::vector<AudioRoute> ignored;
-        ScopedAStatus status = module->getAudioRoutesForAudioPort(connectedPortId, &ignored);
-        ASSERT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
-                << status << " returned when retrieving routes for released connected port id "
-                << connectedPortId;
+        ASSERT_STATUS(EX_ILLEGAL_ARGUMENT,
+                      module->getAudioRoutesForAudioPort(connectedPortId, &ignored))
+                << "when retrieving routes for released connected port id " << connectedPortId;
 
         std::vector<AudioRoute> routesAfter;
-        {
-            ScopedAStatus status = module->getAudioRoutes(&routesAfter);
-            ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
-        }
+        ASSERT_IS_OK(module->getAudioRoutes(&routesAfter));
         ASSERT_EQ(routesBefore.size(), routesAfter.size())
                 << "Sizes of audio route arrays do not match after creating and "
                 << "releasing a connected port";
@@ -1105,39 +1211,67 @@
 template <typename Stream>
 class AudioStream : public AudioCoreModule {
   public:
-    static std::string direction(bool capitalize);
-
     void SetUp() override {
         ASSERT_NO_FATAL_FAILURE(AudioCoreModule::SetUp());
         ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
     }
 
     void CloseTwice() {
-        const auto portConfig = moduleConfig->getSingleConfigForMixPort(IsInput<Stream>());
+        const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits<Stream>::is_input);
         if (!portConfig.has_value()) {
             GTEST_SKIP() << "No mix port for attached devices";
         }
         std::shared_ptr<Stream> heldStream;
         {
             WithStream<Stream> stream(portConfig.value());
-            ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get()));
+            ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
             heldStream = stream.getSharedPointer();
         }
-        ScopedAStatus status = heldStream->close();
-        EXPECT_EQ(EX_ILLEGAL_STATE, status.getExceptionCode())
-                << status << " when closing the stream twice";
+        EXPECT_STATUS(EX_ILLEGAL_STATE, heldStream->close()) << "when closing the stream twice";
     }
 
     void OpenAllConfigs() {
-        const auto allPortConfigs = moduleConfig->getPortConfigsForMixPorts(IsInput<Stream>());
+        const auto allPortConfigs =
+                moduleConfig->getPortConfigsForMixPorts(IOTraits<Stream>::is_input);
         for (const auto& portConfig : allPortConfigs) {
             WithStream<Stream> stream(portConfig);
-            ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get()));
+            ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
         }
     }
 
+    void OpenInvalidBufferSize() {
+        const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits<Stream>::is_input);
+        if (!portConfig.has_value()) {
+            GTEST_SKIP() << "No mix port for attached devices";
+        }
+        WithStream<Stream> stream(portConfig.value());
+        ASSERT_NO_FATAL_FAILURE(stream.SetUpPortConfig(module.get()));
+        // The buffer size of 1 frame should be impractically small, and thus
+        // less than any minimum buffer size suggested by any HAL.
+        for (long bufferSize : std::array<long, 4>{-1, 0, 1, std::numeric_limits<long>::max()}) {
+            EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, stream.SetUpNoChecks(module.get(), bufferSize))
+                    << "for the buffer size " << bufferSize;
+            EXPECT_EQ(nullptr, stream.get());
+        }
+    }
+
+    void OpenInvalidDirection() {
+        // Important! The direction of the port config must be reversed.
+        const auto portConfig =
+                moduleConfig->getSingleConfigForMixPort(!IOTraits<Stream>::is_input);
+        if (!portConfig.has_value()) {
+            GTEST_SKIP() << "No mix port for attached devices";
+        }
+        WithStream<Stream> stream(portConfig.value());
+        ASSERT_NO_FATAL_FAILURE(stream.SetUpPortConfig(module.get()));
+        EXPECT_STATUS(EX_ILLEGAL_ARGUMENT,
+                      stream.SetUpNoChecks(module.get(), kDefaultBufferSizeFrames))
+                << "port config ID " << stream.getPortId();
+        EXPECT_EQ(nullptr, stream.get());
+    }
+
     void OpenOverMaxCount() {
-        constexpr bool isInput = IsInput<Stream>();
+        constexpr bool isInput = IOTraits<Stream>::is_input;
         auto ports = moduleConfig->getMixPorts(isInput);
         bool hasSingleRun = false;
         for (const auto& port : ports) {
@@ -1158,97 +1292,203 @@
                 streamWraps[i].emplace(portConfigs[i]);
                 WithStream<Stream>& stream = streamWraps[i].value();
                 if (i < maxStreamCount) {
-                    ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get()));
+                    ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
                 } else {
                     ASSERT_NO_FATAL_FAILURE(stream.SetUpPortConfig(module.get()));
-                    ScopedAStatus status = stream.SetUpNoChecks(module.get());
-                    EXPECT_EQ(EX_ILLEGAL_STATE, status.getExceptionCode())
-                            << status << " open" << direction(true)
-                            << "Stream returned for port config ID " << stream.getPortId()
-                            << ", maxOpenStreamCount is " << maxStreamCount;
+                    EXPECT_STATUS(EX_ILLEGAL_STATE,
+                                  stream.SetUpNoChecks(module.get(), kDefaultBufferSizeFrames))
+                            << "port config ID " << stream.getPortId() << ", maxOpenStreamCount is "
+                            << maxStreamCount;
                 }
             }
         }
         if (!hasSingleRun) {
-            GTEST_SKIP() << "Not enough " << direction(false)
-                         << " ports to test max open stream count";
+            GTEST_SKIP() << "Not enough ports to test max open stream count";
         }
     }
 
-    void OpenInvalidDirection() {
-        // Important! The direction of the port config must be reversed.
-        const auto portConfig = moduleConfig->getSingleConfigForMixPort(!IsInput<Stream>());
-        if (!portConfig.has_value()) {
-            GTEST_SKIP() << "No mix port for attached devices";
-        }
-        WithStream<Stream> stream(portConfig.value());
-        ASSERT_NO_FATAL_FAILURE(stream.SetUpPortConfig(module.get()));
-        ScopedAStatus status = stream.SetUpNoChecks(module.get());
-        EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
-                << status << " open" << direction(true) << "Stream returned for port config ID "
-                << stream.getPortId();
-        EXPECT_EQ(nullptr, stream.get());
-    }
-
     void OpenTwiceSamePortConfig() {
-        const auto portConfig = moduleConfig->getSingleConfigForMixPort(IsInput<Stream>());
+        const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits<Stream>::is_input);
         if (!portConfig.has_value()) {
             GTEST_SKIP() << "No mix port for attached devices";
         }
         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(IsInput<Stream>());
+        const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits<Stream>::is_input);
         if (!portConfig.has_value()) {
             GTEST_SKIP() << "No mix port for attached devices";
         }
         WithStream<Stream> stream(portConfig.value());
-        ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get()));
-        ScopedAStatus status = module->resetAudioPortConfig(stream.getPortId());
-        EXPECT_EQ(EX_ILLEGAL_STATE, status.getExceptionCode())
-                << status << " returned for port config ID " << stream.getPortId();
+        ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+        EXPECT_STATUS(EX_ILLEGAL_STATE, module->resetAudioPortConfig(stream.getPortId()))
+                << "port config ID " << stream.getPortId();
+    }
+
+    void SendInvalidCommand() {
+        const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits<Stream>::is_input);
+        if (!portConfig.has_value()) {
+            GTEST_SKIP() << "No mix port for attached devices";
+        }
+        EXPECT_NO_FATAL_FAILURE(SendInvalidCommandImpl(portConfig.value()));
     }
 
     void OpenTwiceSamePortConfigImpl(const AudioPortConfig& portConfig) {
         WithStream<Stream> stream1(portConfig);
-        ASSERT_NO_FATAL_FAILURE(stream1.SetUp(module.get()));
+        ASSERT_NO_FATAL_FAILURE(stream1.SetUp(module.get(), kDefaultBufferSizeFrames));
         WithStream<Stream> stream2;
-        ScopedAStatus status = stream2.SetUpNoChecks(module.get(), stream1.getPortConfig());
-        EXPECT_EQ(EX_ILLEGAL_STATE, status.getExceptionCode())
-                << status << " when opening " << direction(false)
-                << " stream twice for the same port config ID " << stream1.getPortId();
+        EXPECT_STATUS(EX_ILLEGAL_STATE, stream2.SetUpNoChecks(module.get(), stream1.getPortConfig(),
+                                                              kDefaultBufferSizeFrames))
+                << "when opening a stream twice for the same port config ID "
+                << 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[4].fmqByteCount = -1;
+        commands[5].code = StreamDescriptor::COMMAND_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);
     }
 };
 using AudioStreamIn = AudioStream<IStreamIn>;
 using AudioStreamOut = AudioStream<IStreamOut>;
 
-template <>
-std::string AudioStreamIn::direction(bool capitalize) {
-    return capitalize ? "Input" : "input";
-}
-template <>
-std::string AudioStreamOut::direction(bool capitalize) {
-    return capitalize ? "Output" : "output";
-}
-
 #define TEST_IO_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_P(AudioStreamOut, OpenTwicePrimary) {
     const auto mixPorts = moduleConfig->getMixPorts(false);
     auto primaryPortIt = std::find_if(mixPorts.begin(), mixPorts.end(), [](const AudioPort& port) {
-        constexpr int primaryOutputFlag = 1 << static_cast<int>(AudioOutputFlags::PRIMARY);
         return port.flags.getTag() == AudioIoFlags::Tag::output &&
-               (port.flags.get<AudioIoFlags::Tag::output>() & primaryOutputFlag) != 0;
+               isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
+                                    AudioOutputFlags::PRIMARY);
     });
     if (primaryPortIt == mixPorts.end()) {
         GTEST_SKIP() << "No primary mix port";
@@ -1262,28 +1502,25 @@
 }
 
 TEST_P(AudioStreamOut, RequireOffloadInfo) {
-    const auto mixPorts = moduleConfig->getMixPorts(false);
-    auto offloadPortIt = std::find_if(mixPorts.begin(), mixPorts.end(), [&](const AudioPort& port) {
-        constexpr int compressOffloadFlag = 1
-                                            << static_cast<int>(AudioOutputFlags::COMPRESS_OFFLOAD);
-        return port.flags.getTag() == AudioIoFlags::Tag::output &&
-               (port.flags.get<AudioIoFlags::Tag::output>() & compressOffloadFlag) != 0 &&
-               !moduleConfig->getAttachedSinkDevicesPortsForMixPort(port).empty();
-    });
-    if (offloadPortIt == mixPorts.end()) {
+    const auto offloadMixPorts =
+            moduleConfig->getOffloadMixPorts(true /*attachedOnly*/, true /*singlePort*/);
+    if (offloadMixPorts.empty()) {
         GTEST_SKIP()
                 << "No mix port for compressed offload that could be routed to attached devices";
     }
-    const auto portConfig = moduleConfig->getSingleConfigForMixPort(false, *offloadPortIt);
+    const auto portConfig =
+            moduleConfig->getSingleConfigForMixPort(false, *offloadMixPorts.begin());
     ASSERT_TRUE(portConfig.has_value())
             << "No profiles specified for the compressed offload mix port";
+    StreamDescriptor descriptor;
     std::shared_ptr<IStreamOut> ignored;
-    ScopedAStatus status = module->openOutputStream(portConfig.value().id,
-                                                    GenerateSourceMetadata(portConfig.value()),
-                                                    {} /* offloadInfo */, &ignored);
-    EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
-            << status
-            << " returned when no offload info is provided for a compressed offload mix port";
+    aidl::android::hardware::audio::core::IModule::OpenOutputStreamArguments args;
+    args.portConfigId = portConfig.value().id;
+    args.sourceMetadata = GenerateSourceMetadata(portConfig.value());
+    args.bufferSizeFrames = kDefaultBufferSizeFrames;
+    aidl::android::hardware::audio::core::IModule::OpenOutputStreamReturn ret;
+    EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->openOutputStream(args, &ret))
+            << "when no offload info is provided for a compressed offload mix port";
 }
 
 // Tests specific to audio patches. The fixure class is named 'AudioModulePatch'
@@ -1304,9 +1541,8 @@
         AudioPatch patch;
         patch.sourcePortConfigIds = sources;
         patch.sinkPortConfigIds = sinks;
-        ScopedAStatus status = module->setAudioPatch(patch, &patch);
-        ASSERT_EQ(expectedException, status.getExceptionCode())
-                << status << ": patch source ids: " << android::internal::ToString(sources)
+        ASSERT_STATUS(expectedException, module->setAudioPatch(patch, &patch))
+                << "patch source ids: " << android::internal::ToString(sources)
                 << "; sink ids: " << android::internal::ToString(sinks);
     }
 
@@ -1324,9 +1560,8 @@
                                           patch.get().sinkPortConfigIds.begin(),
                                           patch.get().sinkPortConfigIds.end());
         for (const auto portConfigId : sourceAndSinkPortConfigIds) {
-            ScopedAStatus status = module->resetAudioPortConfig(portConfigId);
-            EXPECT_EQ(EX_ILLEGAL_STATE, status.getExceptionCode())
-                    << status << " returned for port config ID " << portConfigId;
+            EXPECT_STATUS(EX_ILLEGAL_STATE, module->resetAudioPortConfig(portConfigId))
+                    << "port config ID " << portConfigId;
         }
     }
 
@@ -1371,10 +1606,8 @@
         }
         WithAudioPatch patch(srcSinkPair.value().first, srcSinkPair.value().second);
         ASSERT_NO_FATAL_FAILURE(patch.SetUpPortConfigs(module.get()));
-        ScopedAStatus status = patch.SetUpNoChecks(module.get());
-        EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
-                << status << ": when setting up a patch from "
-                << srcSinkPair.value().first.toString() << " to "
+        EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, patch.SetUpNoChecks(module.get()))
+                << "when setting up a patch from " << srcSinkPair.value().first.toString() << " to "
                 << srcSinkPair.value().second.toString() << " that does not have a route";
     }
 
@@ -1430,10 +1663,9 @@
         for (const auto patchId : GetNonExistentIds(patchIds)) {
             AudioPatch patchWithNonExistendId = patch.get();
             patchWithNonExistendId.id = patchId;
-            ScopedAStatus status =
-                    module->setAudioPatch(patchWithNonExistendId, &patchWithNonExistendId);
-            EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
-                    << status << " returned for patch ID " << patchId;
+            EXPECT_STATUS(EX_ILLEGAL_ARGUMENT,
+                          module->setAudioPatch(patchWithNonExistendId, &patchWithNonExistendId))
+                    << "patch ID " << patchId;
         }
     }
 };
@@ -1455,9 +1687,8 @@
     std::set<int32_t> patchIds;
     ASSERT_NO_FATAL_FAILURE(GetAllPatchIds(&patchIds));
     for (const auto patchId : GetNonExistentIds(patchIds)) {
-        ScopedAStatus status = module->resetAudioPatch(patchId);
-        EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
-                << status << " returned for patch ID " << patchId;
+        EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->resetAudioPatch(patchId))
+                << "patch ID " << patchId;
     }
 }
 
diff --git a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
new file mode 100644
index 0000000..9b100b1
--- /dev/null
+++ b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
@@ -0,0 +1,331 @@
+/*
+ * 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 <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>
+#include <android-base/properties.h>
+#include <android/binder_interface_utils.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include <aidl/android/hardware/audio/effect/IFactory.h>
+
+#include "AudioHalBinderServiceUtil.h"
+
+using namespace android;
+
+using ndk::ScopedAStatus;
+
+using aidl::android::hardware::audio::effect::Descriptor;
+using aidl::android::hardware::audio::effect::IEffect;
+using aidl::android::hardware::audio::effect::IFactory;
+using aidl::android::media::audio::common::AudioUuid;
+
+class EffectFactoryHelper {
+  public:
+    EffectFactoryHelper(const std::string& name) : mServiceName(name) {}
+
+    void ConnectToFactoryService() {
+        mEffectFactory = IFactory::fromBinder(binderUtil.connectToService(mServiceName));
+        ASSERT_NE(mEffectFactory, nullptr);
+    }
+
+    void RestartFactoryService() {
+        ASSERT_NE(mEffectFactory, nullptr);
+        mEffectFactory = IFactory::fromBinder(binderUtil.restartService());
+        ASSERT_NE(mEffectFactory, nullptr);
+    }
+
+    void QueryAllEffects() {
+        EXPECT_NE(mEffectFactory, nullptr);
+        ScopedAStatus status =
+                mEffectFactory->queryEffects(std::nullopt, std::nullopt, &mCompleteIds);
+        EXPECT_EQ(status.getExceptionCode(), EX_NONE);
+    }
+
+    void QueryEffects(const std::optional<AudioUuid>& in_type,
+                      const std::optional<AudioUuid>& in_instance,
+                      std::vector<Descriptor::Identity>* _aidl_return) {
+        EXPECT_NE(mEffectFactory, nullptr);
+        ScopedAStatus status = mEffectFactory->queryEffects(in_type, in_instance, _aidl_return);
+        EXPECT_EQ(status.getExceptionCode(), EX_NONE);
+        mIds = *_aidl_return;
+    }
+
+    void CreateEffects() {
+        EXPECT_NE(mEffectFactory, nullptr);
+        ScopedAStatus status;
+        for (const auto& id : mIds) {
+            std::shared_ptr<IEffect> effect;
+            status = mEffectFactory->createEffect(id.uuid, &effect);
+            EXPECT_EQ(status.getExceptionCode(), EX_NONE) << id.toString();
+            EXPECT_NE(effect, nullptr) << id.toString();
+            mEffectIdMap[effect] = id;
+        }
+    }
+
+    void DestroyEffects() {
+        EXPECT_NE(mEffectFactory, nullptr);
+        ScopedAStatus status;
+        for (const auto& it : mEffectIdMap) {
+            status = mEffectFactory->destroyEffect(it.first);
+            EXPECT_EQ(status.getExceptionCode(), EX_NONE) << it.second.toString();
+        }
+        mEffectIdMap.clear();
+    }
+
+    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::unordered_map<std::shared_ptr<IEffect>, Descriptor::Identity>& GetEffectMap() {
+        return mEffectIdMap;
+    }
+
+  private:
+    std::shared_ptr<IFactory> mEffectFactory;
+    std::string mServiceName;
+    AudioHalBinderServiceUtil binderUtil;
+    std::vector<Descriptor::Identity> mIds;
+    std::vector<Descriptor::Identity> mCompleteIds;
+    std::unordered_map<std::shared_ptr<IEffect>, Descriptor::Identity> mEffectIdMap;
+};
+
+/// Effect factory testing.
+class EffectFactoryTest : public testing::TestWithParam<std::string> {
+  public:
+    void SetUp() override { ASSERT_NO_FATAL_FAILURE(mFactory.ConnectToFactoryService()); }
+
+    void TearDown() override { mFactory.DestroyEffects(); }
+
+    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}};
+};
+
+TEST_P(EffectFactoryTest, SetupAndTearDown) {
+    // Intentionally empty test body.
+}
+
+TEST_P(EffectFactoryTest, CanBeRestarted) {
+    ASSERT_NO_FATAL_FAILURE(mFactory.RestartFactoryService());
+}
+
+TEST_P(EffectFactoryTest, QueriedDescriptorList) {
+    std::vector<Descriptor::Identity> descriptors;
+    mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
+    EXPECT_NE(static_cast<int>(descriptors.size()), 0);
+}
+
+TEST_P(EffectFactoryTest, DescriptorUUIDNotNull) {
+    std::vector<Descriptor::Identity> descriptors;
+    mFactory.QueryEffects(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);
+    }
+}
+
+TEST_P(EffectFactoryTest, QueriedDescriptorNotExistType) {
+    std::vector<Descriptor::Identity> descriptors;
+    mFactory.QueryEffects(nullUuid, std::nullopt, &descriptors);
+    EXPECT_EQ(static_cast<int>(descriptors.size()), 0);
+}
+
+TEST_P(EffectFactoryTest, QueriedDescriptorNotExistInstance) {
+    std::vector<Descriptor::Identity> descriptors;
+    mFactory.QueryEffects(std::nullopt, nullUuid, &descriptors);
+    EXPECT_EQ(static_cast<int>(descriptors.size()), 0);
+}
+
+TEST_P(EffectFactoryTest, CreateAndDestroyRepeat) {
+    std::vector<Descriptor::Identity> descriptors;
+    mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
+    int numIds = static_cast<int>(mFactory.GetEffectIds().size());
+    EXPECT_NE(numIds, 0);
+
+    EXPECT_EQ(static_cast<int>(mFactory.GetEffectMap().size()), 0);
+    mFactory.CreateEffects();
+    EXPECT_EQ(static_cast<int>(mFactory.GetEffectMap().size()), numIds);
+    mFactory.DestroyEffects();
+    EXPECT_EQ(static_cast<int>(mFactory.GetEffectMap().size()), 0);
+
+    // Create and destroy again
+    mFactory.CreateEffects();
+    EXPECT_EQ(static_cast<int>(mFactory.GetEffectMap().size()), numIds);
+    mFactory.DestroyEffects();
+    EXPECT_EQ(static_cast<int>(mFactory.GetEffectMap().size()), 0);
+}
+
+TEST_P(EffectFactoryTest, CreateMultipleInstanceOfSameEffect) {
+    std::vector<Descriptor::Identity> descriptors;
+    mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
+    int numIds = static_cast<int>(mFactory.GetEffectIds().size());
+    EXPECT_NE(numIds, 0);
+
+    EXPECT_EQ(static_cast<int>(mFactory.GetEffectMap().size()), 0);
+    mFactory.CreateEffects();
+    EXPECT_EQ(static_cast<int>(mFactory.GetEffectMap().size()), numIds);
+    // Create effect instances of same implementation
+    mFactory.CreateEffects();
+    EXPECT_EQ(static_cast<int>(mFactory.GetEffectMap().size()), 2 * numIds);
+
+    mFactory.CreateEffects();
+    EXPECT_EQ(static_cast<int>(mFactory.GetEffectMap().size()), 3 * numIds);
+
+    mFactory.DestroyEffects();
+    EXPECT_EQ(static_cast<int>(mFactory.GetEffectMap().size()), 0);
+}
+
+INSTANTIATE_TEST_SUITE_P(EffectFactoryTest, EffectFactoryTest,
+                         testing::ValuesIn(android::getAidlHalInstanceNames(IFactory::descriptor)),
+                         android::PrintInstanceNameToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EffectFactoryTest);
+
+/// Effect testing.
+class AudioEffect : public testing::TestWithParam<std::string> {
+  public:
+    void SetUp() override {
+        ASSERT_NO_FATAL_FAILURE(mFactory.ConnectToFactoryService());
+        ASSERT_NO_FATAL_FAILURE(mFactory.CreateEffects());
+    }
+
+    void TearDown() override {
+        CloseEffects();
+        ASSERT_NO_FATAL_FAILURE(mFactory.DestroyEffects());
+    }
+
+    void OpenEffects() {
+        auto open = [](const std::shared_ptr<IEffect>& effect) {
+            ScopedAStatus status = effect->open();
+            EXPECT_EQ(status.getExceptionCode(), EX_NONE);
+        };
+        EXPECT_NO_FATAL_FAILURE(ForEachEffect(open));
+    }
+
+    void CloseEffects() {
+        auto close = [](const std::shared_ptr<IEffect>& effect) {
+            ScopedAStatus status = effect->close();
+            EXPECT_EQ(status.getExceptionCode(), EX_NONE);
+        };
+        EXPECT_NO_FATAL_FAILURE(ForEachEffect(close));
+    }
+
+    void GetEffectDescriptors() {
+        auto get = [](const std::shared_ptr<IEffect>& effect) {
+            Descriptor desc;
+            ScopedAStatus status = effect->getDescriptor(&desc);
+            EXPECT_EQ(status.getExceptionCode(), EX_NONE);
+        };
+        EXPECT_NO_FATAL_FAILURE(ForEachEffect(get));
+    }
+
+    template <typename Functor>
+    void ForEachEffect(Functor functor) {
+        auto effectMap = mFactory.GetEffectMap();
+        ScopedAStatus status;
+        for (const auto& it : effectMap) {
+            SCOPED_TRACE(it.second.toString());
+            functor(it.first);
+        }
+    }
+
+    EffectFactoryHelper mFactory = EffectFactoryHelper(GetParam());
+};
+
+TEST_P(AudioEffect, OpenEffectTest) {
+    EXPECT_NO_FATAL_FAILURE(OpenEffects());
+}
+
+TEST_P(AudioEffect, OpenAndCloseEffect) {
+    EXPECT_NO_FATAL_FAILURE(OpenEffects());
+    EXPECT_NO_FATAL_FAILURE(CloseEffects());
+}
+
+TEST_P(AudioEffect, CloseUnopenedEffectTest) {
+    EXPECT_NO_FATAL_FAILURE(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());
+
+    EXPECT_NO_FATAL_FAILURE(OpenEffects());
+    EXPECT_NO_FATAL_FAILURE(OpenEffects());
+    EXPECT_NO_FATAL_FAILURE(CloseEffects());
+
+    EXPECT_NO_FATAL_FAILURE(OpenEffects());
+    EXPECT_NO_FATAL_FAILURE(CloseEffects());
+    EXPECT_NO_FATAL_FAILURE(CloseEffects());
+}
+
+TEST_P(AudioEffect, GetDescriptors) {
+    EXPECT_NO_FATAL_FAILURE(GetEffectDescriptors());
+}
+
+TEST_P(AudioEffect, DescriptorIdExistAndUnique) {
+    auto checker = [&](const std::shared_ptr<IEffect>& effect) {
+        Descriptor desc;
+        std::vector<Descriptor::Identity> idList;
+        ScopedAStatus status = effect->getDescriptor(&desc);
+        EXPECT_EQ(status.getExceptionCode(), EX_NONE);
+        mFactory.QueryEffects(desc.common.id.type, desc.common.id.uuid, &idList);
+        EXPECT_EQ(static_cast<int>(idList.size()), 1);
+    };
+    EXPECT_NO_FATAL_FAILURE(ForEachEffect(checker));
+
+    // Check unique with a set
+    auto stringHash = [](const Descriptor::Identity& id) {
+        return std::hash<std::string>()(id.toString());
+    };
+    auto vec = mFactory.GetCompleteEffectIdList();
+    std::unordered_set<Descriptor::Identity, decltype(stringHash)> idSet(0, stringHash);
+    for (auto it : vec) {
+        EXPECT_EQ(static_cast<int>(idSet.count(it)), 0);
+        idSet.insert(it);
+    }
+}
+
+INSTANTIATE_TEST_SUITE_P(AudioEffectTest, AudioEffect,
+                         testing::ValuesIn(android::getAidlHalInstanceNames(IFactory::descriptor)),
+                         android::PrintInstanceNameToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioEffect);
+
+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/common/5.0/Android.bp b/audio/common/5.0/Android.bp
index fd8e85f..a6bb331 100644
--- a/audio/common/5.0/Android.bp
+++ b/audio/common/5.0/Android.bp
@@ -22,6 +22,6 @@
     gen_java_constants: true,
     apex_available: [
         "//apex_available:platform",
-        "com.android.bluetooth",
+        "com.android.btservices",
     ],
 }
diff --git a/audio/core/all-versions/vts/functional/Android.bp b/audio/core/all-versions/vts/functional/Android.bp
index daed7a8..f51a8d0 100644
--- a/audio/core/all-versions/vts/functional/Android.bp
+++ b/audio/core/all-versions/vts/functional/Android.bp
@@ -49,7 +49,10 @@
 
 cc_test {
     name: "VtsHalAudioV2_0TargetTest",
-    defaults: ["VtsHalAudioTargetTest_defaults"],
+    defaults: [
+        "VtsHalAudioTargetTest_defaults",
+        "latest_android_media_audio_common_types_cpp_static",
+    ],
     tidy_timeout_srcs: [
         "2.0/AudioPrimaryHidlHalTest.cpp",
     ],
@@ -62,7 +65,6 @@
         "libmedia_helper",
         "android.hardware.audio@2.0",
         "android.hardware.audio.common@2.0",
-        "android.media.audio.common.types-V1-cpp",
     ],
     cflags: [
         "-DMAJOR_VERSION=2",
@@ -79,7 +81,10 @@
 
 cc_test {
     name: "VtsHalAudioV4_0TargetTest",
-    defaults: ["VtsHalAudioTargetTest_defaults"],
+    defaults: [
+        "VtsHalAudioTargetTest_defaults",
+        "latest_android_media_audio_common_types_cpp_static",
+    ],
     tidy_timeout_srcs: [
         "4.0/AudioPrimaryHidlHalTest.cpp",
     ],
@@ -92,7 +97,6 @@
         "libmedia_helper",
         "android.hardware.audio@4.0",
         "android.hardware.audio.common@4.0",
-        "android.media.audio.common.types-V1-cpp",
     ],
     cflags: [
         "-DMAJOR_VERSION=4",
@@ -109,7 +113,10 @@
 
 cc_test {
     name: "VtsHalAudioV5_0TargetTest",
-    defaults: ["VtsHalAudioTargetTest_defaults"],
+    defaults: [
+        "VtsHalAudioTargetTest_defaults",
+        "latest_android_media_audio_common_types_cpp_static",
+    ],
     srcs: [
         "5.0/AudioPrimaryHidlHalTest.cpp",
     ],
@@ -119,7 +126,6 @@
         "libmedia_helper",
         "android.hardware.audio@5.0",
         "android.hardware.audio.common@5.0",
-        "android.media.audio.common.types-V1-cpp",
     ],
     cflags: [
         "-DMAJOR_VERSION=5",
@@ -136,7 +142,10 @@
 
 cc_test {
     name: "VtsHalAudioV6_0TargetTest",
-    defaults: ["VtsHalAudioTargetTest_defaults"],
+    defaults: [
+        "VtsHalAudioTargetTest_defaults",
+        "latest_android_media_audio_common_types_cpp_static",
+    ],
     tidy_timeout_srcs: [
         "6.0/AudioPrimaryHidlHalTest.cpp",
     ],
@@ -150,7 +159,6 @@
         "libmedia_helper",
         "android.hardware.audio@6.0",
         "android.hardware.audio.common@6.0",
-        "android.media.audio.common.types-V1-cpp",
     ],
     cflags: [
         "-DMAJOR_VERSION=6",
@@ -244,7 +252,10 @@
 
 cc_test {
     name: "HalAudioV6_0GeneratorTest",
-    defaults: ["VtsHalAudioTargetTest_defaults"],
+    defaults: [
+        "VtsHalAudioTargetTest_defaults",
+        "latest_android_media_audio_common_types_cpp_static",
+    ],
     srcs: [
         "6.0/Generators.cpp",
         "tests/generators_tests.cpp",
@@ -252,7 +263,6 @@
     static_libs: [
         "android.hardware.audio@6.0",
         "android.hardware.audio.common@6.0",
-        "android.media.audio.common.types-V1-cpp",
         "libaudiofoundation",
         "libaudiopolicycomponents",
         "libmedia_helper",
diff --git a/audio/effect/all-versions/default/Effect.cpp b/audio/effect/all-versions/default/Effect.cpp
index def3a3f..b57dc63 100644
--- a/audio/effect/all-versions/default/Effect.cpp
+++ b/audio/effect/all-versions/default/Effect.cpp
@@ -316,6 +316,11 @@
 
 Result Effect::getCurrentConfigImpl(uint32_t featureId, uint32_t configSize,
                                     GetCurrentConfigSuccessCallback onSuccess) {
+    if (configSize > kMaxDataSize - sizeof(uint32_t)) {
+        ALOGE("%s: Config size is too big: %" PRIu32, __func__, configSize);
+        android_errorWriteLog(0x534e4554, "240266798");
+        return Result::INVALID_ARGUMENTS;
+    }
     uint32_t halCmd = featureId;
     std::vector<uint32_t> halResult(alignedSizeIn<uint32_t>(sizeof(uint32_t) + configSize), 0);
     uint32_t halResultSize = 0;
@@ -350,8 +355,12 @@
 
 Result Effect::getSupportedConfigsImpl(uint32_t featureId, uint32_t maxConfigs, uint32_t configSize,
                                        GetSupportedConfigsSuccessCallback onSuccess) {
+    if (maxConfigs != 0 && configSize > (kMaxDataSize - 2 * sizeof(uint32_t)) / maxConfigs) {
+        ALOGE("%s: Config size is too big: %" PRIu32, __func__, configSize);
+        return Result::INVALID_ARGUMENTS;
+    }
     uint32_t halCmd[2] = {featureId, maxConfigs};
-    uint32_t halResultSize = 2 * sizeof(uint32_t) + maxConfigs * sizeof(configSize);
+    uint32_t halResultSize = 2 * sizeof(uint32_t) + maxConfigs * configSize;
     std::vector<uint8_t> halResult(static_cast<size_t>(halResultSize), 0);
     return sendCommandReturningStatusAndData(
         EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS, "GET_FEATURE_SUPPORTED_CONFIGS", sizeof(halCmd),
diff --git a/audio/effect/all-versions/default/Effect.h b/audio/effect/all-versions/default/Effect.h
index f0b65df..5d8dccc 100644
--- a/audio/effect/all-versions/default/Effect.h
+++ b/audio/effect/all-versions/default/Effect.h
@@ -184,6 +184,9 @@
     using GetSupportedConfigsSuccessCallback =
         std::function<void(uint32_t supportedConfigs, void* configsData)>;
 
+    // Sets the limit on the maximum size of vendor-provided data structures.
+    static constexpr size_t kMaxDataSize = 1 << 20;
+
     static const char* sContextResultOfCommand;
     static const char* sContextCallToCommand;
     static const char* sContextCallFunction;
diff --git a/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp b/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp
index ffa4c56..d95bb06 100644
--- a/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp
+++ b/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp
@@ -35,6 +35,7 @@
 
 #include <common/all-versions/VersionUtils.h>
 
+#include <cutils/properties.h>
 #include <gtest/gtest.h>
 #include <hidl/GtestPrinter.h>
 #include <hidl/ServiceManagement.h>
@@ -625,6 +626,10 @@
 
 TEST_P(AudioEffectHidlTest, GetParameterInvalidMaxReplySize) {
     description("Verify that GetParameter caps the maximum reply size");
+    const bool isNewDeviceLaunchingOnTPlus = property_get_int32("ro.vendor.api_level", 0) >= 33;
+    if (!isNewDeviceLaunchingOnTPlus) {
+        GTEST_SKIP() << "The test only applies to devices launching on T or later";
+    }
     // Use a non-empty parameter to avoid being rejected by any earlier checks.
     hidl_vec<uint8_t> parameter;
     parameter.resize(16);
@@ -660,6 +665,37 @@
     EXPECT_TRUE(ret.isOk());
 }
 
+TEST_P(AudioEffectHidlTest, GetSupportedConfigsForFeatureInvalidConfigSize) {
+    description("Verify that GetSupportedConfigsForFeature caps the maximum config size");
+    const bool isNewDeviceLaunchingOnTPlus = property_get_int32("ro.vendor.api_level", 0) >= 33;
+    if (!isNewDeviceLaunchingOnTPlus) {
+        GTEST_SKIP() << "The test only applies to devices launching on T or later";
+    }
+    // Use very large size to ensure that the service does not crash.
+    const uint32_t veryLargeConfigSize = std::numeric_limits<uint32_t>::max() - 100;
+    Result retval = Result::OK;
+    Return<void> ret = effect->getSupportedConfigsForFeature(
+            0, 1, veryLargeConfigSize,
+            [&](Result r, uint32_t, const hidl_vec<uint8_t>&) { retval = r; });
+    EXPECT_TRUE(ret.isOk());
+    EXPECT_EQ(Result::INVALID_ARGUMENTS, retval);
+}
+
+TEST_P(AudioEffectHidlTest, GetCurrentConfigForFeatureInvalidConfigSize) {
+    description("Verify that GetCurrentConfigForFeature caps the maximum config size");
+    const bool isNewDeviceLaunchingOnTPlus = property_get_int32("ro.vendor.api_level", 0) >= 33;
+    if (!isNewDeviceLaunchingOnTPlus) {
+        GTEST_SKIP() << "The test only applies to devices launching on T or later";
+    }
+    // Use very large size to ensure that the service does not crash.
+    const uint32_t veryLargeConfigSize = std::numeric_limits<uint32_t>::max() - 100;
+    Result retval = Result::OK;
+    Return<void> ret = effect->getCurrentConfigForFeature(
+            0, veryLargeConfigSize, [&](Result r, const hidl_vec<uint8_t>&) { retval = r; });
+    EXPECT_TRUE(ret.isOk());
+    EXPECT_EQ(Result::INVALID_ARGUMENTS, retval);
+}
+
 // The main test class for Equalizer Audio Effect HIDL HAL.
 class EqualizerAudioEffectHidlTest : public AudioEffectHidlTest {
   public:
diff --git a/automotive/audiocontrol/aidl/Android.bp b/automotive/audiocontrol/aidl/Android.bp
index e5f7a4f..03dab08 100644
--- a/automotive/audiocontrol/aidl/Android.bp
+++ b/automotive/audiocontrol/aidl/Android.bp
@@ -15,7 +15,7 @@
     srcs: ["android/hardware/automotive/audiocontrol/*.aidl"],
     imports: [
         "android.hardware.audio.common-V1",
-        "android.media.audio.common.types-V1",
+        "android.media.audio.common.types-V2",
     ],
     stability: "vintf",
     backend: {
@@ -33,14 +33,14 @@
             version: "1",
             imports: [
                 "android.hardware.audio.common-V1",
-                "android.media.audio.common.types-V1",
+                "android.media.audio.common.types-V2",
             ],
         },
         {
             version: "2",
             imports: [
                 "android.hardware.audio.common-V1",
-                "android.media.audio.common.types-V1",
+                "android.media.audio.common.types-V2",
             ],
         },
 
diff --git a/automotive/audiocontrol/aidl/vts/Android.bp b/automotive/audiocontrol/aidl/vts/Android.bp
index 3d4be48..edac160 100644
--- a/automotive/audiocontrol/aidl/vts/Android.bp
+++ b/automotive/audiocontrol/aidl/vts/Android.bp
@@ -24,6 +24,8 @@
 cc_test {
     name: "VtsAidlHalAudioControlTest",
     defaults: [
+        "latest_android_media_audio_common_types_cpp_static",
+        "latest_android_hardware_audio_common_cpp_static",
         "VtsHalTargetTestDefaults",
         "use_libaidlvintf_gtest_helper_static",
     ],
@@ -38,8 +40,6 @@
     ],
     static_libs: [
         "android.hardware.automotive.audiocontrol-V2-cpp",
-        "android.hardware.audio.common-V1-cpp",
-        "android.media.audio.common.types-V1-cpp",
         "libgmock",
     ],
     test_suites: [
diff --git a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
index 623438f..18a3329 100644
--- a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
+++ b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
@@ -250,8 +250,7 @@
             // Stream configurations are found in metadata
             RawStreamConfig *ptr = reinterpret_cast<RawStreamConfig *>(streamCfgs.data.i32);
             for (unsigned offset = 0; offset < streamCfgs.count; offset += kStreamCfgSz) {
-                if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
-                    ptr->format == HAL_PIXEL_FORMAT_RGBA_8888) {
+                if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT) {
                     targetCfg.width = ptr->width;
                     targetCfg.height = ptr->height;
                     targetCfg.format = static_cast<PixelFormat>(ptr->format);
@@ -2017,13 +2016,12 @@
             // Stream configurations are found in metadata
             RawStreamConfig *ptr = reinterpret_cast<RawStreamConfig *>(streamCfgs.data.i32);
             for (unsigned offset = 0; offset < streamCfgs.count; offset += kStreamCfgSz) {
-                if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
-                    ptr->format == HAL_PIXEL_FORMAT_RGBA_8888) {
-
+                if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT) {
                     if (ptr->width * ptr->height > maxArea &&
                         ptr->framerate >= minReqFps) {
                         targetCfg.width = ptr->width;
                         targetCfg.height = ptr->height;
+                        targetCfg.format = static_cast<PixelFormat>(ptr->format);
 
                         maxArea = ptr->width * ptr->height;
                         foundCfg = true;
@@ -2032,8 +2030,6 @@
                 ++ptr;
             }
         }
-        targetCfg.format =
-            static_cast<PixelFormat>(HAL_PIXEL_FORMAT_RGBA_8888);
 
         if (!foundCfg) {
             // Current EVS camera does not provide stream configurations in the
@@ -2120,13 +2116,12 @@
             // Stream configurations are found in metadata
             RawStreamConfig *ptr = reinterpret_cast<RawStreamConfig *>(streamCfgs.data.i32);
             for (unsigned offset = 0; offset < streamCfgs.count; offset += kStreamCfgSz) {
-                if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
-                    ptr->format == HAL_PIXEL_FORMAT_RGBA_8888) {
-
+                if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT) {
                     if (ptr->width * ptr->height > maxArea &&
                         ptr->framerate >= minReqFps) {
                         targetCfg.width = ptr->width;
                         targetCfg.height = ptr->height;
+                        targetCfg.format = static_cast<PixelFormat>(ptr->format);
 
                         maxArea = ptr->width * ptr->height;
                         foundCfg = true;
@@ -2135,8 +2130,6 @@
                 ++ptr;
             }
         }
-        targetCfg.format =
-            static_cast<PixelFormat>(HAL_PIXEL_FORMAT_RGBA_8888);
 
         if (!foundCfg) {
             LOG(INFO) << "Device " << cam.v1.cameraId
diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/EvsEventDesc.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/EvsEventDesc.aidl
index ebff98f..3abdb54 100644
--- a/automotive/evs/aidl/android/hardware/automotive/evs/EvsEventDesc.aidl
+++ b/automotive/evs/aidl/android/hardware/automotive/evs/EvsEventDesc.aidl
@@ -33,7 +33,9 @@
     @utf8InCpp
     String deviceId;
     /**
-     * Possible additional vendor information that is opaque to the EvsManager
+     * Possible additional vendor information that is opaque to the EvsManager.
+     * The size of the payload must not exceed 16-byte if the HIDL recipients are
+     * expected to exist.
      */
     int[] payload;
 }
diff --git a/automotive/evs/aidl/android/hardware/automotive/evs/IEvsCameraStream.aidl b/automotive/evs/aidl/android/hardware/automotive/evs/IEvsCameraStream.aidl
index 2c2b44c..c599d58 100644
--- a/automotive/evs/aidl/android/hardware/automotive/evs/IEvsCameraStream.aidl
+++ b/automotive/evs/aidl/android/hardware/automotive/evs/IEvsCameraStream.aidl
@@ -47,7 +47,10 @@
     /**
      * Receives calls from the HAL each time an event happens.
      *
-     * @param in event EVS event with possible event information.
+     * @param in event EVS event with possible event information.  If ths HIDL
+     *                 recipients are expected to exist, the size of the event
+     *                 payload must not exceed 16 bytes; otherwise, a notification
+     *                 will not reach them.
      */
     void notify(in EvsEventDesc event);
 }
diff --git a/automotive/evs/aidl/impl/Android.bp b/automotive/evs/aidl/impl/Android.bp
index 7eb0116..0b51a0c 100644
--- a/automotive/evs/aidl/impl/Android.bp
+++ b/automotive/evs/aidl/impl/Android.bp
@@ -20,10 +20,10 @@
 
 cc_defaults {
     name: "EvsHalDefaults",
+    defaults: ["android.hardware.graphics.common-ndk_static"],
     static_libs: [
         "android.hardware.automotive.evs-V1-ndk",
         "android.hardware.common-V2-ndk",
-        "android.hardware.graphics.common-V3-ndk",
     ],
     shared_libs: [
         "libbase",
diff --git a/automotive/evs/aidl/vts/Android.bp b/automotive/evs/aidl/vts/Android.bp
index 980c6d5..5aa9501 100644
--- a/automotive/evs/aidl/vts/Android.bp
+++ b/automotive/evs/aidl/vts/Android.bp
@@ -31,6 +31,7 @@
     ],
     defaults: [
         "VtsHalTargetTestDefaults",
+        "android.hardware.graphics.common-ndk_static",
         "use_libaidlvintf_gtest_helper_static",
     ],
     shared_libs: [
@@ -43,7 +44,6 @@
         "android.hardware.automotive.evs@common-default-lib",
         "android.hardware.automotive.evs-V1-ndk",
         "android.hardware.common-V2-ndk",
-        "android.hardware.graphics.common-V3-ndk",
         "libaidlcommonsupport",
     ],
     test_suites: [
diff --git a/automotive/evs/aidl/vts/VtsHalEvsTargetTest.cpp b/automotive/evs/aidl/vts/VtsHalEvsTargetTest.cpp
index 9c6c573..3ea1eaa 100644
--- a/automotive/evs/aidl/vts/VtsHalEvsTargetTest.cpp
+++ b/automotive/evs/aidl/vts/VtsHalEvsTargetTest.cpp
@@ -231,8 +231,7 @@
             // Stream configurations are found in metadata
             RawStreamConfig* ptr = reinterpret_cast<RawStreamConfig*>(streamCfgs.data.i32);
             for (unsigned offset = 0; offset < streamCfgs.count; offset += kStreamCfgSz) {
-                if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
-                    ptr->format == HAL_PIXEL_FORMAT_RGBA_8888) {
+                if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT) {
                     targetCfg.width = ptr->width;
                     targetCfg.height = ptr->height;
                     targetCfg.format = static_cast<PixelFormat>(ptr->format);
@@ -1732,11 +1731,11 @@
             // Stream configurations are found in metadata
             RawStreamConfig* ptr = reinterpret_cast<RawStreamConfig*>(streamCfgs.data.i32);
             for (unsigned offset = 0; offset < streamCfgs.count; offset += kStreamCfgSz) {
-                if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
-                    ptr->format == HAL_PIXEL_FORMAT_RGBA_8888) {
+                if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT) {
                     if (ptr->width * ptr->height > maxArea && ptr->framerate >= minReqFps) {
                         targetCfg.width = ptr->width;
                         targetCfg.height = ptr->height;
+                        targetCfg.format = static_cast<PixelFormat>(ptr->format);
 
                         maxArea = ptr->width * ptr->height;
                         foundCfg = true;
@@ -1745,7 +1744,6 @@
                 ++ptr;
             }
         }
-        targetCfg.format = static_cast<PixelFormat>(HAL_PIXEL_FORMAT_RGBA_8888);
 
         if (!foundCfg) {
             // Current EVS camera does not provide stream configurations in the
@@ -1829,11 +1827,11 @@
             // Stream configurations are found in metadata
             RawStreamConfig* ptr = reinterpret_cast<RawStreamConfig*>(streamCfgs.data.i32);
             for (unsigned offset = 0; offset < streamCfgs.count; offset += kStreamCfgSz) {
-                if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
-                    ptr->format == HAL_PIXEL_FORMAT_RGBA_8888) {
+                if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT) {
                     if (ptr->width * ptr->height > maxArea && ptr->framerate >= minReqFps) {
                         targetCfg.width = ptr->width;
                         targetCfg.height = ptr->height;
+                        targetCfg.format = static_cast<PixelFormat>(ptr->format);
 
                         maxArea = ptr->width * ptr->height;
                         foundCfg = true;
@@ -1842,7 +1840,6 @@
                 ++ptr;
             }
         }
-        targetCfg.format = static_cast<PixelFormat>(HAL_PIXEL_FORMAT_RGBA_8888);
 
         if (!foundCfg) {
             LOG(INFO) << "Device " << cam.id
diff --git a/automotive/occupant_awareness/aidl/default/Android.bp b/automotive/occupant_awareness/aidl/default/Android.bp
index 3dc7e0d..1b760a5 100644
--- a/automotive/occupant_awareness/aidl/default/Android.bp
+++ b/automotive/occupant_awareness/aidl/default/Android.bp
@@ -42,18 +42,11 @@
 
 cc_fuzz {
     name: "android.hardware.automotive.occupant_awareness-service.fuzzer",
+    defaults: ["service_fuzzer_defaults"],
     static_libs: [
         "android.hardware.automotive.occupant_awareness-V1-ndk",
-        "libbase",
-        "libbinder_random_parcel",
-        "libcutils",
         "liblog",
     ],
-    shared_libs: [
-        "libbinder_ndk",
-        "libbinder",
-        "libutils",
-    ],
     srcs: [
         "fuzzer.cpp",
         "OccupantAwareness.cpp",
diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp
index 0d3253b..33e211c 100644
--- a/automotive/vehicle/2.0/default/Android.bp
+++ b/automotive/vehicle/2.0/default/Android.bp
@@ -84,7 +84,10 @@
     name: "android.hardware.automotive.vehicle@2.0-default-impl-lib",
     vendor: true,
     defaults: ["vhal_v2_0_target_defaults"],
-    cflags: ["-DENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING"],
+    cflags: [
+        "-DENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING",
+        "-DENABLE_GET_PROP_CONFIGS_BY_MULTIPLE_REQUESTS",
+    ],
     srcs: [
         "impl/vhal_v2_0/DefaultVehicleHal.cpp",
         "impl/vhal_v2_0/VehicleHalClient.cpp",
@@ -225,6 +228,25 @@
     test_suites: ["general-tests"],
 }
 
+cc_test {
+    name: "android.hardware.automotive.vehicle@2.0-default-config-test",
+    vendor: true,
+    defaults: ["vhal_v2_0_target_defaults"],
+    srcs: [
+        "impl/vhal_v2_0/tests/DefaultConfigSupportedPropertyIds_test.cpp",
+    ],
+    cflags: [
+        "-DENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING",
+        "-DENABLE_GET_PROP_CONFIGS_BY_MULTIPLE_REQUESTS",
+    ],
+    static_libs: [
+        "android.hardware.automotive.vehicle@2.0-default-impl-lib",
+        "libgtest",
+        "libgmock",
+    ],
+    test_suites: ["general-tests"],
+}
+
 cc_binary {
     name: "android.hardware.automotive.vehicle@2.0-default-service",
     defaults: ["vhal_v2_0_target_defaults"],
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
index edc8949..e803e81 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
@@ -1179,6 +1179,47 @@
                         },
         },
 #endif  // ENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING
+#ifdef ENABLE_GET_PROP_CONFIGS_BY_MULTIPLE_REQUESTS
+        {
+                .config =
+                        {
+                                // SUPPORTED_PROPERTY_IDS
+                                .prop = 289476424,
+                                .access = VehiclePropertyAccess::READ,
+                                .changeMode = VehiclePropertyChangeMode::STATIC,
+                                // Fetch 100 configs in one request. This number is just arbitrarily
+                                // chosen here. But some HAL impl with bigger config data may need a
+                                // smaller number to make sure the configs returned in one request
+                                // fits the binder data size limitation.
+                                .configArray = {100},
+                        },
+                // All supported property IDs. This list is checked by
+                // DefaultConfigSupportedPropertyIds_test.
+                .initialValue =
+                        {.int32Values =
+                                 {291504388, 289472773, 291504390, 289472775, 289407240, 289407241,
+                                  289472780, 286261505, 286261506, 289407235, 289472779, 291504647,
+                                  289408517, 356518832, 356516106, 291504644, 291504649, 291504656,
+                                  291504901, 291504903, 287310600, 291504905, 287310602, 287310603,
+                                  291504908, 291504904, 392168201, 392168202, 289408514, 289408001,
+                                  287310850, 287310851, 287310853, 289408513, 289475088, 289475104,
+                                  289475120, 354419984, 320865540, 320865556, 354419975, 354419976,
+                                  354419986, 354419973, 354419974, 354419978, 354419977, 356517120,
+                                  356517121, 356582673, 356517139, 289408269, 356517131, 358614275,
+                                  291570965, 291505923, 289408270, 289408512, 287310855, 289408000,
+                                  289408008, 289408009, 289407747, 291504900, 568332561, 371198722,
+                                  373295872, 320867268, 322964416, 290521862, 287310858, 287310859,
+                                  289475072, 289475073, 289409539, 299896064, 299896065, 299896066,
+                                  299896067, 289410560, 289410561, 289410562, 289410563, 289410576,
+                                  289410577, 289410578, 289410579, 289476368, 299895808, 639631617,
+                                  627048706, 591397123, 554696964, 289410873, 289410874, 287313669,
+                                  299896583, 299896584, 299896585, 299896586, 299896587, 286265121,
+                                  286265122, 286265123, 290457094, 290459441, 299896626, 290459443,
+                                  289410868, 289476405, 299896630, 289410871, 292556600, 557853201,
+                                  559950353, 555756049, 554707473, 289410887, 557846324, 557911861,
+                                  568332086, 557846327, 560992056, 289476424}},
+        },
+#endif  // ENABLE_GET_PROP_CONFIGS_BY_MULTIPLE_REQUESTS
 };
 
 }  // impl
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultConfigSupportedPropertyIds_test.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultConfigSupportedPropertyIds_test.cpp
new file mode 100644
index 0000000..aa05daa
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultConfigSupportedPropertyIds_test.cpp
@@ -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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <vector>
+
+#include "vhal_v2_0/DefaultConfig.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+namespace impl {
+
+using ::testing::ElementsAreArray;
+
+// Test that VHAL_SUPPORTED_PROPERTY_IDS contains all supported property IDs.
+TEST(DefaultConfigSupportedPropertyIdsTest, testIncludeAllSupportedIds) {
+    const int32_t vhalSupportedPropertyIdsPropId = 289476424;
+
+    std::vector<int32_t> allSupportedIds;
+    std::vector<int32_t> configuredSupportedIds;
+
+    for (const auto& property : impl::kVehicleProperties) {
+        int propId = property.config.prop;
+        allSupportedIds.push_back(propId);
+
+        if (propId == vhalSupportedPropertyIdsPropId) {
+            configuredSupportedIds = property.initialValue.int32Values;
+        }
+    }
+
+    ASSERT_THAT(allSupportedIds, ElementsAreArray(configuredSupportedIds));
+}
+
+}  // namespace impl
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp
index 74f5a7a..df5ada6 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp
@@ -141,7 +141,7 @@
 TEST_F(DefaultVhalImplTest, testListProperties) {
     std::vector<VehiclePropConfig> configs = mHal->listProperties();
 
-    EXPECT_EQ((size_t)123, configs.size());
+    EXPECT_EQ((size_t)124, configs.size());
 }
 
 TEST_F(DefaultVhalImplTest, testGetDefaultPropertyFloat) {
diff --git a/automotive/vehicle/TEST_MAPPING b/automotive/vehicle/TEST_MAPPING
index fa0b791..da8416c 100644
--- a/automotive/vehicle/TEST_MAPPING
+++ b/automotive/vehicle/TEST_MAPPING
@@ -7,6 +7,15 @@
       "name": "VehicleHalDefaultConfigTest"
     },
     {
+      "name": "VehicleHalDefaultConfigTestEnableTestProperties"
+    },
+    {
+      "name": "JsonConfigLoaderUnitTest"
+    },
+    {
+      "name": "JsonConfigLoaderUnitTestEnableTestProperties"
+    },
+    {
       "name": "VehicleHalVehicleUtilsTest"
     },
     {
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/GsrComplianceRequirementType.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/GsrComplianceRequirementType.aidl
index 0d3a061..9c565ee 100644
--- a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/GsrComplianceRequirementType.aidl
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/GsrComplianceRequirementType.aidl
@@ -35,5 +35,5 @@
 @Backing(type="int") @VintfStability
 enum GsrComplianceRequirementType {
   GSR_COMPLIANCE_NOT_REQUIRED = 0,
-  GSR_COMPLIANCE_REQUIRED_THROUGH_SYSTEM_IMAGE = 1,
+  GSR_COMPLIANCE_REQUIRED_V1 = 1,
 }
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleProperty.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleProperty.aidl
index 2d0cde5..64c7ce7 100644
--- a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleProperty.aidl
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleProperty.aidl
@@ -207,4 +207,5 @@
   TRAILER_PRESENT = 289410885,
   VEHICLE_CURB_WEIGHT = 289410886,
   GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT = 289410887,
+  SUPPORTED_PROPERTY_IDS = 289476424,
 }
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/GsrComplianceRequirementType.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/GsrComplianceRequirementType.aidl
index e0609d5..fb3ca9f 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/GsrComplianceRequirementType.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/GsrComplianceRequirementType.aidl
@@ -27,8 +27,9 @@
      * GSR compliance is not required.
      */
     GSR_COMPLIANCE_NOT_REQUIRED = 0,
+
     /**
-     * GSR compliance is required through system image.
+     * GSR compliance is required and the requirement solution version is 1.
      */
-    GSR_COMPLIANCE_REQUIRED_THROUGH_SYSTEM_IMAGE = 1,
+    GSR_COMPLIANCE_REQUIRED_V1 = 1,
 }
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl
index e8a6c85..11cda3d 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl
@@ -542,7 +542,7 @@
      */
     TRACTION_CONTROL_ACTIVE = 0x040B + 0x10000000 + 0x01000000
             + 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:BOOLEAN
-    /*
+    /**
      * HVAC Properties
      *
      * Additional rules for mapping a zoned HVAC property (except
@@ -2468,8 +2468,9 @@
      * VHAL sets this property to change car power policy. Car power policy service subscribes to
      * this property and actually changes the power policy.
      * The request is made by setting the VehiclePropValue with the ID of a power policy which is
-     * defined at /vendor/etc/power_policy.xml. If the given ID is not defined, car power policy
-     * service ignores the request and the current power policy is maintained.
+     * defined at /vendor/etc/automotive/power_policy.xml.
+     * If the given ID is not defined, car power policy service ignores the request
+     * and the current power policy is maintained.
      *
      *   string: "sample_policy_id" // power policy ID
      *
@@ -2853,4 +2854,28 @@
     GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT = 0x0F47 + 0x10000000 + 0x01000000
             + 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
 
+    /**
+     * (Deprecated) List of all supported property IDs.
+     *
+     * A list of all supported property IDs (including this property). This property is required for
+     * HIDL VHAL to work with large amount of vehicle prop configs where the getAllPropConfigs
+     * payload exceeds the binder limitation. This issue is fixed in AIDL version using
+     * LargeParcelable in getAllPropConfigs, so this property is deprecated.
+     *
+     * In HIDL VHAL implementation, if the amount of data returned in getAllPropConfigs exceeds the
+     * binder limitation, vendor must support this property and return all the supported property
+     * IDs. Car service will divide this list into smaller sub lists and use getPropConfigs([ids])
+     * to query the sub lists. The results will be merged together in Car Service.
+     *
+     * The config array for this property must contain one int element which is the number of
+     * configs per getPropConfigs request by Car Service. This number must be small enough so that
+     * each getPropConfigs payload will not exceed binder limitation, however, a smaller number will
+     * cause more requests, which increase overhead to fetch all the configs.
+     *
+     * @change_mode VehiclePropertyChangeMode.STATIC
+     * @access VehiclePropertyAccess.READ
+     */
+    SUPPORTED_PROPERTY_IDS = 0x0F48 + 0x10000000 + 0x01000000
+            + 0x00410000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32_VEC
+
 }
diff --git a/automotive/vehicle/aidl/generated_lib/cpp/AccessForVehicleProperty.h b/automotive/vehicle/aidl/generated_lib/cpp/AccessForVehicleProperty.h
index 14a694d..c91afe2 100644
--- a/automotive/vehicle/aidl/generated_lib/cpp/AccessForVehicleProperty.h
+++ b/automotive/vehicle/aidl/generated_lib/cpp/AccessForVehicleProperty.h
@@ -207,6 +207,7 @@
         {VehicleProperty::TRAILER_PRESENT, VehiclePropertyAccess::READ},
         {VehicleProperty::VEHICLE_CURB_WEIGHT, VehiclePropertyAccess::READ},
         {VehicleProperty::GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT, VehiclePropertyAccess::READ},
+        {VehicleProperty::SUPPORTED_PROPERTY_IDS, VehiclePropertyAccess::READ},
 };
 
 }  // namespace vehicle
diff --git a/automotive/vehicle/aidl/generated_lib/cpp/ChangeModeForVehicleProperty.h b/automotive/vehicle/aidl/generated_lib/cpp/ChangeModeForVehicleProperty.h
index c0416af..807be5d 100644
--- a/automotive/vehicle/aidl/generated_lib/cpp/ChangeModeForVehicleProperty.h
+++ b/automotive/vehicle/aidl/generated_lib/cpp/ChangeModeForVehicleProperty.h
@@ -207,6 +207,7 @@
         {VehicleProperty::TRAILER_PRESENT, VehiclePropertyChangeMode::ON_CHANGE},
         {VehicleProperty::VEHICLE_CURB_WEIGHT, VehiclePropertyChangeMode::STATIC},
         {VehicleProperty::GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT, VehiclePropertyChangeMode::STATIC},
+        {VehicleProperty::SUPPORTED_PROPERTY_IDS, VehiclePropertyChangeMode::STATIC},
 };
 
 }  // namespace vehicle
diff --git a/automotive/vehicle/aidl/generated_lib/java/AccessForVehicleProperty.java b/automotive/vehicle/aidl/generated_lib/java/AccessForVehicleProperty.java
index 0a17ba1..e698f84 100644
--- a/automotive/vehicle/aidl/generated_lib/java/AccessForVehicleProperty.java
+++ b/automotive/vehicle/aidl/generated_lib/java/AccessForVehicleProperty.java
@@ -198,7 +198,8 @@
         Map.entry(VehicleProperty.EV_REGENERATIVE_BRAKING_STATE, VehiclePropertyAccess.READ),
         Map.entry(VehicleProperty.TRAILER_PRESENT, VehiclePropertyAccess.READ),
         Map.entry(VehicleProperty.VEHICLE_CURB_WEIGHT, VehiclePropertyAccess.READ),
-        Map.entry(VehicleProperty.GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT, VehiclePropertyAccess.READ)
+        Map.entry(VehicleProperty.GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT, VehiclePropertyAccess.READ),
+        Map.entry(VehicleProperty.SUPPORTED_PROPERTY_IDS, VehiclePropertyAccess.READ)
     );
 
 }
diff --git a/automotive/vehicle/aidl/generated_lib/java/ChangeModeForVehicleProperty.java b/automotive/vehicle/aidl/generated_lib/java/ChangeModeForVehicleProperty.java
index 5dfcd22..fe69450 100644
--- a/automotive/vehicle/aidl/generated_lib/java/ChangeModeForVehicleProperty.java
+++ b/automotive/vehicle/aidl/generated_lib/java/ChangeModeForVehicleProperty.java
@@ -198,7 +198,8 @@
         Map.entry(VehicleProperty.EV_REGENERATIVE_BRAKING_STATE, VehiclePropertyChangeMode.ON_CHANGE),
         Map.entry(VehicleProperty.TRAILER_PRESENT, VehiclePropertyChangeMode.ON_CHANGE),
         Map.entry(VehicleProperty.VEHICLE_CURB_WEIGHT, VehiclePropertyChangeMode.STATIC),
-        Map.entry(VehicleProperty.GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT, VehiclePropertyChangeMode.STATIC)
+        Map.entry(VehicleProperty.GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT, VehiclePropertyChangeMode.STATIC),
+        Map.entry(VehicleProperty.SUPPORTED_PROPERTY_IDS, VehiclePropertyChangeMode.STATIC)
     );
 
 }
diff --git a/automotive/vehicle/aidl/impl/default_config/Android.bp b/automotive/vehicle/aidl/impl/default_config/Android.bp
deleted file mode 100644
index 0feaf23..0000000
--- a/automotive/vehicle/aidl/impl/default_config/Android.bp
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_library_headers {
-    name: "VehicleHalDefaultConfig",
-    vendor: true,
-    local_include_dirs: ["include"],
-    export_include_dirs: ["include"],
-    defaults: ["VehicleHalDefaults"],
-    static_libs: ["VehicleHalUtils"],
-    header_libs: ["VehicleHalTestUtilHeaders"],
-    export_static_lib_headers: ["VehicleHalUtils"],
-    export_header_lib_headers: ["VehicleHalTestUtilHeaders"],
-}
diff --git a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/Android.bp b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/Android.bp
new file mode 100644
index 0000000..6984d5e
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/Android.bp
@@ -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.
+ */
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library {
+    name: "VehicleHalJsonConfigLoader",
+    vendor: true,
+    srcs: ["src/*.cpp"],
+    local_include_dirs: ["include"],
+    export_include_dirs: ["include"],
+    defaults: ["VehicleHalDefaults"],
+    static_libs: ["VehicleHalUtils"],
+    header_libs: [
+        "IVehicleGeneratedHeaders",
+    ],
+    shared_libs: ["libjsoncpp"],
+}
+
+cc_library {
+    name: "VehicleHalJsonConfigLoaderEnableTestProperties",
+    vendor: true,
+    srcs: ["src/*.cpp"],
+    local_include_dirs: ["include"],
+    export_include_dirs: ["include"],
+    defaults: ["VehicleHalDefaults"],
+    static_libs: ["VehicleHalUtils"],
+    header_libs: [
+        "VehicleHalTestUtilHeaders",
+        "IVehicleGeneratedHeaders",
+    ],
+    cflags: ["-DENABLE_VEHICLE_HAL_TEST_PROPERTIES"],
+    shared_libs: ["libjsoncpp"],
+}
+
+cc_library_headers {
+    name: "VehicleHalJsonConfigLoaderHeaders",
+    vendor: true,
+    local_include_dirs: ["include"],
+    export_include_dirs: ["include"],
+    defaults: ["VehicleHalDefaults"],
+    static_libs: ["VehicleHalUtils"],
+    header_libs: [
+        "IVehicleGeneratedHeaders",
+    ],
+    shared_libs: ["libjsoncpp"],
+}
diff --git a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/include/ConfigDeclaration.h b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/include/ConfigDeclaration.h
new file mode 100644
index 0000000..40ac129
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/include/ConfigDeclaration.h
@@ -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.
+ */
+
+#ifndef android_hardware_automotive_vehicle_aidl_impl_default_config_JsonConfigLoader_include_ConfigDeclaration_H_
+#define android_hardware_automotive_vehicle_aidl_impl_default_config_JsonConfigLoader_include_ConfigDeclaration_H_
+
+#include <VehicleHalTypes.h>
+
+#include <unordered_map>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+// ConfigDeclaration represents one property config, its optional initial value and its optional
+// area configs and initial values for each area.
+struct ConfigDeclaration {
+    aidl::android::hardware::automotive::vehicle::VehiclePropConfig config;
+
+    // This value will be used as an initial value for the property. If this field is specified for
+    // property that supports multiple areas then it will be used for all areas unless particular
+    // area is overridden in initialAreaValue field.
+    aidl::android::hardware::automotive::vehicle::RawPropValues initialValue;
+    // Use initialAreaValues if it is necessary to specify different values per each area.
+    std::unordered_map<int32_t, aidl::android::hardware::automotive::vehicle::RawPropValues>
+            initialAreaValues;
+
+    inline bool operator==(const ConfigDeclaration& other) const {
+        return (config == other.config && initialValue == other.initialValue &&
+                initialAreaValues == other.initialAreaValues);
+    }
+
+    friend std::ostream& operator<<(std::ostream& os, const ConfigDeclaration& c) {
+        return os << "Config Declaration for property: "
+                  << aidl::android::hardware::automotive::vehicle::toString(
+                             static_cast<
+                                     aidl::android::hardware::automotive::vehicle::VehicleProperty>(
+                                     c.config.prop));
+    }
+};
+
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_automotive_vehicle_aidl_impl_default_config_JsonConfigLoader_include_ConfigDeclaration_H_
diff --git a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/include/JsonConfigLoader.h b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/include/JsonConfigLoader.h
new file mode 100644
index 0000000..f3bdbd2
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/include/JsonConfigLoader.h
@@ -0,0 +1,176 @@
+/*
+ * 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.
+ */
+
+#ifndef android_hardware_automotive_vehicle_aidl_impl_default_config_JsonConfigLoader_include_JsonConfigLoader_H_
+#define android_hardware_automotive_vehicle_aidl_impl_default_config_JsonConfigLoader_include_JsonConfigLoader_H_
+
+#include <ConfigDeclaration.h>
+#include <VehicleHalTypes.h>
+
+#include <android-base/result.h>
+#include <json/json.h>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+// private namespace
+namespace jsonconfigloader_impl {
+
+// An abstract interface that represents a ValueParser for any constant value types.
+class ConstantParserInterface {
+  public:
+    // Parses a constant variable name to its actual value.
+    virtual android::base::Result<int> parseValue(const std::string& name) const = 0;
+    virtual ~ConstantParserInterface() = default;
+};
+
+// A class to parse a value field in JSON config file.
+// If the field is a string and the field is in the format of "XX::XX", the value will be parsed
+// as a constant value in the format of "TYPE::NAME". Otherwise, the field will be return as is
+// converted to the expected type.
+class JsonValueParser final {
+  public:
+    JsonValueParser();
+
+    android::base::Result<std::string> parseStringValue(const std::string& fieldName,
+                                                        const Json::Value& value) const;
+
+    template <class T>
+    android::base::Result<std::vector<T>> parseArray(const std::string& fieldName,
+                                                     const Json::Value& value) const;
+
+    template <class T>
+    android::base::Result<T> parseValue(const std::string& fieldName,
+                                        const Json::Value& value) const;
+
+  private:
+    template <class T>
+    static android::base::Result<T> convertValueToType(const std::string& fieldName,
+                                                       const Json::Value& value);
+
+    std::optional<std::pair<std::string, std::string>> maybeGetTypeAndValueName(
+            const std::string& jsonFieldValue) const;
+
+    android::base::Result<int> parseConstantValue(
+            const std::pair<std::string, std::string>& typeValueName) const;
+
+    const ConstantParserInterface* getParser(const std::string& type) const {
+        auto it = mConstantParsersByType.find(type);
+        if (it == mConstantParsersByType.end()) {
+            return nullptr;
+        }
+        return it->second.get();
+    }
+
+  private:
+    inline static const std::string DELIMITER = "::";
+    std::unordered_map<std::string, std::unique_ptr<ConstantParserInterface>>
+            mConstantParsersByType;
+};
+
+// The main class to parse a VHAL config file in JSON format.
+class JsonConfigParser {
+  public:
+    android::base::Result<std::unordered_map<int32_t, ConfigDeclaration>> parseJsonConfig(
+            std::istream& is);
+
+  private:
+    JsonValueParser mValueParser;
+
+    // Parses configuration for each property.
+    std::optional<ConfigDeclaration> parseEachProperty(const Json::Value& propJsonValue,
+                                                       std::vector<std::string>* errors);
+    // Tries to parse a JSON value to a specific type.
+    //
+    // If fieldIsOptional is True, then if the field specified by "fieldName" does not exist,
+    // this method will return true without doing anything, otherwise, it will return false.
+    //
+    // @param parentJsonNode The parent node of the field you are going to parse.
+    // @param fieldName The name for the field.
+    // @param fieldIsOptional Whether the field is optional.
+    // @param outPtr The pointer to output to if the field exists and parsing succeeded.
+    // @param errors The error array to append error to if errors are found.
+    // @return true if the field is optional and does not exist or parsed successfully.
+    template <class T>
+    bool tryParseJsonValueToVariable(const Json::Value& parentJsonNode,
+                                     const std::string& fieldName, bool fieldIsOptional, T* outPtr,
+                                     std::vector<std::string>* errors);
+    // Tries to parse a JSON value to an array of specific type.
+    //
+    // If fieldIsOptional is True, then if the field specified by "fieldName" does not exist,
+    // this method will return true without doing anything, otherwise, it will return false.
+    //
+    // @param parentJsonNode The parent node of the field you are going to parse.
+    // @param fieldName The name for the field.
+    // @param fieldIsOptional Whether the field is optional.
+    // @param outPtr The pointer to output to if the field exists and parsing succeeded.
+    // @param errors The error array to append error to if errors are found.
+    // @return true if the field is optional and does not exist or parsed successfully.
+    template <class T>
+    bool tryParseJsonArrayToVariable(const Json::Value& parentJsonNode,
+                                     const std::string& fieldName, bool fieldIsOptional,
+                                     std::vector<T>* outPtr, std::vector<std::string>* errors);
+    // Parses a JSON field to VehiclePropertyAccess or VehiclePropertyChangeMode.
+    template <class T>
+    void parseAccessChangeMode(
+            const Json::Value& parentJsonNode, const std::string& fieldName, int propId,
+            const std::string& propStr,
+            const std::unordered_map<aidl::android::hardware::automotive::vehicle::VehicleProperty,
+                                     T>& defaultMap,
+            T* outPtr, std::vector<std::string>* errors);
+
+    // Parses a JSON field to RawPropValues.
+    //
+    // @return True if the field exist and can be parsed to a RawPropValues.
+    bool parsePropValues(const Json::Value& parentJsonNode, const std::string& fieldName,
+                         aidl::android::hardware::automotive::vehicle::RawPropValues* outPtr,
+                         std::vector<std::string>* errors);
+
+    // Prase a JSON field as an array of area configs.
+    void parseAreas(const Json::Value& parentJsonNode, const std::string& fieldName,
+                    ConfigDeclaration* outPtr, std::vector<std::string>* errors);
+};
+
+}  // namespace jsonconfigloader_impl
+
+// A class to load vehicle property configs and initial values in JSON format.
+class JsonConfigLoader final {
+  public:
+    JsonConfigLoader();
+
+    // Loads a JSON file stream and parses it to a map from propId to ConfigDeclarations.
+    android::base::Result<std::unordered_map<int32_t, ConfigDeclaration>> loadPropConfig(
+            std::istream& is);
+
+    // Loads a JSON config file and parses it to a map from propId to ConfigDeclarations.
+    android::base::Result<std::unordered_map<int32_t, ConfigDeclaration>> loadPropConfig(
+            const std::string& configPath);
+
+  private:
+    std::unique_ptr<jsonconfigloader_impl::JsonConfigParser> mParser;
+};
+
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_automotive_vehicle_aidl_impl_default_config_JsonConfigLoader_include_JsonConfigLoader_H_
diff --git a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/src/JsonConfigLoader.cpp b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/src/JsonConfigLoader.cpp
new file mode 100644
index 0000000..e4f13e8
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/src/JsonConfigLoader.cpp
@@ -0,0 +1,566 @@
+/*
+ * 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 <JsonConfigLoader.h>
+
+#include <AccessForVehicleProperty.h>
+#include <ChangeModeForVehicleProperty.h>
+#include <PropertyUtils.h>
+
+#ifdef ENABLE_VEHICLE_HAL_TEST_PROPERTIES
+#include <TestPropertyUtils.h>
+#endif  // ENABLE_VEHICLE_HAL_TEST_PROPERTIES
+
+#include <android-base/strings.h>
+#include <fstream>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+namespace jsonconfigloader_impl {
+
+using ::aidl::android::hardware::automotive::vehicle::AccessForVehicleProperty;
+using ::aidl::android::hardware::automotive::vehicle::ChangeModeForVehicleProperty;
+using ::aidl::android::hardware::automotive::vehicle::EvConnectorType;
+using ::aidl::android::hardware::automotive::vehicle::EvsServiceState;
+using ::aidl::android::hardware::automotive::vehicle::EvsServiceType;
+using ::aidl::android::hardware::automotive::vehicle::FuelType;
+using ::aidl::android::hardware::automotive::vehicle::GsrComplianceRequirementType;
+using ::aidl::android::hardware::automotive::vehicle::RawPropValues;
+using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReport;
+using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReq;
+using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig;
+using ::aidl::android::hardware::automotive::vehicle::VehicleAreaMirror;
+using ::aidl::android::hardware::automotive::vehicle::VehicleAreaWindow;
+using ::aidl::android::hardware::automotive::vehicle::VehicleGear;
+using ::aidl::android::hardware::automotive::vehicle::VehicleHvacFanDirection;
+using ::aidl::android::hardware::automotive::vehicle::VehicleIgnitionState;
+using ::aidl::android::hardware::automotive::vehicle::VehicleOilLevel;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
+using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyChangeMode;
+using ::aidl::android::hardware::automotive::vehicle::VehicleSeatOccupancyState;
+using ::aidl::android::hardware::automotive::vehicle::VehicleTurnSignal;
+using ::aidl::android::hardware::automotive::vehicle::VehicleUnit;
+using ::aidl::android::hardware::automotive::vehicle::VehicleVendorPermission;
+
+using ::android::base::Error;
+using ::android::base::Result;
+
+// Defines a map from constant names to constant values, the values defined here corresponds to
+// the "Constants::XXXX" used in JSON config file.
+const std::unordered_map<std::string, int> CONSTANTS_BY_NAME = {
+        {"DOOR_1_RIGHT", DOOR_1_RIGHT},
+        {"DOOR_1_LEFT", DOOR_1_LEFT},
+        {"DOOR_2_RIGHT", DOOR_2_RIGHT},
+        {"DOOR_2_LEFT", DOOR_2_LEFT},
+        {"DOOR_REAR", DOOR_REAR},
+        {"HVAC_ALL", HVAC_ALL},
+        {"HVAC_LEFT", HVAC_LEFT},
+        {"HVAC_RIGHT", HVAC_RIGHT},
+        {"VENDOR_EXTENSION_INT_PROPERTY", VENDOR_EXTENSION_INT_PROPERTY},
+        {"VENDOR_EXTENSION_BOOLEAN_PROPERTY", VENDOR_EXTENSION_BOOLEAN_PROPERTY},
+        {"VENDOR_EXTENSION_STRING_PROPERTY", VENDOR_EXTENSION_STRING_PROPERTY},
+        {"VENDOR_EXTENSION_FLOAT_PROPERTY", VENDOR_EXTENSION_FLOAT_PROPERTY},
+        {"WINDOW_1_LEFT", WINDOW_1_LEFT},
+        {"WINDOW_1_RIGHT", WINDOW_1_RIGHT},
+        {"WINDOW_2_LEFT", WINDOW_2_LEFT},
+        {"WINDOW_2_RIGHT", WINDOW_2_RIGHT},
+        {"WINDOW_ROOF_TOP_1", WINDOW_ROOF_TOP_1},
+        {"WINDOW_1_RIGHT_2_LEFT_2_RIGHT", WINDOW_1_RIGHT | WINDOW_2_LEFT | WINDOW_2_RIGHT},
+        {"SEAT_1_LEFT", SEAT_1_LEFT},
+        {"SEAT_1_RIGHT", SEAT_1_RIGHT},
+        {"SEAT_2_LEFT", SEAT_2_LEFT},
+        {"SEAT_2_RIGHT", SEAT_2_RIGHT},
+        {"SEAT_2_CENTER", SEAT_2_CENTER},
+        {"WHEEL_REAR_RIGHT", WHEEL_REAR_RIGHT},
+        {"WHEEL_REAR_LEFT", WHEEL_REAR_LEFT},
+        {"WHEEL_FRONT_RIGHT", WHEEL_FRONT_RIGHT},
+        {"WHEEL_FRONT_LEFT", WHEEL_FRONT_LEFT},
+        {"CHARGE_PORT_FRONT_LEFT", CHARGE_PORT_FRONT_LEFT},
+        {"CHARGE_PORT_REAR_LEFT", CHARGE_PORT_REAR_LEFT},
+        {"FAN_DIRECTION_FLOOR", FAN_DIRECTION_FLOOR},
+        {"FAN_DIRECTION_FACE", FAN_DIRECTION_FACE},
+        {"FAN_DIRECTION_DEFROST", FAN_DIRECTION_DEFROST},
+        {"FAN_DIRECTION_FACE_FLOOR", FAN_DIRECTION_FACE | FAN_DIRECTION_FLOOR},
+        {"FAN_DIRECTION_FACE_DEFROST", FAN_DIRECTION_FACE | FAN_DIRECTION_DEFROST},
+        {"FAN_DIRECTION_FLOOR_DEFROST", FAN_DIRECTION_FLOOR | FAN_DIRECTION_DEFROST},
+        {"FAN_DIRECTION_FLOOR_DEFROST_FACE",
+         FAN_DIRECTION_FLOOR | FAN_DIRECTION_DEFROST | FAN_DIRECTION_FACE},
+        {"FUEL_DOOR_REAR_LEFT", FUEL_DOOR_REAR_LEFT},
+        {"LIGHT_STATE_ON", LIGHT_STATE_ON},
+        {"LIGHT_SWITCH_OFF", LIGHT_SWITCH_OFF},
+        {"LIGHT_SWITCH_AUTO", LIGHT_SWITCH_AUTO},
+        {"MIRROR_DRIVER_LEFT_RIGHT",
+         toInt(VehicleAreaMirror::DRIVER_LEFT) | toInt(VehicleAreaMirror::DRIVER_RIGHT)},
+#ifdef ENABLE_VEHICLE_HAL_TEST_PROPERTIES
+        // Following are test properties:
+        {"ECHO_REVERSE_BYTES", ECHO_REVERSE_BYTES},
+        {"kMixedTypePropertyForTest", kMixedTypePropertyForTest},
+        {"VENDOR_CLUSTER_NAVIGATION_STATE", VENDOR_CLUSTER_NAVIGATION_STATE},
+        {"VENDOR_CLUSTER_REQUEST_DISPLAY", VENDOR_CLUSTER_REQUEST_DISPLAY},
+        {"VENDOR_CLUSTER_SWITCH_UI", VENDOR_CLUSTER_SWITCH_UI},
+        {"VENDOR_CLUSTER_DISPLAY_STATE", VENDOR_CLUSTER_DISPLAY_STATE},
+        {"VENDOR_CLUSTER_REPORT_STATE", VENDOR_CLUSTER_REPORT_STATE},
+        {"PLACEHOLDER_PROPERTY_INT", PLACEHOLDER_PROPERTY_INT},
+        {"PLACEHOLDER_PROPERTY_FLOAT", PLACEHOLDER_PROPERTY_FLOAT},
+        {"PLACEHOLDER_PROPERTY_BOOLEAN", PLACEHOLDER_PROPERTY_BOOLEAN},
+        {"PLACEHOLDER_PROPERTY_STRING", PLACEHOLDER_PROPERTY_STRING}
+#endif  // ENABLE_VEHICLE_HAL_TEST_PROPERTIES
+};
+
+// A class to parse constant values for type T.
+template <class T>
+class ConstantParser final : public ConstantParserInterface {
+  public:
+    ConstantParser() {
+        for (const T& v : ndk::enum_range<T>()) {
+            std::string name = aidl::android::hardware::automotive::vehicle::toString(v);
+            // We use the same constant for both VehicleUnit::GALLON and VehicleUnit::US_GALLON,
+            // which caused toString() not work properly for US_GALLON. So we explicitly add the
+            // map here.
+            if (name == "GALLON") {
+                mValueByName["US_GALLON"] = toInt(v);
+            }
+            mValueByName[name] = toInt(v);
+        }
+    }
+
+    ~ConstantParser() = default;
+
+    Result<int> parseValue(const std::string& name) const override {
+        auto it = mValueByName.find(name);
+        if (it == mValueByName.end()) {
+            return Error() << "Constant name: " << name << " is not defined";
+        }
+        return it->second;
+    }
+
+  private:
+    std::unordered_map<std::string, int> mValueByName;
+};
+
+// A class to parse constant values defined in CONSTANTS_BY_NAME map.
+class LocalVariableParser final : public ConstantParserInterface {
+  public:
+    ~LocalVariableParser() = default;
+
+    Result<int> parseValue(const std::string& name) const override {
+        auto constantsIt = CONSTANTS_BY_NAME.find(name);
+        if (constantsIt == CONSTANTS_BY_NAME.end()) {
+            return Error() << "Constant variable name: " << name << " is not defined";
+        }
+        return constantsIt->second;
+    }
+};
+
+JsonValueParser::JsonValueParser() {
+    mConstantParsersByType["VehiclePropertyAccess"] =
+            std::make_unique<ConstantParser<VehiclePropertyAccess>>();
+    mConstantParsersByType["VehiclePropertyChangeMode"] =
+            std::make_unique<ConstantParser<VehiclePropertyChangeMode>>();
+    mConstantParsersByType["VehicleGear"] = std::make_unique<ConstantParser<VehicleGear>>();
+    mConstantParsersByType["VehicleAreaWindow"] =
+            std::make_unique<ConstantParser<VehicleAreaWindow>>();
+    mConstantParsersByType["VehicleAreaMirror"] =
+            std::make_unique<ConstantParser<VehicleAreaMirror>>();
+    mConstantParsersByType["VehicleOilLevel"] = std::make_unique<ConstantParser<VehicleOilLevel>>();
+    mConstantParsersByType["VehicleUnit"] = std::make_unique<ConstantParser<VehicleUnit>>();
+    mConstantParsersByType["VehicleSeatOccupancyState"] =
+            std::make_unique<ConstantParser<VehicleSeatOccupancyState>>();
+    mConstantParsersByType["VehicleHvacFanDirection"] =
+            std::make_unique<ConstantParser<VehicleHvacFanDirection>>();
+    mConstantParsersByType["VehicleApPowerStateReport"] =
+            std::make_unique<ConstantParser<VehicleApPowerStateReport>>();
+    mConstantParsersByType["VehicleTurnSignal"] =
+            std::make_unique<ConstantParser<VehicleTurnSignal>>();
+    mConstantParsersByType["VehicleVendorPermission"] =
+            std::make_unique<ConstantParser<VehicleVendorPermission>>();
+    mConstantParsersByType["EvsServiceType"] = std::make_unique<ConstantParser<EvsServiceType>>();
+    mConstantParsersByType["EvsServiceState"] = std::make_unique<ConstantParser<EvsServiceState>>();
+    mConstantParsersByType["EvConnectorType"] = std::make_unique<ConstantParser<EvConnectorType>>();
+    mConstantParsersByType["VehicleProperty"] = std::make_unique<ConstantParser<VehicleProperty>>();
+    mConstantParsersByType["GsrComplianceRequirementType"] =
+            std::make_unique<ConstantParser<GsrComplianceRequirementType>>();
+    mConstantParsersByType["VehicleIgnitionState"] =
+            std::make_unique<ConstantParser<VehicleIgnitionState>>();
+    mConstantParsersByType["FuelType"] = std::make_unique<ConstantParser<FuelType>>();
+    mConstantParsersByType["Constants"] = std::make_unique<LocalVariableParser>();
+}
+
+template <>
+Result<int32_t> JsonValueParser::convertValueToType<int32_t>(const std::string& fieldName,
+                                                             const Json::Value& value) {
+    if (!value.isInt()) {
+        return Error() << "The value: " << value << " for field: " << fieldName
+                       << " is not in correct type, expect int";
+    }
+    return static_cast<int32_t>(value.asInt());
+}
+
+template <>
+Result<float> JsonValueParser::convertValueToType<float>(const std::string& fieldName,
+                                                         const Json::Value& value) {
+    // isFloat value does not exist, so we use isDouble here.
+    if (!value.isDouble()) {
+        return Error() << "The value: " << value << " for field: " << fieldName
+                       << " is not in correct type, expect float";
+    }
+    return value.asFloat();
+}
+
+template <>
+Result<int64_t> JsonValueParser::convertValueToType<int64_t>(const std::string& fieldName,
+                                                             const Json::Value& value) {
+    if (!value.isInt64()) {
+        return Error() << "The value: " << value << " for field: " << fieldName
+                       << " is not in correct type, expect int64";
+    }
+    return static_cast<int64_t>(value.asInt64());
+}
+
+template <>
+Result<std::string> JsonValueParser::convertValueToType<std::string>(const std::string& fieldName,
+                                                                     const Json::Value& value) {
+    if (!value.isString()) {
+        return Error() << "The value: " << value << " for field: " << fieldName
+                       << " is not in correct type, expect string";
+    }
+    return value.asString();
+}
+
+Result<std::string> JsonValueParser::parseStringValue(const std::string& fieldName,
+                                                      const Json::Value& value) const {
+    return convertValueToType<std::string>(fieldName, value);
+}
+
+template <class T>
+Result<T> JsonValueParser::parseValue(const std::string& fieldName,
+                                      const Json::Value& value) const {
+    if (!value.isString()) {
+        return convertValueToType<T>(fieldName, value);
+    }
+    auto maybeTypeAndValue = maybeGetTypeAndValueName(value.asString());
+    if (!maybeTypeAndValue.has_value()) {
+        return Error() << "Invalid constant value: " << value << " for field: " << fieldName;
+    }
+    auto constantParseResult = parseConstantValue(maybeTypeAndValue.value());
+    if (!constantParseResult.ok()) {
+        return constantParseResult.error();
+    }
+    int constantValue = constantParseResult.value();
+    return static_cast<T>(constantValue);
+}
+
+template <>
+Result<std::string> JsonValueParser::parseValue<std::string>(const std::string& fieldName,
+                                                             const Json::Value& value) const {
+    return parseStringValue(fieldName, value);
+}
+
+template <class T>
+Result<std::vector<T>> JsonValueParser::parseArray(const std::string& fieldName,
+                                                   const Json::Value& value) const {
+    if (!value.isArray()) {
+        return Error() << "The value: " << value << " for field: " << fieldName
+                       << " is not in correct type, expect array";
+    }
+    std::vector<T> parsedValues;
+    for (unsigned int i = 0; i < value.size(); i++) {
+        auto result = parseValue<T>(fieldName, value[i]);
+        if (!result.ok()) {
+            return result.error();
+        }
+        parsedValues.push_back(result.value());
+    }
+    return std::move(parsedValues);
+}
+
+std::optional<std::pair<std::string, std::string>> JsonValueParser::maybeGetTypeAndValueName(
+        const std::string& jsonFieldValue) const {
+    size_t pos = jsonFieldValue.find(DELIMITER);
+    if (pos == std::string::npos) {
+        return {};
+    }
+    std::string type = jsonFieldValue.substr(0, pos);
+    std::string valueName = jsonFieldValue.substr(pos + DELIMITER.length(), std::string::npos);
+    if (type != "Constants" && mConstantParsersByType.find(type) == mConstantParsersByType.end()) {
+        return {};
+    }
+    return std::make_pair(type, valueName);
+}
+
+Result<int> JsonValueParser::parseConstantValue(
+        const std::pair<std::string, std::string>& typeValueName) const {
+    const std::string& type = typeValueName.first;
+    const std::string& valueName = typeValueName.second;
+    auto it = mConstantParsersByType.find(type);
+    if (it == mConstantParsersByType.end()) {
+        return Error() << "Unrecognized type: " << type;
+    }
+    auto result = it->second->parseValue(valueName);
+    if (!result.ok()) {
+        return Error() << type << "::" << valueName << " undefined";
+    }
+    return result;
+}
+
+template <class T>
+bool JsonConfigParser::tryParseJsonValueToVariable(const Json::Value& parentJsonNode,
+                                                   const std::string& fieldName,
+                                                   bool fieldIsOptional, T* outPtr,
+                                                   std::vector<std::string>* errors) {
+    if (!parentJsonNode.isObject()) {
+        errors->push_back("Node: " + parentJsonNode.toStyledString() + " is not an object");
+        return false;
+    }
+    if (!parentJsonNode.isMember(fieldName)) {
+        if (!fieldIsOptional) {
+            errors->push_back("Missing required field: " + fieldName +
+                              " in node: " + parentJsonNode.toStyledString());
+            return false;
+        }
+        return true;
+    }
+    auto result = mValueParser.parseValue<T>(fieldName, parentJsonNode[fieldName]);
+    if (!result.ok()) {
+        errors->push_back(result.error().message());
+        return false;
+    }
+    *outPtr = std::move(result.value());
+    return true;
+}
+
+template <class T>
+bool JsonConfigParser::tryParseJsonArrayToVariable(const Json::Value& parentJsonNode,
+                                                   const std::string& fieldName,
+                                                   bool fieldIsOptional, std::vector<T>* outPtr,
+                                                   std::vector<std::string>* errors) {
+    if (!parentJsonNode.isObject()) {
+        errors->push_back("Node: " + parentJsonNode.toStyledString() + " is not an object");
+        return false;
+    }
+    if (!parentJsonNode.isMember(fieldName)) {
+        if (!fieldIsOptional) {
+            errors->push_back("Missing required field: " + fieldName +
+                              " in node: " + parentJsonNode.toStyledString());
+            return false;
+        }
+        return true;
+    }
+    auto result = mValueParser.parseArray<T>(fieldName, parentJsonNode[fieldName]);
+    if (!result.ok()) {
+        errors->push_back(result.error().message());
+        return false;
+    }
+    *outPtr = std::move(result.value());
+    return true;
+}
+
+template <class T>
+void JsonConfigParser::parseAccessChangeMode(
+        const Json::Value& parentJsonNode, const std::string& fieldName, int propId,
+        const std::string& propStr, const std::unordered_map<VehicleProperty, T>& defaultMap,
+        T* outPtr, std::vector<std::string>* errors) {
+    if (!parentJsonNode.isObject()) {
+        errors->push_back("Node: " + parentJsonNode.toStyledString() + " is not an object");
+        return;
+    }
+    if (parentJsonNode.isMember(fieldName)) {
+        auto result = mValueParser.parseValue<int32_t>(fieldName, parentJsonNode[fieldName]);
+        if (!result.ok()) {
+            errors->push_back(result.error().message());
+            return;
+        }
+        *outPtr = static_cast<T>(result.value());
+        return;
+    }
+    auto it = defaultMap.find(static_cast<VehicleProperty>(propId));
+    if (it == defaultMap.end()) {
+        errors->push_back("No " + fieldName + " specified for property: " + propStr);
+        return;
+    }
+    *outPtr = it->second;
+    return;
+}
+
+bool JsonConfigParser::parsePropValues(const Json::Value& parentJsonNode,
+                                       const std::string& fieldName, RawPropValues* outPtr,
+                                       std::vector<std::string>* errors) {
+    if (!parentJsonNode.isObject()) {
+        errors->push_back("Node: " + parentJsonNode.toStyledString() + " is not an object");
+        return false;
+    }
+    if (!parentJsonNode.isMember(fieldName)) {
+        return false;
+    }
+    const Json::Value& jsonValue = parentJsonNode[fieldName];
+    bool success = true;
+    success &= tryParseJsonArrayToVariable(jsonValue, "int32Values",
+                                           /*optional=*/true, &(outPtr->int32Values), errors);
+    success &= tryParseJsonArrayToVariable(jsonValue, "floatValues",
+                                           /*optional=*/true, &(outPtr->floatValues), errors);
+    success &= tryParseJsonArrayToVariable(jsonValue, "int64Values",
+                                           /*optional=*/true, &(outPtr->int64Values), errors);
+    // We don't support "byteValues" yet.
+    success &= tryParseJsonValueToVariable(jsonValue, "stringValue",
+                                           /*optional=*/true, &(outPtr->stringValue), errors);
+    return success;
+}
+
+void JsonConfigParser::parseAreas(const Json::Value& parentJsonNode, const std::string& fieldName,
+                                  ConfigDeclaration* config, std::vector<std::string>* errors) {
+    if (!parentJsonNode.isObject()) {
+        errors->push_back("Node: " + parentJsonNode.toStyledString() + " is not an object");
+        return;
+    }
+    if (!parentJsonNode.isMember(fieldName)) {
+        return;
+    }
+    const Json::Value& jsonValue = parentJsonNode[fieldName];
+
+    if (!jsonValue.isArray()) {
+        errors->push_back("Field: " + fieldName + " is not an array");
+        return;
+    }
+    for (unsigned int i = 0; i < jsonValue.size(); i++) {
+        int32_t areaId;
+        const Json::Value& jsonAreaConfig = jsonValue[i];
+        if (!tryParseJsonValueToVariable(jsonAreaConfig, "areaId",
+                                         /*optional=*/false, &areaId, errors)) {
+            continue;
+        }
+        VehicleAreaConfig areaConfig = {};
+        areaConfig.areaId = areaId;
+        tryParseJsonValueToVariable(jsonAreaConfig, "minInt32Value", /*optional=*/true,
+                                    &areaConfig.minInt32Value, errors);
+        tryParseJsonValueToVariable(jsonAreaConfig, "maxInt32Value", /*optional=*/true,
+                                    &areaConfig.maxInt32Value, errors);
+        tryParseJsonValueToVariable(jsonAreaConfig, "minInt64Value", /*optional=*/true,
+                                    &areaConfig.minInt64Value, errors);
+        tryParseJsonValueToVariable(jsonAreaConfig, "maxInt64Value", /*optional=*/true,
+                                    &areaConfig.maxInt64Value, errors);
+        tryParseJsonValueToVariable(jsonAreaConfig, "minFloatValue", /*optional=*/true,
+                                    &areaConfig.minFloatValue, errors);
+        tryParseJsonValueToVariable(jsonAreaConfig, "maxFloatValue", /*optional=*/true,
+                                    &areaConfig.maxFloatValue, errors);
+        config->config.areaConfigs.push_back(std::move(areaConfig));
+
+        RawPropValues areaValue = {};
+        if (parsePropValues(jsonAreaConfig, "defaultValue", &areaValue, errors)) {
+            config->initialAreaValues[areaId] = std::move(areaValue);
+        }
+    }
+}
+
+std::optional<ConfigDeclaration> JsonConfigParser::parseEachProperty(
+        const Json::Value& propJsonValue, std::vector<std::string>* errors) {
+    size_t initialErrorCount = errors->size();
+    ConfigDeclaration configDecl = {};
+    int32_t propId;
+
+    if (!tryParseJsonValueToVariable(propJsonValue, "property", /*optional=*/false, &propId,
+                                     errors)) {
+        return std::nullopt;
+    }
+
+    configDecl.config.prop = propId;
+    std::string propStr = propJsonValue["property"].toStyledString();
+
+    parseAccessChangeMode(propJsonValue, "access", propId, propStr, AccessForVehicleProperty,
+                          &configDecl.config.access, errors);
+
+    parseAccessChangeMode(propJsonValue, "changeMode", propId, propStr,
+                          ChangeModeForVehicleProperty, &configDecl.config.changeMode, errors);
+
+    tryParseJsonValueToVariable(propJsonValue, "configString", /*optional=*/true,
+                                &configDecl.config.configString, errors);
+
+    tryParseJsonArrayToVariable(propJsonValue, "configArray", /*optional=*/true,
+                                &configDecl.config.configArray, errors);
+
+    parsePropValues(propJsonValue, "defaultValue", &configDecl.initialValue, errors);
+
+    tryParseJsonValueToVariable(propJsonValue, "minSampleRate", /*optional=*/true,
+                                &configDecl.config.minSampleRate, errors);
+
+    tryParseJsonValueToVariable(propJsonValue, "maxSampleRate", /*optional=*/true,
+                                &configDecl.config.maxSampleRate, errors);
+
+    parseAreas(propJsonValue, "areas", &configDecl, errors);
+
+    if (errors->size() != initialErrorCount) {
+        return std::nullopt;
+    }
+    return configDecl;
+}
+
+Result<std::unordered_map<int32_t, ConfigDeclaration>> JsonConfigParser::parseJsonConfig(
+        std::istream& is) {
+    Json::CharReaderBuilder builder;
+    Json::Value root;
+    std::unordered_map<int32_t, ConfigDeclaration> configsByPropId;
+    std::string errs;
+    if (!Json::parseFromStream(builder, is, &root, &errs)) {
+        return Error() << "Failed to parse property config file as JSON, error: " << errs;
+    }
+    if (!root.isObject()) {
+        return Error() << "root element must be an object";
+    }
+    if (!root.isMember("properties") || !root["properties"].isArray()) {
+        return Error() << "Missing 'properties' field in root or the field is not an array";
+    }
+    Json::Value properties = root["properties"];
+    std::vector<std::string> errors;
+    for (unsigned int i = 0; i < properties.size(); i++) {
+        if (auto maybeConfig = parseEachProperty(properties[i], &errors); maybeConfig.has_value()) {
+            configsByPropId[maybeConfig.value().config.prop] = std::move(maybeConfig.value());
+        }
+    }
+    if (!errors.empty()) {
+        return Error() << android::base::Join(errors, '\n');
+    }
+    return configsByPropId;
+}
+
+}  // namespace jsonconfigloader_impl
+
+JsonConfigLoader::JsonConfigLoader() {
+    mParser = std::make_unique<jsonconfigloader_impl::JsonConfigParser>();
+}
+
+android::base::Result<std::unordered_map<int32_t, ConfigDeclaration>>
+JsonConfigLoader::loadPropConfig(std::istream& is) {
+    return mParser->parseJsonConfig(is);
+}
+
+android::base::Result<std::unordered_map<int32_t, ConfigDeclaration>>
+JsonConfigLoader::loadPropConfig(const std::string& configPath) {
+    std::ifstream ifs(configPath.c_str());
+    if (!ifs) {
+        return android::base::Error() << "couldn't open " << configPath << " for parsing.";
+    }
+
+    return loadPropConfig(ifs);
+}
+
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/test/Android.bp b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/test/Android.bp
new file mode 100644
index 0000000..dae37b9
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/test/Android.bp
@@ -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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+    name: "JsonConfigLoaderUnitTest",
+    vendor: true,
+    srcs: ["*.cpp"],
+    static_libs: [
+        "VehicleHalJsonConfigLoader",
+        "VehicleHalUtils",
+        "libgtest",
+    ],
+    shared_libs: [
+        "libjsoncpp",
+    ],
+    defaults: ["VehicleHalDefaults"],
+    test_suites: ["device-tests"],
+}
+
+cc_test {
+    name: "JsonConfigLoaderUnitTestEnableTestProperties",
+    vendor: true,
+    srcs: ["*.cpp"],
+    static_libs: [
+        "VehicleHalJsonConfigLoaderEnableTestProperties",
+        "VehicleHalUtils",
+        "libgtest",
+    ],
+    shared_libs: [
+        "libjsoncpp",
+    ],
+    defaults: ["VehicleHalDefaults"],
+    test_suites: ["device-tests"],
+}
diff --git a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/test/JsonConfigLoaderUnitTest.cpp b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/test/JsonConfigLoaderUnitTest.cpp
new file mode 100644
index 0000000..9ff8c1b
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/test/JsonConfigLoaderUnitTest.cpp
@@ -0,0 +1,622 @@
+/*
+ * 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 <JsonConfigLoader.h>
+
+#include <PropertyUtils.h>
+
+#include <gtest/gtest.h>
+#include <sstream>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+using ::aidl::android::hardware::automotive::vehicle::RawPropValues;
+using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
+using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyChangeMode;
+
+class JsonConfigLoaderUnitTest : public ::testing::Test {
+  protected:
+    JsonConfigLoader mLoader;
+};
+
+TEST_F(JsonConfigLoaderUnitTest, testBasic) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": 291504388
+        }]
+    }
+    )");
+
+    auto result = mLoader.loadPropConfig(iss);
+
+    ASSERT_TRUE(result.ok()) << result.error().message();
+
+    auto configs = result.value();
+    ASSERT_EQ(configs.size(), 1u);
+    ASSERT_EQ(configs.begin()->second.config.prop, 291504388);
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testRootNotObject) {
+    std::istringstream iss(R"(
+    []
+    )");
+
+    ASSERT_FALSE(mLoader.loadPropConfig(iss).ok()) << "root is not an object must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testMissingPropertiesField) {
+    std::istringstream iss(R"(
+    {
+        "abcd": 1234
+    }
+    )");
+
+    ASSERT_FALSE(mLoader.loadPropConfig(iss).ok()) << "Missing 'properties' field must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testPropertiesFieldNotArray) {
+    std::istringstream iss(R"(
+    {
+        "properties': {'a': 'b'}
+    }
+    )");
+
+    ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+            << "'properties' field is not an array must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testPropertyIsEnum) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "VehicleProperty::INFO_FUEL_CAPACITY"
+        }]
+    }
+    )");
+
+    auto result = mLoader.loadPropConfig(iss);
+
+    ASSERT_TRUE(result.ok()) << result.error().message();
+
+    auto configs = result.value();
+    ASSERT_EQ(configs.size(), 1u);
+    ASSERT_EQ(configs.begin()->second.config.prop, toInt(VehicleProperty::INFO_FUEL_CAPACITY));
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testPropertyEnum_FailInvalidEnum) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "VehicleProperty::BLAH"
+        }]
+    }
+    )");
+
+    ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+            << "Invalid VehicleProperty enum must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testPropertyEnum_FailInvalidType) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "test"
+        }]
+    }
+    )");
+
+    ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+            << "Invalid VehicleProperty type must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testProperty_FailInvalidJson) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+    }
+    )");
+
+    ASSERT_FALSE(mLoader.loadPropConfig(iss).ok()) << "Invalid JSON format must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testConfigArray) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+            "configArray": [1, 2, 3]
+        }]
+    }
+    )");
+
+    auto result = mLoader.loadPropConfig(iss);
+
+    ASSERT_TRUE(result.ok()) << result.error().message();
+
+    auto configs = result.value();
+    ASSERT_EQ(configs.size(), 1u);
+    ASSERT_EQ(configs.begin()->second.config.configArray, std::vector<int>({1, 2, 3}));
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testConfigArrayConstants) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+            "configArray": [1, 2, "Constants::FUEL_DOOR_REAR_LEFT"]
+        }]
+    }
+    )");
+
+    auto result = mLoader.loadPropConfig(iss);
+
+    ASSERT_TRUE(result.ok()) << result.error().message();
+
+    auto configs = result.value();
+    ASSERT_EQ(configs.size(), 1u);
+    ASSERT_EQ(configs.begin()->second.config.configArray,
+              std::vector<int>({1, 2, FUEL_DOOR_REAR_LEFT}));
+}
+
+// We have special logic to deal with GALLON and US_GALLON since they share the same value.
+TEST_F(JsonConfigLoaderUnitTest, testConfigArrayUnitGallon) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+            "configArray": [1, 2, "VehicleUnit::GALLON"]
+        }]
+    }
+    )");
+
+    auto result = mLoader.loadPropConfig(iss);
+
+    ASSERT_TRUE(result.ok()) << result.error().message();
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testConfigArrayUnitUsGallon) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+            "configArray": [1, 2, "VehicleUnit::US_GALLON"]
+        }]
+    }
+    )");
+
+    auto result = mLoader.loadPropConfig(iss);
+
+    ASSERT_TRUE(result.ok()) << result.error().message();
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testConfigArray_FailInvalidEnum) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+            "configArray": [1, 2, "VehicleUnits::BLAH"]
+        }]
+    }
+    )");
+
+    ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+            << "Invalid enum in ConfigArray must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testConfigArray_FailNotArray) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+            "configArray": "123"
+        }]
+    }
+    )");
+
+    ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+            << "ConfigArray is not an array must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testConfigString) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+            "configString": "test"
+        }]
+    }
+    )");
+
+    auto result = mLoader.loadPropConfig(iss);
+
+    ASSERT_TRUE(result.ok()) << result.error().message();
+
+    auto configs = result.value();
+    ASSERT_EQ(configs.size(), 1u);
+    ASSERT_EQ(configs.begin()->second.config.configString, "test");
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testConfigString_FailNotString) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+            "configString": 1234
+        }]
+    }
+    )");
+
+    ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+            << "ConfigString is not a String must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testCheckDefaultAccessChangeMode) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "VehicleProperty::INFO_FUEL_CAPACITY"
+        }]
+    }
+    )");
+
+    auto result = mLoader.loadPropConfig(iss);
+
+    ASSERT_TRUE(result.ok()) << result.error().message();
+    auto configs = result.value();
+    ASSERT_EQ(configs.size(), 1u);
+
+    const VehiclePropConfig& propConfig = configs.begin()->second.config;
+    ASSERT_EQ(propConfig.access, VehiclePropertyAccess::READ);
+    ASSERT_EQ(propConfig.changeMode, VehiclePropertyChangeMode::STATIC);
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testAccessOverride) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+            "access": "VehiclePropertyAccess::WRITE"
+        }]
+    }
+    )");
+
+    auto result = mLoader.loadPropConfig(iss);
+
+    ASSERT_TRUE(result.ok()) << result.error().message();
+    auto configs = result.value();
+    ASSERT_EQ(configs.size(), 1u);
+
+    const VehiclePropConfig& propConfig = configs.begin()->second.config;
+    ASSERT_EQ(propConfig.access, VehiclePropertyAccess::WRITE);
+    ASSERT_EQ(propConfig.changeMode, VehiclePropertyChangeMode::STATIC);
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testChangeModeOverride) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+            "changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
+        }]
+    }
+    )");
+
+    auto result = mLoader.loadPropConfig(iss);
+
+    ASSERT_TRUE(result.ok()) << result.error().message();
+    auto configs = result.value();
+    ASSERT_EQ(configs.size(), 1u);
+
+    const VehiclePropConfig& propConfig = configs.begin()->second.config;
+    ASSERT_EQ(propConfig.access, VehiclePropertyAccess::READ);
+    ASSERT_EQ(propConfig.changeMode, VehiclePropertyChangeMode::ON_CHANGE);
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testCustomProp) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": 1234,
+            "access": "VehiclePropertyAccess::WRITE",
+            "changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
+        }]
+    }
+    )");
+
+    auto result = mLoader.loadPropConfig(iss);
+
+    ASSERT_TRUE(result.ok()) << result.error().message();
+    auto configs = result.value();
+    ASSERT_EQ(configs.size(), 1u);
+
+    const VehiclePropConfig& propConfig = configs.begin()->second.config;
+    ASSERT_EQ(propConfig.access, VehiclePropertyAccess::WRITE);
+    ASSERT_EQ(propConfig.changeMode, VehiclePropertyChangeMode::ON_CHANGE);
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testCustomProp_FailMissingAccess) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": 1234,
+            "changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
+        }]
+    }
+    )");
+
+    ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+            << "Missing access for custom property must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testCustomProp_FailMissingChangeMode) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": 1234,
+            "access": "VehiclePropertyAccess::WRITE"
+        }]
+    }
+    )");
+
+    ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+            << "Missing change mode for custom property must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testMinSampleRate) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+            "minSampleRate": 1,
+        }]
+    }
+    )");
+
+    auto result = mLoader.loadPropConfig(iss);
+
+    ASSERT_TRUE(result.ok()) << result.error().message();
+
+    auto configs = result.value();
+    ASSERT_EQ(configs.size(), 1u);
+    ASSERT_EQ(configs.begin()->second.config.minSampleRate, 1);
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testMinSampleRate_FailInvalidType) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+            "minSampleRate": "abcd",
+        }]
+    }
+    )");
+
+    ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+            << "Wrong type for MinSampleRate must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testMaxSampleRate) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+            "maxSampleRate": 1,
+        }]
+    }
+    )");
+
+    auto result = mLoader.loadPropConfig(iss);
+
+    ASSERT_TRUE(result.ok()) << result.error().message();
+
+    auto configs = result.value();
+    ASSERT_EQ(configs.size(), 1u);
+    ASSERT_EQ(configs.begin()->second.config.maxSampleRate, 1);
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testMaxSampleRate_FailInvalidType) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+            "maxSampleRate": "abcd",
+        }]
+    }
+    )");
+
+    ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+            << "Wrong type for MaxSampleRate must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testDefaultValue_Simple) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+            "defaultValue": {
+                "int32Values": [1, 2]
+            }
+        }]
+    }
+    )");
+
+    auto result = mLoader.loadPropConfig(iss);
+
+    ASSERT_TRUE(result.ok()) << result.error().message();
+
+    auto configs = result.value();
+    ASSERT_EQ(configs.size(), 1u);
+    ASSERT_EQ(configs.begin()->second.initialValue.int32Values, std::vector<int32_t>({1, 2}));
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testDefaultValue_Mixed) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+            "defaultValue": {
+                "int32Values": [1, "Constants::FUEL_DOOR_REAR_LEFT"],
+                "int64Values": [2, "Constants::FUEL_DOOR_REAR_LEFT"],
+                "floatValues": [3.0, "Constants::FUEL_DOOR_REAR_LEFT"],
+                "stringValue": "abcd"
+            }
+        }]
+    }
+    )");
+
+    auto result = mLoader.loadPropConfig(iss);
+
+    ASSERT_TRUE(result.ok()) << result.error().message();
+
+    auto configs = result.value();
+    ASSERT_EQ(configs.size(), 1u);
+
+    const RawPropValues& initialValue = configs.begin()->second.initialValue;
+    ASSERT_EQ(initialValue.int32Values, std::vector<int32_t>({1, FUEL_DOOR_REAR_LEFT}));
+    ASSERT_EQ(initialValue.int64Values,
+              std::vector<int64_t>({2, static_cast<int64_t>(FUEL_DOOR_REAR_LEFT)}));
+    ASSERT_EQ(initialValue.floatValues,
+              std::vector<float>({3.0, static_cast<float>(FUEL_DOOR_REAR_LEFT)}));
+    ASSERT_EQ(initialValue.stringValue, "abcd");
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testDefaultValue_FailNotObject) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+            "defaultValue": []
+        }]
+    }
+    )");
+
+    ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+            << "DefaultValue is not an object must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testDefaultValue_FailInvalidType) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+            "defaultValue": {
+                "int32Values": [1.1]
+            }
+        }]
+    }
+    )");
+
+    ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+            << "Wrong type for DefaultValue must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testAreas_Simple) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+            "areas": [{
+                "areaId": "Constants::HVAC_ALL",
+                "minInt32Value": 1,
+                "maxInt32Value": 7
+            }]
+        }]
+    }
+    )");
+
+    auto result = mLoader.loadPropConfig(iss);
+
+    auto configs = result.value();
+    ASSERT_EQ(configs.size(), 1u);
+
+    const VehiclePropConfig& config = configs.begin()->second.config;
+    ASSERT_EQ(config.areaConfigs.size(), 1u);
+    const VehicleAreaConfig& areaConfig = config.areaConfigs[0];
+    ASSERT_EQ(areaConfig.minInt32Value, 1);
+    ASSERT_EQ(areaConfig.maxInt32Value, 7);
+    ASSERT_EQ(areaConfig.areaId, HVAC_ALL);
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testAreas_DefaultValueForEachArea) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+            "areas": [{
+                "areaId": "Constants::HVAC_LEFT",
+                "defaultValue": {
+                    "int32Values": [1]
+                }
+            }, {
+                "areaId": "Constants::HVAC_RIGHT",
+                "defaultValue": {
+                    "int32Values": [2]
+                }
+            }]
+        }]
+    }
+    )");
+
+    auto result = mLoader.loadPropConfig(iss);
+
+    auto configs = result.value();
+    ASSERT_EQ(configs.size(), 1u);
+
+    const VehiclePropConfig& config = configs.begin()->second.config;
+    ASSERT_EQ(config.areaConfigs.size(), 2u);
+    ASSERT_EQ(config.areaConfigs[0].areaId, HVAC_LEFT);
+    ASSERT_EQ(config.areaConfigs[1].areaId, HVAC_RIGHT);
+    ASSERT_EQ(configs.begin()->second.initialAreaValues[HVAC_LEFT],
+              RawPropValues{.int32Values = {1}});
+    ASSERT_EQ(configs.begin()->second.initialAreaValues[HVAC_RIGHT],
+              RawPropValues{.int32Values = {2}});
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testAreas_FailInvalidTypeForOneAreaValue) {
+    std::istringstream iss(R"(
+    {
+        "properties": [{
+            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+            "areas": [{
+                "areaId": "Constants::HVAC_LEFT",
+                "defaultValue": {
+                    "int32Values": [1]
+                }
+            }, {
+                "areaId": "Constants::HVAC_RIGHT",
+                "defaultValue": {
+                    "int32Values": [2.1]
+                }
+            }]
+        }]
+    }
+    )");
+
+    ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+            << "Wrong type for DefaultValue for one area must cause error";
+}
+
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/aidl/impl/default_config/config/Android.bp b/automotive/vehicle/aidl/impl/default_config/config/Android.bp
index c5f86c2..8f1c7d1 100644
--- a/automotive/vehicle/aidl/impl/default_config/config/Android.bp
+++ b/automotive/vehicle/aidl/impl/default_config/config/Android.bp
@@ -21,3 +21,27 @@
     name: "VehicleHalVendorClusterTestProperties_JSON",
     srcs: ["VendorClusterTestProperties.json"],
 }
+
+prebuilt_etc {
+    name: "Prebuilt_VehicleHalDefaultProperties_JSON",
+    filename_from_src: true,
+    src: "DefaultProperties.json",
+    sub_dir: "automotive/vhalconfig/",
+    vendor: true,
+}
+
+prebuilt_etc {
+    name: "Prebuilt_VehicleHalTestProperties_JSON",
+    filename_from_src: true,
+    src: "TestProperties.json",
+    sub_dir: "automotive/vhalconfig/",
+    vendor: true,
+}
+
+prebuilt_etc {
+    name: "Prebuilt_VehicleHalVendorClusterTestProperties_JSON",
+    filename_from_src: true,
+    src: "VendorClusterTestProperties.json",
+    sub_dir: "automotive/vhalconfig/",
+    vendor: true,
+}
diff --git a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
index f2415aa..0c949d6 100644
--- a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
+++ b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
@@ -59,6 +59,12 @@
             }
         },
         {
+            "property": "VehicleProperty::INFO_VIN",
+            "defaultValue": {
+                "stringValue": "1GCARVIN123456789"
+            }
+        },
+        {
             "property": "VehicleProperty::INFO_MAKE",
             "defaultValue": {
                 "stringValue": "Toy Vehicle"
@@ -130,6 +136,896 @@
             ]
         },
         {
+            "property": "VehicleProperty::SEAT_MEMORY_SELECT",
+            "defaultValue": {
+                "int32Values": [
+                    1
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": 0,
+                    "maxInt32Value": 3
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": 0,
+                    "maxInt32Value": 3
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": 0,
+                    "maxInt32Value": 3
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": 0,
+                    "maxInt32Value": 3
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_MEMORY_SET",
+            "defaultValue": {
+                "int32Values": [
+                    1
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": 0,
+                    "maxInt32Value": 3
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": 0,
+                    "maxInt32Value": 3
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": 0,
+                    "maxInt32Value": 3
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": 0,
+                    "maxInt32Value": 3
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_BELT_BUCKLED",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT"
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT"
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT"
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT"
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER"
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_BELT_HEIGHT_POS",
+            "defaultValue": {
+                "int32Values": [
+                    10
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": 0,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": 0,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": 0,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": 0,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER",
+                    "minInt32Value": 0,
+                    "maxInt32Value": 10
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_BELT_HEIGHT_MOVE",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_FORE_AFT_POS",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_FORE_AFT_MOVE",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_BACKREST_ANGLE_1_POS",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_BACKREST_ANGLE_1_MOVE",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_BACKREST_ANGLE_2_POS",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_BACKREST_ANGLE_2_MOVE",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_HEIGHT_POS",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_HEIGHT_MOVE",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_DEPTH_POS",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_DEPTH_MOVE",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_TILT_POS",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_TILT_MOVE",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_LUMBAR_FORE_AFT_POS",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_LUMBAR_FORE_AFT_MOVE",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_LUMBAR_SIDE_SUPPORT_POS",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_LUMBAR_SIDE_SUPPORT_MOVE",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_HEADREST_HEIGHT_MOVE",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_HEADREST_ANGLE_POS",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_HEADREST_ANGLE_MOVE",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_HEADREST_FORE_AFT_POS",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER",
+                    "minInt32Value": -10,
+                    "maxInt32Value": 10
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::SEAT_HEADREST_FORE_AFT_MOVE",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                }
+            ]
+        },
+        {
             "property": "VehicleProperty::SEAT_OCCUPANCY",
             "areas": [
                 {
@@ -536,6 +1432,19 @@
             ]
         },
         {
+            "property": "VehicleProperty::HVAC_ACTUAL_FAN_SPEED_RPM",
+            "defaultValue": {
+                "int32Values": [
+                    50
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::HVAC_ALL"
+                }
+            ]
+        },
+        {
             "property": "VehicleProperty::HVAC_POWER_ON",
             "defaultValue": {
                 "int32Values": [
@@ -783,6 +1692,42 @@
             "comment": "+ve values for heating and -ve for cooling"
         },
         {
+            "property": "VehicleProperty::HVAC_SIDE_MIRROR_HEAT",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::MIRROR_DRIVER_LEFT_RIGHT",
+                    "minInt32Value": 0,
+                    "maxInt32Value": 2
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::HVAC_TEMPERATURE_CURRENT",
+            "areas": [
+                {
+                    "defaultValue": {
+                        "floatValues": [
+                            17.3
+                        ]
+                    },
+                    "areaId": 49
+                },
+                {
+                    "defaultValue": {
+                        "floatValues": [
+                            19.1
+                        ]
+                    },
+                    "areaId": 68
+                }
+            ]
+        },
+        {
             "property": "VehicleProperty::HVAC_TEMPERATURE_SET",
             "areas": [
                 {
@@ -820,7 +1765,7 @@
             "defaultValue": {
                 "floatValues": [
                     66.19999694824219,
-                    49.0,
+                    "VehicleUnit::FAHRENHEIT",
                     19.0,
                     66.5
                 ]
@@ -840,12 +1785,12 @@
             "property": "VehicleProperty::HVAC_TEMPERATURE_DISPLAY_UNITS",
             "defaultValue": {
                 "int32Values": [
-                    49
+                    "VehicleUnit::FAHRENHEIT"
                 ]
             },
             "configArray": [
-                49,
-                48
+                "VehicleUnit::FAHRENHEIT",
+                "VehicleUnit::CELSIUS"
             ]
         },
         {
@@ -1009,6 +1954,122 @@
             ]
         },
         {
+            "property": "VehicleProperty::MIRROR_Z_POS",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "VehicleAreaMirror::DRIVER_LEFT",
+                    "minInt32Value": -3,
+                    "maxInt32Value": 3
+                },
+                {
+                    "areaId": "VehicleAreaMirror::DRIVER_RIGHT",
+                    "minInt32Value": -3,
+                    "maxInt32Value": 3
+                },
+                {
+                    "areaId": "VehicleAreaMirror::DRIVER_CENTER",
+                    "minInt32Value": -3,
+                    "maxInt32Value": 3
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::MIRROR_Z_MOVE",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "VehicleAreaMirror::DRIVER_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "VehicleAreaMirror::DRIVER_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "VehicleAreaMirror::DRIVER_CENTER",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::MIRROR_Y_POS",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "VehicleAreaMirror::DRIVER_LEFT",
+                    "minInt32Value": -3,
+                    "maxInt32Value": 3
+                },
+                {
+                    "areaId": "VehicleAreaMirror::DRIVER_RIGHT",
+                    "minInt32Value": -3,
+                    "maxInt32Value": 3
+                },
+                {
+                    "areaId": "VehicleAreaMirror::DRIVER_CENTER",
+                    "minInt32Value": -3,
+                    "maxInt32Value": 3
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::MIRROR_Y_MOVE",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "VehicleAreaMirror::DRIVER_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "VehicleAreaMirror::DRIVER_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "VehicleAreaMirror::DRIVER_CENTER",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                }
+            ]
+        },
+        {
+            "property": "VehicleProperty::MIRROR_LOCK",
+            "defaultValue": {
+                "int32Values": [
+                    1
+                ]
+            }
+        },
+        {
+            "property": "VehicleProperty::MIRROR_FOLD",
+            "defaultValue": {
+                "int32Values": [
+                    1
+                ]
+            }
+        },
+        {
             "property": "VehicleProperty::WINDOW_LOCK",
             "areas": [
                 {
@@ -1032,22 +2093,22 @@
                 {
                     "areaId": "Constants::WINDOW_1_LEFT",
                     "minInt32Value": 0,
-                    "maxInt32Value": 10,
+                    "maxInt32Value": 10
                 },
                 {
                     "areaId": "Constants::WINDOW_1_RIGHT",
                     "minInt32Value": 0,
-                    "maxInt32Value": 10,
+                    "maxInt32Value": 10
                 },
                 {
                     "areaId": "Constants::WINDOW_2_LEFT",
                     "minInt32Value": 0,
-                    "maxInt32Value": 10,
+                    "maxInt32Value": 10
                 },
                 {
                     "areaId": "Constants::WINDOW_2_RIGHT",
                     "minInt32Value": 0,
-                    "maxInt32Value": 10,
+                    "maxInt32Value": 10
                 },
                 {
                     "areaId": "Constants::WINDOW_ROOF_TOP_1",
@@ -1057,6 +2118,41 @@
             ]
         },
         {
+            "property": "VehicleProperty::WINDOW_MOVE",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::WINDOW_1_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::WINDOW_1_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::WINDOW_2_LEFT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::WINDOW_2_RIGHT",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                },
+                {
+                    "areaId": "Constants::WINDOW_ROOF_TOP_1",
+                    "minInt32Value": -1,
+                    "maxInt32Value": 1
+                }
+            ]
+        },
+        {
             "property": "VehicleProperty::WHEEL_TICK",
             "defaultValue": {
                 "int64Values": [
@@ -1187,6 +2283,39 @@
             }
         },
         {
+            "property": "VehicleProperty::CABIN_LIGHTS_STATE",
+            "defaultValue": {
+                "int32Values": [
+                    "Constants::LIGHT_STATE_ON"
+                ]
+            }
+        },
+        {
+            "property": "VehicleProperty::READING_LIGHTS_STATE",
+            "defaultValue": {
+                "int32Values": [
+                    "Constants::LIGHT_STATE_ON"
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT"
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT"
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT"
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT"
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER"
+                }
+            ]
+        },
+        {
             "property": "VehicleProperty::HEADLIGHTS_SWITCH",
             "defaultValue": {
                 "int32Values": [
@@ -1227,6 +2356,39 @@
             }
         },
         {
+            "property": "VehicleProperty::CABIN_LIGHTS_SWITCH",
+            "defaultValue": {
+                "int32Values": [
+                    "Constants::LIGHT_SWITCH_OFF"
+                ]
+            }
+        },
+        {
+            "property": "VehicleProperty::READING_LIGHTS_SWITCH",
+            "defaultValue": {
+                "int32Values": [
+                    "Constants::LIGHT_SWITCH_OFF"
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": "Constants::SEAT_1_LEFT"
+                },
+                {
+                    "areaId": "Constants::SEAT_1_RIGHT"
+                },
+                {
+                    "areaId": "Constants::SEAT_2_LEFT"
+                },
+                {
+                    "areaId": "Constants::SEAT_2_RIGHT"
+                },
+                {
+                    "areaId": "Constants::SEAT_2_CENTER"
+                }
+            ]
+        },
+        {
             "property": "VehicleProperty::EVS_SERVICE_REQUEST",
             "defaultValue": {
                 "int32Values": [
@@ -1279,13 +2441,13 @@
             "property": "VehicleProperty::CURRENT_POWER_POLICY"
         },
         {
-            "property": "VehicleProperty::ANDROID_EPOCH_TIME",
+            "property": "VehicleProperty::ANDROID_EPOCH_TIME"
         },
         {
-            "property": "VehicleProperty::WATCHDOG_ALIVE",
+            "property": "VehicleProperty::WATCHDOG_ALIVE"
         },
         {
-            "property": "VehicleProperty::WATCHDOG_TERMINATED_PROCESS",
+            "property": "VehicleProperty::WATCHDOG_TERMINATED_PROCESS"
         },
         {
             "property": "VehicleProperty::VHAL_HEARTBEAT"
@@ -1315,7 +2477,7 @@
                 ]
             },
             "comment":
-                    "Value means: 0 /* Off */, -1, -1, -1, -1 /* Bounds */, -1, -1, -1, -1 /* Insets */",
+                    "Value means: 0 /* Off */, -1, -1, -1, -1 /* Bounds */, -1, -1, -1, -1 /* Insets */"
         },
         {
             "property": "VehicleProperty::CLUSTER_REPORT_STATE",
@@ -1332,18 +2494,18 @@
             ]
         },
         {
-            "property": "VehicleProperty::CLUSTER_REQUEST_DISPLAY",
+            "property": "VehicleProperty::CLUSTER_REQUEST_DISPLAY"
         },
         {
-            "property": "VehicleProperty::CLUSTER_NAVIGATION_STATE",
+            "property": "VehicleProperty::CLUSTER_NAVIGATION_STATE"
         },
         {
             "property": "VehicleProperty::GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT",
             "defaultValue": {
                 "int32Values": [
-                    "GsrComplianceRequirementType::GSR_COMPLIANCE_REQUIRED_THROUGH_SYSTEM_IMAGE"
+                    "GsrComplianceRequirementType::GSR_COMPLIANCE_REQUIRED_V1"
                 ]
             }
         }
     ]
-}
\ No newline at end of file
+}
diff --git a/automotive/vehicle/aidl/impl/default_config/config/README.md b/automotive/vehicle/aidl/impl/default_config/config/README.md
new file mode 100644
index 0000000..0e3fc5e
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/default_config/config/README.md
@@ -0,0 +1,151 @@
+# Property Configuration Files
+
+Each JSON file in this folder is a property configuration file for reference
+Vehicle HAL. They contain VehiclePropConfig information along with initial
+value information.
+
+## JSON schema
+
+Each JSON file must be in a schema like the following example:
+(The comment starting with "//" is for documentation only and must be removed
+from the actual JSON file. The "comment" field is used for comment in the
+actual JSON file and will be ignored by the parser)
+
+```
+{
+    // (number) The version for the JSON schema.
+    "apiVersion": 1,
+    // (non-empty array of objects) The property configuration list.
+    //
+    // Each object is a configuration for one property.
+    "properties": [
+        {
+            // (number/string) The ID for the property.
+            // This value is defined in a string value
+            // which represents a constant value, see the "JSON Number-type
+            // Field Values" section for detail.
+            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+            // (optional, number/string) The access mode for the property.
+            // If specified, this overwrite the default access mode specified in
+            // VehicleProperty.aidl. Must be specified for vendor properties.
+            "access": "VehiclePropertyAccess::READ",
+            // (optional, number/string) The change mode for the property.
+            // If specified, this overwrite the default change mode specified in
+            // VehicleProperty.aidl. Must be specified for vendor properties.
+            "changeMode": "VehiclePropertyChangeMode::STATIC",
+            // (optional, string) The config string.
+            "configString": "blahblah",
+            // (optional, array of number/string) The config array.
+            "configArray": [1, 2, "Constants::HVAC_ALL"],
+            // (optional, object) The default value for the property.
+            // If not specified, the property will be shown as unavailable
+            // until its value is set.
+            "defaultValue": {
+                // (optional, array of int number/string) Int values.
+                "int32Values": [1, 2, "Constants::HVAC_ALL"],
+                // (optional, array of int number/string) Long values.
+                "int64Values": [1, 2],
+                // (optional, array of float number/string) Float values.
+                "floatValues": [1.1, 2.2],
+                // (optional, string) String value.
+                "stringValue": "test"
+            },
+            // (optional, number/string) The minimum sample rate in HZ.
+            // Only work for VehiclePropertyChangeMode::CONTINUOUS property.
+            // Must be specified for continuous property.
+            "minSampleRate": 1,
+            // (optional, number/string) The maximum sample rate in HZ.
+            // Only work for VehiclePropertyChangeMode::CONTINUOUS property.
+            // Must be specified for continuous property.
+            "maxSampleRate": 10,
+            // (optional, array of objects) The area configs.
+            "areas:" [
+                {
+                    // (number/string) The area ID.
+                    "areaId": "Constants::DOOR_1_LEFT",
+                    // (optional number/string) The minimum int value.
+                    "minInt32Value": 1,
+                    // (optional number/string) The maximum int value.
+                    "maxInt32Value": 10,
+                    // (optional number/string) The minimum long value.
+                    "minInt64Value": 1,
+                    // (optional number/string) The maximum long value.
+                    "maxInt64Value": 10,
+                    // (optional number/string) The minimum float value.
+                    "minFloatValue": 1,
+                    // (optional number/string) The maximum float value.
+                    "maxFloatValue": 10,
+                    // (optional object) The default value for this area.
+                    // Uses the same format as the "defaultValue" field for
+                    // property object. If specified, this overwrite the global
+                    // defaultValue.
+                    "defaultValue": {
+                        "int32Values": [1, 2, "Constants::HVAC_ALL"],
+                        "int64Values": [1, 2],
+                        "floatValues": [1.1, 2.2],
+                        "stringValue": "test"
+                    }
+                }
+            ]
+        }
+     ]
+}
+```
+
+## JSON Number-type Field Values
+
+For number type field values, they can either be defined as a numeric number,
+e.g., `{"minInt32Value": 1}` or be defined as a string which represents a
+defined constant value, e.g.,
+`{"property": "VehicleProperty::INFO_FUEL_CAPACITY"}`.
+
+For constant values, they must be a string in the format of `XXX::XXX`, where
+the field before `::` is the constant type, and the field after `::` is the
+variable name.
+
+We support the following constant types:
+
+* VehiclePropertyAccess
+
+* VehiclePropertyChangeMode
+
+* VehicleGear
+
+* VehicleAreaWindow
+
+* VehicleOilLevel
+
+* VehicleUnit
+
+* VehicleSeatOccupancyState
+
+* VehicleHvacFanDirection
+
+* VehicleApPowerStateReport
+
+* VehicleTurnSignal
+
+* VehicleVendorPermission
+
+* EvsServiceType
+
+* EvsServiceState
+
+* EvConnectorType
+
+* VehicleProperty
+
+* GsrComplianceRequirementType
+
+* VehicleIgnitionState
+
+* FuelType
+
+* Constants
+
+Every constant type except "Constants" corresponds to a enum defined in Vehicle
+HAL interfac. E.g. "VehicleProperty" corresponds to the enums defined in
+"VehicleProperty.aidl".
+
+"Constants" type refers to the constant variables defined in the paresr.
+Specifically, the "CONSTANTS_BY_NAME" map defined in "JsonConfigLoader.cpp".
diff --git a/automotive/vehicle/aidl/impl/default_config/include/DefaultConfig.h b/automotive/vehicle/aidl/impl/default_config/include/DefaultConfig.h
deleted file mode 100644
index 986d1a6..0000000
--- a/automotive/vehicle/aidl/impl/default_config/include/DefaultConfig.h
+++ /dev/null
@@ -1,1421 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef android_hardware_automotive_vehicle_aidl_impl_default_config_include_DefaultConfig_H_
-#define android_hardware_automotive_vehicle_aidl_impl_default_config_include_DefaultConfig_H_
-
-#include <PropertyUtils.h>
-#include <TestPropertyUtils.h>
-#include <VehicleHalTypes.h>
-
-#include <map>
-#include <vector>
-
-namespace android {
-namespace hardware {
-namespace automotive {
-namespace vehicle {
-
-// Types used in configs, not to be exposed as public API.
-namespace defaultconfig_impl {
-
-using ::aidl::android::hardware::automotive::vehicle::EvConnectorType;
-using ::aidl::android::hardware::automotive::vehicle::EvsServiceState;
-using ::aidl::android::hardware::automotive::vehicle::EvsServiceType;
-using ::aidl::android::hardware::automotive::vehicle::FuelType;
-using ::aidl::android::hardware::automotive::vehicle::GsrComplianceRequirementType;
-using ::aidl::android::hardware::automotive::vehicle::RawPropValues;
-using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReport;
-using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReq;
-using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig;
-using ::aidl::android::hardware::automotive::vehicle::VehicleAreaMirror;
-using ::aidl::android::hardware::automotive::vehicle::VehicleAreaWindow;
-using ::aidl::android::hardware::automotive::vehicle::VehicleGear;
-using ::aidl::android::hardware::automotive::vehicle::VehicleHvacFanDirection;
-using ::aidl::android::hardware::automotive::vehicle::VehicleIgnitionState;
-using ::aidl::android::hardware::automotive::vehicle::VehicleOilLevel;
-using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
-using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
-using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess;
-using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyChangeMode;
-using ::aidl::android::hardware::automotive::vehicle::VehicleSeatOccupancyState;
-using ::aidl::android::hardware::automotive::vehicle::VehicleTurnSignal;
-using ::aidl::android::hardware::automotive::vehicle::VehicleUnit;
-using ::aidl::android::hardware::automotive::vehicle::VehicleVendorPermission;
-
-struct ConfigDeclaration {
-    VehiclePropConfig config;
-
-    // This value will be used as an initial value for the property. If this field is specified for
-    // property that supports multiple areas then it will be used for all areas unless particular
-    // area is overridden in initialAreaValue field.
-    RawPropValues initialValue;
-    // Use initialAreaValues if it is necessary to specify different values per each area.
-    std::map<int32_t, RawPropValues> initialAreaValues;
-};
-
-const std::vector<ConfigDeclaration> kVehicleProperties = {
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::STATIC,
-                 },
-         .initialValue = {.floatValues = {15000.0f}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::INFO_FUEL_TYPE),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::STATIC,
-                 },
-         .initialValue = {.int32Values = {toInt(FuelType::FUEL_TYPE_UNLEADED)}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::INFO_EV_BATTERY_CAPACITY),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::STATIC,
-                 },
-         .initialValue = {.floatValues = {150000.0f}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::INFO_EV_CONNECTOR_TYPE),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::STATIC,
-                 },
-         .initialValue = {.int32Values = {toInt(EvConnectorType::IEC_TYPE_1_AC)}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::INFO_FUEL_DOOR_LOCATION),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::STATIC,
-                 },
-         .initialValue = {.int32Values = {FUEL_DOOR_REAR_LEFT}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::INFO_EV_PORT_LOCATION),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::STATIC,
-                 },
-         .initialValue = {.int32Values = {CHARGE_PORT_FRONT_LEFT}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::INFO_MULTI_EV_PORT_LOCATIONS),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::STATIC,
-                 },
-         .initialValue = {.int32Values = {CHARGE_PORT_FRONT_LEFT, CHARGE_PORT_REAR_LEFT}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::INFO_MAKE),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::STATIC,
-                 },
-         .initialValue = {.stringValue = "Toy Vehicle"}},
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::INFO_MODEL),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::STATIC,
-                 },
-         .initialValue = {.stringValue = "Speedy Model"}},
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::INFO_MODEL_YEAR),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::STATIC,
-                 },
-         .initialValue = {.int32Values = {2020}}},
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::INFO_EXTERIOR_DIMENSIONS),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::STATIC,
-                 },
-         .initialValue = {.int32Values = {1776, 4950, 2008, 2140, 2984, 1665, 1667, 11800}}},
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::PERF_VEHICLE_SPEED),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
-                         .minSampleRate = 1.0f,
-                         .maxSampleRate = 10.0f,
-                 },
-         .initialValue = {.floatValues = {0.0f}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::VEHICLE_SPEED_DISPLAY_UNITS),
-                         .access = VehiclePropertyAccess::READ_WRITE,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                         .configArray = {toInt(VehicleUnit::METER_PER_SEC),
-                                         toInt(VehicleUnit::MILES_PER_HOUR),
-                                         toInt(VehicleUnit::KILOMETERS_PER_HOUR)},
-                 },
-         .initialValue = {.int32Values = {toInt(VehicleUnit::KILOMETERS_PER_HOUR)}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::EV_BATTERY_DISPLAY_UNITS),
-                         .access = VehiclePropertyAccess::READ_WRITE,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                         .configArray = {toInt(VehicleUnit::WATT_HOUR),
-                                         toInt(VehicleUnit::AMPERE_HOURS),
-                                         toInt(VehicleUnit::KILOWATT_HOUR)},
-                 },
-         .initialValue = {.int32Values = {toInt(VehicleUnit::KILOWATT_HOUR)}}},
-
-        {.config = {.prop = toInt(VehicleProperty::SEAT_BELT_BUCKLED),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT},
-                                    VehicleAreaConfig{.areaId = SEAT_1_RIGHT},
-                                    VehicleAreaConfig{.areaId = SEAT_2_LEFT},
-                                    VehicleAreaConfig{.areaId = SEAT_2_RIGHT},
-                                    VehicleAreaConfig{.areaId = SEAT_2_CENTER}}},
-         .initialAreaValues = {{SEAT_1_LEFT, {.int32Values = {0}}},
-                               {SEAT_1_RIGHT, {.int32Values = {0}}},
-                               {SEAT_2_LEFT, {.int32Values = {0}}},
-                               {SEAT_2_RIGHT, {.int32Values = {0}}},
-                               {SEAT_2_CENTER, {.int32Values = {0}}}}},
-
-        {.config = {.prop = toInt(VehicleProperty::SEAT_BELT_HEIGHT_POS),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
-                                                      .minInt32Value = 0,
-                                                      .maxInt32Value = 10},
-                                    VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
-                                                      .minInt32Value = 0,
-                                                      .maxInt32Value = 10},
-                                    VehicleAreaConfig{.areaId = SEAT_2_LEFT,
-                                                      .minInt32Value = 0,
-                                                      .maxInt32Value = 10},
-                                    VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
-                                                      .minInt32Value = 0,
-                                                      .maxInt32Value = 10},
-                                    VehicleAreaConfig{.areaId = SEAT_2_CENTER,
-                                                      .minInt32Value = 0,
-                                                      .maxInt32Value = 10}}},
-         .initialAreaValues = {{SEAT_1_LEFT, {.int32Values = {10}}},
-                               {SEAT_1_RIGHT, {.int32Values = {10}}},
-                               {SEAT_2_LEFT, {.int32Values = {10}}},
-                               {SEAT_2_RIGHT, {.int32Values = {10}}},
-                               {SEAT_2_CENTER, {.int32Values = {10}}}}},
-
-        {.config = {.prop = toInt(VehicleProperty::SEAT_BELT_HEIGHT_MOVE),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
-                                                      .minInt32Value = -1,
-                                                      .maxInt32Value = 1},
-                                    VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
-                                                      .minInt32Value = -1,
-                                                      .maxInt32Value = 1},
-                                    VehicleAreaConfig{.areaId = SEAT_2_LEFT,
-                                                      .minInt32Value = -1,
-                                                      .maxInt32Value = 1},
-                                    VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
-                                                      .minInt32Value = -1,
-                                                      .maxInt32Value = 1},
-                                    VehicleAreaConfig{.areaId = SEAT_2_CENTER,
-                                                      .minInt32Value = -1,
-                                                      .maxInt32Value = 1}}},
-         .initialAreaValues = {{SEAT_1_LEFT, {.int32Values = {0}}},
-                               {SEAT_1_RIGHT, {.int32Values = {0}}},
-                               {SEAT_2_LEFT, {.int32Values = {0}}},
-                               {SEAT_2_RIGHT, {.int32Values = {0}}},
-                               {SEAT_2_CENTER, {.int32Values = {0}}}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::SEAT_OCCUPANCY),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                         .areaConfigs = {VehicleAreaConfig{.areaId = (SEAT_1_LEFT)},
-                                         VehicleAreaConfig{.areaId = (SEAT_1_RIGHT)}},
-                 },
-         .initialAreaValues = {{SEAT_1_LEFT,
-                                {.int32Values = {toInt(VehicleSeatOccupancyState::VACANT)}}},
-                               {SEAT_1_RIGHT,
-                                {.int32Values = {toInt(VehicleSeatOccupancyState::VACANT)}}}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::INFO_DRIVER_SEAT),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::STATIC,
-                         // this was a zoned property on an old vhal, but it is meant to be global
-                         .areaConfigs = {VehicleAreaConfig{.areaId = (0)}},
-                 },
-         .initialValue = {.int32Values = {SEAT_1_LEFT}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::PERF_ODOMETER),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
-                         .minSampleRate = 1.0f,
-                         .maxSampleRate = 10.0f,
-                 },
-         .initialValue = {.floatValues = {0.0f}}},
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::PERF_STEERING_ANGLE),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
-                         .minSampleRate = 1.0f,
-                         .maxSampleRate = 10.0f,
-                 },
-         .initialValue = {.floatValues = {0.0f}}},
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::PERF_REAR_STEERING_ANGLE),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
-                         .minSampleRate = 1.0f,
-                         .maxSampleRate = 10.0f,
-                 },
-         .initialValue = {.floatValues = {0.0f}}},
-        {
-                .config =
-                        {
-                                .prop = toInt(VehicleProperty::ENGINE_RPM),
-                                .access = VehiclePropertyAccess::READ,
-                                .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
-                                .minSampleRate = 1.0f,
-                                .maxSampleRate = 10.0f,
-                        },
-                .initialValue = {.floatValues = {0.0f}},
-        },
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::FUEL_LEVEL),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
-                         .minSampleRate = 1.0f,
-                         .maxSampleRate = 100.0f,
-                 },
-         .initialValue = {.floatValues = {15000.0f}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::FUEL_DOOR_OPEN),
-                         .access = VehiclePropertyAccess::READ_WRITE,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {0}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::EV_BATTERY_LEVEL),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
-                         .minSampleRate = 1.0f,
-                         .maxSampleRate = 100.0f,
-                 },
-         .initialValue = {.floatValues = {150000.0f}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::EV_CHARGE_PORT_OPEN),
-                         .access = VehiclePropertyAccess::READ_WRITE,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {0}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::EV_CHARGE_PORT_CONNECTED),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {0}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::EV_BATTERY_INSTANTANEOUS_CHARGE_RATE),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
-                         .minSampleRate = 1.0f,
-                         .maxSampleRate = 10.0f,
-                 },
-         .initialValue = {.floatValues = {0.0f}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::EV_CHARGE_CURRENT_DRAW_LIMIT),
-                         .access = VehiclePropertyAccess::READ_WRITE,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                         .configArray = {/*max current draw allowed by vehicle in amperes=*/20},
-                 },
-         .initialValue = {.floatValues = {(float)12.5}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::EV_CHARGE_PERCENT_LIMIT),
-                         .access = VehiclePropertyAccess::READ_WRITE,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                         .configArray = {20, 40, 60, 80, 100},
-                 },
-         .initialValue = {.floatValues = {40}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::EV_CHARGE_STATE),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {2}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::EV_CHARGE_SWITCH),
-                         .access = VehiclePropertyAccess::READ_WRITE,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {0 /* false */}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::EV_CHARGE_TIME_REMAINING),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
-                         .minSampleRate = 1.0f,
-                         .maxSampleRate = 10.0f,
-                 },
-         .initialValue = {.int32Values = {20}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::EV_REGENERATIVE_BRAKING_STATE),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {2}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::TRAILER_PRESENT),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {2}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::VEHICLE_CURB_WEIGHT),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::STATIC,
-                         .configArray = {/*gross weight kg=*/2948},
-                 },
-         .initialValue = {.int32Values = {2211 /*kg*/}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::RANGE_REMAINING),
-                         .access = VehiclePropertyAccess::READ_WRITE,
-                         .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
-                         .minSampleRate = 1.0f,
-                         .maxSampleRate = 2.0f,
-                 },
-         .initialValue = {.floatValues = {50000.0f}}},  // units in meters
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::TIRE_PRESSURE),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
-                         .areaConfigs = {VehicleAreaConfig{
-                                                 .areaId = WHEEL_FRONT_LEFT,
-                                                 .minFloatValue = 193.0f,
-                                                 .maxFloatValue = 300.0f,
-                                         },
-                                         VehicleAreaConfig{
-                                                 .areaId = WHEEL_FRONT_RIGHT,
-                                                 .minFloatValue = 193.0f,
-                                                 .maxFloatValue = 300.0f,
-                                         },
-                                         VehicleAreaConfig{
-                                                 .areaId = WHEEL_REAR_LEFT,
-                                                 .minFloatValue = 193.0f,
-                                                 .maxFloatValue = 300.0f,
-                                         },
-                                         VehicleAreaConfig{
-                                                 .areaId = WHEEL_REAR_RIGHT,
-                                                 .minFloatValue = 193.0f,
-                                                 .maxFloatValue = 300.0f,
-                                         }},
-                         .minSampleRate = 1.0f,
-                         .maxSampleRate = 2.0f,
-                 },
-         .initialValue = {.floatValues = {200.0f}}},  // units in kPa
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::CRITICALLY_LOW_TIRE_PRESSURE),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::STATIC,
-                         .areaConfigs = {VehicleAreaConfig{.areaId = WHEEL_FRONT_LEFT},
-                                         VehicleAreaConfig{.areaId = WHEEL_FRONT_RIGHT},
-                                         VehicleAreaConfig{.areaId = WHEEL_REAR_RIGHT},
-                                         VehicleAreaConfig{.areaId = WHEEL_REAR_LEFT}},
-                 },
-         .initialAreaValues = {{WHEEL_FRONT_LEFT, {.floatValues = {137.0f}}},
-                               {WHEEL_FRONT_RIGHT, {.floatValues = {137.0f}}},
-                               {WHEEL_REAR_RIGHT, {.floatValues = {137.0f}}},
-                               {WHEEL_REAR_LEFT, {.floatValues = {137.0f}}}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::TIRE_PRESSURE_DISPLAY_UNITS),
-                         .access = VehiclePropertyAccess::READ_WRITE,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                         .configArray = {toInt(VehicleUnit::KILOPASCAL), toInt(VehicleUnit::PSI),
-                                         toInt(VehicleUnit::BAR)},
-                 },
-         .initialValue = {.int32Values = {toInt(VehicleUnit::PSI)}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::CURRENT_GEAR),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                         .configArray = {toInt(VehicleGear::GEAR_PARK),
-                                         toInt(VehicleGear::GEAR_NEUTRAL),
-                                         toInt(VehicleGear::GEAR_REVERSE),
-                                         toInt(VehicleGear::GEAR_1), toInt(VehicleGear::GEAR_2),
-                                         toInt(VehicleGear::GEAR_3), toInt(VehicleGear::GEAR_4),
-                                         toInt(VehicleGear::GEAR_5)},
-                 },
-         .initialValue = {.int32Values = {toInt(VehicleGear::GEAR_PARK)}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::PARKING_BRAKE_ON),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {1}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::PARKING_BRAKE_AUTO_APPLY),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {1}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::FUEL_LEVEL_LOW),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {0}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::FUEL_VOLUME_DISPLAY_UNITS),
-                         .access = VehiclePropertyAccess::READ_WRITE,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                         .configArray = {(int)VehicleUnit::LITER, (int)VehicleUnit::US_GALLON},
-                 },
-         .initialValue = {.int32Values = {(int)VehicleUnit::LITER}}},
-
-        {.config =
-                 {
-                         .prop = toInt(
-                                 VehicleProperty::FUEL_CONSUMPTION_UNITS_DISTANCE_OVER_VOLUME),
-                         .access = VehiclePropertyAccess::READ_WRITE,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {1}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::HW_KEY_INPUT),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {0, 0, 0}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::HW_ROTARY_INPUT),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {0, 0, 0}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::HW_CUSTOM_INPUT),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                         .configArray = {0, 0, 0, 3, 0, 0, 0, 0, 0},
-                 },
-         .initialValue =
-                 {
-                         .int32Values = {0, 0, 0},
-                 }},
-
-        {.config = {.prop = toInt(VehicleProperty::HVAC_POWER_ON),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs = {VehicleAreaConfig{.areaId = HVAC_ALL}},
-                    // TODO(bryaneyler): Ideally, this is generated dynamically from
-                    // kHvacPowerProperties.
-                    .configArray = {toInt(VehicleProperty::HVAC_FAN_SPEED),
-                                    toInt(VehicleProperty::HVAC_FAN_DIRECTION)}},
-         .initialValue = {.int32Values = {1}}},
-
-        {
-                .config = {.prop = toInt(VehicleProperty::HVAC_DEFROSTER),
-                           .access = VehiclePropertyAccess::READ_WRITE,
-                           .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                           .areaConfigs =
-                                   {VehicleAreaConfig{
-                                            .areaId = toInt(VehicleAreaWindow::FRONT_WINDSHIELD)},
-                                    VehicleAreaConfig{
-                                            .areaId = toInt(VehicleAreaWindow::REAR_WINDSHIELD)}}},
-                .initialValue = {.int32Values = {0}}  // Will be used for all areas.
-        },
-        {
-                .config = {.prop = toInt(VehicleProperty::HVAC_ELECTRIC_DEFROSTER_ON),
-                           .access = VehiclePropertyAccess::READ_WRITE,
-                           .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                           .areaConfigs =
-                                   {VehicleAreaConfig{
-                                            .areaId = toInt(VehicleAreaWindow::FRONT_WINDSHIELD)},
-                                    VehicleAreaConfig{
-                                            .areaId = toInt(VehicleAreaWindow::REAR_WINDSHIELD)}}},
-                .initialValue = {.int32Values = {0}}  // Will be used for all areas.
-        },
-
-        {.config = {.prop = toInt(VehicleProperty::HVAC_MAX_DEFROST_ON),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs = {VehicleAreaConfig{.areaId = HVAC_ALL}}},
-         .initialValue = {.int32Values = {0}}},
-
-        {.config = {.prop = toInt(VehicleProperty::HVAC_RECIRC_ON),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs = {VehicleAreaConfig{.areaId = HVAC_ALL}}},
-         .initialValue = {.int32Values = {1}}},
-
-        {.config = {.prop = toInt(VehicleProperty::HVAC_AUTO_RECIRC_ON),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs = {VehicleAreaConfig{.areaId = HVAC_ALL}}},
-         .initialValue = {.int32Values = {0}}},
-
-        {.config = {.prop = toInt(VehicleProperty::HVAC_AC_ON),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs = {VehicleAreaConfig{.areaId = HVAC_ALL}}},
-         .initialValue = {.int32Values = {1}}},
-
-        {.config = {.prop = toInt(VehicleProperty::HVAC_MAX_AC_ON),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs = {VehicleAreaConfig{.areaId = HVAC_ALL}}},
-         .initialValue = {.int32Values = {0}}},
-
-        {.config = {.prop = toInt(VehicleProperty::HVAC_AUTO_ON),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs = {VehicleAreaConfig{.areaId = HVAC_ALL}}},
-         .initialValue = {.int32Values = {1}}},
-
-        {.config = {.prop = toInt(VehicleProperty::HVAC_DUAL_ON),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs = {VehicleAreaConfig{.areaId = HVAC_ALL}}},
-         .initialValue = {.int32Values = {0}}},
-
-        {.config = {.prop = toInt(VehicleProperty::HVAC_FAN_SPEED),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs = {VehicleAreaConfig{
-                            .areaId = HVAC_ALL, .minInt32Value = 1, .maxInt32Value = 7}}},
-         .initialValue = {.int32Values = {3}}},
-
-        {.config = {.prop = toInt(VehicleProperty::HVAC_FAN_DIRECTION),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs = {VehicleAreaConfig{.areaId = HVAC_ALL}}},
-         .initialValue = {.int32Values = {toInt(VehicleHvacFanDirection::FACE)}}},
-
-        {.config = {.prop = toInt(VehicleProperty::HVAC_FAN_DIRECTION_AVAILABLE),
-                    .access = VehiclePropertyAccess::READ,
-                    .changeMode = VehiclePropertyChangeMode::STATIC,
-                    .areaConfigs = {VehicleAreaConfig{.areaId = HVAC_ALL}}},
-         .initialValue = {.int32Values = {FAN_DIRECTION_FACE, FAN_DIRECTION_FLOOR,
-                                          FAN_DIRECTION_FACE | FAN_DIRECTION_FLOOR,
-                                          FAN_DIRECTION_DEFROST,
-                                          FAN_DIRECTION_FACE | FAN_DIRECTION_DEFROST,
-                                          FAN_DIRECTION_FLOOR | FAN_DIRECTION_DEFROST,
-                                          FAN_DIRECTION_FLOOR | FAN_DIRECTION_DEFROST |
-                                                  FAN_DIRECTION_FACE}}},
-        {.config = {.prop = toInt(VehicleProperty::HVAC_SEAT_VENTILATION),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs = {VehicleAreaConfig{
-                                            .areaId = SEAT_1_LEFT,
-                                            .minInt32Value = 0,
-                                            .maxInt32Value = 3,
-                                    },
-                                    VehicleAreaConfig{
-                                            .areaId = SEAT_1_RIGHT,
-                                            .minInt32Value = 0,
-                                            .maxInt32Value = 3,
-                                    }}},
-         .initialValue =
-                 {.int32Values = {0}}},  // 0 is off and +ve values indicate ventilation level.
-
-        {.config = {.prop = toInt(VehicleProperty::HVAC_STEERING_WHEEL_HEAT),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs = {VehicleAreaConfig{
-                            .areaId = (0), .minInt32Value = -2, .maxInt32Value = 2}}},
-         .initialValue = {.int32Values = {0}}},  // +ve values for heating and -ve for cooling
-
-        {.config = {.prop = toInt(VehicleProperty::HVAC_SEAT_TEMPERATURE),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs = {VehicleAreaConfig{
-                                            .areaId = SEAT_1_LEFT,
-                                            .minInt32Value = -2,
-                                            .maxInt32Value = 2,
-                                    },
-                                    VehicleAreaConfig{
-                                            .areaId = SEAT_1_RIGHT,
-                                            .minInt32Value = -2,
-                                            .maxInt32Value = 2,
-                                    }}},
-         .initialValue = {.int32Values = {0}}},  // +ve values for heating and -ve for cooling
-
-        {.config = {.prop = toInt(VehicleProperty::HVAC_SIDE_MIRROR_HEAT),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs = {VehicleAreaConfig{
-                            .areaId = toInt(VehicleAreaMirror::DRIVER_LEFT) |
-                                      toInt(VehicleAreaMirror::DRIVER_RIGHT),
-                            .minInt32Value = 0,
-                            .maxInt32Value = 2,
-                    }}},
-         .initialValue = {.int32Values = {0}}},
-
-        {.config = {.prop = toInt(VehicleProperty::HVAC_TEMPERATURE_SET),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .configArray = {160, 280, 5, 605, 825, 10},
-                    .areaConfigs = {VehicleAreaConfig{
-                                            .areaId = HVAC_LEFT,
-                                            .minFloatValue = 16,
-                                            .maxFloatValue = 32,
-                                    },
-                                    VehicleAreaConfig{
-                                            .areaId = HVAC_RIGHT,
-                                            .minFloatValue = 16,
-                                            .maxFloatValue = 32,
-                                    }}},
-         .initialAreaValues = {{HVAC_LEFT, {.floatValues = {16}}},
-                               {HVAC_RIGHT, {.floatValues = {20}}}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION),
-                         .access = VehiclePropertyAccess::READ_WRITE,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.floatValues = {66.2f, (float)VehicleUnit::FAHRENHEIT, 19.0f, 66.5f}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::ENV_OUTSIDE_TEMPERATURE),
-                         .access = VehiclePropertyAccess::READ,
-                         // TODO(bryaneyler): Support ON_CHANGE as well.
-                         .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
-                         .minSampleRate = 1.0f,
-                         .maxSampleRate = 2.0f,
-                 },
-         .initialValue = {.floatValues = {25.0f}}},
-
-        {.config = {.prop = toInt(VehicleProperty::HVAC_TEMPERATURE_DISPLAY_UNITS),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .configArray = {toInt(VehicleUnit::FAHRENHEIT), toInt(VehicleUnit::CELSIUS)}},
-         .initialValue = {.int32Values = {toInt(VehicleUnit::FAHRENHEIT)}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::DISTANCE_DISPLAY_UNITS),
-                         .access = VehiclePropertyAccess::READ_WRITE,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                         .areaConfigs = {VehicleAreaConfig{.areaId = (0)}},
-                         .configArray = {toInt(VehicleUnit::KILOMETER), toInt(VehicleUnit::MILE)},
-                 },
-         .initialValue = {.int32Values = {toInt(VehicleUnit::MILE)}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::NIGHT_MODE),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {0}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::GEAR_SELECTION),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                         .configArray = {toInt(VehicleGear::GEAR_PARK),
-                                         toInt(VehicleGear::GEAR_NEUTRAL),
-                                         toInt(VehicleGear::GEAR_REVERSE),
-                                         toInt(VehicleGear::GEAR_DRIVE), toInt(VehicleGear::GEAR_1),
-                                         toInt(VehicleGear::GEAR_2), toInt(VehicleGear::GEAR_3),
-                                         toInt(VehicleGear::GEAR_4), toInt(VehicleGear::GEAR_5)},
-                 },
-         .initialValue = {.int32Values = {toInt(VehicleGear::GEAR_PARK)}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::TURN_SIGNAL_STATE),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {toInt(VehicleTurnSignal::NONE)}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::IGNITION_STATE),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {toInt(VehicleIgnitionState::ON)}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::ENGINE_COOLANT_TEMP),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
-                         .minSampleRate = 1.0f,
-                         .maxSampleRate = 10.0f,
-                 },
-         .initialValue = {.floatValues = {75.0f}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::ENGINE_OIL_LEVEL),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {toInt(VehicleOilLevel::NORMAL)}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::ENGINE_OIL_TEMP),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
-                         .minSampleRate = 0.1,  // 0.1 Hz, every 10 seconds
-                         .maxSampleRate = 10,   // 10 Hz, every 100 ms
-                 },
-         .initialValue = {.floatValues = {101.0f}}},
-
-        {
-                .config = {.prop = kMixedTypePropertyForTest,
-                           .access = VehiclePropertyAccess::READ_WRITE,
-                           .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                           .configArray = {1, 1, 0, 2, 0, 0, 1, 0, 0}},
-                .initialValue =
-                        {
-                                .int32Values = {1 /* indicate TRUE boolean value */, 2, 3},
-                                .floatValues = {4.5f},
-                                .stringValue = "MIXED property",
-                        },
-        },
-
-        {.config = {.prop = toInt(VehicleProperty::DOOR_LOCK),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs = {VehicleAreaConfig{.areaId = DOOR_1_LEFT},
-                                    VehicleAreaConfig{.areaId = DOOR_1_RIGHT},
-                                    VehicleAreaConfig{.areaId = DOOR_2_LEFT},
-                                    VehicleAreaConfig{.areaId = DOOR_2_RIGHT}}},
-         .initialAreaValues = {{DOOR_1_LEFT, {.int32Values = {1}}},
-                               {DOOR_1_RIGHT, {.int32Values = {1}}},
-                               {DOOR_2_LEFT, {.int32Values = {1}}},
-                               {DOOR_2_RIGHT, {.int32Values = {1}}}}},
-
-        {.config = {.prop = toInt(VehicleProperty::DOOR_POS),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs =
-                            {VehicleAreaConfig{
-                                     .areaId = DOOR_1_LEFT, .minInt32Value = 0, .maxInt32Value = 1},
-                             VehicleAreaConfig{.areaId = DOOR_1_RIGHT,
-                                               .minInt32Value = 0,
-                                               .maxInt32Value = 1},
-                             VehicleAreaConfig{
-                                     .areaId = DOOR_2_LEFT, .minInt32Value = 0, .maxInt32Value = 1},
-                             VehicleAreaConfig{.areaId = DOOR_2_RIGHT,
-                                               .minInt32Value = 0,
-                                               .maxInt32Value = 1},
-                             VehicleAreaConfig{
-                                     .areaId = DOOR_REAR, .minInt32Value = 0, .maxInt32Value = 1}}},
-         .initialValue = {.int32Values = {0}}},
-
-        {.config = {.prop = toInt(VehicleProperty::WINDOW_LOCK),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs = {VehicleAreaConfig{.areaId = WINDOW_1_RIGHT | WINDOW_2_LEFT |
-                                                                WINDOW_2_RIGHT}}},
-         .initialAreaValues = {{WINDOW_1_RIGHT | WINDOW_2_LEFT | WINDOW_2_RIGHT,
-                                {.int32Values = {0}}}}},
-
-        {.config = {.prop = toInt(VehicleProperty::WINDOW_POS),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs = {VehicleAreaConfig{.areaId = WINDOW_1_LEFT,
-                                                      .minInt32Value = 0,
-                                                      .maxInt32Value = 10},
-                                    VehicleAreaConfig{.areaId = WINDOW_1_RIGHT,
-                                                      .minInt32Value = 0,
-                                                      .maxInt32Value = 10},
-                                    VehicleAreaConfig{.areaId = WINDOW_2_LEFT,
-                                                      .minInt32Value = 0,
-                                                      .maxInt32Value = 10},
-                                    VehicleAreaConfig{.areaId = WINDOW_2_RIGHT,
-                                                      .minInt32Value = 0,
-                                                      .maxInt32Value = 10},
-                                    VehicleAreaConfig{.areaId = WINDOW_ROOF_TOP_1,
-                                                      .minInt32Value = -10,
-                                                      .maxInt32Value = 10}}},
-         .initialValue = {.int32Values = {0}}},
-
-        {.config =
-                 {
-                         .prop = WHEEL_TICK,
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
-                         .configArray = {ALL_WHEELS, 50000, 50000, 50000, 50000},
-                         .minSampleRate = 1.0f,
-                         .maxSampleRate = 10.0f,
-                 },
-         .initialValue = {.int64Values = {0, 100000, 200000, 300000, 400000}}},
-
-        {.config = {.prop = ABS_ACTIVE,
-                    .access = VehiclePropertyAccess::READ,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE},
-         .initialValue = {.int32Values = {0}}},
-
-        {.config = {.prop = TRACTION_CONTROL_ACTIVE,
-                    .access = VehiclePropertyAccess::READ,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE},
-         .initialValue = {.int32Values = {0}}},
-
-        {.config = {.prop = toInt(VehicleProperty::AP_POWER_STATE_REQ),
-                    .access = VehiclePropertyAccess::READ,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .configArray = {0}}},
-
-        {.config = {.prop = toInt(VehicleProperty::AP_POWER_STATE_REPORT),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE},
-         .initialValue = {.int32Values = {toInt(VehicleApPowerStateReport::WAIT_FOR_VHAL), 0}}},
-
-        {.config = {.prop = toInt(VehicleProperty::DISPLAY_BRIGHTNESS),
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs = {VehicleAreaConfig{.minInt32Value = 0, .maxInt32Value = 100}}},
-         .initialValue = {.int32Values = {100}}},
-
-        {
-                .config = {.prop = OBD2_LIVE_FRAME,
-                           .access = VehiclePropertyAccess::READ,
-                           .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                           .configArray = {0, 0}},
-        },
-
-        {
-                .config = {.prop = OBD2_FREEZE_FRAME,
-                           .access = VehiclePropertyAccess::READ,
-                           .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                           .configArray = {0, 0}},
-        },
-
-        {
-                .config = {.prop = OBD2_FREEZE_FRAME_INFO,
-                           .access = VehiclePropertyAccess::READ,
-                           .changeMode = VehiclePropertyChangeMode::ON_CHANGE},
-        },
-
-        {
-                .config = {.prop = OBD2_FREEZE_FRAME_CLEAR,
-                           .access = VehiclePropertyAccess::WRITE,
-                           .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                           .configArray = {1}},
-        },
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::HEADLIGHTS_STATE),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {LIGHT_STATE_ON}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::HIGH_BEAM_LIGHTS_STATE),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {LIGHT_STATE_ON}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::FRONT_FOG_LIGHTS_STATE),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {LIGHT_STATE_ON}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::REAR_FOG_LIGHTS_STATE),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {LIGHT_STATE_ON}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::HAZARD_LIGHTS_STATE),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {LIGHT_STATE_ON}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::HEADLIGHTS_SWITCH),
-                         .access = VehiclePropertyAccess::READ_WRITE,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {LIGHT_SWITCH_AUTO}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::HIGH_BEAM_LIGHTS_SWITCH),
-                         .access = VehiclePropertyAccess::READ_WRITE,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {LIGHT_SWITCH_AUTO}}},
-
-        // FOG_LIGHTS_SWITCH must not be implemented when FRONT_FOG_LIGHTS_SWITCH is implemented
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::FRONT_FOG_LIGHTS_SWITCH),
-                         .access = VehiclePropertyAccess::READ_WRITE,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {LIGHT_SWITCH_AUTO}}},
-
-        // FOG_LIGHTS_SWITCH must not be implemented when REAR_FOG_LIGHTS_SWITCH is implemented
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::REAR_FOG_LIGHTS_SWITCH),
-                         .access = VehiclePropertyAccess::READ_WRITE,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {LIGHT_SWITCH_AUTO}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::HAZARD_LIGHTS_SWITCH),
-                         .access = VehiclePropertyAccess::READ_WRITE,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {LIGHT_SWITCH_AUTO}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::EVS_SERVICE_REQUEST),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                 },
-         .initialValue = {.int32Values = {toInt(EvsServiceType::REARVIEW),
-                                          toInt(EvsServiceState::OFF)}}},
-
-        {.config = {.prop = VEHICLE_MAP_SERVICE,
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE}},
-
-        // Example Vendor Extension properties for testing
-        {.config = {.prop = VENDOR_EXTENSION_BOOLEAN_PROPERTY,
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs = {VehicleAreaConfig{.areaId = DOOR_1_LEFT},
-                                    VehicleAreaConfig{.areaId = DOOR_1_RIGHT},
-                                    VehicleAreaConfig{.areaId = DOOR_2_LEFT},
-                                    VehicleAreaConfig{.areaId = DOOR_2_RIGHT}}},
-         .initialAreaValues = {{DOOR_1_LEFT, {.int32Values = {1}}},
-                               {DOOR_1_RIGHT, {.int32Values = {1}}},
-                               {DOOR_2_LEFT, {.int32Values = {0}}},
-                               {DOOR_2_RIGHT, {.int32Values = {0}}}}},
-
-        {.config = {.prop = VENDOR_EXTENSION_FLOAT_PROPERTY,
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs = {VehicleAreaConfig{.areaId = HVAC_LEFT,
-                                                      .minFloatValue = -10,
-                                                      .maxFloatValue = 10},
-                                    VehicleAreaConfig{.areaId = HVAC_RIGHT,
-                                                      .minFloatValue = -10,
-                                                      .maxFloatValue = 10}}},
-         .initialAreaValues = {{HVAC_LEFT, {.floatValues = {1}}},
-                               {HVAC_RIGHT, {.floatValues = {2}}}}},
-
-        {.config = {.prop = VENDOR_EXTENSION_INT_PROPERTY,
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                    .areaConfigs =
-                            {VehicleAreaConfig{.areaId = toInt(VehicleAreaWindow::FRONT_WINDSHIELD),
-                                               .minInt32Value = -100,
-                                               .maxInt32Value = 100},
-                             VehicleAreaConfig{.areaId = toInt(VehicleAreaWindow::REAR_WINDSHIELD),
-                                               .minInt32Value = -100,
-                                               .maxInt32Value = 100},
-                             VehicleAreaConfig{.areaId = toInt(VehicleAreaWindow::ROOF_TOP_1),
-                                               .minInt32Value = -100,
-                                               .maxInt32Value = 100}}},
-         .initialAreaValues = {{toInt(VehicleAreaWindow::FRONT_WINDSHIELD), {.int32Values = {1}}},
-                               {toInt(VehicleAreaWindow::REAR_WINDSHIELD), {.int32Values = {0}}},
-                               {toInt(VehicleAreaWindow::ROOF_TOP_1), {.int32Values = {-1}}}}},
-
-        {.config = {.prop = VENDOR_EXTENSION_STRING_PROPERTY,
-                    .access = VehiclePropertyAccess::READ_WRITE,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE},
-         .initialValue = {.stringValue = "Vendor String Property"}},
-
-        {.config = {.prop = toInt(VehicleProperty::ELECTRONIC_TOLL_COLLECTION_CARD_TYPE),
-                    .access = VehiclePropertyAccess::READ,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE},
-         .initialValue = {.int32Values = {0}}},
-
-        {.config = {.prop = toInt(VehicleProperty::ELECTRONIC_TOLL_COLLECTION_CARD_STATUS),
-                    .access = VehiclePropertyAccess::READ,
-                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE},
-         .initialValue = {.int32Values = {0}}},
-
-        {.config =
-                 {
-                         .prop = toInt(VehicleProperty::SUPPORT_CUSTOMIZE_VENDOR_PERMISSION),
-                         .access = VehiclePropertyAccess::READ,
-                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                         .configArray = {kMixedTypePropertyForTest,
-                                         toInt(VehicleVendorPermission::
-                                                       PERMISSION_GET_VENDOR_CATEGORY_INFO),
-                                         toInt(VehicleVendorPermission::
-                                                       PERMISSION_SET_VENDOR_CATEGORY_INFO),
-                                         VENDOR_EXTENSION_INT_PROPERTY,
-                                         toInt(VehicleVendorPermission::
-                                                       PERMISSION_GET_VENDOR_CATEGORY_SEAT),
-                                         toInt(VehicleVendorPermission::PERMISSION_NOT_ACCESSIBLE),
-                                         VENDOR_EXTENSION_FLOAT_PROPERTY,
-                                         toInt(VehicleVendorPermission::PERMISSION_DEFAULT),
-                                         toInt(VehicleVendorPermission::PERMISSION_DEFAULT)},
-                 },
-         .initialValue = {.int32Values = {1}}},
-
-        {
-                .config =
-                        {
-                                .prop = toInt(VehicleProperty::INITIAL_USER_INFO),
-                                .access = VehiclePropertyAccess::READ_WRITE,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-        },
-        {
-                .config =
-                        {
-                                .prop = toInt(VehicleProperty::SWITCH_USER),
-                                .access = VehiclePropertyAccess::READ_WRITE,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-        },
-        {
-                .config =
-                        {
-                                .prop = toInt(VehicleProperty::CREATE_USER),
-                                .access = VehiclePropertyAccess::READ_WRITE,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-        },
-        {
-                .config =
-                        {
-                                .prop = toInt(VehicleProperty::REMOVE_USER),
-                                .access = VehiclePropertyAccess::READ_WRITE,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-        },
-        {
-                .config =
-                        {
-                                .prop = toInt(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION),
-                                .access = VehiclePropertyAccess::READ_WRITE,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-        },
-        {
-                .config =
-                        {
-                                .prop = toInt(VehicleProperty::POWER_POLICY_REQ),
-                                .access = VehiclePropertyAccess::READ,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-        },
-        {
-                .config =
-                        {
-                                .prop = toInt(VehicleProperty::POWER_POLICY_GROUP_REQ),
-                                .access = VehiclePropertyAccess::READ,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-        },
-        {
-                .config =
-                        {
-                                .prop = toInt(VehicleProperty::CURRENT_POWER_POLICY),
-                                .access = VehiclePropertyAccess::READ_WRITE,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-        },
-        {
-                .config =
-                        {
-                                .prop = toInt(VehicleProperty::ANDROID_EPOCH_TIME),
-                                .access = VehiclePropertyAccess::WRITE,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-        },
-        {
-                .config =
-                        {
-                                .prop = toInt(VehicleProperty::WATCHDOG_ALIVE),
-                                .access = VehiclePropertyAccess::WRITE,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-        },
-        {
-                .config =
-                        {
-                                .prop = toInt(VehicleProperty::WATCHDOG_TERMINATED_PROCESS),
-                                .access = VehiclePropertyAccess::WRITE,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-        },
-        {
-                .config =
-                        {
-                                .prop = toInt(VehicleProperty::VHAL_HEARTBEAT),
-                                .access = VehiclePropertyAccess::READ,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-        },
-        {
-                .config =
-                        {
-                                .prop = toInt(VehicleProperty::CLUSTER_SWITCH_UI),
-                                .access = VehiclePropertyAccess::READ,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-                .initialValue = {.int32Values = {0 /* ClusterHome */}},
-        },
-        {
-                .config =
-                        {
-                                .prop = toInt(VehicleProperty::CLUSTER_DISPLAY_STATE),
-                                .access = VehiclePropertyAccess::READ,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-                .initialValue = {.int32Values = {0 /* Off */, -1, -1, -1, -1 /* Bounds */, -1, -1,
-                                                 -1, -1 /* Insets */}},
-        },
-        {
-                .config =
-                        {
-                                .prop = toInt(VehicleProperty::CLUSTER_REPORT_STATE),
-                                .access = VehiclePropertyAccess::WRITE,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                                .configArray = {0, 0, 0, 11, 0, 0, 0, 0, 16},
-                        },
-        },
-        {
-                .config =
-                        {
-                                .prop = toInt(VehicleProperty::CLUSTER_REQUEST_DISPLAY),
-                                .access = VehiclePropertyAccess::WRITE,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-        },
-        {
-                .config =
-                        {
-                                .prop = toInt(VehicleProperty::CLUSTER_NAVIGATION_STATE),
-                                .access = VehiclePropertyAccess::WRITE,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-        },
-        {
-                .config =
-                        {
-                                .prop = PLACEHOLDER_PROPERTY_INT,
-                                .access = VehiclePropertyAccess::READ_WRITE,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-                .initialValue = {.int32Values = {0}},
-        },
-        {
-                .config =
-                        {
-                                .prop = PLACEHOLDER_PROPERTY_FLOAT,
-                                .access = VehiclePropertyAccess::READ_WRITE,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-                .initialValue = {.floatValues = {0.0f}},
-        },
-        {
-                .config =
-                        {
-                                .prop = PLACEHOLDER_PROPERTY_BOOLEAN,
-                                .access = VehiclePropertyAccess::READ_WRITE,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-                .initialValue = {.int32Values = {0 /* false */}},
-        },
-        {
-                .config =
-                        {
-                                .prop = PLACEHOLDER_PROPERTY_STRING,
-                                .access = VehiclePropertyAccess::READ_WRITE,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-                .initialValue = {.stringValue = {"Test"}},
-        },
-        {
-                .config =
-                        {
-                                .prop = ECHO_REVERSE_BYTES,
-                                .access = VehiclePropertyAccess::READ_WRITE,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-        },
-        {
-                .config =
-                        {
-                                .prop = toInt(
-                                        VehicleProperty::
-                                                GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT),
-                                .access = VehiclePropertyAccess::READ,
-                                .changeMode = VehiclePropertyChangeMode::STATIC,
-                        },
-                .initialValue = {.int32Values = {toInt(
-                                         GsrComplianceRequirementType::
-                                                 GSR_COMPLIANCE_REQUIRED_THROUGH_SYSTEM_IMAGE)}},
-        },
-#ifdef ENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING
-        // Vendor propetry for E2E ClusterHomeService testing.
-        {
-                .config =
-                        {
-                                .prop = VENDOR_CLUSTER_SWITCH_UI,
-                                .access = VehiclePropertyAccess::WRITE,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-        },
-        {
-                .config =
-                        {
-                                .prop = VENDOR_CLUSTER_DISPLAY_STATE,
-                                .access = VehiclePropertyAccess::WRITE,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-        },
-        {
-                .config =
-                        {
-                                .prop = VENDOR_CLUSTER_REPORT_STATE,
-                                .access = VehiclePropertyAccess::READ,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                                .configArray = {0, 0, 0, 11, 0, 0, 0, 0, 16},
-                        },
-                .initialValue = {.int32Values = {0 /* Off */, -1, -1, -1, -1 /* Bounds */, -1, -1,
-                                                 -1, -1 /* Insets */, 0 /* ClusterHome */,
-                                                 -1 /* ClusterNone */}},
-        },
-        {
-                .config =
-                        {
-                                .prop = VENDOR_CLUSTER_REQUEST_DISPLAY,
-                                .access = VehiclePropertyAccess::READ,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-                .initialValue = {.int32Values = {0 /* ClusterHome */}},
-        },
-        {
-                .config =
-                        {
-                                .prop = VENDOR_CLUSTER_NAVIGATION_STATE,
-                                .access = VehiclePropertyAccess::READ,
-                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                        },
-        },
-#endif  // ENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING
-};
-
-}  // namespace defaultconfig_impl
-
-// public namespace
-namespace defaultconfig {
-
-typedef defaultconfig_impl::ConfigDeclaration ConfigDeclaration;
-
-inline constexpr const std::vector<ConfigDeclaration>& getDefaultConfigs() {
-    return defaultconfig_impl::kVehicleProperties;
-}
-
-}  // namespace defaultconfig
-
-}  // namespace vehicle
-}  // namespace automotive
-}  // namespace hardware
-}  // namespace android
-
-#endif  // android_hardware_automotive_vehicle_aidl_impl_default_config_include_DefaultConfig_H_
diff --git a/automotive/vehicle/aidl/impl/default_config/test/Android.bp b/automotive/vehicle/aidl/impl/default_config/test/Android.bp
index 771472c..8702eae 100644
--- a/automotive/vehicle/aidl/impl/default_config/test/Android.bp
+++ b/automotive/vehicle/aidl/impl/default_config/test/Android.bp
@@ -24,11 +24,47 @@
     defaults: ["VehicleHalDefaults"],
     srcs: ["*.cpp"],
     static_libs: [
+        "VehicleHalJsonConfigLoader",
         "VehicleHalUtils",
+        "libgmock",
         "libgtest",
     ],
     header_libs: [
-        "VehicleHalDefaultConfig",
+        "IVehicleGeneratedHeaders",
+    ],
+    shared_libs: [
+        "libjsoncpp",
+    ],
+    data: [
+        ":VehicleHalDefaultProperties_JSON",
+    ],
+    test_suites: ["device-tests"],
+}
+
+cc_test {
+    name: "VehicleHalDefaultConfigTestEnableTestProperties",
+    vendor: true,
+    defaults: ["VehicleHalDefaults"],
+    srcs: ["*.cpp"],
+    static_libs: [
+        "VehicleHalJsonConfigLoaderEnableTestProperties",
+        "VehicleHalUtils",
+        "libgmock",
+        "libgtest",
+    ],
+    cflags: [
+        "-DENABLE_VEHICLE_HAL_TEST_PROPERTIES",
+    ],
+    header_libs: [
+        "IVehicleGeneratedHeaders",
+    ],
+    shared_libs: [
+        "libjsoncpp",
+    ],
+    data: [
+        ":VehicleHalDefaultProperties_JSON",
+        ":VehicleHalTestProperties_JSON",
+        ":VehicleHalVendorClusterTestProperties_JSON",
     ],
     test_suites: ["device-tests"],
 }
diff --git a/automotive/vehicle/aidl/impl/default_config/test/DefaultConfigTest.cpp b/automotive/vehicle/aidl/impl/default_config/test/DefaultConfigTest.cpp
index baaae75..95fecfe 100644
--- a/automotive/vehicle/aidl/impl/default_config/test/DefaultConfigTest.cpp
+++ b/automotive/vehicle/aidl/impl/default_config/test/DefaultConfigTest.cpp
@@ -14,27 +14,75 @@
  * limitations under the License.
  */
 
-#include <DefaultConfig.h>
+#include <JsonConfigLoader.h>
 #include <VehicleUtils.h>
+#include <android-base/file.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
+#include <fstream>
+#include <unordered_map>
 
 namespace android {
 namespace hardware {
 namespace automotive {
 namespace vehicle {
-namespace defaultconfig {
 
 namespace test {
 
-TEST(DefaultConfigTest, loadDefaultConfigs) {
-    for (ConfigDeclaration config : getDefaultConfigs()) {
-        ASSERT_NE(0, config.config.prop);
-    }
+using ::android::base::Error;
+using ::android::base::Result;
+using ::testing::UnorderedElementsAreArray;
+
+constexpr char kDefaultPropertiesConfigFile[] = "DefaultProperties.json";
+
+#ifdef ENABLE_VEHICLE_HAL_TEST_PROPERTIES
+constexpr char kTestPropertiesConfigFile[] = "TestProperties.json";
+constexpr char kVendorClusterTestPropertiesConfigFile[] = "VendorClusterTestProperties.json";
+#endif  // ENABLE_VEHICLE_HAL_TEST_PROPERTIES
+
+std::string getTestFilePath(const char* filename) {
+    static std::string baseDir = android::base::GetExecutableDirectory();
+    return baseDir + "/" + filename;
 }
 
+Result<std::unordered_map<int32_t, ConfigDeclaration>> loadConfig(JsonConfigLoader& loader,
+                                                                  const char* path) {
+    std::string configPath = getTestFilePath(path);
+    std::ifstream ifs(configPath.c_str());
+    if (!ifs) {
+        return Error() << "couldn't open %s for parsing." << configPath;
+    }
+
+    return loader.loadPropConfig(ifs);
+}
+
+TEST(DefaultConfigTest, TestloadDefaultProperties) {
+    JsonConfigLoader loader;
+    auto result = loadConfig(loader, kDefaultPropertiesConfigFile);
+
+    ASSERT_TRUE(result.ok()) << result.error().message();
+}
+
+#ifdef ENABLE_VEHICLE_HAL_TEST_PROPERTIES
+
+TEST(DefaultConfigTest, TestloadTestProperties) {
+    JsonConfigLoader loader;
+    auto result = loadConfig(loader, kTestPropertiesConfigFile);
+
+    ASSERT_TRUE(result.ok()) << result.error().message();
+}
+
+TEST(DefaultConfigTest, TestloadVendorClusterTestProperties) {
+    JsonConfigLoader loader;
+    auto result = loadConfig(loader, kVendorClusterTestPropertiesConfigFile);
+
+    ASSERT_TRUE(result.ok()) << result.error().message();
+}
+
+#endif  // ENABLE_VEHICLE_HAL_TEST_PROPERTIES
+
 }  // namespace test
 
-}  // namespace defaultconfig
 }  // namespace vehicle
 }  // namespace automotive
 }  // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/Android.bp b/automotive/vehicle/aidl/impl/fake_impl/hardware/Android.bp
index dcd9208..4c17cde 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/Android.bp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/Android.bp
@@ -24,6 +24,7 @@
     srcs: ["src/*.cpp"],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
+    cflags: ["-DENABLE_VEHICLE_HAL_TEST_PROPERTIES"],
     defaults: [
         "VehicleHalDefaults",
         "FakeVehicleHardwareDefaults",
@@ -32,18 +33,23 @@
 
 cc_defaults {
     name: "FakeVehicleHardwareDefaults",
-    cflags: ["-DENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING"],
     header_libs: [
         "IVehicleHardware",
-        "VehicleHalDefaultConfig",
+        "VehicleHalTestUtilHeaders",
     ],
     export_header_lib_headers: ["IVehicleHardware"],
     static_libs: [
+        "VehicleHalJsonConfigLoaderEnableTestProperties",
         "VehicleHalUtils",
         "FakeVehicleHalValueGenerators",
         "FakeObd2Frame",
         "FakeUserHal",
     ],
+    required: [
+        "Prebuilt_VehicleHalDefaultProperties_JSON",
+        "Prebuilt_VehicleHalTestProperties_JSON",
+        "Prebuilt_VehicleHalVendorClusterTestProperties_JSON",
+    ],
     shared_libs: [
         "libjsoncpp",
     ],
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
index 8cc19b1..1636cb6 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
@@ -18,11 +18,12 @@
 #define android_hardware_automotive_vehicle_aidl_impl_fake_impl_hardware_include_FakeVehicleHardware_H_
 
 #include <ConcurrentQueue.h>
-#include <DefaultConfig.h>
+#include <ConfigDeclaration.h>
 #include <FakeObd2Frame.h>
 #include <FakeUserHal.h>
 #include <GeneratorHub.h>
 #include <IVehicleHardware.h>
+#include <JsonConfigLoader.h>
 #include <RecurrentTimer.h>
 #include <VehicleHalTypes.h>
 #include <VehiclePropertyStore.h>
@@ -32,9 +33,9 @@
 #include <android-base/stringprintf.h>
 #include <android-base/thread_annotations.h>
 
-#include <map>
 #include <memory>
 #include <mutex>
+#include <unordered_map>
 #include <vector>
 
 namespace android {
@@ -49,7 +50,8 @@
 
     FakeVehicleHardware();
 
-    explicit FakeVehicleHardware(std::unique_ptr<VehiclePropValuePool> valuePool);
+    FakeVehicleHardware(std::string defaultConfigDir, std::string overrideConfigDir,
+                        bool forceOverride);
 
     ~FakeVehicleHardware();
 
@@ -151,17 +153,23 @@
                                   aidl::android::hardware::automotive::vehicle::SetValueRequest>
             mPendingSetValueRequests;
 
+    const std::string mDefaultConfigDir;
+    const std::string mOverrideConfigDir;
+    const bool mForceOverride;
+
+    // Only used during initialization.
+    JsonConfigLoader mLoader;
+
     void init();
     // Stores the initial value to property store.
-    void storePropInitialValue(const defaultconfig::ConfigDeclaration& config);
+    void storePropInitialValue(const ConfigDeclaration& config);
     // The callback that would be called when a vehicle property value change happens.
     void onValueChangeCallback(
             const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value);
-    // If property "persist.vendor.vhal_init_value_override" is set to true, override the properties
-    // using config files in 'overrideDir'.
-    void maybeOverrideProperties(const char* overrideDir);
-    // Override the properties using config files in 'overrideDir'.
-    void overrideProperties(const char* overrideDir);
+    // Load the config files in format '*.json' from the directory and parse the config files
+    // into a map from property ID to ConfigDeclarations.
+    void loadPropConfigsFromDir(const std::string& dirPath,
+                                std::unordered_map<int32_t, ConfigDeclaration>* configs);
     // Function to be called when a value change event comes from vehicle bus. In our fake
     // implementation, this function is only called during "--inject-event" dump command.
     void eventFromVehicleBus(
@@ -185,6 +193,8 @@
             const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value) const;
     bool isHvacPropAndHvacNotAvailable(int32_t propId);
 
+    std::unordered_map<int32_t, ConfigDeclaration> loadConfigDeclarations();
+
     std::string dumpAllProperties();
     std::string dumpOnePropertyByConfig(
             int rowNumber,
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
index 7c451fe..d87e5aa 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
@@ -19,7 +19,6 @@
 
 #include "FakeVehicleHardware.h"
 
-#include <DefaultConfig.h>
 #include <FakeObd2Frame.h>
 #include <JsonFakeValueGenerator.h>
 #include <LinearFakeValueGenerator.h>
@@ -27,6 +26,8 @@
 #include <TestPropertyUtils.h>
 #include <VehicleHalTypes.h>
 #include <VehicleUtils.h>
+
+#include <android-base/file.h>
 #include <android-base/parsedouble.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
@@ -74,9 +75,16 @@
 using ::android::base::StartsWith;
 using ::android::base::StringPrintf;
 
-const char* VENDOR_OVERRIDE_DIR = "/vendor/etc/automotive/vhaloverride/";
-const char* OVERRIDE_PROPERTY = "persist.vendor.vhal_init_value_override";
-const char* POWER_STATE_REQ_CONFIG_PROPERTY = "ro.vendor.fake_vhal.ap_power_state_req.config";
+// The directory for default property configuration file.
+// For config file format, see impl/default_config/config/README.md.
+constexpr char DEFAULT_CONFIG_DIR[] = "/vendor/etc/automotive/vhalconfig/";
+// The directory for property configuration file that overrides the default configuration file.
+// For config file format, see impl/default_config/config/README.md.
+constexpr char OVERRIDE_CONFIG_DIR[] = "/vendor/etc/automotive/vhaloverride/";
+// If OVERRIDE_PROPERTY is set, we will use the configuration files from OVERRIDE_CONFIG_DIR to
+// overwrite the default configs.
+constexpr char OVERRIDE_PROPERTY[] = "persist.vendor.vhal_init_value_override";
+constexpr char POWER_STATE_REQ_CONFIG_PROPERTY[] = "ro.vendor.fake_vhal.ap_power_state_req.config";
 
 // A list of supported options for "--set" command.
 const std::unordered_set<std::string> SET_PROP_OPTIONS = {
@@ -97,7 +105,7 @@
 
 }  // namespace
 
-void FakeVehicleHardware::storePropInitialValue(const defaultconfig::ConfigDeclaration& config) {
+void FakeVehicleHardware::storePropInitialValue(const ConfigDeclaration& config) {
     const VehiclePropConfig& vehiclePropConfig = config.config;
     int propId = vehiclePropConfig.prop;
 
@@ -139,10 +147,11 @@
 }
 
 FakeVehicleHardware::FakeVehicleHardware()
-    : FakeVehicleHardware(std::make_unique<VehiclePropValuePool>()) {}
+    : FakeVehicleHardware(DEFAULT_CONFIG_DIR, OVERRIDE_CONFIG_DIR, false) {}
 
-FakeVehicleHardware::FakeVehicleHardware(std::unique_ptr<VehiclePropValuePool> valuePool)
-    : mValuePool(std::move(valuePool)),
+FakeVehicleHardware::FakeVehicleHardware(std::string defaultConfigDir,
+                                         std::string overrideConfigDir, bool forceOverride)
+    : mValuePool(std::make_unique<VehiclePropValuePool>()),
       mServerSidePropStore(new VehiclePropertyStore(mValuePool)),
       mFakeObd2Frame(new obd2frame::FakeObd2Frame(mServerSidePropStore)),
       mFakeUserHal(new FakeUserHal(mValuePool)),
@@ -150,7 +159,10 @@
       mGeneratorHub(new GeneratorHub(
               [this](const VehiclePropValue& value) { eventFromVehicleBus(value); })),
       mPendingGetValueRequests(this),
-      mPendingSetValueRequests(this) {
+      mPendingSetValueRequests(this),
+      mDefaultConfigDir(defaultConfigDir),
+      mOverrideConfigDir(overrideConfigDir),
+      mForceOverride(forceOverride) {
     init();
 }
 
@@ -160,9 +172,19 @@
     mGeneratorHub.reset();
 }
 
+std::unordered_map<int32_t, ConfigDeclaration> FakeVehicleHardware::loadConfigDeclarations() {
+    std::unordered_map<int32_t, ConfigDeclaration> configsByPropId;
+    loadPropConfigsFromDir(mDefaultConfigDir, &configsByPropId);
+    if (mForceOverride ||
+        android::base::GetBoolProperty(OVERRIDE_PROPERTY, /*default_value=*/false)) {
+        loadPropConfigsFromDir(mOverrideConfigDir, &configsByPropId);
+    }
+    return configsByPropId;
+}
+
 void FakeVehicleHardware::init() {
-    for (auto& it : defaultconfig::getDefaultConfigs()) {
-        VehiclePropConfig cfg = it.config;
+    for (auto& [_, configDeclaration] : loadConfigDeclarations()) {
+        VehiclePropConfig cfg = configDeclaration.config;
         VehiclePropertyStore::TokenFunction tokenFunction = nullptr;
 
         if (cfg.prop == toInt(VehicleProperty::AP_POWER_STATE_REQ)) {
@@ -178,15 +200,18 @@
             // logic.
             continue;
         }
-        storePropInitialValue(it);
+        storePropInitialValue(configDeclaration);
     }
 
-    maybeOverrideProperties(VENDOR_OVERRIDE_DIR);
-
     // OBD2_LIVE_FRAME and OBD2_FREEZE_FRAME must be configured in default configs.
-    mFakeObd2Frame->initObd2LiveFrame(*mServerSidePropStore->getConfig(OBD2_LIVE_FRAME).value());
-    mFakeObd2Frame->initObd2FreezeFrame(
-            *mServerSidePropStore->getConfig(OBD2_FREEZE_FRAME).value());
+    auto maybeObd2LiveFrame = mServerSidePropStore->getConfig(OBD2_LIVE_FRAME);
+    if (maybeObd2LiveFrame.has_value()) {
+        mFakeObd2Frame->initObd2LiveFrame(*maybeObd2LiveFrame.value());
+    }
+    auto maybeObd2FreezeFrame = mServerSidePropStore->getConfig(OBD2_FREEZE_FRAME);
+    if (maybeObd2FreezeFrame.has_value()) {
+        mFakeObd2Frame->initObd2FreezeFrame(*maybeObd2FreezeFrame.value());
+    }
 
     mServerSidePropStore->setOnValueChangeCallback(
             [this](const VehiclePropValue& value) { return onValueChangeCallback(value); });
@@ -408,7 +433,7 @@
             *isSpecialValue = true;
             return mFakeObd2Frame->clearObd2FreezeFrames(value);
 
-#ifdef ENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING
+#ifdef ENABLE_VEHICLE_HAL_TEST_PROPERTIES
         case toInt(VehicleProperty::CLUSTER_REPORT_STATE):
             [[fallthrough]];
         case toInt(VehicleProperty::CLUSTER_REQUEST_DISPLAY):
@@ -436,7 +461,7 @@
                        << getErrorMsg(writeResult);
             }
             return {};
-#endif  // ENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING
+#endif  // ENABLE_VEHICLE_HAL_TEST_PROPERTIES
 
         default:
             break;
@@ -1264,33 +1289,26 @@
     (*mOnPropertyChangeCallback)(std::move(updatedValues));
 }
 
-void FakeVehicleHardware::maybeOverrideProperties(const char* overrideDir) {
-    if (android::base::GetBoolProperty(OVERRIDE_PROPERTY, false)) {
-        overrideProperties(overrideDir);
-    }
-}
-
-void FakeVehicleHardware::overrideProperties(const char* overrideDir) {
-    ALOGI("loading vendor override properties from %s", overrideDir);
-    if (auto dir = opendir(overrideDir); dir != NULL) {
+void FakeVehicleHardware::loadPropConfigsFromDir(
+        const std::string& dirPath,
+        std::unordered_map<int32_t, ConfigDeclaration>* configsByPropId) {
+    ALOGI("loading properties from %s", dirPath.c_str());
+    if (auto dir = opendir(dirPath.c_str()); dir != NULL) {
         std::regex regJson(".*[.]json", std::regex::icase);
         while (auto f = readdir(dir)) {
             if (!std::regex_match(f->d_name, regJson)) {
                 continue;
             }
-            std::string file = overrideDir + std::string(f->d_name);
-            JsonFakeValueGenerator tmpGenerator(file);
-
-            std::vector<VehiclePropValue> propValues = tmpGenerator.getAllEvents();
-            for (const VehiclePropValue& prop : propValues) {
-                auto propToStore = mValuePool->obtain(prop);
-                propToStore->timestamp = elapsedRealtimeNano();
-                if (auto result = mServerSidePropStore->writeValue(std::move(propToStore),
-                                                                   /*updateStatus=*/true);
-                    !result.ok()) {
-                    ALOGW("failed to write vendor override properties: %d, error: %s, code: %d",
-                          prop.prop, getErrorMsg(result).c_str(), getIntErrorCode(result));
-                }
+            std::string filePath = dirPath + "/" + std::string(f->d_name);
+            ALOGI("loading properties from %s", filePath.c_str());
+            auto result = mLoader.loadPropConfig(filePath);
+            if (!result.ok()) {
+                ALOGE("failed to load config file: %s, error: %s", filePath.c_str(),
+                      result.error().message().c_str());
+                continue;
+            }
+            for (auto& [propId, configDeclaration] : result.value()) {
+                (*configsByPropId)[propId] = std::move(configDeclaration);
             }
         }
         closedir(dir);
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp
index cfd6577..8d8fcf5 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp
@@ -22,13 +22,13 @@
     name: "FakeVehicleHardwareTest",
     vendor: true,
     srcs: ["*.cpp"],
-    cflags: ["-DENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING"],
+    cflags: ["-DENABLE_VEHICLE_HAL_TEST_PROPERTIES"],
     header_libs: [
         "IVehicleHardware",
-        "VehicleHalDefaultConfig",
         "VehicleHalTestUtilHeaders",
     ],
     static_libs: [
+        "VehicleHalJsonConfigLoaderEnableTestProperties",
         "VehicleHalUtils",
         "FakeVehicleHardware",
         "FakeVehicleHalValueGenerators",
@@ -41,6 +41,9 @@
         "libjsoncpp",
     ],
     data: [
+        ":VehicleHalDefaultProperties_JSON",
+        ":VehicleHalTestProperties_JSON",
+        ":VehicleHalVendorClusterTestProperties_JSON",
         ":FakeVehicleHardwareTestOverrideJson",
         ":FakeVehicleHardwareTestPropJson",
     ],
@@ -55,5 +58,5 @@
 
 filegroup {
     name: "FakeVehicleHardwareTestPropJson",
-    srcs: ["prop.json"],
+    srcs: ["fakedata/prop.json"],
 }
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
index ab6bf51..c230c51 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
@@ -16,7 +16,6 @@
 
 #include <FakeVehicleHardware.h>
 
-#include <DefaultConfig.h>
 #include <FakeObd2Frame.h>
 #include <FakeUserHal.h>
 #include <PropertyUtils.h>
@@ -80,7 +79,9 @@
   public:
     FakeVehicleHardwareTestHelper(FakeVehicleHardware* hardware) { mHardware = hardware; }
 
-    void overrideProperties(const char* overrideDir) { mHardware->overrideProperties(overrideDir); }
+    std::unordered_map<int32_t, ConfigDeclaration> loadConfigDeclarations() {
+        return mHardware->loadConfigDeclarations();
+    }
 
   private:
     FakeVehicleHardware* mHardware;
@@ -89,7 +90,9 @@
 class FakeVehicleHardwareTest : public ::testing::Test {
   protected:
     void SetUp() override {
-        mHardware = std::make_unique<FakeVehicleHardware>();
+        mHardware = std::make_unique<FakeVehicleHardware>(android::base::GetExecutableDirectory(),
+                                                          /*overrideConfigDir=*/"",
+                                                          /*forceOverride=*/false);
         auto callback = std::make_unique<IVehicleHardware::PropertyChangeCallback>(
                 [this](const std::vector<VehiclePropValue>& values) {
                     onPropertyChangeEvent(values);
@@ -109,6 +112,10 @@
 
     FakeVehicleHardware* getHardware() { return mHardware.get(); }
 
+    void setHardware(std::unique_ptr<FakeVehicleHardware> hardware) {
+        mHardware = std::move(hardware);
+    }
+
     StatusCode setValues(const std::vector<SetValueRequest>& requests) {
         {
             std::scoped_lock<std::mutex> lockGuard(mLock);
@@ -374,7 +381,8 @@
 TEST_F(FakeVehicleHardwareTest, testGetAllPropertyConfigs) {
     std::vector<VehiclePropConfig> configs = getHardware()->getAllPropertyConfigs();
 
-    ASSERT_EQ(configs.size(), defaultconfig::getDefaultConfigs().size());
+    FakeVehicleHardwareTestHelper helper(getHardware());
+    ASSERT_EQ(configs.size(), helper.loadConfigDeclarations().size());
 }
 
 TEST_F(FakeVehicleHardwareTest, testGetDefaultValues) {
@@ -382,7 +390,8 @@
     std::vector<GetValueResult> expectedGetValueResults;
     int64_t requestId = 1;
 
-    for (auto& config : defaultconfig::getDefaultConfigs()) {
+    FakeVehicleHardwareTestHelper helper(getHardware());
+    for (auto& [propId, config] : helper.loadConfigDeclarations()) {
         if (obd2frame::FakeObd2Frame::isDiagnosticProperty(config.config)) {
             // Ignore storing default value for diagnostic property. They have special get/set
             // logic.
@@ -394,12 +403,11 @@
             continue;
         }
 
-        if (config.config.prop == ECHO_REVERSE_BYTES) {
+        if (propId == ECHO_REVERSE_BYTES) {
             // Ignore ECHO_REVERSE_BYTES, it has special logic.
             continue;
         }
 
-        int propId = config.config.prop;
         if (isGlobalProp(propId)) {
             if (config.initialValue == RawPropValues{}) {
                 addGetValueRequest(getValueRequests, expectedGetValueResults, requestId++,
@@ -657,10 +665,12 @@
 }
 
 TEST_F(FakeVehicleHardwareTest, testVendorOverrideProperties) {
-    std::string overrideDir = android::base::GetExecutableDirectory() + "/override/";
+    std::string currentDir = android::base::GetExecutableDirectory();
+    std::string overrideDir = currentDir + "/override/";
     // Set vendor override directory.
-    FakeVehicleHardwareTestHelper helper(getHardware());
-    helper.overrideProperties(overrideDir.c_str());
+    std::unique_ptr<FakeVehicleHardware> hardware =
+            std::make_unique<FakeVehicleHardware>(currentDir, overrideDir, /*forceOverride=*/true);
+    setHardware(std::move(hardware));
 
     // This is the same as the prop in 'gear_selection.json'.
     int gearProp = toInt(VehicleProperty::GEAR_SELECTION);
@@ -695,10 +705,12 @@
 }
 
 TEST_F(FakeVehicleHardwareTest, testVendorOverridePropertiesMultipleAreas) {
-    std::string overrideDir = android::base::GetExecutableDirectory() + "/override/";
+    std::string currentDir = android::base::GetExecutableDirectory();
+    std::string overrideDir = currentDir + "/override/";
     // Set vendor override directory.
-    FakeVehicleHardwareTestHelper helper(getHardware());
-    helper.overrideProperties(overrideDir.c_str());
+    std::unique_ptr<FakeVehicleHardware> hardware =
+            std::make_unique<FakeVehicleHardware>(currentDir, overrideDir, /*forceOverride=*/true);
+    setHardware(std::move(hardware));
 
     // This is the same as the prop in 'hvac_temperature_set.json'.
     int hvacProp = toInt(VehicleProperty::HVAC_TEMPERATURE_SET);
@@ -711,22 +723,16 @@
     ASSERT_TRUE(result.ok()) << "expect to get the overridden property ok: " << getStatus(result);
     ASSERT_EQ(static_cast<size_t>(1), result.value().value.floatValues.size());
     ASSERT_EQ(30.0f, result.value().value.floatValues[0]);
-
-    // HVAC_RIGHT should not be affected and return the default value.
-    result = getValue(VehiclePropValue{
-            .prop = hvacProp,
-            .areaId = HVAC_RIGHT,
-    });
-
-    ASSERT_TRUE(result.ok()) << "expect to get the default property ok: " << getStatus(result);
-    ASSERT_EQ(static_cast<size_t>(1), result.value().value.floatValues.size());
-    ASSERT_EQ(20.0f, result.value().value.floatValues[0]);
 }
 
 TEST_F(FakeVehicleHardwareTest, testVendorOverridePropertiesDirDoesNotExist) {
-    // Set vendor override directory to a non-existing dir
-    FakeVehicleHardwareTestHelper helper(getHardware());
-    helper.overrideProperties("123");
+    std::string currentDir = android::base::GetExecutableDirectory();
+    std::string overrideDir = currentDir + "/override/";
+    // Set vendor override directory to a non-existing dir.
+    std::unique_ptr<FakeVehicleHardware> hardware =
+            std::make_unique<FakeVehicleHardware>(currentDir, "1234", /*forceOverride=*/true);
+    setHardware(std::move(hardware));
+
     auto result = getValue(VehiclePropValue{
             .prop = toInt(VehicleProperty::GEAR_SELECTION),
     });
@@ -1840,7 +1846,7 @@
 
 std::string getTestFilePath(const char* filename) {
     static std::string baseDir = android::base::GetExecutableDirectory();
-    return baseDir + "/" + filename;
+    return baseDir + "/fakedata/" + filename;
 }
 
 TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJson) {
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/prop.json b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/fakedata/prop.json
similarity index 100%
rename from automotive/vehicle/aidl/impl/fake_impl/hardware/test/prop.json
rename to automotive/vehicle/aidl/impl/fake_impl/hardware/test/fakedata/prop.json
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/override/gear_selection.json b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/override/gear_selection.json
index 59666b8..693f1e2 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/override/gear_selection.json
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/override/gear_selection.json
@@ -1,9 +1,13 @@
-[
-  {
-    "timestamp": 1000000,
-    "areaId": 0,
-    "value": 8,
-    // GEAR_SELECTION
-    "prop": 289408000
-  }
-]
+{
+    "apiVersion": 1,
+    "properties": [
+        {
+            "property": "VehicleProperty::GEAR_SELECTION",
+            "defaultValue": {
+                "int32Values": [
+                    8
+                ]
+            }
+        }
+    ]
+}
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/override/hvac_temperature_set.json b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/override/hvac_temperature_set.json
index 93a97ed..07cfebb 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/override/hvac_temperature_set.json
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/override/hvac_temperature_set.json
@@ -1,10 +1,20 @@
-[
-  {
-    "timestamp": 1000000,
-    // HVAC_LEFT
-    "areaId": 49,
-    "value": 30,
-    // HVAC_TEMPERATURE_SET
-    "prop": 358614275
-  }
-]
+{
+    "apiVersion": 1,
+    "properties": [
+        {
+            "property": "VehicleProperty::HVAC_TEMPERATURE_SET",
+            "areas": [
+                {
+                    "defaultValue": {
+                        "floatValues": [
+                            30.0
+                        ]
+                    },
+                    "areaId": 49,
+                    "minFloatValue": 16.0,
+                    "maxFloatValue": 32.0
+                }
+            ]
+        }
+    ]
+}
diff --git a/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/Android.bp b/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/Android.bp
index 7670c25..2b4059c 100644
--- a/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/Android.bp
+++ b/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/Android.bp
@@ -47,13 +47,19 @@
     ],
     vendor: true,
     defaults: ["VehicleHalDefaults"],
-    shared_libs: ["libprotobuf-cpp-full"],
+    shared_libs: [
+        "libprotobuf-cpp-full",
+        "libjsoncpp",
+    ],
     static_libs: [
+        "VehicleHalJsonConfigLoaderEnableTestProperties",
         "VehicleHalProtoMessageConverter",
         "VehicleHalProtos",
         "VehicleHalUtils",
         "libgtest",
     ],
-    header_libs: ["VehicleHalDefaultConfig"],
+    data: [
+        ":VehicleHalDefaultProperties_JSON",
+    ],
     test_suites: ["device-tests"],
 }
diff --git a/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/test/proto_message_converter_test.cpp b/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/test/proto_message_converter_test.cpp
index c742db5..308be46 100644
--- a/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/test/proto_message_converter_test.cpp
+++ b/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/test/proto_message_converter_test.cpp
@@ -16,9 +16,11 @@
 
 #include <vector>
 
-#include <DefaultConfig.h>
+#include <JsonConfigLoader.h>
 #include <ProtoMessageConverter.h>
 #include <VehicleHalTypes.h>
+
+#include <android-base/file.h>
 #include <android-base/format.h>
 #include <android/hardware/automotive/vehicle/VehiclePropConfig.pb.h>
 #include <android/hardware/automotive/vehicle/VehiclePropValue.pb.h>
@@ -35,23 +37,39 @@
 namespace proto = ::android::hardware::automotive::vehicle::proto;
 namespace aidl_vehicle = ::aidl::android::hardware::automotive::vehicle;
 
+constexpr char DEFAULT_PROPERTIES_CONFIG[] = "DefaultProperties.json";
+
+inline std::string getConfigPath(const std::string& name) {
+    return android::base::GetExecutableDirectory() + "/" + name;
+}
+
 std::vector<aidl_vehicle::VehiclePropConfig> prepareTestConfigs() {
+    JsonConfigLoader loader;
+    auto result = loader.loadPropConfig(getConfigPath(DEFAULT_PROPERTIES_CONFIG));
+    if (!result.ok()) {
+        return {};
+    }
     std::vector<aidl_vehicle::VehiclePropConfig> configs;
-    for (auto& property : defaultconfig::getDefaultConfigs()) {
-        configs.push_back(property.config);
+    for (auto& [_, configDeclaration] : result.value()) {
+        configs.push_back(configDeclaration.config);
     }
     return configs;
 }
 
 std::vector<aidl_vehicle::VehiclePropValue> prepareTestValues() {
+    JsonConfigLoader loader;
+    auto result = loader.loadPropConfig(getConfigPath(DEFAULT_PROPERTIES_CONFIG));
+    if (!result.ok()) {
+        return {};
+    }
     std::vector<aidl_vehicle::VehiclePropValue> values;
     int64_t timestamp = 1;
-    for (auto& property : defaultconfig::getDefaultConfigs()) {
+    for (auto& [_, configDeclaration] : result.value()) {
         values.push_back({
                 .timestamp = timestamp,
                 .areaId = 123,
-                .prop = property.config.prop,
-                .value = property.initialValue,
+                .prop = configDeclaration.config.prop,
+                .value = configDeclaration.initialValue,
                 .status = aidl_vehicle::VehiclePropertyStatus::ERROR,
         });
     }
diff --git a/automotive/vehicle/aidl/impl/utils/common/include/PropertyUtils.h b/automotive/vehicle/aidl/impl/utils/common/include/PropertyUtils.h
index 2743578..caf17e8 100644
--- a/automotive/vehicle/aidl/impl/utils/common/include/PropertyUtils.h
+++ b/automotive/vehicle/aidl/impl/utils/common/include/PropertyUtils.h
@@ -96,6 +96,7 @@
 constexpr int CHARGE_PORT_FRONT_LEFT = toInt(propertyutils_impl::PortLocationType::FRONT_LEFT);
 constexpr int CHARGE_PORT_REAR_LEFT = toInt(propertyutils_impl::PortLocationType::REAR_LEFT);
 constexpr int LIGHT_STATE_ON = toInt(propertyutils_impl::VehicleLightState::ON);
+constexpr int LIGHT_SWITCH_OFF = toInt(propertyutils_impl::VehicleLightSwitch::OFF);
 constexpr int LIGHT_SWITCH_AUTO = toInt(propertyutils_impl::VehicleLightSwitch::AUTOMATIC);
 constexpr int WHEEL_FRONT_LEFT = toInt(propertyutils_impl::VehicleAreaWheel::LEFT_FRONT);
 constexpr int WHEEL_FRONT_RIGHT = toInt(propertyutils_impl::VehicleAreaWheel::RIGHT_FRONT);
diff --git a/automotive/vehicle/aidl/impl/utils/common/src/RecurrentTimer.cpp b/automotive/vehicle/aidl/impl/utils/common/src/RecurrentTimer.cpp
index fbc79fa..43f5d69 100644
--- a/automotive/vehicle/aidl/impl/utils/common/src/RecurrentTimer.cpp
+++ b/automotive/vehicle/aidl/impl/utils/common/src/RecurrentTimer.cpp
@@ -50,7 +50,7 @@
         std::scoped_lock<std::mutex> lockGuard(mLock);
 
         // Aligns the nextTime to multiply of interval.
-        int64_t nextTime = ceil(elapsedRealtimeNano() / intervalInNano) * intervalInNano;
+        int64_t nextTime = ceil(uptimeNanos() / intervalInNano) * intervalInNano;
 
         std::unique_ptr<CallbackInfo> info = std::make_unique<CallbackInfo>();
         info->callback = callback;
@@ -130,7 +130,7 @@
             }
             // The first element is the nearest next event.
             int64_t nextTime = mCallbackQueue[0]->nextTime;
-            int64_t now = elapsedRealtimeNano();
+            int64_t now = uptimeNanos();
             if (nextTime > now) {
                 interval = nextTime - now;
             } else {
@@ -148,7 +148,7 @@
 
         {
             ScopedLockAssertion lockAssertion(mLock);
-            int64_t now = elapsedRealtimeNano();
+            int64_t now = uptimeNanos();
             while (mCallbackQueue.size() > 0) {
                 int64_t nextTime = mCallbackQueue[0]->nextTime;
                 if (nextTime > now) {
diff --git a/automotive/vehicle/aidl/impl/utils/test/include/TestPropertyUtils.h b/automotive/vehicle/aidl/impl/utils/test/include/TestPropertyUtils.h
index d512713..e6ea6fe 100644
--- a/automotive/vehicle/aidl/impl/utils/test/include/TestPropertyUtils.h
+++ b/automotive/vehicle/aidl/impl/utils/test/include/TestPropertyUtils.h
@@ -35,7 +35,6 @@
 
 }  // namespace testpropertyutils_impl
 
-#ifdef ENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING
 // Converts the system property to the vendor property.
 // WARNING: This is only for the end-to-end testing, Should NOT include in the user build.
 inline constexpr int32_t toVendor(
@@ -55,7 +54,6 @@
         toVendor(testpropertyutils_impl::VehicleProperty::CLUSTER_REQUEST_DISPLAY);
 constexpr int32_t VENDOR_CLUSTER_NAVIGATION_STATE =
         toVendor(testpropertyutils_impl::VehicleProperty::CLUSTER_NAVIGATION_STATE);
-#endif  // ENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING
 
 // These properties are placeholder properties for developers to test new features without
 // implementing a real property.
diff --git a/automotive/vehicle/aidl/impl/vhal/Android.bp b/automotive/vehicle/aidl/impl/vhal/Android.bp
index 8b4f559..4feea79 100644
--- a/automotive/vehicle/aidl/impl/vhal/Android.bp
+++ b/automotive/vehicle/aidl/impl/vhal/Android.bp
@@ -74,23 +74,12 @@
         "FakeVehicleHardwareDefaults",
         "VehicleHalDefaults",
         "android-automotive-large-parcelable-defaults",
-    ],
-    header_libs: [
-        "IVehicleHardware",
-        "VehicleHalDefaultConfig",
+        "service_fuzzer_defaults",
     ],
     static_libs: [
         "DefaultVehicleHal",
         "FakeVehicleHardware",
         "VehicleHalUtils",
-        "libbase",
-        "libbinder_random_parcel",
-        "libcutils",
-    ],
-    shared_libs: [
-        "libbinder_ndk",
-        "libbinder",
-        "libutils",
     ],
     srcs: ["src/fuzzer.cpp"],
     fuzz_config: {
diff --git a/biometrics/fingerprint/aidl/default/Android.bp b/biometrics/fingerprint/aidl/default/Android.bp
index 2aa7bbd..dc0199c 100644
--- a/biometrics/fingerprint/aidl/default/Android.bp
+++ b/biometrics/fingerprint/aidl/default/Android.bp
@@ -16,6 +16,9 @@
     local_include_dirs: ["include"],
     srcs: [
         "FakeFingerprintEngine.cpp",
+        "FakeFingerprintEngineRear.cpp",
+        "FakeFingerprintEngineUdfps.cpp",
+        "FakeFingerprintEngineSide.cpp",
         "Fingerprint.cpp",
         "Session.cpp",
         "main.cpp",
@@ -41,6 +44,32 @@
     shared_libs: [
         "libbase",
         "libbinder_ndk",
+        "android.hardware.biometrics.common.thread",
+    ],
+    static_libs: [
+        "libandroid.hardware.biometrics.fingerprint.VirtualProps",
+        "android.hardware.biometrics.fingerprint-V2-ndk",
+        "android.hardware.biometrics.common-V2-ndk",
+        "android.hardware.keymaster-V3-ndk",
+        "android.hardware.biometrics.common.util",
+    ],
+    vendor: true,
+    test_suites: ["general-tests"],
+    require_root: true,
+}
+
+cc_test {
+    name: "android.hardware.biometrics.fingerprint.FakeFingerprintEngineUdfpsTest",
+    local_include_dirs: ["include"],
+    srcs: [
+        "tests/FakeFingerprintEngineUdfpsTest.cpp",
+        "FakeFingerprintEngineUdfps.cpp",
+        "FakeFingerprintEngine.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder_ndk",
+        "android.hardware.biometrics.common.thread",
     ],
     static_libs: [
         "libandroid.hardware.biometrics.fingerprint.VirtualProps",
diff --git a/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp b/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
index 138caa0..68a1f26 100644
--- a/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
+++ b/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
@@ -15,8 +15,10 @@
  */
 
 #include "FakeFingerprintEngine.h"
+#include "Fingerprint.h"
 
 #include <android-base/logging.h>
+#include <android-base/parseint.h>
 
 #include <fingerprint.sysprop.h>
 
@@ -24,6 +26,7 @@
 #include "util/Util.h"
 
 using namespace ::android::fingerprint::virt;
+using ::android::base::ParseInt;
 
 namespace aidl::android::hardware::biometrics::fingerprint {
 
@@ -172,6 +175,11 @@
     BEGIN_OP(0);
 
     std::vector<int32_t> ids;
+    // There are some enrollment sync issue with framework, which results in
+    //  a single template removal during the very firt sync command after reboot.
+    //  This is a workaround for now. TODO(b/243129174)
+    ids.push_back(-1);
+
     for (auto& enrollment : FingerprintHalProperties::enrollments()) {
         auto id = enrollment.value_or(0);
         if (id > 0) {
@@ -203,7 +211,11 @@
 
 void FakeFingerprintEngine::getAuthenticatorIdImpl(ISessionCallback* cb) {
     BEGIN_OP(0);
-    cb->onAuthenticatorIdRetrieved(FingerprintHalProperties::authenticator_id().value_or(0));
+    int64_t authenticatorId = FingerprintHalProperties::authenticator_id().value_or(0);
+    if (FingerprintHalProperties::enrollments().size() > 0 && authenticatorId == 0) {
+        authenticatorId = 99999999;  // default authenticatorId, TODO(b/230515082)
+    }
+    cb->onAuthenticatorIdRetrieved(authenticatorId);
 }
 
 void FakeFingerprintEngine::invalidateAuthenticatorIdImpl(ISessionCallback* cb) {
@@ -221,4 +233,57 @@
     cb->onLockoutCleared();
 }
 
+ndk::ScopedAStatus FakeFingerprintEngine::onPointerDownImpl(int32_t /*pointerId*/, int32_t /*x*/,
+                                                            int32_t /*y*/, float /*minor*/,
+                                                            float /*major*/) {
+    BEGIN_OP(0);
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus FakeFingerprintEngine::onPointerUpImpl(int32_t /*pointerId*/) {
+    BEGIN_OP(0);
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus FakeFingerprintEngine::onUiReadyImpl() {
+    BEGIN_OP(0);
+    return ndk::ScopedAStatus::ok();
+}
+
+bool FakeFingerprintEngine::getSensorLocationConfig(SensorLocation& out) {
+    auto loc = FingerprintHalProperties::sensor_location().value_or("");
+    auto isValidStr = false;
+    auto dim = Util::split(loc, ":");
+
+    if (dim.size() < 3 or dim.size() > 4) {
+        if (!loc.empty()) LOG(WARNING) << "Invalid sensor location input (x:y:radius):" + loc;
+        return false;
+    } else {
+        int32_t x, y, r;
+        std::string d = "";
+        if (dim.size() >= 3) {
+            isValidStr = ParseInt(dim[0], &x) && ParseInt(dim[1], &y) && ParseInt(dim[2], &r);
+        }
+        if (dim.size() >= 4) {
+            d = dim[3];
+        }
+        if (isValidStr) out = {0, x, y, r, d};
+
+        return isValidStr;
+    }
+}
+SensorLocation FakeFingerprintEngine::getSensorLocation() {
+    SensorLocation location;
+
+    if (getSensorLocationConfig(location)) {
+        return location;
+    } else {
+        return defaultSensorLocation();
+    }
+}
+
+SensorLocation FakeFingerprintEngine::defaultSensorLocation() {
+    return {0 /* displayId (not used) */, 0 /* sensorLocationX */, 0 /* sensorLocationY */,
+            0 /* sensorRadius */, "" /* display */};
+}
 }  // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/FakeFingerprintEngineRear.cpp b/biometrics/fingerprint/aidl/default/FakeFingerprintEngineRear.cpp
new file mode 100644
index 0000000..eea80d5
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/FakeFingerprintEngineRear.cpp
@@ -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.
+ */
+
+#include "FakeFingerprintEngineRear.h"
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+
+#include <fingerprint.sysprop.h>
+
+#include "util/CancellationSignal.h"
+#include "util/Util.h"
+
+using namespace ::android::fingerprint::virt;
+
+namespace aidl::android::hardware::biometrics::fingerprint {}
diff --git a/biometrics/fingerprint/aidl/default/FakeFingerprintEngineSide.cpp b/biometrics/fingerprint/aidl/default/FakeFingerprintEngineSide.cpp
new file mode 100644
index 0000000..9f736e7
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/FakeFingerprintEngineSide.cpp
@@ -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.
+ */
+
+#include "FakeFingerprintEngineSide.h"
+
+#include <android-base/logging.h>
+
+#include <fingerprint.sysprop.h>
+
+#include "util/CancellationSignal.h"
+#include "util/Util.h"
+
+using namespace ::android::fingerprint::virt;
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+SensorLocation FakeFingerprintEngineSide::defaultSensorLocation() {
+    SensorLocation location;
+
+    return {0 /* displayId (not used) */, defaultSensorLocationX /* sensorLocationX */,
+            defaultSensorLocationY /* sensorLocationY */, defaultSensorRadius /* sensorRadius */,
+            "" /* display */};
+}
+}  // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/FakeFingerprintEngineUdfps.cpp b/biometrics/fingerprint/aidl/default/FakeFingerprintEngineUdfps.cpp
new file mode 100644
index 0000000..d8579a4
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/FakeFingerprintEngineUdfps.cpp
@@ -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.
+ */
+
+#include "FakeFingerprintEngineUdfps.h"
+
+#include <android-base/logging.h>
+
+#include <fingerprint.sysprop.h>
+
+#include "util/CancellationSignal.h"
+#include "util/Util.h"
+
+using namespace ::android::fingerprint::virt;
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+SensorLocation FakeFingerprintEngineUdfps::defaultSensorLocation() {
+    return {0 /* displayId (not used) */, defaultSensorLocationX /* sensorLocationX */,
+            defaultSensorLocationY /* sensorLocationY */, defaultSensorRadius /* sensorRadius */,
+            "" /* display */};
+}
+
+ndk::ScopedAStatus FakeFingerprintEngineUdfps::onPointerDownImpl(int32_t /*pointerId*/,
+                                                                 int32_t /*x*/, int32_t /*y*/,
+                                                                 float /*minor*/, float /*major*/) {
+    BEGIN_OP(0);
+
+    // TODO(b/230515082): if need to handle display touch events
+
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus FakeFingerprintEngineUdfps::onPointerUpImpl(int32_t /*pointerId*/) {
+    BEGIN_OP(0);
+    // TODO(b/230515082)
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus FakeFingerprintEngineUdfps::onUiReadyImpl() {
+    BEGIN_OP(0);
+    // TODO(b/230515082)
+    return ndk::ScopedAStatus::ok();
+}
+
+}  // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/Fingerprint.cpp b/biometrics/fingerprint/aidl/default/Fingerprint.cpp
index 71dc660..922781c 100644
--- a/biometrics/fingerprint/aidl/default/Fingerprint.cpp
+++ b/biometrics/fingerprint/aidl/default/Fingerprint.cpp
@@ -26,7 +26,7 @@
 namespace aidl::android::hardware::biometrics::fingerprint {
 namespace {
 constexpr size_t MAX_WORKER_QUEUE_SIZE = 5;
-constexpr int SENSOR_ID = 1;
+constexpr int SENSOR_ID = 5;
 constexpr common::SensorStrength SENSOR_STRENGTH = common::SensorStrength::STRONG;
 constexpr int MAX_ENROLLMENTS_PER_USER = 5;
 constexpr bool SUPPORTS_NAVIGATION_GESTURES = true;
@@ -39,8 +39,25 @@
 
 }  // namespace
 
-Fingerprint::Fingerprint()
-    : mEngine(std::make_unique<FakeFingerprintEngine>()), mWorker(MAX_WORKER_QUEUE_SIZE) {}
+Fingerprint::Fingerprint() : mWorker(MAX_WORKER_QUEUE_SIZE) {
+    std::string sensorTypeProp = FingerprintHalProperties::type().value_or("");
+    if (sensorTypeProp == "" || sensorTypeProp == "default" || sensorTypeProp == "rear") {
+        mSensorType = FingerprintSensorType::REAR;
+        mEngine = std::make_unique<FakeFingerprintEngineRear>();
+    } else if (sensorTypeProp == "udfps") {
+        mSensorType = FingerprintSensorType::UNDER_DISPLAY_OPTICAL;
+        mEngine = std::make_unique<FakeFingerprintEngineUdfps>();
+    } else if (sensorTypeProp == "side") {
+        mSensorType = FingerprintSensorType::POWER_BUTTON;
+        mEngine = std::make_unique<FakeFingerprintEngineSide>();
+    } else {
+        mSensorType = FingerprintSensorType::UNKNOWN;
+        mEngine = std::make_unique<FakeFingerprintEngineRear>();
+        UNIMPLEMENTED(FATAL) << "unrecognized or unimplemented fingerprint behavior: "
+                             << sensorTypeProp;
+    }
+    LOG(INFO) << "sensorTypeProp:" << sensorTypeProp;
+}
 
 ndk::ScopedAStatus Fingerprint::getSensorProps(std::vector<SensorProps>* out) {
     std::vector<common::ComponentInfo> componentInfo = {
@@ -51,22 +68,12 @@
     common::CommonProps commonProps = {SENSOR_ID, SENSOR_STRENGTH, MAX_ENROLLMENTS_PER_USER,
                                        componentInfo};
 
-    SensorLocation sensorLocation = {0 /* displayId (not used) */, 0 /* sensorLocationX */,
-                                     0 /* sensorLocationY */, 0 /* sensorRadius */,
-                                     "" /* display */};
+    SensorLocation sensorLocation = mEngine->getSensorLocation();
 
-    FingerprintSensorType sensorType = FingerprintSensorType::UNKNOWN;
-    std::string sensorTypeProp = FingerprintHalProperties::type().value_or("");
-    if (sensorTypeProp == "" || sensorTypeProp == "default" || sensorTypeProp == "rear") {
-        sensorType = FingerprintSensorType::REAR;
-    }
-    if (sensorType == FingerprintSensorType::UNKNOWN) {
-        UNIMPLEMENTED(FATAL) << "unrecognized or unimplemented fingerprint behavior: "
-                             << sensorTypeProp;
-    }
+    LOG(INFO) << "sensor type:" << (int)mSensorType << " location:" << sensorLocation.toString();
 
     *out = {{commonProps,
-             sensorType,
+             mSensorType,
              {sensorLocation},
              SUPPORTS_NAVIGATION_GESTURES,
              false /* supportsDetectInteraction */}};
@@ -80,6 +87,8 @@
 
     mSession = SharedRefBase::make<Session>(sensorId, userId, cb, mEngine.get(), &mWorker);
     *out = mSession;
+
+    LOG(INFO) << "createSession: sensorId:" << sensorId << " userId:" << userId;
     return ndk::ScopedAStatus::ok();
 }
 
diff --git a/biometrics/fingerprint/aidl/default/README.md b/biometrics/fingerprint/aidl/default/README.md
index a6e6b81..bb6bb48 100644
--- a/biometrics/fingerprint/aidl/default/README.md
+++ b/biometrics/fingerprint/aidl/default/README.md
@@ -23,37 +23,17 @@
 First, set the type of sensor the device should use, enable the virtual
 extensions in the framework, and reboot.
 
-This doesn't work with HIDL and you typically need to have a PIN or password set
-for things to work correctly, so this is a good time to set those too.
-
 ```shell
 $ adb root
 $ adb shell settings put secure biometric_virtual_enabled 1
 $ adb shell setprop persist.vendor.fingerprint.virtual.type rear
-$ adb shell locksettings set-pin 0000
-$ adb shell settings put secure com.android.server.biometrics.AuthService.hidlDisabled 1
 $ adb reboot
 ```
 
 ### Enrollments
 
 Next, setup enrollments on the device. This can either be done through the UI,
-or via adb.
-
-#### UI Enrollment
-
-1. Tee up the results of the enrollment before starting the process:
-
-      ```shell
-      $ adb shell setprop vendor.fingerprint.virtual.next_enrollment 1:100,100,100:true
-      ```
-2. Navigate to `Settings -> Security -> Fingerprint Unlock` and follow the
-   prompts.
-3. Verify the enrollments in the UI:
-
-      ```shell
-      $ adb shell getprop persist.vendor.fingerprint.virtual.enrollments
-      ```
+or via adb directly.
 
 #### Direct Enrollment
 
@@ -61,14 +41,29 @@
 
 ```shell
 $ adb root
+$ adb shell locksettings set-pin 0000
 $ adb shell setprop persist.vendor.fingerprint.virtual.enrollments 1
 $ adb shell cmd fingerprint sync
 ```
 
-**Note: You may need to do this twice.** The templates are checked as part of
-some lazy operations, like user switching and startup, which can cause the
-framework to delete the enrollments before the sync operation runs. Until this
-is fixed, just run the commands twice as a workaround.
+#### UI Enrollment
+
+1. Set pin
+      ```shell
+      $ adb shell locksettings set-pin 0000
+      ```
+2. Tee up the results of the enrollment before starting the process:
+
+      ```shell
+      $ adb shell setprop vendor.fingerprint.virtual.next_enrollment 1:100,100,100:true
+      ```
+3. Navigate to `Settings -> Security -> Fingerprint Unlock` and follow the
+   prompts.
+4. Verify the enrollments in the UI:
+
+      ```shell
+      $ adb shell getprop persist.vendor.fingerprint.virtual.enrollments
+      ```
 
 ### Authenticate
 
@@ -81,7 +76,7 @@
 
 ### View HAL State
 
-To view all the properties of the HAL (see `fingerprint.sysprop` for the API):
+To view all the properties of the HAL (see `fingerprint.sysprop` file for the API):
 
 ```shell
 $ adb shell getprop | grep vendor.fingerprint.virtual
diff --git a/biometrics/fingerprint/aidl/default/Session.cpp b/biometrics/fingerprint/aidl/default/Session.cpp
index ab91e98..e51f677 100644
--- a/biometrics/fingerprint/aidl/default/Session.cpp
+++ b/biometrics/fingerprint/aidl/default/Session.cpp
@@ -167,7 +167,7 @@
 }
 
 ndk::ScopedAStatus Session::removeEnrollments(const std::vector<int32_t>& enrollmentIds) {
-    LOG(INFO) << "removeEnrollments";
+    LOG(INFO) << "removeEnrollments, size:" << enrollmentIds.size();
     scheduleStateOrCrash(SessionState::REMOVING_ENROLLMENTS);
 
     mWorker->schedule(Callable::from([this, enrollmentIds] {
@@ -228,19 +228,31 @@
     return ndk::ScopedAStatus::ok();
 }
 
-ndk::ScopedAStatus Session::onPointerDown(int32_t /*pointerId*/, int32_t /*x*/, int32_t /*y*/,
-                                          float /*minor*/, float /*major*/) {
+ndk::ScopedAStatus Session::onPointerDown(int32_t pointerId, int32_t x, int32_t y, float minor,
+                                          float major) {
     LOG(INFO) << "onPointerDown";
+    mWorker->schedule(Callable::from([this, pointerId, x, y, minor, major] {
+        mEngine->onPointerDownImpl(pointerId, x, y, minor, major);
+        enterIdling();
+    }));
     return ndk::ScopedAStatus::ok();
 }
 
-ndk::ScopedAStatus Session::onPointerUp(int32_t /*pointerId*/) {
+ndk::ScopedAStatus Session::onPointerUp(int32_t pointerId) {
     LOG(INFO) << "onPointerUp";
+    mWorker->schedule(Callable::from([this, pointerId] {
+        mEngine->onPointerUpImpl(pointerId);
+        enterIdling();
+    }));
     return ndk::ScopedAStatus::ok();
 }
 
 ndk::ScopedAStatus Session::onUiReady() {
     LOG(INFO) << "onUiReady";
+    mWorker->schedule(Callable::from([this] {
+        mEngine->onUiReadyImpl();
+        enterIdling();
+    }));
     return ndk::ScopedAStatus::ok();
 }
 
diff --git a/biometrics/fingerprint/aidl/default/api/android.hardware.biometrics.fingerprint.VirtualProps-current.txt b/biometrics/fingerprint/aidl/default/api/android.hardware.biometrics.fingerprint.VirtualProps-current.txt
index 4724ff4..9dfb74d 100644
--- a/biometrics/fingerprint/aidl/default/api/android.hardware.biometrics.fingerprint.VirtualProps-current.txt
+++ b/biometrics/fingerprint/aidl/default/api/android.hardware.biometrics.fingerprint.VirtualProps-current.txt
@@ -76,6 +76,12 @@
     prop_name: "vendor.fingerprint.virtual.operation_enroll_latency"
   }
   prop {
+    api_name: "sensor_location"
+    type: String
+    access: ReadWrite
+    prop_name: "persist.vendor.fingerprint.virtual.sensor_location"
+  }
+  prop {
     api_name: "type"
     type: String
     access: ReadWrite
diff --git a/biometrics/fingerprint/aidl/default/fingerprint.sysprop b/biometrics/fingerprint/aidl/default/fingerprint.sysprop
index 12c8648..85e93b0 100644
--- a/biometrics/fingerprint/aidl/default/fingerprint.sysprop
+++ b/biometrics/fingerprint/aidl/default/fingerprint.sysprop
@@ -133,3 +133,13 @@
     access: ReadWrite
     api_name: "operation_authenticate_duration"
 }
+
+# sensor location
+#    <x>:<y>:<radius> in pixel
+prop {
+    prop_name: "persist.vendor.fingerprint.virtual.sensor_location"
+    type: String
+    scope: Public
+    access: ReadWrite
+    api_name: "sensor_location"
+}
diff --git a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
index eb810da..d7df818 100644
--- a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
+++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
@@ -20,6 +20,7 @@
 
 #include <random>
 
+#include <aidl/android/hardware/biometrics/fingerprint/SensorLocation.h>
 #include <future>
 #include <vector>
 
@@ -31,6 +32,7 @@
 class FakeFingerprintEngine {
   public:
     FakeFingerprintEngine() : mRandom(std::mt19937::default_seed) {}
+    virtual ~FakeFingerprintEngine() {}
 
     void generateChallengeImpl(ISessionCallback* cb);
     void revokeChallengeImpl(ISessionCallback* cb, int64_t challenge);
@@ -44,6 +46,18 @@
     void getAuthenticatorIdImpl(ISessionCallback* cb);
     void invalidateAuthenticatorIdImpl(ISessionCallback* cb);
     void resetLockoutImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& /*hat*/);
+    bool getSensorLocationConfig(SensorLocation& out);
+
+    virtual ndk::ScopedAStatus onPointerDownImpl(int32_t pointerId, int32_t x, int32_t y,
+                                                 float minor, float major);
+
+    virtual ndk::ScopedAStatus onPointerUpImpl(int32_t pointerId);
+
+    virtual ndk::ScopedAStatus onUiReadyImpl();
+
+    virtual SensorLocation getSensorLocation();
+
+    virtual SensorLocation defaultSensorLocation();
 
     std::mt19937 mRandom;
 };
diff --git a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineRear.h b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineRear.h
new file mode 100644
index 0000000..1600a4b
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineRear.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include "FakeFingerprintEngine.h"
+
+using namespace ::aidl::android::hardware::biometrics::common;
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+// A fake engine that is backed by system properties instead of hardware.
+class FakeFingerprintEngineRear : public FakeFingerprintEngine {
+  public:
+    FakeFingerprintEngineRear() : FakeFingerprintEngine() {}
+    ~FakeFingerprintEngineRear() {}
+};
+
+}  // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineSide.h b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineSide.h
new file mode 100644
index 0000000..4e44d16
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineSide.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include "FakeFingerprintEngine.h"
+
+using namespace ::aidl::android::hardware::biometrics::common;
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+// A fake engine that is backed by system properties instead of hardware.
+class FakeFingerprintEngineSide : public FakeFingerprintEngine {
+  public:
+    static constexpr int32_t defaultSensorLocationX = 0;
+    static constexpr int32_t defaultSensorLocationY = 600;
+    static constexpr int32_t defaultSensorRadius = 150;
+
+    FakeFingerprintEngineSide() : FakeFingerprintEngine() {}
+    ~FakeFingerprintEngineSide() {}
+
+    virtual SensorLocation defaultSensorLocation() override;
+};
+
+}  // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineUdfps.h b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineUdfps.h
new file mode 100644
index 0000000..b86af73
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineUdfps.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include "FakeFingerprintEngine.h"
+
+using namespace ::aidl::android::hardware::biometrics::common;
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+// A fake engine that is backed by system properties instead of hardware.
+class FakeFingerprintEngineUdfps : public FakeFingerprintEngine {
+  public:
+    static constexpr int32_t defaultSensorLocationX = 400;
+    static constexpr int32_t defaultSensorLocationY = 1600;
+    static constexpr int32_t defaultSensorRadius = 150;
+
+    FakeFingerprintEngineUdfps() : FakeFingerprintEngine() {}
+    ~FakeFingerprintEngineUdfps() {}
+
+    virtual ndk::ScopedAStatus onPointerDownImpl(int32_t pointerId, int32_t x, int32_t y,
+                                                 float minor, float major) override;
+
+    virtual ndk::ScopedAStatus onPointerUpImpl(int32_t pointerId) override;
+
+    virtual ndk::ScopedAStatus onUiReadyImpl() override;
+
+    virtual SensorLocation defaultSensorLocation() override;
+};
+
+}  // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/include/Fingerprint.h b/biometrics/fingerprint/aidl/default/include/Fingerprint.h
index 20def0c..64aafa3 100644
--- a/biometrics/fingerprint/aidl/default/include/Fingerprint.h
+++ b/biometrics/fingerprint/aidl/default/include/Fingerprint.h
@@ -16,9 +16,15 @@
 
 #pragma once
 
+#define LOG_TAG "FingerprintVirtualHal"
+
 #include <aidl/android/hardware/biometrics/fingerprint/BnFingerprint.h>
 
 #include "FakeFingerprintEngine.h"
+#include "FakeFingerprintEngineRear.h"
+#include "FakeFingerprintEngineSide.h"
+#include "FakeFingerprintEngineUdfps.h"
+
 #include "Session.h"
 #include "thread/WorkerThread.h"
 
@@ -38,6 +44,7 @@
     std::unique_ptr<FakeFingerprintEngine> mEngine;
     WorkerThread mWorker;
     std::shared_ptr<Session> mSession;
+    FingerprintSensorType mSensorType;
 };
 
 }  // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
index 742d933..8696d26 100644
--- a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
+++ b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
@@ -264,7 +264,9 @@
 TEST_F(FakeFingerprintEngineTest, EnumerateEnrolled) {
     FingerprintHalProperties::enrollments({2, 4, 8});
     mEngine.enumerateEnrollmentsImpl(mCallback.get());
-    ASSERT_EQ(3, mCallback->mLastEnrollmentEnumerated.size());
+    ASSERT_EQ(
+            4,
+            mCallback->mLastEnrollmentEnumerated.size());  // Due to workaround. TODO (b/243129174)
     for (auto id : FingerprintHalProperties::enrollments()) {
         ASSERT_TRUE(std::find(mCallback->mLastEnrollmentEnumerated.begin(),
                               mCallback->mLastEnrollmentEnumerated.end(),
diff --git a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineUdfpsTest.cpp b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineUdfpsTest.cpp
new file mode 100644
index 0000000..485f401
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineUdfpsTest.cpp
@@ -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.
+ */
+
+#include <android/binder_process.h>
+#include <fingerprint.sysprop.h>
+#include <gtest/gtest.h>
+
+#include <android-base/logging.h>
+
+#include "FakeFingerprintEngine.h"
+#include "FakeFingerprintEngineUdfps.h"
+
+using namespace ::android::fingerprint::virt;
+using namespace ::aidl::android::hardware::biometrics::fingerprint;
+using namespace ::aidl::android::hardware::keymaster;
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+class FakeFingerprintEngineUdfpsTest : public ::testing::Test {
+  protected:
+    void SetUp() override {}
+
+    void TearDown() override {
+        // reset to default
+        FingerprintHalProperties::sensor_location("");
+    }
+
+    FakeFingerprintEngineUdfps mEngine;
+};
+
+bool isDefaultLocation(SensorLocation& sc) {
+    return (sc.sensorLocationX == FakeFingerprintEngineUdfps::defaultSensorLocationX &&
+            sc.sensorLocationY == FakeFingerprintEngineUdfps::defaultSensorLocationY &&
+            sc.sensorRadius == FakeFingerprintEngineUdfps::defaultSensorRadius && sc.display == "");
+}
+
+TEST_F(FakeFingerprintEngineUdfpsTest, getSensorLocation) {
+    FingerprintHalProperties::sensor_location("");
+    SensorLocation sc = mEngine.getSensorLocation();
+    ASSERT_TRUE(isDefaultLocation(sc));
+
+    auto loc = "100:200:30";
+    FingerprintHalProperties::sensor_location(loc);
+    sc = mEngine.getSensorLocation();
+    ASSERT_TRUE(sc.sensorLocationX == 100);
+    ASSERT_TRUE(sc.sensorLocationY == 200);
+    ASSERT_TRUE(sc.sensorRadius == 30);
+
+    loc = "100:200:30:screen1";
+    FingerprintHalProperties::sensor_location(loc);
+    sc = mEngine.getSensorLocation();
+    ASSERT_TRUE(sc.sensorLocationX == 100);
+    ASSERT_TRUE(sc.sensorLocationY == 200);
+    ASSERT_TRUE(sc.sensorRadius == 30);
+    ASSERT_TRUE(sc.display == "screen1");
+
+    loc = "100";
+    FingerprintHalProperties::sensor_location(loc);
+    sc = mEngine.getSensorLocation();
+    ASSERT_TRUE(isDefaultLocation(sc));
+
+    loc = "10:20";
+    FingerprintHalProperties::sensor_location(loc);
+    sc = mEngine.getSensorLocation();
+    ASSERT_TRUE(isDefaultLocation(sc));
+
+    loc = "10,20,5";
+    FingerprintHalProperties::sensor_location(loc);
+    sc = mEngine.getSensorLocation();
+    ASSERT_TRUE(isDefaultLocation(sc));
+
+    loc = "a:b:c";
+    FingerprintHalProperties::sensor_location(loc);
+    sc = mEngine.getSensorLocation();
+    ASSERT_TRUE(isDefaultLocation(sc));
+}
+
+// More
+}  // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/bluetooth/1.0/Android.bp b/bluetooth/1.0/Android.bp
index 20775dd..bd1ca69 100644
--- a/bluetooth/1.0/Android.bp
+++ b/bluetooth/1.0/Android.bp
@@ -23,6 +23,6 @@
     gen_java: true,
     apex_available: [
         "//apex_available:platform",
-        "com.android.bluetooth",
+        "com.android.btservices",
     ],
 }
diff --git a/bluetooth/1.1/Android.bp b/bluetooth/1.1/Android.bp
index 4ac2009..f8a05f1 100644
--- a/bluetooth/1.1/Android.bp
+++ b/bluetooth/1.1/Android.bp
@@ -23,6 +23,6 @@
     gen_java: true,
     apex_available: [
         "//apex_available:platform",
-        "com.android.bluetooth",
+        "com.android.btservices",
     ],
 }
diff --git a/bluetooth/a2dp/1.0/Android.bp b/bluetooth/a2dp/1.0/Android.bp
index 20776dc..6ffbefa 100644
--- a/bluetooth/a2dp/1.0/Android.bp
+++ b/bluetooth/a2dp/1.0/Android.bp
@@ -23,6 +23,6 @@
     gen_java: false,
     apex_available: [
         "//apex_available:platform",
-        "com.android.bluetooth",
+        "com.android.btservices",
     ],
 }
diff --git a/bluetooth/audio/2.0/Android.bp b/bluetooth/audio/2.0/Android.bp
index e4d48c1..725ec11 100644
--- a/bluetooth/audio/2.0/Android.bp
+++ b/bluetooth/audio/2.0/Android.bp
@@ -26,6 +26,6 @@
     gen_java: false,
     apex_available: [
         "//apex_available:platform",
-        "com.android.bluetooth",
+        "com.android.btservices",
     ],
 }
diff --git a/bluetooth/audio/2.1/Android.bp b/bluetooth/audio/2.1/Android.bp
index 1175fb3..4ca0bef 100644
--- a/bluetooth/audio/2.1/Android.bp
+++ b/bluetooth/audio/2.1/Android.bp
@@ -25,7 +25,7 @@
     ],
     apex_available: [
         "//apex_available:platform",
-        "com.android.bluetooth",
+        "com.android.btservices",
     ],
     gen_java: false,
 }
diff --git a/bluetooth/audio/aidl/Android.bp b/bluetooth/audio/aidl/Android.bp
index a687162..4aea83f 100644
--- a/bluetooth/audio/aidl/Android.bp
+++ b/bluetooth/audio/aidl/Android.bp
@@ -42,7 +42,7 @@
         ndk: {
             apex_available: [
                 "//apex_available:platform",
-                "com.android.bluetooth",
+                "com.android.btservices",
             ],
             min_sdk_version: "31",
         },
diff --git a/bluetooth/audio/utils/Android.bp b/bluetooth/audio/utils/Android.bp
index d08cb0a..674dd11 100644
--- a/bluetooth/audio/utils/Android.bp
+++ b/bluetooth/audio/utils/Android.bp
@@ -40,9 +40,13 @@
         "aidl_session/BluetoothAudioCodecs.cpp",
         "aidl_session/BluetoothAudioSession.cpp",
         "aidl_session/HidlToAidlMiddleware.cpp",
+        "aidl_session/BluetoothLeAudioCodecsProvider.cpp",
     ],
     export_include_dirs: ["aidl_session/"],
-    header_libs: ["libhardware_headers"],
+    header_libs: [
+        "libhardware_headers",
+        "libxsdc-utils",
+    ],
     shared_libs: [
         "android.hardware.bluetooth.audio@2.0",
         "android.hardware.bluetooth.audio@2.1",
@@ -53,5 +57,15 @@
         "liblog",
         "android.hardware.bluetooth.audio-V2-ndk",
         "libhidlbase",
+        "libxml2",
     ],
+    generated_sources: ["le_audio_codec_capabilities"],
+    generated_headers: ["le_audio_codec_capabilities"],
+}
+
+xsd_config {
+    name: "le_audio_codec_capabilities",
+    srcs: ["le_audio_codec_capabilities/le_audio_codec_capabilities.xsd"],
+    package_name: "aidl.android.hardware.bluetooth.audio.setting",
+    api_dir: "le_audio_codec_capabilities/schema",
 }
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp b/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp
index d5e85b8..855dd28 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp
+++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp
@@ -32,6 +32,8 @@
 #include <aidl/android/hardware/bluetooth/audio/SbcChannelMode.h>
 #include <android-base/logging.h>
 
+#include "BluetoothLeAudioCodecsProvider.h"
+
 namespace aidl {
 namespace android {
 namespace hardware {
@@ -96,67 +98,6 @@
 
 std::vector<LeAudioCodecCapabilitiesSetting> kDefaultOffloadLeAudioCapabilities;
 
-static const UnicastCapability kInvalidUnicastCapability = {
-    .codecType = CodecType::UNKNOWN};
-
-static const BroadcastCapability kInvalidBroadcastCapability = {
-    .codecType = CodecType::UNKNOWN};
-
-// Default Supported Codecs
-// LC3 16_1: sample rate: 16 kHz, frame duration: 7.5 ms, octets per frame: 30
-static const Lc3Capabilities kLc3Capability_16_1 = {
-    .samplingFrequencyHz = {16000},
-    .frameDurationUs = {7500},
-    .octetsPerFrame = {30}};
-
-// Default Supported Codecs
-// LC3 16_2: sample rate: 16 kHz, frame duration: 10 ms, octets per frame: 40
-static const Lc3Capabilities kLc3Capability_16_2 = {
-    .samplingFrequencyHz = {16000},
-    .frameDurationUs = {10000},
-    .octetsPerFrame = {40}};
-
-// Default Supported Codecs
-// LC3 24_2: sample rate: 24 kHz, frame duration: 10 ms, octets per frame: 60
-static const Lc3Capabilities kLc3Capability_24_2 = {
-    .samplingFrequencyHz = {24000},
-    .frameDurationUs = {10000},
-    .octetsPerFrame = {60}};
-
-// Default Supported Codecs
-// LC3 32_2: sample rate: 32 kHz, frame duration: 10 ms, octets per frame: 80
-static const Lc3Capabilities kLc3Capability_32_2 = {
-    .samplingFrequencyHz = {32000},
-    .frameDurationUs = {10000},
-    .octetsPerFrame = {80}};
-
-// Default Supported Codecs
-// LC3 48_4: sample rate: 48 kHz, frame duration: 10 ms, octets per frame: 120
-static const Lc3Capabilities kLc3Capability_48_4 = {
-    .samplingFrequencyHz = {48000},
-    .frameDurationUs = {10000},
-    .octetsPerFrame = {120}};
-
-static const std::vector<Lc3Capabilities> supportedLc3CapabilityList = {
-    kLc3Capability_48_4, kLc3Capability_32_2, kLc3Capability_24_2,
-    kLc3Capability_16_2, kLc3Capability_16_1};
-
-static AudioLocation stereoAudio = static_cast<AudioLocation>(
-    static_cast<uint8_t>(AudioLocation::FRONT_LEFT) |
-    static_cast<uint8_t>(AudioLocation::FRONT_RIGHT));
-static AudioLocation monoAudio = AudioLocation::UNKNOWN;
-
-// Stores the supported setting of audio location, connected device, and the
-// channel count for each device
-std::vector<std::tuple<AudioLocation, uint8_t, uint8_t>>
-    supportedDeviceSetting = {
-        // Stereo, two connected device, one for L one for R
-        std::make_tuple(stereoAudio, 2, 1),
-        // Stereo, one connected device for both L and R
-        std::make_tuple(stereoAudio, 1, 2),
-        // Mono
-        std::make_tuple(monoAudio, 1, 1)};
-
 template <class T>
 bool BluetoothAudioCodecs::ContainedInVector(
     const std::vector<T>& vector, const typename identity<T>::type& target) {
@@ -444,19 +385,6 @@
   return false;
 }
 
-UnicastCapability composeUnicastLc3Capability(
-    AudioLocation audioLocation, uint8_t deviceCnt, uint8_t channelCount,
-    const Lc3Capabilities& capability) {
-  return {
-      .codecType = CodecType::LC3,
-      .supportedChannel = audioLocation,
-      .deviceCount = deviceCnt,
-      .channelCountPerDevice = channelCount,
-      .leAudioCodecCapabilities =
-          UnicastCapability::LeAudioCodecCapabilities(capability),
-  };
-}
-
 std::vector<LeAudioCodecCapabilitiesSetting>
 BluetoothAudioCodecs::GetLeAudioOffloadCodecCapabilities(
     const SessionType& session_type) {
@@ -470,34 +398,8 @@
   }
 
   if (kDefaultOffloadLeAudioCapabilities.empty()) {
-    for (auto [audioLocation, deviceCnt, channelCount] :
-         supportedDeviceSetting) {
-      for (auto capability : supportedLc3CapabilityList) {
-        UnicastCapability lc3Capability = composeUnicastLc3Capability(
-            audioLocation, deviceCnt, channelCount, capability);
-        UnicastCapability lc3MonoDecodeCapability =
-            composeUnicastLc3Capability(monoAudio, 1, 1, capability);
-
-        // Adds the capability for encode only
-        kDefaultOffloadLeAudioCapabilities.push_back(
-            {.unicastEncodeCapability = lc3Capability,
-             .unicastDecodeCapability = kInvalidUnicastCapability,
-             .broadcastCapability = kInvalidBroadcastCapability});
-
-        // Adds the capability for decode only
-        kDefaultOffloadLeAudioCapabilities.push_back(
-            {.unicastEncodeCapability = kInvalidUnicastCapability,
-             .unicastDecodeCapability = lc3Capability,
-             .broadcastCapability = kInvalidBroadcastCapability});
-
-        // Adds the capability for the case that encode and decode exist at the
-        // same time
-        kDefaultOffloadLeAudioCapabilities.push_back(
-            {.unicastEncodeCapability = lc3Capability,
-             .unicastDecodeCapability = lc3MonoDecodeCapability,
-             .broadcastCapability = kInvalidBroadcastCapability});
-      }
-    }
+    kDefaultOffloadLeAudioCapabilities =
+        BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities();
   }
 
   return kDefaultOffloadLeAudioCapabilities;
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp
index 292d352..2b0caad 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp
+++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp
@@ -60,12 +60,14 @@
     LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
                << " MqDescriptor Invalid";
     audio_config_ = nullptr;
+    leaudio_connection_map_ = nullptr;
   } else {
     stack_iface_ = stack_iface;
     latency_modes_ = latency_modes;
     LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
               << ", AudioConfiguration=" << audio_config.toString();
     ReportSessionStatus();
+    is_streaming_ = false;
   }
 }
 
@@ -74,11 +76,13 @@
   bool toggled = IsSessionReady();
   LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
   audio_config_ = nullptr;
+  leaudio_connection_map_ = nullptr;
   stack_iface_ = nullptr;
   UpdateDataPath(nullptr);
   if (toggled) {
     ReportSessionStatus();
   }
+  is_streaming_ = false;
 }
 
 /***
@@ -106,18 +110,72 @@
   return *audio_config_;
 }
 
+const AudioConfiguration BluetoothAudioSession::GetLeAudioConnectionMap() {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  if (!IsSessionReady()) {
+    return AudioConfiguration(LeAudioConfiguration{});
+  }
+  return *leaudio_connection_map_;
+}
+
 void BluetoothAudioSession::ReportAudioConfigChanged(
     const AudioConfiguration& audio_config) {
   if (session_type_ !=
           SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH &&
       session_type_ !=
-          SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH &&
-      session_type_ !=
-          SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
+          SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
     return;
   }
+
   std::lock_guard<std::recursive_mutex> guard(mutex_);
-  audio_config_ = std::make_unique<AudioConfiguration>(audio_config);
+  if (audio_config.getTag() != AudioConfiguration::leAudioConfig) {
+    LOG(ERROR) << __func__ << " invalid audio config type for SessionType ="
+               << toString(session_type_);
+    return;
+  }
+
+  if (is_streaming_) {
+    if (audio_config_ == nullptr) {
+      LOG(ERROR) << __func__ << " for SessionType=" << toString(session_type_)
+                 << " audio_config_ is nullptr during streaming. It shouldn't "
+                    "be happened";
+      return;
+    }
+
+    auto new_leaudio_config =
+        audio_config.get<AudioConfiguration::leAudioConfig>();
+    auto current_leaudio_config =
+        (*audio_config_).get<AudioConfiguration::leAudioConfig>();
+    if (new_leaudio_config.codecType != current_leaudio_config.codecType) {
+      LOG(ERROR)
+          << __func__ << " for SessionType=" << toString(session_type_)
+          << " codec type changed during streaming. It shouldn't be happened ";
+    }
+    auto new_lc3_config = new_leaudio_config.leAudioCodecConfig
+                              .get<LeAudioCodecConfiguration::lc3Config>();
+    auto current_lc3_config = current_leaudio_config.leAudioCodecConfig
+                                  .get<LeAudioCodecConfiguration::lc3Config>();
+    if ((new_lc3_config.pcmBitDepth != current_lc3_config.pcmBitDepth) ||
+        (new_lc3_config.samplingFrequencyHz !=
+         current_lc3_config.samplingFrequencyHz) ||
+        (new_lc3_config.frameDurationUs !=
+         current_lc3_config.frameDurationUs) ||
+        (new_lc3_config.octetsPerFrame != current_lc3_config.octetsPerFrame) ||
+        (new_lc3_config.blocksPerSdu != current_lc3_config.blocksPerSdu)) {
+      LOG(ERROR)
+          << __func__ << " for SessionType=" << toString(session_type_)
+          << " lc3 config changed during streaming. It shouldn't be happened";
+      return;
+    }
+
+    leaudio_connection_map_ =
+        std::make_unique<AudioConfiguration>(audio_config);
+  } else {
+    audio_config_ = std::make_unique<AudioConfiguration>(audio_config);
+    leaudio_connection_map_ =
+        std::make_unique<AudioConfiguration>(audio_config);
+  }
+
   if (observers_.empty()) {
     LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
                  << " has NO port state observer";
@@ -129,7 +187,11 @@
     LOG(INFO) << __func__ << " for SessionType=" << toString(session_type_)
               << ", bluetooth_audio=0x"
               << ::android::base::StringPrintf("%04x", cookie);
-    if (cb->audio_configuration_changed_cb_ != nullptr) {
+    if (is_streaming_) {
+      if (cb->soft_audio_configuration_changed_cb_ != nullptr) {
+        cb->soft_audio_configuration_changed_cb_(cookie);
+      }
+    } else if (cb->audio_configuration_changed_cb_ != nullptr) {
       cb->audio_configuration_changed_cb_(cookie);
     }
   }
@@ -419,6 +481,12 @@
                  << " has NO port state observer";
     return;
   }
+  if (start_resp && status == BluetoothAudioStatus::SUCCESS) {
+    is_streaming_ = true;
+  } else if (!start_resp && (status == BluetoothAudioStatus::SUCCESS ||
+                             status == BluetoothAudioStatus::RECONFIGURATION)) {
+    is_streaming_ = false;
+  }
   for (auto& observer : observers_) {
     uint16_t cookie = observer.first;
     std::shared_ptr<PortStatusCallbacks> callback = observer.second;
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.h b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.h
index 5bf17bd..faf4ffb 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.h
+++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.h
@@ -102,6 +102,13 @@
    ***/
   std::function<void(uint16_t cookie, bool allowed)>
       low_latency_mode_allowed_cb_;
+  /***
+   * soft_audio_configuration_changed_cb_ - when the Bluetooth stack change
+   * the streamMap during the streaming, the BluetoothAudioProvider will invoke
+   * this callback to report to the bluetooth_audio module.
+   * @param: cookie - indicates which bluetooth_audio output should handle
+   ***/
+  std::function<void(uint16_t cookie)> soft_audio_configuration_changed_cb_;
 };
 
 class BluetoothAudioSession {
@@ -159,6 +166,12 @@
   const AudioConfiguration GetAudioConfig();
 
   /***
+   * The control function is for the bluetooth_audio module to get the current
+   * LE audio connection map
+   ***/
+  const AudioConfiguration GetLeAudioConnectionMap();
+
+  /***
    * The report function is used to report that the Bluetooth stack has notified
    * the audio configuration changed, and will invoke
    * audio_configuration_changed_cb_ to notify registered bluetooth_audio
@@ -206,8 +219,11 @@
   std::unique_ptr<DataMQ> data_mq_;
   // audio data configuration for both software and offloading
   std::unique_ptr<AudioConfiguration> audio_config_;
+  std::unique_ptr<AudioConfiguration> leaudio_connection_map_;
   std::vector<LatencyMode> latency_modes_;
   bool low_latency_allowed_ = true;
+  // saving those steaming state based on the session_type
+  bool is_streaming_ = false;
 
   // saving those registered bluetooth_audio's callbacks
   std::unordered_map<uint16_t, std::shared_ptr<struct PortStatusCallbacks>>
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioSessionControl.h b/bluetooth/audio/utils/aidl_session/BluetoothAudioSessionControl.h
index 0782c82..881c6c1 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothAudioSessionControl.h
+++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioSessionControl.h
@@ -95,6 +95,25 @@
   }
 
   /***
+   * The control API for the bluetooth_audio module to get current
+   * LE audio connection map
+   ***/
+  static const AudioConfiguration GetLeAudioConnectionMap(
+      const SessionType& session_type) {
+    std::shared_ptr<BluetoothAudioSession> session_ptr =
+        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+    if ((session_type ==
+             SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
+         session_type ==
+             SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH) &&
+        session_ptr != nullptr) {
+      return session_ptr->GetLeAudioConnectionMap();
+    }
+
+    return AudioConfiguration(LeAudioConfiguration{});
+  }
+
+  /***
    * Those control APIs for the bluetooth_audio module to start / suspend /
   stop
    * stream, to check position, and to update metadata.
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.cpp b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.cpp
new file mode 100644
index 0000000..bf49270
--- /dev/null
+++ b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.cpp
@@ -0,0 +1,312 @@
+/*
+ * 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 "BTAudioCodecsProviderAidl"
+
+#include "BluetoothLeAudioCodecsProvider.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+
+static const char* kLeAudioCodecCapabilitiesFile =
+    "/vendor/etc/le_audio_codec_capabilities.xml";
+
+static const AudioLocation kStereoAudio = static_cast<AudioLocation>(
+    static_cast<uint8_t>(AudioLocation::FRONT_LEFT) |
+    static_cast<uint8_t>(AudioLocation::FRONT_RIGHT));
+static const AudioLocation kMonoAudio = AudioLocation::UNKNOWN;
+
+static std::vector<LeAudioCodecCapabilitiesSetting> leAudioCodecCapabilities;
+
+std::vector<LeAudioCodecCapabilitiesSetting>
+BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities() {
+  if (!leAudioCodecCapabilities.empty()) {
+    return leAudioCodecCapabilities;
+  }
+
+  const auto le_audio_offload_setting =
+      setting::readLeAudioOffloadSetting(kLeAudioCodecCapabilitiesFile);
+  if (!le_audio_offload_setting.has_value()) {
+    LOG(ERROR) << __func__ << ": Failed to read "
+               << kLeAudioCodecCapabilitiesFile;
+    return {};
+  }
+
+  std::vector<setting::Scenario> supported_scenarios =
+      GetScenarios(le_audio_offload_setting);
+  if (supported_scenarios.empty()) {
+    LOG(ERROR) << __func__ << ": No scenarios in "
+               << kLeAudioCodecCapabilitiesFile;
+    return {};
+  }
+
+  UpdateConfigurationsToMap(le_audio_offload_setting);
+  if (configuration_map_.empty()) {
+    LOG(ERROR) << __func__ << ": No configurations in "
+               << kLeAudioCodecCapabilitiesFile;
+    return {};
+  }
+
+  UpdateCodecConfigurationsToMap(le_audio_offload_setting);
+  if (codec_configuration_map_.empty()) {
+    LOG(ERROR) << __func__ << ": No codec configurations in "
+               << kLeAudioCodecCapabilitiesFile;
+    return {};
+  }
+
+  UpdateStrategyConfigurationsToMap(le_audio_offload_setting);
+  if (strategy_configuration_map_.empty()) {
+    LOG(ERROR) << __func__ << ": No strategy configurations in "
+               << kLeAudioCodecCapabilitiesFile;
+    return {};
+  }
+
+  leAudioCodecCapabilities =
+      ComposeLeAudioCodecCapabilities(supported_scenarios);
+  return leAudioCodecCapabilities;
+}
+
+std::vector<setting::Scenario> BluetoothLeAudioCodecsProvider::GetScenarios(
+    const std::optional<setting::LeAudioOffloadSetting>&
+        le_audio_offload_setting) {
+  std::vector<setting::Scenario> supported_scenarios;
+  if (le_audio_offload_setting->hasScenarioList()) {
+    for (const auto& scenario_list :
+         le_audio_offload_setting->getScenarioList()) {
+      if (!scenario_list.hasScenario()) {
+        continue;
+      }
+      for (const auto& scenario : scenario_list.getScenario()) {
+        if (scenario.hasEncode() && scenario.hasDecode()) {
+          supported_scenarios.push_back(scenario);
+        }
+      }
+    }
+  }
+  return supported_scenarios;
+}
+
+void BluetoothLeAudioCodecsProvider::UpdateConfigurationsToMap(
+    const std::optional<setting::LeAudioOffloadSetting>&
+        le_audio_offload_setting) {
+  if (le_audio_offload_setting->hasConfigurationList()) {
+    for (const auto& configuration_list :
+         le_audio_offload_setting->getConfigurationList()) {
+      if (!configuration_list.hasConfiguration()) {
+        continue;
+      }
+      for (const auto& configuration : configuration_list.getConfiguration()) {
+        if (configuration.hasName() && configuration.hasCodecConfiguration() &&
+            configuration.hasStrategyConfiguration()) {
+          configuration_map_.insert(
+              make_pair(configuration.getName(), configuration));
+        }
+      }
+    }
+  }
+}
+
+void BluetoothLeAudioCodecsProvider::UpdateCodecConfigurationsToMap(
+    const std::optional<setting::LeAudioOffloadSetting>&
+        le_audio_offload_setting) {
+  if (le_audio_offload_setting->hasCodecConfigurationList()) {
+    for (const auto& codec_configuration_list :
+         le_audio_offload_setting->getCodecConfigurationList()) {
+      if (!codec_configuration_list.hasCodecConfiguration()) {
+        continue;
+      }
+      for (const auto& codec_configuration :
+           codec_configuration_list.getCodecConfiguration()) {
+        if (IsValidCodecConfiguration(codec_configuration)) {
+          codec_configuration_map_.insert(
+              make_pair(codec_configuration.getName(), codec_configuration));
+        }
+      }
+    }
+  }
+}
+
+void BluetoothLeAudioCodecsProvider::UpdateStrategyConfigurationsToMap(
+    const std::optional<setting::LeAudioOffloadSetting>&
+        le_audio_offload_setting) {
+  if (le_audio_offload_setting->hasStrategyConfigurationList()) {
+    for (const auto& strategy_configuration_list :
+         le_audio_offload_setting->getStrategyConfigurationList()) {
+      if (!strategy_configuration_list.hasStrategyConfiguration()) {
+        continue;
+      }
+      for (const auto& strategy_configuration :
+           strategy_configuration_list.getStrategyConfiguration()) {
+        if (IsValidStrategyConfiguration(strategy_configuration)) {
+          strategy_configuration_map_.insert(make_pair(
+              strategy_configuration.getName(), strategy_configuration));
+        }
+      }
+    }
+  }
+}
+
+std::vector<LeAudioCodecCapabilitiesSetting>
+BluetoothLeAudioCodecsProvider::ComposeLeAudioCodecCapabilities(
+    const std::vector<setting::Scenario>& supported_scenarios) {
+  std::vector<LeAudioCodecCapabilitiesSetting> le_audio_codec_capabilities;
+  for (const auto& scenario : supported_scenarios) {
+    UnicastCapability unicast_encode_capability =
+        GetUnicastCapability(scenario.getEncode());
+    UnicastCapability unicast_decode_capability =
+        GetUnicastCapability(scenario.getDecode());
+    // encode and decode cannot be unknown at the same time
+    if (unicast_encode_capability.codecType == CodecType::UNKNOWN &&
+        unicast_decode_capability.codecType == CodecType::UNKNOWN) {
+      continue;
+    }
+    BroadcastCapability broadcast_capability = {.codecType =
+                                                    CodecType::UNKNOWN};
+    le_audio_codec_capabilities.push_back(
+        {.unicastEncodeCapability = unicast_encode_capability,
+         .unicastDecodeCapability = unicast_decode_capability,
+         .broadcastCapability = broadcast_capability});
+  }
+  return le_audio_codec_capabilities;
+}
+
+UnicastCapability BluetoothLeAudioCodecsProvider::GetUnicastCapability(
+    const std::string& coding_direction) {
+  if (coding_direction == "invalid") {
+    return {.codecType = CodecType::UNKNOWN};
+  }
+
+  auto configuration_iter = configuration_map_.find(coding_direction);
+  if (configuration_iter == configuration_map_.end()) {
+    return {.codecType = CodecType::UNKNOWN};
+  }
+
+  auto codec_configuration_iter = codec_configuration_map_.find(
+      configuration_iter->second.getCodecConfiguration());
+  if (codec_configuration_iter == codec_configuration_map_.end()) {
+    return {.codecType = CodecType::UNKNOWN};
+  }
+
+  auto strategy_configuration_iter = strategy_configuration_map_.find(
+      configuration_iter->second.getStrategyConfiguration());
+  if (strategy_configuration_iter == strategy_configuration_map_.end()) {
+    return {.codecType = CodecType::UNKNOWN};
+  }
+
+  CodecType codec_type =
+      GetCodecType(codec_configuration_iter->second.getCodec());
+  if (codec_type == CodecType::LC3) {
+    return ComposeUnicastCapability(
+        codec_type,
+        GetAudioLocation(
+            strategy_configuration_iter->second.getAudioLocation()),
+        strategy_configuration_iter->second.getConnectedDevice(),
+        strategy_configuration_iter->second.getChannelCount(),
+        ComposeLc3Capability(codec_configuration_iter->second));
+  }
+  return {.codecType = CodecType::UNKNOWN};
+}
+
+template <class T>
+UnicastCapability BluetoothLeAudioCodecsProvider::ComposeUnicastCapability(
+    const CodecType& codec_type, const AudioLocation& audio_location,
+    const uint8_t& device_cnt, const uint8_t& channel_count,
+    const T& capability) {
+  return {
+      .codecType = codec_type,
+      .supportedChannel = audio_location,
+      .deviceCount = device_cnt,
+      .channelCountPerDevice = channel_count,
+      .leAudioCodecCapabilities =
+          UnicastCapability::LeAudioCodecCapabilities(capability),
+  };
+}
+
+Lc3Capabilities BluetoothLeAudioCodecsProvider::ComposeLc3Capability(
+    const setting::CodecConfiguration& codec_configuration) {
+  return {.samplingFrequencyHz = {codec_configuration.getSamplingFrequency()},
+          .frameDurationUs = {codec_configuration.getFrameDurationUs()},
+          .octetsPerFrame = {codec_configuration.getOctetsPerCodecFrame()}};
+}
+
+AudioLocation BluetoothLeAudioCodecsProvider::GetAudioLocation(
+    const setting::AudioLocation& audio_location) {
+  switch (audio_location) {
+    case setting::AudioLocation::MONO:
+      return kMonoAudio;
+    case setting::AudioLocation::STEREO:
+      return kStereoAudio;
+    default:
+      return AudioLocation::UNKNOWN;
+  }
+}
+
+CodecType BluetoothLeAudioCodecsProvider::GetCodecType(
+    const setting::CodecType& codec_type) {
+  switch (codec_type) {
+    case setting::CodecType::LC3:
+      return CodecType::LC3;
+    default:
+      return CodecType::UNKNOWN;
+  }
+}
+
+bool BluetoothLeAudioCodecsProvider::IsValidCodecConfiguration(
+    const setting::CodecConfiguration& codec_configuration) {
+  return codec_configuration.hasName() && codec_configuration.hasCodec() &&
+         codec_configuration.hasSamplingFrequency() &&
+         codec_configuration.hasFrameDurationUs() &&
+         codec_configuration.hasOctetsPerCodecFrame();
+}
+
+bool BluetoothLeAudioCodecsProvider::IsValidStrategyConfiguration(
+    const setting::StrategyConfiguration& strategy_configuration) {
+  if (!strategy_configuration.hasName() ||
+      !strategy_configuration.hasAudioLocation() ||
+      !strategy_configuration.hasConnectedDevice() ||
+      !strategy_configuration.hasChannelCount()) {
+    return false;
+  }
+  if (strategy_configuration.getAudioLocation() ==
+      setting::AudioLocation::STEREO) {
+    if ((strategy_configuration.getConnectedDevice() == 2 &&
+         strategy_configuration.getChannelCount() == 1) ||
+        (strategy_configuration.getConnectedDevice() == 1 &&
+         strategy_configuration.getChannelCount() == 2)) {
+      // Stereo
+      // 1. two connected device, one for L one for R
+      // 2. one connected device for both L and R
+      return true;
+    }
+  } else if (strategy_configuration.getAudioLocation() ==
+             setting::AudioLocation::MONO) {
+    if (strategy_configuration.getConnectedDevice() == 1 &&
+        strategy_configuration.getChannelCount() == 1) {
+      // Mono
+      return true;
+    }
+  }
+  return false;
+}
+
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.h b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.h
new file mode 100644
index 0000000..402235f
--- /dev/null
+++ b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.h
@@ -0,0 +1,87 @@
+/*
+ * 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/bluetooth/audio/LeAudioCodecCapabilitiesSetting.h>
+#include <android-base/logging.h>
+
+#include <unordered_map>
+
+#include "aidl_android_hardware_bluetooth_audio_setting.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+
+class BluetoothLeAudioCodecsProvider {
+ public:
+  static std::vector<LeAudioCodecCapabilitiesSetting>
+  GetLeAudioCodecCapabilities();
+
+ private:
+  static inline std::unordered_map<std::string, setting::Configuration>
+      configuration_map_;
+  static inline std::unordered_map<std::string, setting::CodecConfiguration>
+      codec_configuration_map_;
+  static inline std::unordered_map<std::string, setting::StrategyConfiguration>
+      strategy_configuration_map_;
+
+  static std::vector<setting::Scenario> GetScenarios(
+      const std::optional<setting::LeAudioOffloadSetting>&
+          le_audio_offload_setting);
+  static void UpdateConfigurationsToMap(
+      const std::optional<setting::LeAudioOffloadSetting>&
+          le_audio_offload_setting);
+  static void UpdateCodecConfigurationsToMap(
+      const std::optional<setting::LeAudioOffloadSetting>&
+          le_audio_offload_setting);
+  static void UpdateStrategyConfigurationsToMap(
+      const std::optional<setting::LeAudioOffloadSetting>&
+          le_audio_offload_setting);
+
+  static std::vector<LeAudioCodecCapabilitiesSetting>
+  ComposeLeAudioCodecCapabilities(
+      const std::vector<setting::Scenario>& supported_scenarios);
+
+  static UnicastCapability GetUnicastCapability(
+      const std::string& coding_direction);
+  template <class T>
+  static inline UnicastCapability ComposeUnicastCapability(
+      const CodecType& codec_type, const AudioLocation& audio_location,
+      const uint8_t& device_cnt, const uint8_t& channel_count,
+      const T& capability);
+
+  static inline Lc3Capabilities ComposeLc3Capability(
+      const setting::CodecConfiguration& codec_configuration);
+
+  static inline AudioLocation GetAudioLocation(
+      const setting::AudioLocation& audio_location);
+  static inline CodecType GetCodecType(const setting::CodecType& codec_type);
+
+  static inline bool IsValidCodecConfiguration(
+      const setting::CodecConfiguration& codec_configuration);
+  static inline bool IsValidStrategyConfiguration(
+      const setting::StrategyConfiguration& strategy_configuration);
+};
+
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/bluetooth/audio/utils/le_audio_codec_capabilities/le_audio_codec_capabilities.xml b/bluetooth/audio/utils/le_audio_codec_capabilities/le_audio_codec_capabilities.xml
new file mode 100644
index 0000000..c7904b3
--- /dev/null
+++ b/bluetooth/audio/utils/le_audio_codec_capabilities/le_audio_codec_capabilities.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!---
+  This is an example to configure LE Audio hardware offload supported capability settings
+  In the follow list, there would be only one list in this file. Add element into each list as needed.
+
+  codecConfigurationList:
+    Supported codec capability along with its parameter setting
+
+  strategyConfigurationList:
+     ASE Configuration strategies
+
+  configurationList:
+    For each configuration , there are two attributes
+      - codecConfiguration
+      - strategyConfiguration
+
+  scenarioList:
+    There would be only one `scenarios` group
+    For each scenario, the are two attributes
+      - encode
+      - decode
+    If a scenario is unidirectional, mark another direction as `invalid`
+    The configuration should be chosen from `configurationList`
+-->
+<leAudioOffloadSetting>
+  <scenarioList>
+    <!-- encode only -->
+    <scenario encode="OneChanMono_16_1" decode="invalid"/>
+    <scenario encode="TwoChanStereo_16_1" decode="invalid"/>
+    <scenario encode="OneChanStereo_16_1" decode="invalid"/>
+    <scenario encode="OneChanMono_16_2" decode="invalid"/>
+    <scenario encode="TwoChanStereo_16_2" decode="invalid"/>
+    <scenario encode="OneChanStereo_16_2" decode="invalid"/>
+    <!-- encode and decode -->
+    <scenario encode="OneChanStereo_16_1" decode="OneChanStereo_16_1"/>
+    <scenario encode="OneChanStereo_16_1" decode="OneChanMono_16_1"/>
+    <scenario encode="TwoChanStereo_16_1" decode="OneChanMono_16_1"/>
+    <scenario encode="OneChanMono_16_1" decode="OneChanMono_16_1"/>
+    <scenario encode="OneChanStereo_16_2" decode="OneChanStereo_16_2"/>
+    <scenario encode="OneChanStereo_16_2" decode="OneChanMono_16_2"/>
+    <scenario encode="TwoChanStereo_16_2" decode="OneChanMono_16_2"/>
+    <scenario encode="OneChanMono_16_2" decode="OneChanMono_16_2"/>
+  </scenarioList>
+  <configurationList>
+    <configuration name="OneChanMono_16_1" codecConfiguration="LC3_16k_1" strategyConfiguration="MONO_ONE_CIS_PER_DEVICE"/>
+    <configuration name="TwoChanStereo_16_1" codecConfiguration="LC3_16k_1" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/>
+    <configuration name="OneChanStereo_16_1" codecConfiguration="LC3_16k_1" strategyConfiguration="STEREO_ONE_CIS_PER_DEVICE"/>
+    <configuration name="OneChanMono_16_2" codecConfiguration="LC3_16k_2" strategyConfiguration="MONO_ONE_CIS_PER_DEVICE"/>
+    <configuration name="TwoChanStereo_16_2" codecConfiguration="LC3_16k_2" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/>
+    <configuration name="OneChanStereo_16_2" codecConfiguration="LC3_16k_2" strategyConfiguration="STEREO_ONE_CIS_PER_DEVICE"/>
+  </configurationList>
+  <codecConfigurationList>
+    <codecConfiguration name="LC3_16k_1" codec="LC3" samplingFrequency="16000" frameDurationUs="7500" octetsPerCodecFrame="30"/>
+    <codecConfiguration name="LC3_16k_2" codec="LC3" samplingFrequency="16000" frameDurationUs="10000" octetsPerCodecFrame="40"/>
+  </codecConfigurationList>
+  <strategyConfigurationList>
+    <strategyConfiguration name="STEREO_ONE_CIS_PER_DEVICE" audioLocation="STEREO" connectedDevice="2" channelCount="1"/>
+    <strategyConfiguration name="STEREO_TWO_CISES_PER_DEVICE" audioLocation="STEREO" connectedDevice="1" channelCount="2"/>
+    <strategyConfiguration name="MONO_ONE_CIS_PER_DEVICE" audioLocation="MONO" connectedDevice="1" channelCount="1"/>
+  </strategyConfigurationList>
+</leAudioOffloadSetting>
diff --git a/bluetooth/audio/utils/le_audio_codec_capabilities/le_audio_codec_capabilities.xsd b/bluetooth/audio/utils/le_audio_codec_capabilities/le_audio_codec_capabilities.xsd
new file mode 100644
index 0000000..213e597
--- /dev/null
+++ b/bluetooth/audio/utils/le_audio_codec_capabilities/le_audio_codec_capabilities.xsd
@@ -0,0 +1,74 @@
+<!-- LE Audio Offload Codec Capability Schema -->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+  <xs:element name="leAudioOffloadSetting">
+    <xs:complexType>
+      <xs:element ref="scenarioList" minOccurs="1" maxOccurs="1"/>
+      <xs:element ref="configurationList" minOccurs="1" maxOccurs="1"/>
+      <xs:element ref="codecConfigurationList" minOccurs="1" maxOccurs="1"/>
+      <xs:element ref="strategyConfigurationList" minOccurs="1" maxOccurs="1"/>
+    </xs:complexType>
+  </xs:element>
+  <xs:element name="scenarioList">
+    <xs:complexType>
+      <xs:element ref="scenario" minOccurs="1" maxOccurs="unbounded"/>
+    </xs:complexType>
+  </xs:element>
+  <xs:element name="configurationList">
+    <xs:complexType>
+      <xs:element ref="configuration" minOccurs="1" maxOccurs="unbounded"/>
+    </xs:complexType>
+  </xs:element>
+  <xs:element name="codecConfigurationList">
+    <xs:complexType>
+      <xs:element ref="codecConfiguration" minOccurs="1" maxOccurs="unbounded"/>
+    </xs:complexType>
+  </xs:element>
+  <xs:element name="strategyConfigurationList">
+    <xs:complexType>
+      <xs:element ref="strategyConfiguration" minOccurs="1" maxOccurs="unbounded"/>
+    </xs:complexType>
+  </xs:element>
+  <xs:element name="scenario">
+    <xs:complexType>
+      <xs:attribute name="encode" type="xs:string"/>
+      <xs:attribute name="decode" type="xs:string"/>
+    </xs:complexType>
+  </xs:element>
+  <xs:element name="configuration">
+    <xs:complexType>
+      <xs:attribute name="name" type="xs:string"/>
+      <xs:attribute name="codecConfiguration" type="xs:string"/>
+      <xs:attribute name="strategyConfiguration" type="xs:string"/>
+    </xs:complexType>
+  </xs:element>
+  <xs:element name="codecConfiguration">
+    <xs:complexType>
+      <xs:attribute name="name" type="xs:string"/>
+      <xs:attribute name="codec" type="codecType"/>
+      <xs:attribute name="pcmBitDepth" type="xs:unsignedByte"/>
+      <xs:attribute name="samplingFrequency" type="xs:int"/>
+      <xs:attribute name="frameDurationUs" type="xs:int"/>
+      <xs:attribute name="octetsPerCodecFrame" type="xs:int"/>
+      <xs:attribute name="codecFrameBlocksPerSdu" type="xs:unsignedByte"/>
+    </xs:complexType>
+  </xs:element>
+  <xs:element name="strategyConfiguration">
+    <xs:complexType>
+      <xs:attribute name="name" type="xs:string"/>
+      <xs:attribute name="audioLocation" type="audioLocation"/>
+      <xs:attribute name="connectedDevice" type="xs:unsignedByte"/>
+      <xs:attribute name="channelCount" type="xs:unsignedByte"/>
+    </xs:complexType>
+  </xs:element>
+  <xs:simpleType name="audioLocation">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="MONO"/>
+      <xs:enumeration value="STEREO"/>
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="codecType">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="LC3"/>
+    </xs:restriction>
+  </xs:simpleType>
+</xs:schema>
diff --git a/bluetooth/audio/utils/le_audio_codec_capabilities/schema/current.txt b/bluetooth/audio/utils/le_audio_codec_capabilities/schema/current.txt
new file mode 100644
index 0000000..06aa21a
--- /dev/null
+++ b/bluetooth/audio/utils/le_audio_codec_capabilities/schema/current.txt
@@ -0,0 +1,111 @@
+// Signature format: 2.0
+package aidl.android.hardware.bluetooth.audio.setting {
+
+  public enum AudioLocation {
+    method public String getRawName();
+    enum_constant public static final aidl.android.hardware.bluetooth.audio.setting.AudioLocation MONO;
+    enum_constant public static final aidl.android.hardware.bluetooth.audio.setting.AudioLocation STEREO;
+  }
+
+  public class CodecConfiguration {
+    ctor public CodecConfiguration();
+    method public aidl.android.hardware.bluetooth.audio.setting.CodecType getCodec();
+    method public short getCodecFrameBlocksPerSdu();
+    method public int getFrameDurationUs();
+    method public String getName();
+    method public int getOctetsPerCodecFrame();
+    method public short getPcmBitDepth();
+    method public int getSamplingFrequency();
+    method public void setCodec(aidl.android.hardware.bluetooth.audio.setting.CodecType);
+    method public void setCodecFrameBlocksPerSdu(short);
+    method public void setFrameDurationUs(int);
+    method public void setName(String);
+    method public void setOctetsPerCodecFrame(int);
+    method public void setPcmBitDepth(short);
+    method public void setSamplingFrequency(int);
+  }
+
+  public class CodecConfigurationList {
+    ctor public CodecConfigurationList();
+    method public java.util.List<aidl.android.hardware.bluetooth.audio.setting.CodecConfiguration> getCodecConfiguration();
+  }
+
+  public enum CodecType {
+    method public String getRawName();
+    enum_constant public static final aidl.android.hardware.bluetooth.audio.setting.CodecType LC3;
+  }
+
+  public class Configuration {
+    ctor public Configuration();
+    method public String getCodecConfiguration();
+    method public String getName();
+    method public String getStrategyConfiguration();
+    method public void setCodecConfiguration(String);
+    method public void setName(String);
+    method public void setStrategyConfiguration(String);
+  }
+
+  public class ConfigurationList {
+    ctor public ConfigurationList();
+    method public java.util.List<aidl.android.hardware.bluetooth.audio.setting.Configuration> getConfiguration();
+  }
+
+  public class LeAudioOffloadSetting {
+    ctor public LeAudioOffloadSetting();
+    method public aidl.android.hardware.bluetooth.audio.setting.CodecConfigurationList getCodecConfigurationList();
+    method public aidl.android.hardware.bluetooth.audio.setting.ConfigurationList getConfigurationList();
+    method public aidl.android.hardware.bluetooth.audio.setting.ScenarioList getScenarioList();
+    method public aidl.android.hardware.bluetooth.audio.setting.StrategyConfigurationList getStrategyConfigurationList();
+    method public void setCodecConfigurationList(aidl.android.hardware.bluetooth.audio.setting.CodecConfigurationList);
+    method public void setConfigurationList(aidl.android.hardware.bluetooth.audio.setting.ConfigurationList);
+    method public void setScenarioList(aidl.android.hardware.bluetooth.audio.setting.ScenarioList);
+    method public void setStrategyConfigurationList(aidl.android.hardware.bluetooth.audio.setting.StrategyConfigurationList);
+  }
+
+  public class Scenario {
+    ctor public Scenario();
+    method public String getDecode();
+    method public String getEncode();
+    method public void setDecode(String);
+    method public void setEncode(String);
+  }
+
+  public class ScenarioList {
+    ctor public ScenarioList();
+    method public java.util.List<aidl.android.hardware.bluetooth.audio.setting.Scenario> getScenario();
+  }
+
+  public class StrategyConfiguration {
+    ctor public StrategyConfiguration();
+    method public aidl.android.hardware.bluetooth.audio.setting.AudioLocation getAudioLocation();
+    method public short getChannelCount();
+    method public short getConnectedDevice();
+    method public String getName();
+    method public void setAudioLocation(aidl.android.hardware.bluetooth.audio.setting.AudioLocation);
+    method public void setChannelCount(short);
+    method public void setConnectedDevice(short);
+    method public void setName(String);
+  }
+
+  public class StrategyConfigurationList {
+    ctor public StrategyConfigurationList();
+    method public java.util.List<aidl.android.hardware.bluetooth.audio.setting.StrategyConfiguration> getStrategyConfiguration();
+  }
+
+  public class XmlParser {
+    ctor public XmlParser();
+    method public static aidl.android.hardware.bluetooth.audio.setting.CodecConfiguration readCodecConfiguration(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static aidl.android.hardware.bluetooth.audio.setting.CodecConfigurationList readCodecConfigurationList(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static aidl.android.hardware.bluetooth.audio.setting.Configuration readConfiguration(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static aidl.android.hardware.bluetooth.audio.setting.ConfigurationList readConfigurationList(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static aidl.android.hardware.bluetooth.audio.setting.LeAudioOffloadSetting readLeAudioOffloadSetting(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static aidl.android.hardware.bluetooth.audio.setting.Scenario readScenario(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static aidl.android.hardware.bluetooth.audio.setting.ScenarioList readScenarioList(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static aidl.android.hardware.bluetooth.audio.setting.StrategyConfiguration readStrategyConfiguration(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static aidl.android.hardware.bluetooth.audio.setting.StrategyConfigurationList readStrategyConfigurationList(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+  }
+
+}
+
diff --git a/bluetooth/audio/utils/le_audio_codec_capabilities/schema/last_current.txt b/bluetooth/audio/utils/le_audio_codec_capabilities/schema/last_current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/bluetooth/audio/utils/le_audio_codec_capabilities/schema/last_current.txt
diff --git a/bluetooth/audio/utils/le_audio_codec_capabilities/schema/last_removed.txt b/bluetooth/audio/utils/le_audio_codec_capabilities/schema/last_removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/bluetooth/audio/utils/le_audio_codec_capabilities/schema/last_removed.txt
diff --git a/bluetooth/audio/utils/le_audio_codec_capabilities/schema/removed.txt b/bluetooth/audio/utils/le_audio_codec_capabilities/schema/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/bluetooth/audio/utils/le_audio_codec_capabilities/schema/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/boot/1.1/default/boot_control/Android.bp b/boot/1.1/default/boot_control/Android.bp
index ad71700..6aa30c2 100644
--- a/boot/1.1/default/boot_control/Android.bp
+++ b/boot/1.1/default/boot_control/Android.bp
@@ -25,8 +25,6 @@
 
 cc_defaults {
     name: "libboot_control_defaults",
-    vendor: true,
-    recovery_available: true,
     relative_install_path: "hw",
 
     cflags: [
@@ -42,7 +40,7 @@
         "liblog",
     ],
     static_libs: [
-        "libbootloader_message_vendor",
+        "libbootloader_message",
         "libfstab",
     ],
 }
@@ -51,6 +49,8 @@
     name: "libboot_control",
     defaults: ["libboot_control_defaults"],
     export_include_dirs: ["include"],
+    recovery_available: true,
+    vendor_available: true,
 
     srcs: ["libboot_control.cpp"],
 }
@@ -58,6 +58,8 @@
 cc_library_shared {
     name: "bootctrl.default",
     defaults: ["libboot_control_defaults"],
+    recovery_available: true,
+    vendor_available: true,
 
     srcs: ["legacy_boot_control.cpp"],
 
diff --git a/boot/aidl/default/Android.bp b/boot/aidl/default/Android.bp
index 6aefae8..dcb40db 100644
--- a/boot/aidl/default/Android.bp
+++ b/boot/aidl/default/Android.bp
@@ -23,13 +23,11 @@
     default_applicable_licenses: ["hardware_interfaces_license"],
 }
 
-cc_binary {
-    name: "android.hardware.boot-service.default",
-    defaults: ["libboot_control_defaults"],
+cc_defaults {
+    name: "android.hardware.boot-service_common",
     relative_install_path: "hw",
-    init_rc: ["boot-default.rc"],
-    vintf_fragments: ["boot-default.xml"],
-    vendor: true,
+    defaults: ["libboot_control_defaults"],
+    vintf_fragments: ["android.hardware.boot-service.default.xml"],
     shared_libs: [
         "libbase",
         "libbinder_ndk",
@@ -41,3 +39,17 @@
     ],
     srcs: ["main.cpp", "BootControl.cpp"],
 }
+
+cc_binary {
+    name: "android.hardware.boot-service.default",
+    defaults: ["android.hardware.boot-service_common"],
+    init_rc: ["android.hardware.boot-service.default.rc"],
+    vendor: true,
+}
+
+cc_binary {
+    name: "android.hardware.boot-service.default_recovery",
+    defaults: ["android.hardware.boot-service_common"],
+    init_rc: ["android.hardware.boot-service.default_recovery.rc"],
+    recovery: true,
+}
diff --git a/boot/aidl/default/boot-default.rc b/boot/aidl/default/android.hardware.boot-service.default.rc
similarity index 100%
rename from boot/aidl/default/boot-default.rc
rename to boot/aidl/default/android.hardware.boot-service.default.rc
diff --git a/boot/aidl/default/boot-default.xml b/boot/aidl/default/android.hardware.boot-service.default.xml
similarity index 100%
rename from boot/aidl/default/boot-default.xml
rename to boot/aidl/default/android.hardware.boot-service.default.xml
diff --git a/boot/aidl/default/android.hardware.boot-service.default_recovery.rc b/boot/aidl/default/android.hardware.boot-service.default_recovery.rc
new file mode 100644
index 0000000..cb08a39
--- /dev/null
+++ b/boot/aidl/default/android.hardware.boot-service.default_recovery.rc
@@ -0,0 +1,7 @@
+service vendor.boot-default /system/bin/hw/android.hardware.boot-service.default_recovery
+    class early_hal
+    user root
+    group root
+    seclabel u:r:hal_bootctl_default:s0
+    interface aidl android.hardware.boot.IBootControl/default
+
diff --git a/boot/aidl/vts/functional/Android.bp b/boot/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..e46cbef
--- /dev/null
+++ b/boot/aidl/vts/functional/Android.bp
@@ -0,0 +1,41 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+    // 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: "VtsHalBootAidlTargetTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static"
+    ],
+    srcs: ["VtsHalBootAidlTargetTest.cpp"],
+    static_libs: [
+        "android.hardware.boot-V1-ndk",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+        "libvintf",
+    ],
+    test_suites: ["general-tests", "vts"],
+}
diff --git a/boot/aidl/vts/functional/OWNERS b/boot/aidl/vts/functional/OWNERS
new file mode 100644
index 0000000..bc813d8
--- /dev/null
+++ b/boot/aidl/vts/functional/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 30545
+zhangkelvin@google.com
diff --git a/boot/aidl/vts/functional/VtsHalBootAidlTargetTest.cpp b/boot/aidl/vts/functional/VtsHalBootAidlTargetTest.cpp
new file mode 100644
index 0000000..ab3c789
--- /dev/null
+++ b/boot/aidl/vts/functional/VtsHalBootAidlTargetTest.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+
+#include <cutils/properties.h>
+
+#include <aidl/android/hardware/boot/IBootControl.h>
+
+#include <aidl/Vintf.h>
+#include <android/binder_manager.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+#include <unordered_set>
+
+using aidl::android::hardware::boot::IBootControl;
+using std::string;
+using std::unordered_set;
+
+// The main test class for the Boot HIDL HAL.
+class BootHidlTest : public ::testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        const auto instance_name = GetParam();
+        ASSERT_TRUE(AServiceManager_isDeclared(instance_name.c_str()))
+                << " instance " << instance_name << " not declared.";
+        boot = ::aidl::android::hardware::boot::IBootControl::fromBinder(
+                ndk::SpAIBinder(AServiceManager_waitForService(instance_name.c_str())));
+        ASSERT_NE(boot, nullptr);
+    }
+
+    std::shared_ptr<IBootControl> boot;
+};
+
+// validity check Boot::getNumberSlots().
+TEST_P(BootHidlTest, GetNumberSlots) {
+    int32_t slots{};
+    boot->getNumberSlots(&slots);
+    ASSERT_LE(2, slots);
+}
+
+// validity check Boot::getCurrentSlot().
+TEST_P(BootHidlTest, GetCurrentSlot) {
+    int curSlot = -1;
+    boot->getCurrentSlot(&curSlot);
+    int slots = 0;
+    boot->getNumberSlots(&slots);
+    ASSERT_LT(curSlot, slots);
+}
+
+// validity check Boot::markBootSuccessful().
+TEST_P(BootHidlTest, MarkBootSuccessful) {
+    const auto result = boot->markBootSuccessful();
+    ASSERT_TRUE(result.isOk());
+    int curSlot = 0;
+    boot->getCurrentSlot(&curSlot);
+    bool ret = false;
+    boot->isSlotMarkedSuccessful(curSlot, &ret);
+    ASSERT_TRUE(ret);
+}
+
+TEST_P(BootHidlTest, SetActiveBootSlot) {
+    int curSlot = -1;
+    boot->getCurrentSlot(&curSlot);
+    ASSERT_GE(curSlot, 0);
+    int otherSlot = curSlot ? 0 : 1;
+    bool otherBootable = true;
+    boot->isSlotBootable(otherSlot, &otherBootable);
+
+    for (int s = 0; s < 2; s++) {
+        const auto result = boot->setActiveBootSlot(s);
+        ASSERT_TRUE(result.isOk());
+    }
+    {
+        // Restore original flags to avoid problems on reboot
+        auto result = boot->setActiveBootSlot(curSlot);
+        ASSERT_TRUE(result.isOk());
+
+        if (!otherBootable) {
+            const auto result = boot->setSlotAsUnbootable(otherSlot);
+            ASSERT_TRUE(result.isOk());
+        }
+
+        result = boot->markBootSuccessful();
+        ASSERT_TRUE(result.isOk());
+    }
+    {
+        int slots = 0;
+        boot->getNumberSlots(&slots);
+        const auto result = boot->setActiveBootSlot(slots);
+        ASSERT_FALSE(result.isOk()) << "setActiveBootSlot on invalid slot should fail";
+    }
+}
+
+TEST_P(BootHidlTest, SetSlotAsUnbootable) {
+    int curSlot = -1;
+    boot->getCurrentSlot(&curSlot);
+    ASSERT_GE(curSlot, 0);
+    int otherSlot = curSlot ? 0 : 1;
+    bool otherBootable = false;
+    boot->isSlotBootable(otherSlot, &otherBootable);
+    {
+        auto result = boot->setSlotAsUnbootable(otherSlot);
+        ASSERT_TRUE(result.isOk());
+        boot->isSlotBootable(otherSlot, &otherBootable);
+        ASSERT_FALSE(otherBootable);
+
+        // Restore original flags to avoid problems on reboot
+        if (otherBootable) {
+            result = boot->setActiveBootSlot(otherSlot);
+            ASSERT_TRUE(result.isOk());
+        }
+        result = boot->setActiveBootSlot(curSlot);
+        ASSERT_TRUE(result.isOk());
+        result = boot->markBootSuccessful();
+        ASSERT_TRUE(result.isOk());
+    }
+    {
+        int32_t slots = 0;
+        boot->getNumberSlots(&slots);
+        const auto result = boot->setSlotAsUnbootable(slots);
+        ASSERT_FALSE(result.isOk());
+    }
+}
+
+// validity check Boot::isSlotBootable() on good and bad inputs.
+TEST_P(BootHidlTest, IsSlotBootable) {
+    for (int s = 0; s < 2; s++) {
+        bool bootable = false;
+        const auto res = boot->isSlotBootable(s, &bootable);
+        ASSERT_TRUE(res.isOk()) << res.getMessage();
+    }
+    int32_t slots = 0;
+    boot->getNumberSlots(&slots);
+    bool bootable = false;
+    const auto res = boot->isSlotBootable(slots, &bootable);
+    ASSERT_FALSE(res.isOk());
+}
+
+// validity check Boot::isSlotMarkedSuccessful() on good and bad inputs.
+TEST_P(BootHidlTest, IsSlotMarkedSuccessful) {
+    for (int32_t s = 0; s < 2; s++) {
+        bool isSuccess = false;
+        const auto res = boot->isSlotMarkedSuccessful(s, &isSuccess);
+    }
+    int32_t slots = 0;
+    boot->getNumberSlots(&slots);
+    bool isSuccess = false;
+    const auto res = boot->isSlotMarkedSuccessful(slots, &isSuccess);
+    ASSERT_FALSE(res.isOk());
+}
+
+// validity check Boot::getSuffix() on good and bad inputs.
+TEST_P(BootHidlTest, GetSuffix) {
+    string suffixStr;
+    unordered_set<string> suffixes;
+    int numSlots = 0;
+    boot->getNumberSlots(&numSlots);
+    for (int32_t i = 0; i < numSlots; i++) {
+        std::string suffix;
+        const auto result = boot->getSuffix(i, &suffixStr);
+        ASSERT_TRUE(result.isOk());
+        ASSERT_EQ('_', suffixStr[0]);
+        ASSERT_LE((unsigned)2, suffixStr.size());
+        suffixes.insert(suffixStr);
+    }
+    // All suffixes should be unique
+    ASSERT_EQ(numSlots, suffixes.size());
+    {
+        const string emptySuffix = "";
+        const auto result = boot->getSuffix(numSlots, &suffixStr);
+        ASSERT_TRUE(result.isOk());
+        ASSERT_EQ(suffixStr, emptySuffix);
+    }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+        PerInstance, BootHidlTest,
+        testing::ValuesIn(android::getAidlHalInstanceNames(IBootControl::descriptor)));
diff --git a/broadcastradio/2.0/default/VirtualRadio.cpp b/broadcastradio/2.0/default/VirtualRadio.cpp
index c59fd8f..e6b1017 100644
--- a/broadcastradio/2.0/default/VirtualRadio.cpp
+++ b/broadcastradio/2.0/default/VirtualRadio.cpp
@@ -46,9 +46,9 @@
 VirtualRadio gDabRadio(
     "DAB radio mock",
     {
-        {make_selector_dab(12345, 225648), "BBC Radio 1", "Khalid", "Talk"},  // 12B
-        {make_selector_dab(22345, 222064), "Classic FM", "Jean Sibelius", "Andante Festivo"},  // 11D
-        {make_selector_dab(32345, 222064), "Absolute Radio", "Coldplay", "Clocks"},  // 11D
+        {make_selector_dab(0xA00001u, 0x0001u), "BBC Radio 1", "Khalid", "Talk"},
+        {make_selector_dab(0xB00001u, 0x1001u), "Classic FM", "Jean Sibelius", "Andante Festivo"},
+        {make_selector_dab(0xB00002u, 0x1001u), "Absolute Radio", "Coldplay", "Clocks"},
     });
 // clang-format on
 
diff --git a/broadcastradio/aidl/OWNERS b/broadcastradio/aidl/OWNERS
new file mode 100644
index 0000000..302fdd7
--- /dev/null
+++ b/broadcastradio/aidl/OWNERS
@@ -0,0 +1,4 @@
+xuweilin@google.com
+oscarazu@google.com
+ericjeong@google.com
+keunyoung@google.com
diff --git a/camera/common/1.0/default/HandleImporter.cpp b/camera/common/1.0/default/HandleImporter.cpp
index fbe8686..d2fdf02 100644
--- a/camera/common/1.0/default/HandleImporter.cpp
+++ b/camera/common/1.0/default/HandleImporter.cpp
@@ -18,6 +18,7 @@
 #include "HandleImporter.h"
 
 #include <gralloctypes/Gralloc4.h>
+#include "aidl/android/hardware/graphics/common/Smpte2086.h"
 #include <log/log.h>
 
 namespace android {
@@ -30,6 +31,7 @@
 using aidl::android::hardware::graphics::common::PlaneLayout;
 using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
 using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
+using aidl::android::hardware::graphics::common::Smpte2086;
 using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
 using MapperErrorV2 = android::hardware::graphics::mapper::V2_0::Error;
 using MapperErrorV3 = android::hardware::graphics::mapper::V3_0::Error;
@@ -127,16 +129,35 @@
 bool isMetadataPesent(const sp<IMapperV4> mapper, const buffer_handle_t& buf,
         MetadataType metadataType) {
     auto buffer = const_cast<native_handle_t*>(buf);
-    mapper->get(buffer, metadataType, [] (const auto& tmpError,
+    bool ret = false;
+    hidl_vec<uint8_t> vec;
+    mapper->get(buffer, metadataType, [&] (const auto& tmpError,
                 const auto& tmpMetadata) {
                     if (tmpError == MapperErrorV4::NONE) {
-                        return tmpMetadata.size() > 0;
+                        vec = tmpMetadata;
                     } else {
                         ALOGE("%s: failed to get metadata %d!", __FUNCTION__, tmpError);
-                        return false;
                     }});
 
-    return false;
+    if (vec.size() > 0) {
+            if (metadataType == gralloc4::MetadataType_Smpte2086){
+                std::optional<Smpte2086> realSmpte2086;
+                gralloc4::decodeSmpte2086(vec, &realSmpte2086);
+                ret = realSmpte2086.has_value();
+            } else if (metadataType == gralloc4::MetadataType_Smpte2094_10) {
+                std::optional<std::vector<uint8_t>> realSmpte2094_10;
+                gralloc4::decodeSmpte2094_10(vec, &realSmpte2094_10);
+                ret = realSmpte2094_10.has_value();
+            } else if (metadataType == gralloc4::MetadataType_Smpte2094_40) {
+                std::optional<std::vector<uint8_t>> realSmpte2094_40;
+                gralloc4::decodeSmpte2094_40(vec, &realSmpte2094_40);
+                ret = realSmpte2094_40.has_value();
+            } else {
+                ALOGE("%s: Unknown metadata type!", __FUNCTION__);
+            }
+    }
+
+    return ret;
 }
 
 std::vector<PlaneLayout> getPlaneLayouts(const sp<IMapperV4> mapper, buffer_handle_t& buf) {
diff --git a/camera/provider/aidl/vts/Android.bp b/camera/provider/aidl/vts/Android.bp
index 727ef03..32da2fe 100644
--- a/camera/provider/aidl/vts/Android.bp
+++ b/camera/provider/aidl/vts/Android.bp
@@ -27,6 +27,7 @@
     name: "VtsAidlHalCameraProvider_TargetTest",
     defaults: [
         "VtsHalTargetTestDefaults",
+        "android.hardware.graphics.common-ndk_static",
         "use_libaidlvintf_gtest_helper_static",
     ],
     srcs: [
@@ -61,7 +62,6 @@
         "android.hardware.camera.device-V1-ndk",
         "android.hardware.camera.metadata-V1-ndk",
         "android.hardware.camera.provider-V1-ndk",
-        "android.hardware.graphics.common-V3-ndk",
         "android.hidl.allocator@1.0",
         "libgrallocusage",
         "libhidlmemory",
diff --git a/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp b/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp
index fe03732..70ab7a0 100644
--- a/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp
+++ b/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp
@@ -1739,6 +1739,10 @@
 
         std::list<PixelFormat> pixelFormats = {PixelFormat::YCBCR_420_888, PixelFormat::RAW16};
         for (PixelFormat format : pixelFormats) {
+            previewStream.usage =
+                static_cast<aidl::android::hardware::graphics::common::BufferUsage>(
+                        GRALLOC1_CONSUMER_USAGE_CPU_READ);
+            previewStream.dataSpace = Dataspace::UNKNOWN;
             configureStreams(name, mProvider, format, &mSession, &previewStream, &halStreams,
                              &supportsPartialResults, &partialResultCount, &useHalBufManager, &cb,
                              0, /*maxResolution*/ true);
@@ -1843,7 +1847,6 @@
 TEST_P(CameraAidlTest, process10BitDynamicRangeRequest) {
     std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider);
     int64_t bufferId = 1;
-    int32_t frameNumber = 1;
     CameraMetadata settings;
 
     for (const auto& name : cameraDeviceNames) {
@@ -1866,7 +1869,7 @@
         CameraMetadata req;
         android::hardware::camera::common::V1_0::helper::CameraMetadata defaultSettings;
         ndk::ScopedAStatus ret =
-                mSession->constructDefaultRequestSettings(RequestTemplate::STILL_CAPTURE, &req);
+                mSession->constructDefaultRequestSettings(RequestTemplate::PREVIEW, &req);
         ASSERT_TRUE(ret.isOk());
 
         const camera_metadata_t* metadata =
@@ -1896,6 +1899,10 @@
         Stream previewStream;
         std::shared_ptr<DeviceCb> cb;
         for (const auto& profile : profileList) {
+            previewStream.usage =
+                static_cast<aidl::android::hardware::graphics::common::BufferUsage>(
+                        GRALLOC1_CONSUMER_USAGE_HWCOMPOSER);
+            previewStream.dataSpace = getDataspace(PixelFormat::IMPLEMENTATION_DEFINED);
             configureStreams(name, mProvider, PixelFormat::IMPLEMENTATION_DEFINED, &mSession,
                              &previewStream, &halStreams, &supportsPartialResults,
                              &partialResultCount, &useHalBufManager, &cb, 0,
@@ -1916,63 +1923,75 @@
                 // Don't use the queue onwards.
             }
 
-            std::vector<buffer_handle_t> graphicBuffers;
-            graphicBuffers.reserve(halStreams.size());
+            mInflightMap.clear();
+            // Stream as long as needed to fill the Hal inflight queue
+            std::vector<CaptureRequest> requests(halStreams[0].maxBuffers);
 
-            std::shared_ptr<InFlightRequest> inflightReq = std::make_shared<InFlightRequest>(
-                    static_cast<ssize_t>(halStreams.size()), false, supportsPartialResults,
-                    partialResultCount, std::unordered_set<std::string>(), resultQueue);
+            for (int32_t frameNumber = 0; frameNumber < requests.size(); frameNumber++) {
+                std::shared_ptr<InFlightRequest> inflightReq = std::make_shared<InFlightRequest>(
+                        static_cast<ssize_t>(halStreams.size()), false, supportsPartialResults,
+                        partialResultCount, std::unordered_set<std::string>(), resultQueue);
 
-            std::vector<CaptureRequest> requests(1);
-            CaptureRequest& request = requests[0];
-            std::vector<StreamBuffer>& outputBuffers = request.outputBuffers;
-            outputBuffers.resize(halStreams.size());
+                CaptureRequest& request = requests[frameNumber];
+                std::vector<StreamBuffer>& outputBuffers = request.outputBuffers;
+                outputBuffers.resize(halStreams.size());
 
-            size_t k = 0;
-            for (const auto& halStream : halStreams) {
-                buffer_handle_t buffer_handle;
-                if (useHalBufManager) {
-                    outputBuffers[k] = {halStream.id,   0,
-                                        NativeHandle(), BufferStatus::OK,
-                                        NativeHandle(), NativeHandle()};
-                } else {
-                    allocateGraphicBuffer(previewStream.width, previewStream.height,
-                                          android_convertGralloc1To0Usage(
-                                                  static_cast<uint64_t>(halStream.producerUsage),
-                                                  static_cast<uint64_t>(halStream.consumerUsage)),
-                                          halStream.overrideFormat, &buffer_handle);
+                size_t k = 0;
+                inflightReq->mOutstandingBufferIds.resize(halStreams.size());
+                std::vector<buffer_handle_t> graphicBuffers;
+                graphicBuffers.reserve(halStreams.size());
 
-                    graphicBuffers.push_back(buffer_handle);
-                    outputBuffers[k] = {
-                            halStream.id,     bufferId,       android::makeToAidl(buffer_handle),
-                            BufferStatus::OK, NativeHandle(), NativeHandle()};
-                    bufferId++;
+                for (const auto& halStream : halStreams) {
+                    buffer_handle_t buffer_handle;
+                    if (useHalBufManager) {
+                        outputBuffers[k] = {halStream.id,   0,
+                                            NativeHandle(), BufferStatus::OK,
+                                            NativeHandle(), NativeHandle()};
+                    } else {
+                        auto usage = android_convertGralloc1To0Usage(
+                                static_cast<uint64_t>(halStream.producerUsage),
+                                static_cast<uint64_t>(halStream.consumerUsage));
+                        allocateGraphicBuffer(previewStream.width, previewStream.height, usage,
+                                              halStream.overrideFormat, &buffer_handle);
+
+                        inflightReq->mOutstandingBufferIds[halStream.id][bufferId] = buffer_handle;
+                        graphicBuffers.push_back(buffer_handle);
+                        outputBuffers[k] = {halStream.id, bufferId,
+                            android::makeToAidl(buffer_handle), BufferStatus::OK, NativeHandle(),
+                            NativeHandle()};
+                        bufferId++;
+                    }
+                    k++;
                 }
-                k++;
-            }
 
-            request.inputBuffer = {
-                    -1, 0, NativeHandle(), BufferStatus::ERROR, NativeHandle(), NativeHandle()};
-            request.frameNumber = frameNumber;
-            request.fmqSettingsSize = 0;
-            request.settings = settings;
-            request.inputWidth = 0;
-            request.inputHeight = 0;
+                request.inputBuffer = {
+                        -1, 0, NativeHandle(), BufferStatus::ERROR, NativeHandle(), NativeHandle()};
+                request.frameNumber = frameNumber;
+                request.fmqSettingsSize = 0;
+                request.settings = settings;
+                request.inputWidth = 0;
+                request.inputHeight = 0;
 
-            {
-                std::unique_lock<std::mutex> l(mLock);
-                mInflightMap.clear();
-                mInflightMap[frameNumber] = inflightReq;
+                {
+                    std::unique_lock<std::mutex> l(mLock);
+                    mInflightMap[frameNumber] = inflightReq;
+                }
+
             }
 
             int32_t numRequestProcessed = 0;
             std::vector<BufferCache> cachesToRemove;
             ndk::ScopedAStatus returnStatus =
-                    mSession->processCaptureRequest(requests, cachesToRemove, &numRequestProcessed);
+                mSession->processCaptureRequest(requests, cachesToRemove, &numRequestProcessed);
             ASSERT_TRUE(returnStatus.isOk());
-            ASSERT_EQ(numRequestProcessed, 1u);
+            ASSERT_EQ(numRequestProcessed, requests.size());
 
-            {
+            returnStatus = mSession->repeatingRequestEnd(requests.size() - 1,
+                    std::vector<int32_t> {halStreams[0].id});
+            ASSERT_TRUE(returnStatus.isOk());
+
+            for (int32_t frameNumber = 0; frameNumber < requests.size(); frameNumber++) {
+                const auto& inflightReq = mInflightMap[frameNumber];
                 std::unique_lock<std::mutex> l(mLock);
                 while (!inflightReq->errorCodeValid &&
                        ((0 < inflightReq->numBuffersLeft) || (!inflightReq->haveResultMetadata))) {
@@ -1985,6 +2004,7 @@
                 ASSERT_NE(inflightReq->resultOutputBuffers.size(), 0u);
                 verify10BitMetadata(mHandleImporter, *inflightReq, profile);
             }
+
             if (useHalBufManager) {
                 std::vector<int32_t> streamIds(halStreams.size());
                 for (size_t i = 0; i < streamIds.size(); i++) {
diff --git a/camera/provider/aidl/vts/camera_aidl_test.cpp b/camera/provider/aidl/vts/camera_aidl_test.cpp
index c11fc0c..20f32bf 100644
--- a/camera/provider/aidl/vts/camera_aidl_test.cpp
+++ b/camera/provider/aidl/vts/camera_aidl_test.cpp
@@ -2639,8 +2639,20 @@
 
     outputStreams.clear();
     Size maxSize;
-    auto rc = getMaxOutputSizeForFormat(staticMeta, format, &maxSize, maxResolution);
-    ASSERT_EQ(Status::OK, rc);
+    if (maxResolution) {
+        auto rc = getMaxOutputSizeForFormat(staticMeta, format, &maxSize, maxResolution);
+        ASSERT_EQ(Status::OK, rc);
+    } else {
+        AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
+            static_cast<int32_t>(format)};
+        auto rc = getAvailableOutputStreams(staticMeta, outputStreams, &previewThreshold);
+
+        ASSERT_EQ(Status::OK, rc);
+        ASSERT_FALSE(outputStreams.empty());
+        maxSize.width = outputStreams[0].width;
+        maxSize.height = outputStreams[0].height;
+    }
+
 
     std::vector<Stream> streams(1);
     streams[0] = {0,
@@ -2648,9 +2660,8 @@
                   maxSize.width,
                   maxSize.height,
                   format,
-                  static_cast<::aidl::android::hardware::graphics::common::BufferUsage>(
-                          GRALLOC1_CONSUMER_USAGE_CPU_READ),
-                  Dataspace::UNKNOWN,
+                  previewStream->usage,
+                  previewStream->dataSpace,
                   StreamRotation::ROTATION_0,
                   "",
                   0,
@@ -2736,7 +2747,8 @@
         HandleImporter& importer, const InFlightRequest& request,
         aidl::android::hardware::camera::metadata::RequestAvailableDynamicRangeProfilesMap
                 profile) {
-    for (const auto& b : request.resultOutputBuffers) {
+    for (auto b : request.resultOutputBuffers) {
+        importer.importBuffer(b.buffer.buffer);
         bool smpte2086Present = importer.isSmpte2086Present(b.buffer.buffer);
         bool smpte2094_10Present = importer.isSmpte2094_10Present(b.buffer.buffer);
         bool smpte2094_40Present = importer.isSmpte2094_40Present(b.buffer.buffer);
@@ -2753,7 +2765,6 @@
                 ASSERT_FALSE(smpte2094_40Present);
                 break;
             case ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10_PLUS:
-                ASSERT_FALSE(smpte2086Present);
                 ASSERT_FALSE(smpte2094_10Present);
                 ASSERT_TRUE(smpte2094_40Present);
                 break;
@@ -2774,6 +2785,7 @@
                       profile);
                 ADD_FAILURE();
         }
+        importer.freeBuffer(b.buffer.buffer);
     }
 }
 
diff --git a/camera/provider/aidl/vts/camera_aidl_test.h b/camera/provider/aidl/vts/camera_aidl_test.h
index ac4b2c9..d828cee 100644
--- a/camera/provider/aidl/vts/camera_aidl_test.h
+++ b/camera/provider/aidl/vts/camera_aidl_test.h
@@ -399,6 +399,10 @@
         // Result metadata
         ::android::hardware::camera::common::V1_0::helper::CameraMetadata collectedResult;
 
+        // Inflight buffers
+        using OutstandingBuffers = std::unordered_map<uint64_t, buffer_handle_t>;
+        std::vector<OutstandingBuffers> mOutstandingBufferIds;
+
         // A copy-able StreamBuffer using buffer_handle_t instead of AIDLs NativeHandle
         struct NativeStreamBuffer {
             int32_t streamId;
diff --git a/camera/provider/aidl/vts/device_cb.cpp b/camera/provider/aidl/vts/device_cb.cpp
index e5f2f1e..4698b4a 100644
--- a/camera/provider/aidl/vts/device_cb.cpp
+++ b/camera/provider/aidl/vts/device_cb.cpp
@@ -155,7 +155,7 @@
                     BufferStatus::OK, NativeHandle(), NativeHandle(),
             };
 
-            mOutstandingBufferIds[idx][mNextBufferId++] = ::android::dupToAidl(handle);
+            mOutstandingBufferIds[idx][mNextBufferId++] = handle;
         }
         atLeastOneStreamOk = true;
         bufRets[i].streamId = stream.id;
@@ -427,9 +427,13 @@
         }
 
         CameraAidlTest::InFlightRequest::StreamBufferAndTimestamp streamBufferAndTimestamp;
+        auto outstandingBuffers = mUseHalBufManager ? mOutstandingBufferIds :
+            request->mOutstandingBufferIds;
+        auto outputBuffer = outstandingBuffers.empty() ? ::android::makeFromAidl(buffer.buffer) :
+            outstandingBuffers[buffer.streamId][buffer.bufferId];
         streamBufferAndTimestamp.buffer = {buffer.streamId,
                                            buffer.bufferId,
-                                           ::android::makeFromAidl(buffer.buffer),
+                                           outputBuffer,
                                            buffer.status,
                                            ::android::makeFromAidl(buffer.acquireFence),
                                            ::android::makeFromAidl(buffer.releaseFence)};
diff --git a/camera/provider/aidl/vts/device_cb.h b/camera/provider/aidl/vts/device_cb.h
index 82ca10d..3ae7d10 100644
--- a/camera/provider/aidl/vts/device_cb.h
+++ b/camera/provider/aidl/vts/device_cb.h
@@ -73,7 +73,7 @@
     std::vector<Stream> mStreams;
     std::vector<HalStream> mHalStreams;
     int64_t mNextBufferId = 1;
-    using OutstandingBuffers = std::unordered_map<uint64_t, NativeHandle>;
+    using OutstandingBuffers = std::unordered_map<uint64_t, buffer_handle_t>;
     // size == mStreams.size(). Tracking each streams outstanding buffers
     std::vector<OutstandingBuffers> mOutstandingBufferIds;
     std::condition_variable mFlushedCondition;
diff --git a/common/aidl/Android.bp b/common/aidl/Android.bp
index 549d970..43f8c4d 100644
--- a/common/aidl/Android.bp
+++ b/common/aidl/Android.bp
@@ -30,7 +30,7 @@
         ndk: {
             apex_available: [
                 "//apex_available:platform",
-                "com.android.bluetooth",
+                "com.android.btservices",
                 "com.android.media.swcodec",
                 "com.android.neuralnetworks",
             ],
diff --git a/common/fmq/aidl/Android.bp b/common/fmq/aidl/Android.bp
index 6fd4200..a85597c 100644
--- a/common/fmq/aidl/Android.bp
+++ b/common/fmq/aidl/Android.bp
@@ -32,7 +32,7 @@
         ndk: {
             apex_available: [
                 "//apex_available:platform",
-                "com.android.bluetooth",
+                "com.android.btservices",
             ],
             min_sdk_version: "29",
         },
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 07be769..16b61fe 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -16,7 +16,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.audio.effect</name>
         <version>6.0</version>
         <version>7.0</version>
@@ -38,6 +38,14 @@
         </interface>
     </hal>
     <hal format="aidl" optional="true">
+        <name>android.hardware.audio.effect</name>
+        <version>1</version>
+        <interface>
+            <name>IFactory</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
          <name>android.hardware.authsecret</name>
          <version>1</version>
          <interface>
@@ -104,22 +112,6 @@
             <regex-instance>.*</regex-instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.automotive.vehicle</name>
-        <version>2.0</version>
-        <interface>
-            <name>IVehicle</name>
-            <regex-instance>.*</regex-instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.biometrics.face</name>
-        <version>1.0</version>
-        <interface>
-            <name>IBiometricsFace</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.biometrics.face</name>
         <version>2</version>
@@ -128,14 +120,6 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.biometrics.fingerprint</name>
-        <version>2.1-3</version>
-        <interface>
-            <name>IBiometricsFingerprint</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.biometrics.fingerprint</name>
         <version>2</version>
@@ -161,14 +145,6 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.boot</name>
-        <version>1.2</version>
-        <interface>
-            <name>IBootControl</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.boot</name>
         <interface>
@@ -239,18 +215,6 @@
             <regex-instance>.*</regex-instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.drm</name>
-        <version>1.3-4</version>
-        <interface>
-            <name>ICryptoFactory</name>
-            <regex-instance>.*</regex-instance>
-        </interface>
-        <interface>
-            <name>IDrmFactory</name>
-            <regex-instance>.*</regex-instance>
-        </interface>
-    </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.dumpstate</name>
         <interface>
@@ -266,14 +230,6 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.gnss</name>
-        <version>2.0-1</version>
-        <interface>
-            <name>IGnss</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.gnss</name>
         <version>2</version>
@@ -298,21 +254,6 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <!-- Either the AIDL or the HIDL allocator HAL must exist on the device.
-         If the HIDL composer HAL exists, it must be at least version 2.0.
-         See DeviceManifestTest.GrallocHal -->
-    <hal format="hidl" optional="true">
-        <name>android.hardware.graphics.allocator</name>
-        <!-- New, non-Go devices should use 4.0 or the AIDL hal.
-             See DeviceManifestTest.GrallocVersionCompatibility. -->
-        <version>2.0</version>
-        <version>3.0</version>
-        <version>4.0</version>
-        <interface>
-            <name>IAllocator</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.graphics.allocator</name>
         <version>1</version>
@@ -321,17 +262,6 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <!-- Either the AIDL or the HIDL composer HAL must exist on the device.
-         If the HIDL composer HAL exists, it must be at least version 2.1.
-         See DeviceManifestTest.ComposerHal -->
-    <hal format="hidl" optional="true">
-        <name>android.hardware.graphics.composer</name>
-        <version>2.1-4</version>
-        <interface>
-            <name>IComposer</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.graphics.composer3</name>
         <version>1</version>
@@ -406,23 +336,6 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.keymaster</name>
-        <version>3.0</version>
-        <version>4.0-1</version>
-        <interface>
-            <name>IKeymasterDevice</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.keymaster</name>
-        <version>4.0-1</version>
-        <interface>
-            <name>IKeymasterDevice</name>
-            <instance>strongbox</instance>
-        </interface>
-    </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.security.dice</name>
         <version>1</version>
@@ -502,14 +415,6 @@
             <regex-instance>.*</regex-instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.nfc</name>
-        <version>1.2</version>
-        <interface>
-            <name>INfc</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.nfc</name>
         <interface>
@@ -683,23 +588,6 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.sensors</name>
-        <version>1.0</version>
-        <version>2.0-1</version>
-        <interface>
-            <name>ISensors</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.soundtrigger</name>
-        <version>2.3</version>
-        <interface>
-            <name>ISoundTriggerHw</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
     <hal format="aidl" optional="true">
          <name>android.hardware.soundtrigger3</name>
          <version>1</version>
@@ -748,14 +636,6 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.tv.tuner</name>
-        <version>1.0-1</version>
-        <interface>
-            <name>ITuner</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.tv.tuner</name>
         <version>1</version>
@@ -764,11 +644,11 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.usb</name>
-        <version>1.0-3</version>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.tv.input</name>
+        <version>1</version>
         <interface>
-            <name>IUsb</name>
+            <name>ITvInput</name>
             <instance>default</instance>
         </interface>
     </hal>
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/1.0/vts/functional/VtsHalGatekeeperV1_0TargetTest.cpp b/gatekeeper/1.0/vts/functional/VtsHalGatekeeperV1_0TargetTest.cpp
index f3fa0b4..e938b01 100644
--- a/gatekeeper/1.0/vts/functional/VtsHalGatekeeperV1_0TargetTest.cpp
+++ b/gatekeeper/1.0/vts/functional/VtsHalGatekeeperV1_0TargetTest.cpp
@@ -244,6 +244,47 @@
 }
 
 /**
+ * Ensure that passwords containing a NUL byte aren't truncated
+ */
+TEST_P(GatekeeperHidlTest, PasswordIsBinaryData) {
+  GatekeeperResponse enrollRsp;
+  GatekeeperResponse verifyRsp;
+  hidl_vec<uint8_t> rightPassword = {'A', 'B', 'C', '\0', 'D', 'E', 'F'};
+  hidl_vec<uint8_t> wrongPassword = {'A', 'B', 'C', '\0', '\0', '\0', '\0'};
+
+  ALOGI("Testing Enroll+Verify of password with embedded NUL (expected success)");
+  enrollNewPassword(rightPassword, enrollRsp, true);
+  verifyPassword(rightPassword, enrollRsp.data, 1, verifyRsp, true);
+
+  ALOGI("Testing Verify of wrong password (expected failure)");
+  verifyPassword(wrongPassword, enrollRsp.data, 1, verifyRsp, false);
+
+  ALOGI("PasswordIsBinaryData test done");
+}
+
+/**
+ * Ensure that long passwords aren't truncated
+ */
+TEST_P(GatekeeperHidlTest, LongPassword) {
+  GatekeeperResponse enrollRsp;
+  GatekeeperResponse verifyRsp;
+  hidl_vec<uint8_t> password;
+
+  password.resize(64); // maximum length used by Android
+  memset(password.data(), 'A', password.size());
+
+  ALOGI("Testing Enroll+Verify of long password (expected success)");
+  enrollNewPassword(password, enrollRsp, true);
+  verifyPassword(password, enrollRsp.data, 1, verifyRsp, true);
+
+  ALOGI("Testing Verify of wrong password (expected failure)");
+  password[password.size() - 1] ^= 1;
+  verifyPassword(password, enrollRsp.data, 1, verifyRsp, false);
+
+  ALOGI("LongPassword test done");
+}
+
+/**
  * Ensure we can securely update password (keep the same
  * secure user_id) if we prove we know old password
  */
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/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/bufferqueue/1.0/Android.bp b/graphics/bufferqueue/1.0/Android.bp
index 11559b2..82c71f1 100644
--- a/graphics/bufferqueue/1.0/Android.bp
+++ b/graphics/bufferqueue/1.0/Android.bp
@@ -27,7 +27,7 @@
     gen_java: true,
     apex_available: [
         "//apex_available:platform",
-        "com.android.bluetooth",
+        "com.android.btservices",
         "com.android.media",
         "com.android.media.swcodec",
     ],
diff --git a/graphics/bufferqueue/2.0/Android.bp b/graphics/bufferqueue/2.0/Android.bp
index 552daff..3067e24 100644
--- a/graphics/bufferqueue/2.0/Android.bp
+++ b/graphics/bufferqueue/2.0/Android.bp
@@ -29,7 +29,7 @@
     gen_java: true,
     apex_available: [
         "//apex_available:platform",
-        "com.android.bluetooth",
+        "com.android.btservices",
         "com.android.media",
         "com.android.media.swcodec",
     ],
diff --git a/graphics/common/1.0/Android.bp b/graphics/common/1.0/Android.bp
index 19c51cd..3288583 100644
--- a/graphics/common/1.0/Android.bp
+++ b/graphics/common/1.0/Android.bp
@@ -23,7 +23,7 @@
     gen_java_constants: true,
     apex_available: [
         "//apex_available:platform",
-        "com.android.bluetooth",
+        "com.android.btservices",
         "com.android.media.swcodec",
         "test_com.android.media.swcodec",
     ],
diff --git a/graphics/common/1.1/Android.bp b/graphics/common/1.1/Android.bp
index 0f1b5bf..5d07eae 100644
--- a/graphics/common/1.1/Android.bp
+++ b/graphics/common/1.1/Android.bp
@@ -26,7 +26,7 @@
     gen_java_constants: true,
     apex_available: [
         "//apex_available:platform",
-        "com.android.bluetooth",
+        "com.android.btservices",
         "com.android.media.swcodec",
         "test_com.android.media.swcodec",
     ],
diff --git a/graphics/common/1.2/Android.bp b/graphics/common/1.2/Android.bp
index ce3350d..4aa4af5 100644
--- a/graphics/common/1.2/Android.bp
+++ b/graphics/common/1.2/Android.bp
@@ -27,7 +27,7 @@
     gen_java_constants: true,
     apex_available: [
         "//apex_available:platform",
-        "com.android.bluetooth",
+        "com.android.btservices",
         "com.android.media.swcodec",
         "test_com.android.media.swcodec",
     ],
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/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.2/utils/vts/Android.bp b/graphics/composer/2.2/utils/vts/Android.bp
index cca5323..478e2b7 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,10 +39,8 @@
         "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",
         "android.hardware.graphics.mapper@2.1-vts",
         "libarect",
         "libgtest",
@@ -53,7 +55,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/utils/vts/RenderEngineVts.cpp b/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp
index 4a33fb5..1700b2a 100644
--- a/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp
+++ b/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp
@@ -71,14 +71,12 @@
                    });
     auto texture = std::make_shared<renderengine::impl::ExternalTexture>(
             mGraphicBuffer, *mRenderEngine, renderengine::impl::ExternalTexture::Usage::WRITEABLE);
-    auto [status, readyFence] = mRenderEngine
-                                        ->drawLayers(mDisplaySettings, compositionLayers, texture,
-                                                     true, std::move(bufferFence))
-                                        .get();
-    int fd = readyFence.release();
-    if (fd != -1) {
-        ASSERT_EQ(0, sync_wait(fd, -1));
-        ASSERT_EQ(0, close(fd));
+    auto result = mRenderEngine
+                          ->drawLayers(mDisplaySettings, compositionLayers, texture, true,
+                                       std::move(bufferFence))
+                          .get();
+    if (result.ok()) {
+        result.value()->waitForever(LOG_TAG);
     }
 }
 
diff --git a/graphics/composer/2.2/vts/functional/Android.bp b/graphics/composer/2.2/vts/functional/Android.bp
index 960b62d..c693d35 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",
@@ -68,7 +69,6 @@
         "android.hardware.graphics.composer@2.1-vts",
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.composer@2.2-vts",
-        "android.hardware.graphics.composer3-V1-ndk",
         "android.hardware.graphics.mapper@2.0-vts",
         "android.hardware.graphics.mapper@2.1-vts",
         "android.hardware.graphics.mapper@3.0-vts",
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.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/aidl/Android.bp b/graphics/composer/aidl/Android.bp
index 9b88d98..a5ca0a0 100644
--- a/graphics/composer/aidl/Android.bp
+++ b/graphics/composer/aidl/Android.bp
@@ -67,9 +67,9 @@
 
 cc_library_headers {
     name: "android.hardware.graphics.composer3-command-buffer",
+    defaults: ["android.hardware.graphics.composer3-ndk_shared"],
     vendor_available: true,
     shared_libs: [
-        "android.hardware.graphics.composer3-V1-ndk",
         "android.hardware.common-V2-ndk",
         "libbase",
         "libfmq",
@@ -87,12 +87,12 @@
 
 cc_test {
     name: "android.hardware.graphics.composer3-hidl2aidl-asserts",
+    defaults: ["android.hardware.graphics.composer3-ndk_shared"],
     vendor_available: true,
     srcs: ["android/hardware/graphics/composer3/Hidl2AidlAsserts.cpp"],
     shared_libs: [
         "libbinder_ndk",
         "libhidlbase",
-        "android.hardware.graphics.composer3-V1-ndk",
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.composer@2.4",
     ],
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/ChangedCompositionTypes.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/ChangedCompositionTypes.aidl
index ddd45b7..9e8c768 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/ChangedCompositionTypes.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/ChangedCompositionTypes.aidl
@@ -23,7 +23,6 @@
 parcelable ChangedCompositionTypes {
     /**
      * The display which this commands refers to.
-     * @see IComposer.createDisplay
      */
     long display;
 
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.aidl
index ba6fe97..ea54a89 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.aidl
@@ -23,7 +23,6 @@
 parcelable ClientTargetPropertyWithBrightness {
     /**
      * The display which this commands refers to.
-     * @see IComposer.createDisplay
      */
     long display;
 
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayCommand.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayCommand.aidl
index b6df147..4f69aee 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayCommand.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayCommand.aidl
@@ -26,7 +26,6 @@
 parcelable DisplayCommand {
     /**
      * The display which this commands refers to.
-     * @see IComposer.createDisplay
      */
     long display;
 
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayRequest.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayRequest.aidl
index 27fe1e6..3a50b6e 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayRequest.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayRequest.aidl
@@ -34,7 +34,6 @@
 
     /**
      * The display which this commands refers to.
-     * @see IComposer.createDisplay
      */
     long display;
 
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/PresentFence.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/PresentFence.aidl
index 244b4e5..b757656 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/PresentFence.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/PresentFence.aidl
@@ -20,7 +20,6 @@
 parcelable PresentFence {
     /**
      * The display which this commands refers to.
-     * @see IComposer.createDisplay
      */
     long display;
 
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/PresentOrValidate.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/PresentOrValidate.aidl
index 5ae8940..e15dbf2 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/PresentOrValidate.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/PresentOrValidate.aidl
@@ -20,7 +20,6 @@
 parcelable PresentOrValidate {
     /**
      * The display which this commands refers to.
-     * @see IComposer.createDisplay
      */
     long display;
 
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/ReleaseFences.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/ReleaseFences.aidl
index 459a042..58649d5 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/ReleaseFences.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/ReleaseFences.aidl
@@ -20,7 +20,6 @@
 parcelable ReleaseFences {
     /**
      * The display which this commands refers to.
-     * @see IComposer.createDisplay
      */
     long display;
     @VintfStability
diff --git a/graphics/composer/aidl/vts/Android.bp b/graphics/composer/aidl/vts/Android.bp
index 1e70a0e..cbd2da5 100644
--- a/graphics/composer/aidl/vts/Android.bp
+++ b/graphics/composer/aidl/vts/Android.bp
@@ -30,6 +30,8 @@
         "use_libaidlvintf_gtest_helper_static",
         // Needed for librenderengine
         "skia_deps",
+        "android.hardware.graphics.common-ndk_static",
+        "android.hardware.graphics.composer3-ndk_static",
     ],
     srcs: [
         "VtsHalGraphicsComposer3_TargetTest.cpp",
@@ -67,8 +69,6 @@
         "android.hardware.graphics.composer3-command-buffer",
     ],
     static_libs: [
-        "android.hardware.graphics.composer3-V1-ndk",
-        "android.hardware.graphics.common-V3-ndk",
         "android.hardware.graphics.common@1.2",
         "android.hardware.common-V2-ndk",
         "android.hardware.common.fmq-V1-ndk",
diff --git a/graphics/composer/aidl/vts/RenderEngineVts.cpp b/graphics/composer/aidl/vts/RenderEngineVts.cpp
index 71b011c..66779c8 100644
--- a/graphics/composer/aidl/vts/RenderEngineVts.cpp
+++ b/graphics/composer/aidl/vts/RenderEngineVts.cpp
@@ -66,14 +66,12 @@
     auto texture = std::make_shared<::android::renderengine::impl::ExternalTexture>(
             mGraphicBuffer, *mRenderEngine,
             ::android::renderengine::impl::ExternalTexture::Usage::WRITEABLE);
-    auto [status, readyFence] = mRenderEngine
-                                        ->drawLayers(mDisplaySettings, compositionLayers, texture,
-                                                     true, std::move(bufferFence))
-                                        .get();
-    int fd = readyFence.release();
-    if (fd != -1) {
-        ASSERT_EQ(0, sync_wait(fd, -1));
-        ASSERT_EQ(0, close(fd));
+    auto result = mRenderEngine
+                          ->drawLayers(mDisplaySettings, compositionLayers, texture, true,
+                                       std::move(bufferFence))
+                          .get();
+    if (result.ok()) {
+        result.value()->waitForever(LOG_TAG);
     }
 }
 
diff --git a/graphics/composer/aidl/vts/VtsComposerClient.cpp b/graphics/composer/aidl/vts/VtsComposerClient.cpp
index 2b60703..5bc7296 100644
--- a/graphics/composer/aidl/vts/VtsComposerClient.cpp
+++ b/graphics/composer/aidl/vts/VtsComposerClient.cpp
@@ -488,10 +488,13 @@
 }
 
 bool VtsComposerClient::destroyAllLayers() {
-    for (const auto& it : mDisplayResources) {
-        const auto& [display, resource] = it;
+    std::unordered_map<int64_t, DisplayResource> physicalDisplays;
+    while (!mDisplayResources.empty()) {
+        const auto& it = mDisplayResources.begin();
+        const auto& [display, resource] = *it;
 
-        for (auto layer : resource.layers) {
+        while (!resource.layers.empty()) {
+            auto layer = *resource.layers.begin();
             const auto status = destroyLayer(display, layer);
             if (!status.isOk()) {
                 ALOGE("Unable to destroy all the layers, failed at layer %" PRId64 " with error %s",
@@ -507,8 +510,12 @@
                       status.getDescription().c_str());
                 return false;
             }
+        } else {
+            auto extractIter = mDisplayResources.extract(it);
+            physicalDisplays.insert(std::move(extractIter));
         }
     }
+    mDisplayResources.swap(physicalDisplays);
     mDisplayResources.clear();
     return true;
 }
diff --git a/graphics/mapper/2.0/utils/passthrough/include/mapper-passthrough/2.0/Gralloc1Hal.h b/graphics/mapper/2.0/utils/passthrough/include/mapper-passthrough/2.0/Gralloc1Hal.h
index db7e67d..5f0a176 100644
--- a/graphics/mapper/2.0/utils/passthrough/include/mapper-passthrough/2.0/Gralloc1Hal.h
+++ b/graphics/mapper/2.0/utils/passthrough/include/mapper-passthrough/2.0/Gralloc1Hal.h
@@ -259,19 +259,22 @@
 
         for (int i = 0; i < 3; i++) {
             const auto& plane = flex.planes[i];
-            // must have 8-bit depth
-            if (plane.bits_per_component != 8 || plane.bits_used != 8) {
+            // Must be a positive multiple of 8.
+            if (plane.bits_per_component <= 0 || (plane.bits_per_component % 8) != 0) {
                 return false;
             }
-
+            // Must be between 1 and bits_per_component, inclusive.
+            if (plane.bits_used < 1 || plane.bits_used > plane.bits_per_component) {
+                return false;
+            }
             if (plane.component == FLEX_COMPONENT_Y) {
                 // Y must not be interleaved
-                if (plane.h_increment != 1) {
+                if (plane.h_increment != 1 && plane.h_increment != 2) {
                     return false;
                 }
             } else {
                 // Cb and Cr can be interleaved
-                if (plane.h_increment != 1 && plane.h_increment != 2) {
+                if (plane.h_increment != 1 && plane.h_increment != 2 && plane.h_increment != 4) {
                     return false;
                 }
             }
diff --git a/graphics/mapper/4.0/utils/vts/Android.bp b/graphics/mapper/4.0/utils/vts/Android.bp
index 7abf5db..55e721e 100644
--- a/graphics/mapper/4.0/utils/vts/Android.bp
+++ b/graphics/mapper/4.0/utils/vts/Android.bp
@@ -25,16 +25,18 @@
 
 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",
         "libaidlcommonsupport",
     ],
@@ -44,7 +46,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/functional/Android.bp b/graphics/mapper/4.0/vts/functional/Android.bp
index e830633..6208ae9 100644
--- a/graphics/mapper/4.0/vts/functional/Android.bp
+++ b/graphics/mapper/4.0/vts/functional/Android.bp
@@ -27,18 +27,18 @@
     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"],
     static_libs: [
-        "android.hardware.graphics.common-V3-ndk",
         "android.hardware.graphics.mapper@4.0-vts",
         "libaidlcommonsupport",
         "libgralloctypes",
         "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/health/aidl/default/Android.bp b/health/aidl/default/Android.bp
index 0d426da..4eb3cb1 100644
--- a/health/aidl/default/Android.bp
+++ b/health/aidl/default/Android.bp
@@ -192,33 +192,14 @@
     name: "android.hardware.health-service.aidl_fuzzer",
     defaults: [
         "libhealth_aidl_impl_user",
+        "service_fuzzer_defaults",
     ],
     static_libs: [
         "android.hardware.health-V1-ndk",
         "libbase",
-        "libbinder_random_parcel",
-        "libcutils",
         "liblog",
-        "libutils",
         "fuzz_libhealth_aidl_impl",
     ],
-    target: {
-        android: {
-            shared_libs: [
-                "libbinder_ndk",
-                "libbinder",
-            ],
-        },
-        host: {
-            static_libs: [
-                "libbinder_ndk",
-                "libbinder",
-            ],
-        },
-        darwin: {
-            enabled: false,
-        },
-    },
     srcs: ["fuzzer.cpp"],
     fuzz_config: {
         cc: [
diff --git a/identity/aidl/Android.bp b/identity/aidl/Android.bp
index f6855e8..57451ed 100644
--- a/identity/aidl/Android.bp
+++ b/identity/aidl/Android.bp
@@ -59,3 +59,27 @@
     ],
 
 }
+
+// cc_defaults that includes the latest Identity AIDL library.
+// Modules that depend on Identity directly can include this cc_defaults to
+// avoid managing dependency versions explicitly.
+cc_defaults {
+    name: "identity_use_latest_hal_aidl_ndk_static",
+    static_libs: [
+        "android.hardware.identity-V4-ndk",
+    ],
+}
+
+cc_defaults {
+    name: "identity_use_latest_hal_aidl_ndk_shared",
+    shared_libs: [
+        "android.hardware.identity-V4-ndk",
+    ],
+}
+
+cc_defaults {
+    name: "identity_use_latest_hal_aidl_cpp_static",
+    static_libs: [
+        "android.hardware.identity-V4-cpp",
+    ],
+}
diff --git a/identity/aidl/default/EicOpsImpl.cc b/identity/aidl/default/EicOpsImpl.cc
index 3fd9f1d..b6d324f 100644
--- a/identity/aidl/default/EicOpsImpl.cc
+++ b/identity/aidl/default/EicOpsImpl.cc
@@ -100,6 +100,7 @@
     if (size != EIC_SHA256_DIGEST_SIZE) {
         LOG(ERROR) << "Expected 32 bytes from HMAC_Final, got " << size;
     }
+    HMAC_CTX_cleanup(realCtx);
 }
 
 void eicOpsSha256Init(EicSha256Ctx* ctx) {
@@ -394,14 +395,17 @@
     }
 
     if (BN_bn2binpad(sig->r, signature, 32) != 32) {
+        ECDSA_SIG_free(sig);
         eicDebug("Error encoding r");
         return false;
     }
     if (BN_bn2binpad(sig->s, signature + 32, 32) != 32) {
+        ECDSA_SIG_free(sig);
         eicDebug("Error encoding s");
         return false;
     }
 
+    ECDSA_SIG_free(sig);
     return true;
 }
 
diff --git a/light/aidl/default/Lights.h b/light/aidl/default/Lights.h
index 8cba5a1..cba147f 100644
--- a/light/aidl/default/Lights.h
+++ b/light/aidl/default/Lights.h
@@ -23,10 +23,10 @@
 namespace hardware {
 namespace light {
 
-// Default implementation that reports no supported lights.
+// Default implementation that reports a few placeholder lights.
 class Lights : public BnLights {
     ndk::ScopedAStatus setLightState(int id, const HwLightState& state) override;
-    ndk::ScopedAStatus getLights(std::vector<HwLight>* types) override;
+    ndk::ScopedAStatus getLights(std::vector<HwLight>* lights) override;
 };
 
 }  // namespace light
diff --git a/neuralnetworks/1.0/vts/functional/ValidateModel.cpp b/neuralnetworks/1.0/vts/functional/ValidateModel.cpp
index 5ffbd43..34bc962 100644
--- a/neuralnetworks/1.0/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.0/vts/functional/ValidateModel.cpp
@@ -232,26 +232,24 @@
 //     currently 1Mb, which is shared by all transactions in progress
 //     for the process."
 //
-// Will our representation fit under this limit?  There are two complications:
+// Will our representation fit under this limit?  There are three complications:
 // - Our representation size is just approximate (see sizeForBinder()).
-// - This object may not be the only occupant of the Binder transaction buffer.
+// - This object may not be the only occupant of the Binder transaction buffer
+//   (although our VTS test suite should not be putting multiple objects in the
+//   buffer at once).
+// - IBinder.MAX_IPC_SIZE recommends limiting a transaction to 64 * 1024 bytes.
 // So we'll be very conservative: We want the representation size to be no
-// larger than half the transaction buffer size.
+// larger than half the recommended limit.
 //
 // If our representation grows large enough that it still fits within
 // the transaction buffer but combined with other transactions may
 // exceed the buffer size, then we may see intermittent HAL transport
 // errors.
 static bool exceedsBinderSizeLimit(size_t representationSize) {
-    // Instead of using this fixed buffer size, we might instead be able to use
-    // ProcessState::self()->getMmapSize(). However, this has a potential
-    // problem: The binder/mmap size of the current process does not necessarily
-    // indicate the binder/mmap size of the service (i.e., the other process).
-    // The only way it would be a good indication is if both the current process
-    // and the service use the default size.
-    static const size_t kHalfBufferSize = 1024 * 1024 / 2;
+    // There is no C++ API to retrieve the value of the Java variable IBinder.MAX_IPC_SIZE.
+    static const size_t kHalfMaxIPCSize = 64 * 1024 / 2;
 
-    return representationSize > kHalfBufferSize;
+    return representationSize > kHalfMaxIPCSize;
 }
 
 ///////////////////////// VALIDATE EXECUTION ORDER ////////////////////////////
diff --git a/neuralnetworks/1.1/vts/functional/ValidateModel.cpp b/neuralnetworks/1.1/vts/functional/ValidateModel.cpp
index 1f4e4ed..dbabbaf 100644
--- a/neuralnetworks/1.1/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.1/vts/functional/ValidateModel.cpp
@@ -252,26 +252,24 @@
 //     currently 1Mb, which is shared by all transactions in progress
 //     for the process."
 //
-// Will our representation fit under this limit?  There are two complications:
+// Will our representation fit under this limit?  There are three complications:
 // - Our representation size is just approximate (see sizeForBinder()).
-// - This object may not be the only occupant of the Binder transaction buffer.
+// - This object may not be the only occupant of the Binder transaction buffer
+//   (although our VTS test suite should not be putting multiple objects in the
+//   buffer at once).
+// - IBinder.MAX_IPC_SIZE recommends limiting a transaction to 64 * 1024 bytes.
 // So we'll be very conservative: We want the representation size to be no
-// larger than half the transaction buffer size.
+// larger than half the recommended limit.
 //
 // If our representation grows large enough that it still fits within
 // the transaction buffer but combined with other transactions may
 // exceed the buffer size, then we may see intermittent HAL transport
 // errors.
 static bool exceedsBinderSizeLimit(size_t representationSize) {
-    // Instead of using this fixed buffer size, we might instead be able to use
-    // ProcessState::self()->getMmapSize(). However, this has a potential
-    // problem: The binder/mmap size of the current process does not necessarily
-    // indicate the binder/mmap size of the service (i.e., the other process).
-    // The only way it would be a good indication is if both the current process
-    // and the service use the default size.
-    static const size_t kHalfBufferSize = 1024 * 1024 / 2;
+    // There is no C++ API to retrieve the value of the Java variable IBinder.MAX_IPC_SIZE.
+    static const size_t kHalfMaxIPCSize = 64 * 1024 / 2;
 
-    return representationSize > kHalfBufferSize;
+    return representationSize > kHalfMaxIPCSize;
 }
 
 ///////////////////////// VALIDATE EXECUTION ORDER ////////////////////////////
diff --git a/neuralnetworks/1.2/vts/functional/ValidateModel.cpp b/neuralnetworks/1.2/vts/functional/ValidateModel.cpp
index 3375602..d7cd6f5 100644
--- a/neuralnetworks/1.2/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.2/vts/functional/ValidateModel.cpp
@@ -291,26 +291,24 @@
 //     currently 1Mb, which is shared by all transactions in progress
 //     for the process."
 //
-// Will our representation fit under this limit?  There are two complications:
+// Will our representation fit under this limit?  There are three complications:
 // - Our representation size is just approximate (see sizeForBinder()).
-// - This object may not be the only occupant of the Binder transaction buffer.
+// - This object may not be the only occupant of the Binder transaction buffer
+//   (although our VTS test suite should not be putting multiple objects in the
+//   buffer at once).
+// - IBinder.MAX_IPC_SIZE recommends limiting a transaction to 64 * 1024 bytes.
 // So we'll be very conservative: We want the representation size to be no
-// larger than half the transaction buffer size.
+// larger than half the recommended limit.
 //
 // If our representation grows large enough that it still fits within
 // the transaction buffer but combined with other transactions may
 // exceed the buffer size, then we may see intermittent HAL transport
 // errors.
 static bool exceedsBinderSizeLimit(size_t representationSize) {
-    // Instead of using this fixed buffer size, we might instead be able to use
-    // ProcessState::self()->getMmapSize(). However, this has a potential
-    // problem: The binder/mmap size of the current process does not necessarily
-    // indicate the binder/mmap size of the service (i.e., the other process).
-    // The only way it would be a good indication is if both the current process
-    // and the service use the default size.
-    static const size_t kHalfBufferSize = 1024 * 1024 / 2;
+    // There is no C++ API to retrieve the value of the Java variable IBinder.MAX_IPC_SIZE.
+    static const size_t kHalfMaxIPCSize = 64 * 1024 / 2;
 
-    return representationSize > kHalfBufferSize;
+    return representationSize > kHalfMaxIPCSize;
 }
 
 ///////////////////////// VALIDATE EXECUTION ORDER ////////////////////////////
diff --git a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
index 849ef7b..d8c7cd1 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
@@ -308,26 +308,24 @@
 //     currently 1Mb, which is shared by all transactions in progress
 //     for the process."
 //
-// Will our representation fit under this limit?  There are two complications:
+// Will our representation fit under this limit?  There are three complications:
 // - Our representation size is just approximate (see sizeForBinder()).
-// - This object may not be the only occupant of the Binder transaction buffer.
+// - This object may not be the only occupant of the Binder transaction buffer
+//   (although our VTS test suite should not be putting multiple objects in the
+//   buffer at once).
+// - IBinder.MAX_IPC_SIZE recommends limiting a transaction to 64 * 1024 bytes.
 // So we'll be very conservative: We want the representation size to be no
-// larger than half the transaction buffer size.
+// larger than half the recommended limit.
 //
 // If our representation grows large enough that it still fits within
 // the transaction buffer but combined with other transactions may
 // exceed the buffer size, then we may see intermittent HAL transport
 // errors.
 static bool exceedsBinderSizeLimit(size_t representationSize) {
-    // Instead of using this fixed buffer size, we might instead be able to use
-    // ProcessState::self()->getMmapSize(). However, this has a potential
-    // problem: The binder/mmap size of the current process does not necessarily
-    // indicate the binder/mmap size of the service (i.e., the other process).
-    // The only way it would be a good indication is if both the current process
-    // and the service use the default size.
-    static const size_t kHalfBufferSize = 1024 * 1024 / 2;
+    // There is no C++ API to retrieve the value of the Java variable IBinder.MAX_IPC_SIZE.
+    static const size_t kHalfMaxIPCSize = 64 * 1024 / 2;
 
-    return representationSize > kHalfBufferSize;
+    return representationSize > kHalfMaxIPCSize;
 }
 
 ///////////////////////// VALIDATE EXECUTION ORDER ////////////////////////////
diff --git a/neuralnetworks/aidl/utils/Android.bp b/neuralnetworks/aidl/utils/Android.bp
index 3258092..f0b458a 100644
--- a/neuralnetworks/aidl/utils/Android.bp
+++ b/neuralnetworks/aidl/utils/Android.bp
@@ -25,7 +25,10 @@
 
 cc_defaults {
     name: "neuralnetworks_utils_hal_aidl_defaults",
-    defaults: ["neuralnetworks_utils_defaults"],
+    defaults: [
+        "android.hardware.graphics.common-ndk_static",
+        "neuralnetworks_utils_defaults",
+    ],
     srcs: [
         // AIDL utils that a driver may depend on.
         "src/BufferTracker.cpp",
@@ -38,7 +41,6 @@
     export_include_dirs: ["include"],
     cflags: ["-Wthread-safety"],
     static_libs: [
-        "android.hardware.graphics.common-V3-ndk",
         "libaidlcommonsupport",
         "libarect",
         "neuralnetworks_types",
@@ -90,9 +92,9 @@
 // AIDL features can include this cc_defaults to avoid managing dependency versions explicitly.
 cc_defaults {
     name: "neuralnetworks_use_latest_utils_hal_aidl",
+    defaults: ["android.hardware.graphics.common-ndk_static"],
     static_libs: [
         "android.hardware.common-V2-ndk",
-        "android.hardware.graphics.common-V3-ndk",
         "android.hardware.neuralnetworks-V4-ndk",
         "neuralnetworks_utils_hal_aidl",
     ],
diff --git a/neuralnetworks/aidl/vts/functional/ValidateModel.cpp b/neuralnetworks/aidl/vts/functional/ValidateModel.cpp
index 060434e..d7baf19 100644
--- a/neuralnetworks/aidl/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/aidl/vts/functional/ValidateModel.cpp
@@ -344,26 +344,24 @@
 //     currently 1Mb, which is shared by all transactions in progress
 //     for the process."
 //
-// Will our representation fit under this limit?  There are two complications:
+// Will our representation fit under this limit?  There are three complications:
 // - Our representation size is just approximate (see sizeForBinder()).
-// - This object may not be the only occupant of the Binder transaction buffer.
+// - This object may not be the only occupant of the Binder transaction buffer
+//   (although our VTS test suite should not be putting multiple objects in the
+//   buffer at once).
+// - IBinder.MAX_IPC_SIZE recommends limiting a transaction to 64 * 1024 bytes.
 // So we'll be very conservative: We want the representation size to be no
-// larger than half the transaction buffer size.
+// larger than half the recommended limit.
 //
 // If our representation grows large enough that it still fits within
 // the transaction buffer but combined with other transactions may
 // exceed the buffer size, then we may see intermittent HAL transport
 // errors.
 static bool exceedsBinderSizeLimit(size_t representationSize) {
-    // Instead of using this fixed buffer size, we might instead be able to use
-    // ProcessState::self()->getMmapSize(). However, this has a potential
-    // problem: The binder/mmap size of the current process does not necessarily
-    // indicate the binder/mmap size of the service (i.e., the other process).
-    // The only way it would be a good indication is if both the current process
-    // and the service use the default size.
-    static const size_t kHalfBufferSize = 1024 * 1024 / 2;
+    // There is no C++ API to retrieve the value of the Java variable IBinder.MAX_IPC_SIZE.
+    static const size_t kHalfMaxIPCSize = 64 * 1024 / 2;
 
-    return representationSize > kHalfBufferSize;
+    return representationSize > kHalfMaxIPCSize;
 }
 
 ///////////////////////// VALIDATE EXECUTION ORDER ////////////////////////////
diff --git a/power/OWNERS b/power/OWNERS
new file mode 100644
index 0000000..7229b22
--- /dev/null
+++ b/power/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 826709
+
+# ADPF virtual team
+lpy@google.com
+wvw@google.com
diff --git a/radio/1.0/Android.bp b/radio/1.0/Android.bp
index 8d0d782..e49a50d 100644
--- a/radio/1.0/Android.bp
+++ b/radio/1.0/Android.bp
@@ -25,7 +25,7 @@
     ],
     apex_available: [
         "//apex_available:platform",
-        "com.android.bluetooth",
+        "com.android.btservices",
     ],
     gen_java: true,
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/AmrMode.aidl b/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/AmrMode.aidl
index dad6d0a..5179169 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/AmrMode.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/AmrMode.aidl
@@ -34,13 +34,13 @@
 package android.hardware.radio.ims.media;
 @Backing(type="int") @VintfStability
 enum AmrMode {
-  AMR_MODE_0 = 0,
-  AMR_MODE_1 = 1,
-  AMR_MODE_2 = 2,
-  AMR_MODE_3 = 3,
-  AMR_MODE_4 = 4,
-  AMR_MODE_5 = 5,
-  AMR_MODE_6 = 6,
-  AMR_MODE_7 = 7,
-  AMR_MODE_8 = 8,
+  AMR_MODE_0 = 1 << 0,
+  AMR_MODE_1 = 1 << 1,
+  AMR_MODE_2 = 1 << 2,
+  AMR_MODE_3 = 1 << 3,
+  AMR_MODE_4 = 1 << 4,
+  AMR_MODE_5 = 1 << 5,
+  AMR_MODE_6 = 1 << 6,
+  AMR_MODE_7 = 1 << 7,
+  AMR_MODE_8 = 1 << 8,
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/AnbrBitrate.aidl b/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/AnbrBitrate.aidl
index 598d995..711ac19 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/AnbrBitrate.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/AnbrBitrate.aidl
@@ -34,7 +34,7 @@
 package android.hardware.radio.ims.media;
 @VintfStability
 parcelable AnbrBitrate {
-    const int INVALID_ANBR_BITRATE = -1;
-    int uplinkBps;
-    int downlinkBps;
-}
\ No newline at end of file
+  int uplinkBps;
+  int downlinkBps;
+  const int INVALID_ANBR_BITRATE = -1;
+}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/EvsMode.aidl b/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/EvsMode.aidl
index 6b56d5d..a067357 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/EvsMode.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/EvsMode.aidl
@@ -34,25 +34,25 @@
 package android.hardware.radio.ims.media;
 @Backing(type="int") @VintfStability
 enum EvsMode {
-  EVS_MODE_0 = 0,
-  EVS_MODE_1 = 1,
-  EVS_MODE_2 = 2,
-  EVS_MODE_3 = 3,
-  EVS_MODE_4 = 4,
-  EVS_MODE_5 = 5,
-  EVS_MODE_6 = 6,
-  EVS_MODE_7 = 7,
-  EVS_MODE_8 = 8,
-  EVS_MODE_9 = 9,
-  EVS_MODE_10 = 10,
-  EVS_MODE_11 = 11,
-  EVS_MODE_12 = 12,
-  EVS_MODE_13 = 13,
-  EVS_MODE_14 = 14,
-  EVS_MODE_15 = 15,
-  EVS_MODE_16 = 16,
-  EVS_MODE_17 = 17,
-  EVS_MODE_18 = 18,
-  EVS_MODE_19 = 19,
-  EVS_MODE_20 = 20,
+  EVS_MODE_0 = 1 << 0,
+  EVS_MODE_1 = 1 << 1,
+  EVS_MODE_2 = 1 << 2,
+  EVS_MODE_3 = 1 << 3,
+  EVS_MODE_4 = 1 << 4,
+  EVS_MODE_5 = 1 << 5,
+  EVS_MODE_6 = 1 << 6,
+  EVS_MODE_7 = 1 << 7,
+  EVS_MODE_8 = 1 << 8,
+  EVS_MODE_9 = 1 << 9,
+  EVS_MODE_10 = 1 << 10,
+  EVS_MODE_11 = 1 << 11,
+  EVS_MODE_12 = 1 << 12,
+  EVS_MODE_13 = 1 << 13,
+  EVS_MODE_14 = 1 << 14,
+  EVS_MODE_15 = 1 << 15,
+  EVS_MODE_16 = 1 << 16,
+  EVS_MODE_17 = 1 << 17,
+  EVS_MODE_18 = 1 << 18,
+  EVS_MODE_19 = 1 << 19,
+  EVS_MODE_20 = 1 << 20,
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/EvsParams.aidl b/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/EvsParams.aidl
index 3363123..bdd0a4b 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/EvsParams.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/EvsParams.aidl
@@ -37,7 +37,6 @@
   android.hardware.radio.ims.media.EvsBandwidth bandwidth;
   android.hardware.radio.ims.media.EvsMode evsMode;
   byte channelAwareMode;
-  boolean useHeaderFullOnlyOnTx;
-  boolean useHeaderFullOnlyOnRx;
+  boolean useHeaderFullOnly;
   boolean useEvsModeSwitch;
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/IImsMediaListener.aidl b/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/IImsMediaListener.aidl
index efd59c3..40f7107 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/IImsMediaListener.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/IImsMediaListener.aidl
@@ -36,4 +36,5 @@
 interface IImsMediaListener {
   oneway void onOpenSessionSuccess(int sessionId, android.hardware.radio.ims.media.IImsMediaSession session);
   oneway void onOpenSessionFailure(int sessionId, android.hardware.radio.ims.media.RtpError error);
+  oneway void onSessionClosed(int sessionId);
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/IImsMediaSession.aidl b/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/IImsMediaSession.aidl
index 38d9d02..7fab555 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/IImsMediaSession.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/IImsMediaSession.aidl
@@ -40,6 +40,8 @@
   oneway void deleteConfig(in android.hardware.radio.ims.media.RtpConfig config);
   oneway void confirmConfig(in android.hardware.radio.ims.media.RtpConfig config);
   oneway void sendDtmf(char dtmfDigit, int duration);
+  oneway void startDtmf(char dtmfDigit);
+  oneway void stopDtmf();
   oneway void sendHeaderExtension(in List<android.hardware.radio.ims.media.RtpHeaderExtension> extensions);
   oneway void setMediaQualityThreshold(in android.hardware.radio.ims.media.MediaQualityThreshold threshold);
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/SignalThresholdInfo.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/SignalThresholdInfo.aidl
index 040932c..744eed7 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/SignalThresholdInfo.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/SignalThresholdInfo.aidl
@@ -48,4 +48,5 @@
   const int SIGNAL_MEASUREMENT_TYPE_SSRSRP = 6;
   const int SIGNAL_MEASUREMENT_TYPE_SSRSRQ = 7;
   const int SIGNAL_MEASUREMENT_TYPE_SSSINR = 8;
+  const int SIGNAL_MEASUREMENT_TYPE_ECNO = 9;
 }
diff --git a/radio/aidl/android/hardware/radio/ims/media/AmrMode.aidl b/radio/aidl/android/hardware/radio/ims/media/AmrMode.aidl
index e9065b3..66d8ef0 100644
--- a/radio/aidl/android/hardware/radio/ims/media/AmrMode.aidl
+++ b/radio/aidl/android/hardware/radio/ims/media/AmrMode.aidl
@@ -21,21 +21,21 @@
 @Backing(type="int")
 enum AmrMode {
     /** 4.75 kbps for AMR / 6.6 kbps for AMR-WB */
-    AMR_MODE_0 = 0,
+    AMR_MODE_0 = 1 << 0,
     /** 5.15 kbps for AMR / 8.855 kbps for AMR-WB */
-    AMR_MODE_1 = 1,
+    AMR_MODE_1 = 1 << 1,
     /** 5.9 kbps for AMR / 12.65 kbps for AMR-WB */
-    AMR_MODE_2 = 2,
+    AMR_MODE_2 = 1 << 2,
     /** 6.7 kbps for AMR / 14.25 kbps for AMR-WB */
-    AMR_MODE_3 = 3,
+    AMR_MODE_3 = 1 << 3,
     /** 7.4 kbps for AMR / 15.85 kbps for AMR-WB */
-    AMR_MODE_4 = 4,
+    AMR_MODE_4 = 1 << 4,
     /** 7.95 kbps for AMR / 18.25 kbps for AMR-WB */
-    AMR_MODE_5 = 5,
+    AMR_MODE_5 = 1 << 5,
     /** 10.2 kbps for AMR / 19.85 kbps for AMR-WB */
-    AMR_MODE_6 = 6,
+    AMR_MODE_6 = 1 << 6,
     /** 12.2 kbps for AMR / 23.05 kbps for AMR-WB */
-    AMR_MODE_7 = 7,
+    AMR_MODE_7 = 1 << 7,
     /** Silence frame for AMR / 23.85 kbps for AMR-WB */
-    AMR_MODE_8 = 8,
+    AMR_MODE_8 = 1 << 8,
 }
diff --git a/radio/aidl/android/hardware/radio/ims/media/EvsMode.aidl b/radio/aidl/android/hardware/radio/ims/media/EvsMode.aidl
index fabec5e..95bd6c7 100644
--- a/radio/aidl/android/hardware/radio/ims/media/EvsMode.aidl
+++ b/radio/aidl/android/hardware/radio/ims/media/EvsMode.aidl
@@ -21,45 +21,45 @@
 @Backing(type="int")
 enum EvsMode {
     /** 6.6 kbps for EVS AMR-WB IO */
-    EVS_MODE_0 = 0,
+    EVS_MODE_0 = 1 << 0,
     /** 8.855 kbps for AMR-WB IO */
-    EVS_MODE_1 = 1,
+    EVS_MODE_1 = 1 << 1,
     /** 12.65 kbps for AMR-WB IO */
-    EVS_MODE_2 = 2,
+    EVS_MODE_2 = 1 << 2,
     /** 14.25 kbps for AMR-WB IO */
-    EVS_MODE_3 = 3,
+    EVS_MODE_3 = 1 << 3,
     /** 15.85 kbps for AMR-WB IO */
-    EVS_MODE_4 = 4,
+    EVS_MODE_4 = 1 << 4,
     /** 18.25 kbps for AMR-WB IO */
-    EVS_MODE_5 = 5,
+    EVS_MODE_5 = 1 << 5,
     /** 19.85 kbps for AMR-WB IO */
-    EVS_MODE_6 = 6,
+    EVS_MODE_6 = 1 << 6,
     /** 23.05 kbps for AMR-WB IO */
-    EVS_MODE_7 = 7,
+    EVS_MODE_7 = 1 << 7,
     /** 23.85 kbps for AMR-WB IO */
-    EVS_MODE_8 = 8,
+    EVS_MODE_8 = 1 << 8,
     /** 5.9 kbps for EVS primary */
-    EVS_MODE_9 = 9,
+    EVS_MODE_9 = 1 << 9,
     /** 7.2 kbps for EVS primary */
-    EVS_MODE_10 = 10,
+    EVS_MODE_10 = 1 << 10,
     /** 8.0 kbps for EVS primary */
-    EVS_MODE_11 = 11,
+    EVS_MODE_11 = 1 << 11,
     /** 9.6 kbps for EVS primary */
-    EVS_MODE_12 = 12,
+    EVS_MODE_12 = 1 << 12,
     /** 13.2 kbps for EVS primary */
-    EVS_MODE_13 = 13,
+    EVS_MODE_13 = 1 << 13,
     /** 16.4 kbps for EVS primary */
-    EVS_MODE_14 = 14,
+    EVS_MODE_14 = 1 << 14,
     /** 24.4 kbps for EVS primary */
-    EVS_MODE_15 = 15,
+    EVS_MODE_15 = 1 << 15,
     /** 32.0 kbps for EVS primary */
-    EVS_MODE_16 = 16,
+    EVS_MODE_16 = 1 << 16,
     /** 48.0 kbps for EVS primary */
-    EVS_MODE_17 = 17,
+    EVS_MODE_17 = 1 << 17,
     /** 64.0 kbps for EVS primary */
-    EVS_MODE_18 = 18,
+    EVS_MODE_18 = 1 << 18,
     /** 96.0 kbps for EVS primary */
-    EVS_MODE_19 = 19,
+    EVS_MODE_19 = 1 << 19,
     /** 128.0 kbps for EVS primary */
-    EVS_MODE_20 = 20,
+    EVS_MODE_20 = 1 << 20,
 }
diff --git a/radio/aidl/android/hardware/radio/ims/media/EvsParams.aidl b/radio/aidl/android/hardware/radio/ims/media/EvsParams.aidl
index 4a4d71b2..7b420c8 100644
--- a/radio/aidl/android/hardware/radio/ims/media/EvsParams.aidl
+++ b/radio/aidl/android/hardware/radio/ims/media/EvsParams.aidl
@@ -37,17 +37,11 @@
      */
     byte channelAwareMode;
     /**
-     * hf-only: Header full only is used for the outgoing packets. If it's true then
-     * the session shall support header full format only else the session could
-     * support both header full format and compact format.
+     * hf-only: Header full only is used for the outgoing and incoming packets.
+     * If it's true then the session shall support header full format only else the
+     * session could support both header full format and compact format.
      */
-    boolean useHeaderFullOnlyOnTx;
-    /**
-     * hf-only: Header full only used on the incoming packets. If it's true then the
-     * session shall support header full format only else the session could support
-     * both header full format and compact format.
-     */
-    boolean useHeaderFullOnlyOnRx;
+    boolean useHeaderFullOnly;
     /**
      * evs-mode-switch: Used for switching between EVS Primary mode and EVS AMR-WB IO mode,
      * If this value is true, the codec operates in AMR-WB IO mode
diff --git a/radio/aidl/android/hardware/radio/ims/media/IImsMediaListener.aidl b/radio/aidl/android/hardware/radio/ims/media/IImsMediaListener.aidl
index 4078ea2..228acb7 100644
--- a/radio/aidl/android/hardware/radio/ims/media/IImsMediaListener.aidl
+++ b/radio/aidl/android/hardware/radio/ims/media/IImsMediaListener.aidl
@@ -24,7 +24,6 @@
  */
 @VintfStability
 oneway interface IImsMediaListener {
-
     /**
      * Fired when a IImsMedia#openSession() API is successful.
      *
@@ -45,5 +44,12 @@
      *   RtpError :NO_RESOURCES
      *   RtpError :PORT_UNAVAILABLE
      */
-     void onOpenSessionFailure(int sessionId, RtpError error);
+    void onOpenSessionFailure(int sessionId, RtpError error);
+
+    /**
+     * Fired when IImsMedia#closeSession() API handled.
+     *
+     * @param sessionId identifier of the session
+     */
+    void onSessionClosed(int sessionId);
 }
diff --git a/radio/aidl/android/hardware/radio/ims/media/IImsMediaSession.aidl b/radio/aidl/android/hardware/radio/ims/media/IImsMediaSession.aidl
index 8c73bbc..d291f44 100644
--- a/radio/aidl/android/hardware/radio/ims/media/IImsMediaSession.aidl
+++ b/radio/aidl/android/hardware/radio/ims/media/IImsMediaSession.aidl
@@ -83,6 +83,21 @@
     void sendDtmf(char dtmfDigit, int duration);
 
     /**
+     * Start sending DTMF digit until the stopDtmf() API is received.
+     * If the implementation is currently sending a DTMF tone for which
+     * stopDtmf() is not received yet, then that digit must be stopped first
+     *
+     * @param dtmfDigit single char having one of 12 values: 0-9, *, #
+     */
+    void startDtmf(char dtmfDigit);
+
+    /**
+     * Stop sending the last DTMF digit started by startDtmf().
+     * stopDtmf() without preceding startDtmf() must be ignored.
+     */
+    void stopDtmf();
+
+    /**
      * Send RTP header extension to the other party in the next RTP packet.
      *
      * @param extensions data to be transmitted via RTP header extension
diff --git a/radio/aidl/android/hardware/radio/network/SignalThresholdInfo.aidl b/radio/aidl/android/hardware/radio/network/SignalThresholdInfo.aidl
index 2f90180..0a8e9ce 100644
--- a/radio/aidl/android/hardware/radio/network/SignalThresholdInfo.aidl
+++ b/radio/aidl/android/hardware/radio/network/SignalThresholdInfo.aidl
@@ -82,7 +82,13 @@
      * Reference: 3GPP TS 38.215 section 5.1.*, 3GPP TS 38.133 section 10.1.16.1.
      */
     const int SIGNAL_MEASUREMENT_TYPE_SSSINR = 8;
-
+    /**
+     * EcNo value
+     * Range: -24 dBm to 1 dBm.
+     * Used RAN: UTRAN
+     * Reference: 3GPP TS 25.215 5.1.5
+     */
+    const int SIGNAL_MEASUREMENT_TYPE_ECNO = 9;
     /**
      * Signal Measurement Type
      * Values are SIGNAL_MEASUREMENT_TYPE_
diff --git a/radio/aidl/compat/libradiocompat/data/structs.cpp b/radio/aidl/compat/libradiocompat/data/structs.cpp
index 4ff89a1..cc6dcbc 100644
--- a/radio/aidl/compat/libradiocompat/data/structs.cpp
+++ b/radio/aidl/compat/libradiocompat/data/structs.cpp
@@ -28,14 +28,14 @@
 
 V1_5::DataProfileInfo toHidl(const aidl::DataProfileInfo& info) {
     return {
-            .profileId = V1_0::DataProfileId{info.profileId},
+            .profileId = static_cast<V1_0::DataProfileId>(info.profileId),
             .apn = info.apn,
-            .protocol = V1_4::PdpProtocolType{info.protocol},
-            .roamingProtocol = V1_4::PdpProtocolType{info.roamingProtocol},
-            .authType = V1_0::ApnAuthType{info.authType},
+            .protocol = static_cast<V1_4::PdpProtocolType>(info.protocol),
+            .roamingProtocol = static_cast<V1_4::PdpProtocolType>(info.roamingProtocol),
+            .authType = static_cast<V1_0::ApnAuthType>(info.authType),
             .user = info.user,
             .password = info.password,
-            .type = V1_0::DataProfileInfoType{info.type},
+            .type = static_cast<V1_0::DataProfileInfoType>(info.type),
             .maxConnsTime = info.maxConnsTime,
             .maxConns = info.maxConns,
             .waitTime = info.waitTime,
@@ -74,7 +74,7 @@
             .sliceDifferentiator = info.sliceDifferentiator,
             .mappedHplmnSst = static_cast<V1_6::SliceServiceType>(info.mappedHplmnSst),
             .mappedHplmnSD = info.mappedHplmnSd,
-            .status = V1_6::SliceStatus{info.status},
+            .status = static_cast<V1_6::SliceStatus>(info.status),
     };
 }
 
@@ -106,7 +106,7 @@
 
 V1_1::KeepaliveRequest toHidl(const aidl::KeepaliveRequest& keep) {
     return {
-            .type = V1_1::KeepaliveType{keep.type},
+            .type = static_cast<V1_1::KeepaliveType>(keep.type),
             .sourceAddress = keep.sourceAddress,
             .sourcePort = keep.sourcePort,
             .destinationAddress = keep.destinationAddress,
diff --git a/radio/aidl/compat/libradiocompat/messaging/structs.cpp b/radio/aidl/compat/libradiocompat/messaging/structs.cpp
index 9019680..f30c5ce 100644
--- a/radio/aidl/compat/libradiocompat/messaging/structs.cpp
+++ b/radio/aidl/compat/libradiocompat/messaging/structs.cpp
@@ -45,11 +45,11 @@
 
 static V1_0::CdmaSmsAddress toHidl(const aidl::CdmaSmsAddress& addr) {
     return {
-            .digitMode = V1_0::CdmaSmsDigitMode{addr.digitMode},
+            .digitMode = static_cast<V1_0::CdmaSmsDigitMode>(addr.digitMode),
             .numberMode = addr.isNumberModeDataNetwork ? V1_0::CdmaSmsNumberMode::DATA_NETWORK
                                                        : V1_0::CdmaSmsNumberMode::NOT_DATA_NETWORK,
-            .numberType = V1_0::CdmaSmsNumberType{addr.numberType},
-            .numberPlan = V1_0::CdmaSmsNumberPlan{addr.numberPlan},
+            .numberType = static_cast<V1_0::CdmaSmsNumberType>(addr.numberType),
+            .numberPlan = static_cast<V1_0::CdmaSmsNumberPlan>(addr.numberPlan),
             .digits = addr.digits,
     };
 }
@@ -64,7 +64,7 @@
 
 static V1_0::CdmaSmsSubaddress toHidl(const aidl::CdmaSmsSubaddress& addr) {
     return {
-            .subaddressType = V1_0::CdmaSmsSubaddressType{addr.subaddressType},
+            .subaddressType = static_cast<V1_0::CdmaSmsSubaddressType>(addr.subaddressType),
             .odd = addr.odd,
             .digits = addr.digits,
     };
@@ -94,7 +94,7 @@
 
 V1_0::ImsSmsMessage toHidl(const aidl::ImsSmsMessage& msg) {
     return {
-            .tech = V1_0::RadioTechnologyFamily{msg.tech},
+            .tech = static_cast<V1_0::RadioTechnologyFamily>(msg.tech),
             .retry = msg.retry,
             .messageRef = msg.messageRef,
             .cdmaMessage = toHidl(msg.cdmaMessage),
@@ -147,14 +147,14 @@
 
 V1_0::CdmaSmsWriteArgs toHidl(const aidl::CdmaSmsWriteArgs& args) {
     return {
-            .status = V1_0::CdmaSmsWriteArgsStatus{args.status},
+            .status = static_cast<V1_0::CdmaSmsWriteArgsStatus>(args.status),
             .message = toHidl(args.message),
     };
 }
 
 V1_0::SmsWriteArgs toHidl(const aidl::SmsWriteArgs& args) {
     return {
-            .status = V1_0::SmsWriteArgsStatus{args.status},
+            .status = static_cast<V1_0::SmsWriteArgsStatus>(args.status),
             .pdu = args.pdu,
             .smsc = args.smsc,
     };
diff --git a/radio/aidl/compat/libradiocompat/modem/structs.cpp b/radio/aidl/compat/libradiocompat/modem/structs.cpp
index 69e651b..6f32cdf 100644
--- a/radio/aidl/compat/libradiocompat/modem/structs.cpp
+++ b/radio/aidl/compat/libradiocompat/modem/structs.cpp
@@ -30,7 +30,7 @@
 
 V1_0::NvWriteItem toHidl(const aidl::NvWriteItem& item) {
     return {
-            .itemId = V1_0::NvItem{item.itemId},
+            .itemId = static_cast<V1_0::NvItem>(item.itemId),
             .value = item.value,
     };
 }
@@ -48,10 +48,10 @@
 V1_0::RadioCapability toHidl(const aidl::RadioCapability& capa) {
     return {
             .session = capa.session,
-            .phase = V1_0::RadioCapabilityPhase{capa.phase},
+            .phase = static_cast<V1_0::RadioCapabilityPhase>(capa.phase),
             .raf = toHidlBitfield<V1_0::RadioAccessFamily>(capa.raf),
             .logicalModemUuid = capa.logicalModemUuid,
-            .status = V1_0::RadioCapabilityStatus{capa.status},
+            .status = static_cast<V1_0::RadioCapabilityStatus>(capa.status),
     };
 }
 
diff --git a/radio/aidl/compat/libradiocompat/network/structs.cpp b/radio/aidl/compat/libradiocompat/network/structs.cpp
index d0b3b90..30d4f6d 100644
--- a/radio/aidl/compat/libradiocompat/network/structs.cpp
+++ b/radio/aidl/compat/libradiocompat/network/structs.cpp
@@ -66,7 +66,7 @@
 
 V1_5::SignalThresholdInfo toHidl(const aidl::SignalThresholdInfo& info) {
     return {
-            .signalMeasurement = V1_5::SignalMeasurementType{info.signalMeasurement},
+            .signalMeasurement = static_cast<V1_5::SignalMeasurementType>(info.signalMeasurement),
             .hysteresisMs = info.hysteresisMs,
             .hysteresisDb = info.hysteresisDb,
             .thresholds = info.thresholds,
@@ -155,7 +155,7 @@
 
 V1_5::NetworkScanRequest toHidl(const aidl::NetworkScanRequest& req) {
     return {
-            .type = V1_1::ScanType{req.type},
+            .type = static_cast<V1_1::ScanType>(req.type),
             .interval = req.interval,
             .specifiers = toHidl(req.specifiers),
             .maxSearchTime = req.maxSearchTime,
diff --git a/radio/aidl/compat/libradiocompat/sim/structs.cpp b/radio/aidl/compat/libradiocompat/sim/structs.cpp
index bfbff02..00db2b8 100644
--- a/radio/aidl/compat/libradiocompat/sim/structs.cpp
+++ b/radio/aidl/compat/libradiocompat/sim/structs.cpp
@@ -65,7 +65,7 @@
     return {
             .mcc = carrier.mcc,
             .mnc = carrier.mnc,
-            .matchType = V1_0::CarrierMatchType{carrier.matchType},
+            .matchType = static_cast<V1_0::CarrierMatchType>(carrier.matchType),
             .matchData = carrier.matchData,
     };
 }
@@ -107,7 +107,7 @@
 V1_6::ImsiEncryptionInfo toHidl_1_6(const aidl::ImsiEncryptionInfo& info) {
     return {
             .base = toHidl(info),
-            .keyType = V1_6::PublicKeyType{info.keyType},
+            .keyType = static_cast<V1_6::PublicKeyType>(info.keyType),
     };
 }
 
diff --git a/radio/aidl/compat/libradiocompat/voice/structs.cpp b/radio/aidl/compat/libradiocompat/voice/structs.cpp
index 254ea20..35c8d46 100644
--- a/radio/aidl/compat/libradiocompat/voice/structs.cpp
+++ b/radio/aidl/compat/libradiocompat/voice/structs.cpp
@@ -29,15 +29,15 @@
 V1_0::Dial toHidl(const aidl::Dial& info) {
     return {
             .address = info.address,
-            .clir = V1_0::Clir{info.clir},
+            .clir = static_cast<V1_0::Clir>(info.clir),
             .uusInfo = toHidl(info.uusInfo),
     };
 }
 
 V1_0::UusInfo toHidl(const aidl::UusInfo& info) {
     return {
-            .uusType = V1_0::UusType{info.uusType},
-            .uusDcs = V1_0::UusDcs{info.uusDcs},
+            .uusType = static_cast<V1_0::UusType>(info.uusType),
+            .uusDcs = static_cast<V1_0::UusDcs>(info.uusDcs),
             .uusData = info.uusData,
     };
 }
@@ -55,7 +55,7 @@
 
 V1_0::CallForwardInfo toHidl(const aidl::CallForwardInfo& info) {
     return {
-            .status = V1_0::CallForwardInfoStatus{info.status},
+            .status = static_cast<V1_0::CallForwardInfoStatus>(info.status),
             .reason = info.reason,
             .serviceClass = info.serviceClass,
             .toa = info.toa,
diff --git a/security/keymint/RKP_CHANGELOG.md b/security/keymint/RKP_CHANGELOG.md
index 67d68d4..dfcc938 100644
--- a/security/keymint/RKP_CHANGELOG.md
+++ b/security/keymint/RKP_CHANGELOG.md
@@ -6,13 +6,28 @@
 ## Releases
 * **Android S (12):** IRemotelyProvisionedComponent v1
 * **Android T (13):** IRemotelyProvisionedComponent v2
+* **Android U (14):** IRemotelyProvisionedComponent v3
 
 ## IRemotelyProvisionedComponent 1 -> 2
 * DeviceInfo
- * Most entries are no longer optional.
- * `att_id_state` is now `fused`. `fused` is used to indicate if SecureBoot is enabled.
- * `version` is now `2`.
- * `board` has been removed.
- * `device` has been added.
+  * Most entries are no longer optional.
+  * `att_id_state` is now `fused`. `fused` is used to indicate if SecureBoot is enabled.
+  * `version` is now `2`.
+  * `board` has been removed.
+  * `device` has been added.
 * RpcHardwareInfo
- * `uniqueId` String added as a field in order to differentiate IRPC instances on device.
\ No newline at end of file
+  * `uniqueId` String added as a field in order to differentiate IRPC instances on device.
+
+## IRemotelyProvisionedComponent 2 -> 3
+* ProtectedData has been removed.
+* DeviceInfo
+  * `cert_type` has been added, with values corresponding to `widevine` or `keymint`
+  * `version` has moved to a top-level field within the CSR generated by the HAL
+* IRemotelyProvisionedComponent
+  * The need for an EEK has been removed. There is no longer an encrypted portion of the CSR.
+  * Test mode has been removed.
+  * The schema for the CSR itself has been significantly simplified, please see
+    IRemotelyProvisionedComponent.aidl for more details.
+    * Notably, the chain of signing, MACing, and encryption operations has been replaced with a single
+      COSE_Sign1 object.
+
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
index f566462..626ece8 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
@@ -38,9 +38,11 @@
   android.hardware.security.keymint.RpcHardwareInfo getHardwareInfo();
   byte[] generateEcdsaP256KeyPair(in boolean testMode, out android.hardware.security.keymint.MacedPublicKey macedPublicKey);
   byte[] generateCertificateRequest(in boolean testMode, in android.hardware.security.keymint.MacedPublicKey[] keysToSign, in byte[] endpointEncryptionCertChain, in byte[] challenge, out android.hardware.security.keymint.DeviceInfo deviceInfo, out android.hardware.security.keymint.ProtectedData protectedData);
+  byte[] generateCertificateRequestV2(in android.hardware.security.keymint.MacedPublicKey[] keysToSign, in byte[] challenge);
   const int STATUS_FAILED = 1;
   const int STATUS_INVALID_MAC = 2;
   const int STATUS_PRODUCTION_KEY_IN_TEST_REQUEST = 3;
   const int STATUS_TEST_KEY_IN_PRODUCTION_REQUEST = 4;
   const int STATUS_INVALID_EEK = 5;
+  const int STATUS_REMOVED = 6;
 }
diff --git a/security/keymint/aidl/android/hardware/security/keymint/DeviceInfo.aidl b/security/keymint/aidl/android/hardware/security/keymint/DeviceInfo.aidl
index abb2a7b..6954d65 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/DeviceInfo.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/DeviceInfo.aidl
@@ -37,19 +37,19 @@
      *         "product" : tstr,
      *         "model" : tstr,
      *         "device" : tstr,
-     *         "vb_state" : "green" / "yellow" / "orange",    // Taken from the AVB values
-     *         "bootloader_state" : "locked" / "unlocked",    // Taken from the AVB values
-     *         "vbmeta_digest": bstr,                         // Taken from the AVB values
-     *         ? "os_version" : tstr,                         // Same as
-     *                                                        // android.os.Build.VERSION.release
-     *                                                        // Not optional for TEE.
-     *         "system_patch_level" : uint,                   // YYYYMMDD
-     *         "boot_patch_level" : uint,                     // YYYYMMDD
-     *         "vendor_patch_level" : uint,                   // YYYYMMDD
-     *         "version" : 2,                                 // The CDDL schema version.
+     *         "vb_state" : "green" / "yellow" / "orange",    ; Taken from the AVB values
+     *         "bootloader_state" : "locked" / "unlocked",    ; Taken from the AVB values
+     *         "vbmeta_digest": bstr,                         ; Taken from the AVB values
+     *         ? "os_version" : tstr,                         ; Same as
+     *                                                        ; android.os.Build.VERSION.release
+     *                                                        ; Not optional for TEE.
+     *         "system_patch_level" : uint,                   ; YYYYMMDD
+     *         "boot_patch_level" : uint,                     ; YYYYMMDD
+     *         "vendor_patch_level" : uint,                   ; YYYYMMDD
      *         "security_level" : "tee" / "strongbox",
-     *         "fused": 1 / 0,  // 1 if secure boot is enforced for the processor that the IRPC
-     *                          // implementation is contained in. 0 otherwise.
+     *         "fused": 1 / 0,  ; 1 if secure boot is enforced for the processor that the IRPC
+     *                          ; implementation is contained in. 0 otherwise.
+     *         "cert_type": "widevine" / "keymint"
      *     }
      */
     byte[] deviceInfo;
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl b/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
index a29fb08..c2acbed 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
@@ -31,26 +31,31 @@
  * This interface does not provide any way to use the generated and certified key pairs. It's
  * intended to be implemented by a HAL service that does other things with keys (e.g. Keymint).
  *
- * The root of trust for secure provisioning is something called the "Boot Certificate Chain", or
- * BCC. The BCC is a chain of public key certificates, represented as COSE_Sign1 objects containing
- * COSE_Key representations of the public keys. The "root" of the BCC is
- * a device-unique public key, denoted DK_pub. All public keys in the BCC are device-unique. The
- * public key from each certificate in the chain is used to sign the next certificate in the
- * chain. The final, "leaf" certificate contains a public key, denoted KM_pub, whose corresponding
- * private key, denoted KM_priv, is available for use by the IRemotelyProvisionedComponent.
+ * The root of trust for secure provisioning is something called the Device Identifier Composition
+ * Engine (DICE) Chain. The DICE Chain is a chain of certificates, represented as COSE_Sign1 objects
+ * containing CBOR Web Tokens (CWT) which have descriptions about the stage of firmware being
+ * signed, including a COSE_Key representation of that stage's public key.
  *
- * BCC Design
- * ==========
+ * DICE Chain Design
+ * =================
  *
- * The BCC is designed to mirror the boot stages of a device, and to prove the content and integrity
- * of each firmware image. In a proper BCC, each boot stage hashes its own private key with the code
- * and any relevant configuration parameters of the next stage to produce a key pair for the next
- * stage. Each stage also uses its own private key to sign the public key of the next stage,
- * including in the certificate the hash of the next firmware stage, then loads the next stage,
- * passing the private key and certificate to it in a manner that does not leak the private key to
- * later boot stages. The BCC root key pair is generated by immutable code (e.g. ROM), from a
- * device-unique secret. After the device-unique secret is used, it must be made unavailable to any
- * later boot stage.
+ * For a more exhaustive and thorough look at DICE and the implementation used within this protocol,
+ * please see: https://pigweed.googlesource.com/open-dice/+/HEAD/docs/specification.md
+ *
+ * The DICE Chain is designed to mirror the boot stages of a device, and to prove the content and
+ * integrity of each firmware image. In a proper DICE Chain, each boot stage hashes its own private
+ * key material with the code and any relevant configuration parameters of the next stage to produce
+ * a Compound Device Identifier, or CDI, which is used as the secret key material for the next
+ * stage. From the CDI, a key pair - CDI_*_Pub and CDI_*_Priv - is derived and certified for the
+ * next stage by the current stages CDI_*_Priv. The next stage is then loaded and given its CDI and
+ * the DICE certificate chain generated so far in a manner that does not leak the previous stage's
+ * CDI_*_Priv or CDI to later boot stages. The final, "leaf" CDI certificate contains a public key,
+ * denoted CDI_Leaf_Pub, whose corresponding private key, denoted CDI_Leaf_Priv, is available for
+ * use by the IRemotelyProvisionedComponent.
+ *
+ * The root keypair is generated by immutable code (e.g. ROM), from a Unique Device Secret (UDS).
+ * The keypair that is generated from it can be referred to as the UDS_Pub/UDS_Priv keys. After the
+ * device-unique secret is used, it must be made unavailable to any later boot stage.
  *
  * In this way, booting the device incrementally builds a certificate chain that (a) identifies and
  * validates the integrity of every stage and (b) contains a set of public keys that correspond to
@@ -58,64 +63,64 @@
  * (given the necessary input), but no stage can compute the secret of any preceding stage. Updating
  * the firmware or configuration of any stage changes the key pair of that stage, and of all
  * subsequent stages, and no attacker who compromised the previous version of the updated firmware
- * can know or predict the post-update key pairs. It is recommended and expected that the BCC is
- * constructed using the Open Profile for DICE.
+ * can know or predict the post-update key pairs. It is recommended and expected that the DICE Chain
+ * is constructed using the Open Profile for DICE.
  *
- * When the provisioning server receives a message signed by KM_priv and containing a BCC that
- * chains from DK_pub to KM_pub, it can be certain that (barring vulnerabilities in some boot
- * stage), the CertificateRequest came from the device associated with DK_pub, running the specific
- * software identified by the certificates in the BCC. If the server has some mechanism for knowing
- * which the DK_pub values of "valid" devices, it can determine whether signing certificates is
- * appropriate.
+ * When the provisioning server receives a message signed by CDI_Leaf_Priv and containing a DICE
+ * chain that chains from UDS_Pub to CDI_Leaf_Pub, it can be certain that (barring vulnerabilities
+ * in some boot stage), the CertificateRequest came from the device associated with UDS_Pub, running
+ * the specific software identified by the certificates in the chain. If the server has some
+ * mechanism for knowing the hash values of compromised stages, it can determine whether signing
+ * certificates is appropriate.
  *
- * Degenerate BCCs
- * ===============
+ * Degenerate DICE Chains
+ * ======================
  *
- * While a proper BCC, as described above, reflects the complete boot sequence from boot ROM to the
- * secure area image of the IRemotelyProvisionedComponent, it's also possible to use a "degenerate"
- * BCC which consists only of a single, self-signed certificate containing the public key of a
- * hardware-bound key pair. This is an appopriate solution for devices which haven't implemented
- * everything necessary to produce a proper BCC, but can derive a unique key pair in the secure
- * area.  In this degenerate case, DK_pub is the same as KM_pub.
+ * While a proper DICE Chain, as described above, reflects the complete boot sequence from boot ROM
+ * to the secure area image of the IRemotelyProvisionedComponent, it's also possible to use a
+ * "degenerate" DICE Chain which consists only of a single, self-signed certificate containing the
+ * public key of a hardware-bound key pair. This is an appopriate solution for devices which haven't
+ * implemented everything necessary to produce a proper DICE Chain, but can derive a unique key pair
+ * in the secure area. In this degenerate case, UDS_Pub is the same as CDI_Leaf_Pub.
  *
- * BCC Privacy
- * ===========
+ * DICE Chain Privacy
+ * ==================
  *
- * Because the BCC constitutes an unspoofable, device-unique identifier, special care is taken to
- * prevent its availability to entities who may wish to track devices. Two precautions are taken:
+ * Because the DICE Chain constitutes an unspoofable, device-unique identifier, special care is
+ * taken to prevent its availability to entities who may wish to track devices. Three precautions
+ * are taken:
  *
- * 1.  The BCC is never exported from the IRemotelyProvisionedComponent except in encrypted
- *     form. The portion of the CertificateRequest that contains the BCC is encrypted using an
- *     Endpoint Encryption Key (EEK).  The EEK is provided in the form of a certificate chain whose
- *     root must be pre-provisioned into the secure area (hardcoding the roots into the secure area
- *     firmware image is a recommended approach). Multiple roots may be provisioned. If the provided
- *     EEK does not chain back to this already-known root, the IRemotelyProvisionedComponent must
- *     reject it.
+ * 1) The DICE chain is only handled by the native Remote Key Provisioning Daemon (RKPD) service on
+ *    the HLOS and is not exposed to apps running on device.
  *
- * 2.  Precaution 1 above ensures that only an entity with a valid EEK private key can decrypt the
- *     BCC. To make it feasible to build a provisioning server which cannot use the BCC to track
- *     devices, the CertificateRequest is structured so that the server can be partitioned into two
- *     components.  The "decrypter" decrypts the BCC, verifies DK_pub and the device's right to
- *     receive provisioned certificates, but does not see the public keys to be signed or the
- *     resulting certificates.  The "certifier" gets informed of the results of the decrypter's
- *     validation and sees the public keys to be signed and resulting certificates, but does not see
- *     the BCC.
+ * 2) The CDI_Leaf_Priv key cannot be used to sign arbitrary data.
  *
- * Test Mode
- * =========
+ * 3) Backend infrastructure does not correlate UDS_Pub with the certificates signed and sent back
+ *    to the device.
  *
- * The IRemotelyProvisionedComponent supports a test mode, allowing the generation of test key pairs
- * and test CertificateRequests. Test keys/requests are annotated as such, and the BCC used for test
- * CertificateRequests must contain freshly-generated keys, not the real BCC key pairs.
+ * Versioning
+ * ==========
+ * Versions 1 and 2 of the schema, as previously defined in DeviceInfo.aidl, diverge in
+ * functionality from Version 3. Version 3 removes the need to have testMode in function calls and
+ * deprecates the Endpoint Encryption Key (EEK) as well. Vendors implementing Version 1
+ * (Android S/12) or Version 2 (Android T/13) do not need to implement generateCertificateRequestV2.
+ * Vendors implementing Version 3 (Android U/14) need to implement generateCertificateRequestV2.
+ *
+ * For better coverage of changes from version to version, please see RKP_CHANGELOG.md in the root
+ * of the keymint interface directory.
+ *
  * @hide
  */
 @VintfStability
 interface IRemotelyProvisionedComponent {
     const int STATUS_FAILED = 1;
     const int STATUS_INVALID_MAC = 2;
+    // --------- START: Versions 1 and 2 Only ----------
     const int STATUS_PRODUCTION_KEY_IN_TEST_REQUEST = 3;
     const int STATUS_TEST_KEY_IN_PRODUCTION_REQUEST = 4;
     const int STATUS_INVALID_EEK = 5;
+    // --------- END: Versions 1 and 2 Only ------------
+    const int STATUS_REMOVED = 6;
 
     /**
      * @return info which contains information about the underlying IRemotelyProvisionedComponent
@@ -124,11 +129,14 @@
     RpcHardwareInfo getHardwareInfo();
 
     /**
-     * generateKeyPair generates a new ECDSA P-256 key pair that can be certified.  Note that this
-     * method only generates ECDSA P-256 key pairs, but the interface can be extended to add methods
-     * for generating keys for other algorithms, if necessary.
+     * generateKeyPair generates a new ECDSA P-256 key pair that can be attested by the remote
+     * server.
      *
-     * @param in boolean testMode indicates whether the generated key is for testing only. Test keys
+     * @param in boolean testMode this field is now deprecated. It is ignored by the implementation
+     *        in v3, but retained to simplify backwards compatibility support. V1 and V2
+     *        implementations must still respect the testMode flag.
+     *
+     *        testMode indicates whether the generated key is for testing only. Test keys
      *        are marked (see the definition of PublicKey in the MacedPublicKey structure) to
      *        prevent them from being confused with production keys.
      *
@@ -142,6 +150,10 @@
     byte[] generateEcdsaP256KeyPair(in boolean testMode, out MacedPublicKey macedPublicKey);
 
     /**
+     * This method has been removed in version 3 of the HAL. The header is kept around for
+     * backwards compatibility purposes. Calling this method should return STATUS_REMOVED on v3.
+     *
+     * For v1 and v2 implementations:
      * generateCertificateRequest creates a certificate request to be sent to the provisioning
      * server.
      *
@@ -167,9 +179,9 @@
      *
      *            EekChain = [ + SignedSignatureKey, SignedEek ]
      *
-     *            SignedSignatureKey = [              // COSE_Sign1
+     *            SignedSignatureKey = [              ; COSE_Sign1
      *                protected: bstr .cbor {
-     *                    1 : AlgorithmEdDSA / AlgorithmES256,  // Algorithm
+     *                    1 : AlgorithmEdDSA / AlgorithmES256,  ; Algorithm
      *                },
      *                unprotected: {},
      *                payload: bstr .cbor SignatureKeyEd25519 /
@@ -178,63 +190,58 @@
      *                           bstr ECDSA(.cbor SignatureKeySignatureInput)
      *            ]
      *
-     *            SignatureKeyEd25519 = {                    // COSE_Key
-     *                 1 : 1,                         // Key type : Octet Key Pair
-     *                 3 : AlgorithmEdDSA,            // Algorithm
-     *                 -1 : 6,                        // Curve : Ed25519
-     *                 -2 : bstr                      // Ed25519 public key
+     *            SignatureKeyEd25519 = {             ; COSE_Key
+     *                 1 : 1,                         ; Key type : Octet Key Pair
+     *                 3 : AlgorithmEdDSA,            ; Algorithm
+     *                 -1 : 6,                        ; Curve : Ed25519
+     *                 -2 : bstr                      ; Ed25519 public key
      *            }
      *
      *            SignatureKeyP256 = {
-     *                 1 : 2,                         // Key type : EC2
-     *                 3 : AlgorithmES256,            // Algorithm
-     *                 -1 : 1,                        // Curve: P256
-     *                 -2 : bstr,                     // X coordinate
-     *                 -3 : bstr                      // Y coordinate
+     *                 1 : 2,                         ; Key type : EC2
+     *                 3 : AlgorithmES256,            ; Algorithm
+     *                 -1 : 1,                        ; Curve: P256
+     *                 -2 : bstr,                     ; X coordinate
+     *                 -3 : bstr                      ; Y coordinate
      *            }
      *
      *            SignatureKeySignatureInput = [
      *                context: "Signature1",
-     *                body_protected: bstr .cbor {
-     *                    1 : AlgorithmEdDSA / AlgorithmES256,     // Algorithm
-     *                },
+     *                body_protected: bstr .cbor { 1 : AlgorithmEdDSA / AlgorithmES256 },
      *                external_aad: bstr .size 0,
      *                payload: bstr .cbor SignatureKeyEd25519 /
      *                         bstr .cbor SignatureKeyP256
      *            ]
      *
-     *            SignedEek = [                       // COSE_Sign1
-     *                protected: bstr .cbor {
-     *                    1 : AlgorithmEdDSA / AlgorithmES256,  // Algorithm
-     *                },
+     *            ; COSE_Sign1
+     *            SignedEek = [
+     *                protected: bstr .cbor { 1 : AlgorithmEdDSA / AlgorithmES256 },
      *                unprotected: {},
      *                payload: bstr .cbor EekX25519 / .cbor EekP256,
      *                signature: bstr PureEd25519(.cbor EekSignatureInput) /
      *                           bstr ECDSA(.cbor EekSignatureInput)
      *            ]
      *
-     *            EekX25519 = {            // COSE_Key
-     *                1 : 1,               // Key type : Octet Key Pair
-     *                2 : bstr             // KID : EEK ID
-     *                3 : -25,             // Algorithm : ECDH-ES + HKDF-256
-     *                -1 : 4,              // Curve : X25519
-     *                -2 : bstr            // Ed25519 public key
+     *            EekX25519 = {            ; COSE_Key
+     *                1 : 1,               ; Key type : Octet Key Pair
+     *                2 : bstr             ; KID : EEK ID
+     *                3 : -25,             ; Algorithm : ECDH-ES + HKDF-256
+     *                -1 : 4,              ; Curve : X25519
+     *                -2 : bstr            ; Ed25519 public key
      *            }
      *
-     *            EekP256 = {              // COSE_Key
-     *                1 : 2,               // Key type : EC2
-     *                2 : bstr             // KID : EEK ID
-     *                3 : -25,             // Algorithm : ECDH-ES + HKDF-256
-     *                -1 : 1,              // Curve : P256
-     *                -2 : bstr            // Sender X coordinate
-     *                -3 : bstr            // Sender Y coordinate
+     *            EekP256 = {              ; COSE_Key
+     *                1 : 2,               ; Key type : EC2
+     *                2 : bstr             ; KID : EEK ID
+     *                3 : -25,             ; Algorithm : ECDH-ES + HKDF-256
+     *                -1 : 1,              ; Curve : P256
+     *                -2 : bstr            ; Sender X coordinate
+     *                -3 : bstr            ; Sender Y coordinate
      *            }
      *
      *            EekSignatureInput = [
      *                context: "Signature1",
-     *                body_protected: bstr .cbor {
-     *                    1 : AlgorithmEdDSA / AlgorithmES256,     // Algorithm
-     *                },
+     *                body_protected: bstr .cbor { 1 : AlgorithmEdDSA / AlgorithmES256 },
      *                external_aad: bstr .size 0,
      *                payload: bstr .cbor EekX25519 / .cbor EekP256
      *            ]
@@ -270,25 +277,188 @@
      *        Where EK_mac is an ephemeral MAC key, found in ProtectedData (see below).  The MACed
      *        data is the "tag" field of a COSE_Mac0 structure like:
      *
-     *            MacedKeys = [                            // COSE_Mac0
+     *            MacedKeys = [                            ; COSE_Mac0
      *                protected : bstr .cbor {
-     *                    1 : 5,                           // Algorithm : HMAC-256
+     *                    1 : 5,                           ; Algorithm : HMAC-256
      *                },
      *                unprotected : {},
-     *                // Payload is PublicKeys from keysToSign argument, in provided order.
+     *                ; Payload is PublicKeys from keysToSign argument, in provided order.
      *                payload: bstr .cbor [ * PublicKey ],
      *                tag: bstr
      *            ]
      *
      *            KeysToMacStructure = [
      *                context : "MAC0",
-     *                protected : bstr .cbor { 1 : 5 },    // Algorithm : HMAC-256
+     *                protected : bstr .cbor { 1 : 5 },    ; Algorithm : HMAC-256
      *                external_aad : bstr .size 0,
-     *                // Payload is PublicKeys from keysToSign argument, in provided order.
+     *                ; Payload is PublicKeys from keysToSign argument, in provided order.
      *                payload : bstr .cbor [ * PublicKey ]
      *            ]
      */
     byte[] generateCertificateRequest(in boolean testMode, in MacedPublicKey[] keysToSign,
             in byte[] endpointEncryptionCertChain, in byte[] challenge, out DeviceInfo deviceInfo,
             out ProtectedData protectedData);
+
+    /**
+     * generateCertificateRequestV2 creates a certificate signing request to be sent to the
+     * provisioning server.
+     *
+     * @param in MacedPublicKey[] keysToSign contains the set of keys to certify. The
+     *        IRemotelyProvisionedComponent must validate the MACs on each key.  If any entry in the
+     *        array lacks a valid MAC, the method must return STATUS_INVALID_MAC.
+     *
+     * @param in challenge contains a byte string from the provisioning server which will be
+     *        included in the signed data of the CSR structure. Different provisioned backends may
+     *        use different semantic data for this field, but the supported sizes must be between 32
+     *        and 64 bytes, inclusive.
+     *
+     * @return the following CBOR Certificate Signing Request (Csr) serialized into a byte array:
+     *
+     * Csr = [
+     *    version: 3,              ; The CDDL Schema version.
+     *    UdsCerts,
+     *    DiceCertChain,
+     *    SignedData
+     * ]
+     *
+     * ; COSE_Sign1 (untagged)
+     * SignedData = [
+     *     protected: bstr .cbor { 1 : AlgorithmEdDSA / AlgorithmES256 },
+     *     unprotected: {},
+     *     payload: bstr .cbor SignedDataPayload,
+     *     signature: bstr            ; PureEd25519(CDI_Leaf_Priv, bstr .cbor SignedDataSigStruct) /
+     *                                ; ECDSA(CDI_Leaf_Priv, bstr .cbor SignedDataSigStruct)
+     * ]
+     *
+     * ; Sig_structure for SignedData
+     * SignedDataSigStruct = [
+     *     context: "Signature1",
+     *     protected: bstr .cbor { 1 : AlgorithmEdDSA / AlgorithmES256 },
+     *     external_aad: bstr .size 0,
+     *     payload: bstr .cbor SignedDataPayload
+     * ]
+     *
+     * SignedDataPayload = [               ; CBOR Array defining the payload for SignedData
+     *     DeviceInfo,                     ; Defined in DeviceInfo.aidl
+     *     challenge: bstr .size (32..64), ; Provided by the method parameters
+     *     KeysToSign,                     ; Provided by the method parameters
+     * ]
+     *
+     * KeysToSign = [ * PublicKey ]   ; Please see MacedPublicKey.aidl for the PublicKey definition.
+     *
+     * ; UdsCerts allows the platform to provide additional certifications for the UDS_Pub. For
+     * ; example, this could be provided by the hardware vendor, who certifies all of their chips.
+     * ; The SignerName is a free-form string describing who generated the signature. The root
+     * ; certificate will need to be communicated to the verifier out of band, along with the
+     * ; SignerName that is expected for the given root certificate.
+     * UdsCerts = {
+     *     * SignerName => UdsCertChain
+     * }
+     *
+     * ; SignerName is a string identifier that indicates both the signing authority as
+     * ; well as the format of the UdsCertChain
+     * SignerName = tstr
+     *
+     * UdsCertChain = [
+     *     2* X509Certificate       ; Root -> ... -> Leaf. "Root" is the vendor self-signed
+     *                              ; cert, "Leaf" contains UDS_Public. There may also be
+     *                              ; intermediate certificates between Root and Leaf.
+     * ]
+     *
+     * ; A bstr containing a DER-encoded X.509 certificate (RSA, NIST P-curve, or edDSA)
+     * X509Certificate = bstr
+     *
+     * ; The DICE Chain contains measurements about the device firmware.
+     * ; The first entry in the DICE Chain is the UDS_Pub, encoded as a COSE_key. All entries
+     * ; after the first describe a link in the boot chain (e.g. bootloaders: BL1, BL2, ... BLN)
+     * ; Note that there is no DiceChainEntry for UDS_pub, only a "bare" COSE_key.
+     * DiceCertChain = [
+     *     PubKeyEd25519 / PubKeyECDSA256,  ; UDS_Pub
+     *     + DiceChainEntry,                ; First CDI_Certificate -> Last CDI_Certificate
+     *                                      ; Last certificate corresponds to KeyMint's DICE key.
+     * ]
+     *
+     * ; This is the signed payload for each entry in the DCC. Note that the "Configuration
+     * ; Input Values" described by the Open Profile are not used here. Instead, the Dcc
+     * ; defines its own configuration values for the Configuration Descriptor field. See
+     * ; the Open Profile for DICE for more details on the fields. SHA256 and SHA512 are acceptable
+     * ; hash algorithms. The digest bstr values in the payload are the digest values without any
+     * ; padding. Note that for SHA256, this implies the digest bstr is 32 bytes. This is an
+     * ; intentional, minor deviation from Open Profile for DICE, which specifies all digests are
+     * ; 64 bytes.
+     * DiceChainEntryPayload = {                    ; CWT [RFC8392]
+     *     1 : tstr,                                ; Issuer
+     *     2 : tstr,                                ; Subject
+     *     -4670552 : bstr .cbor PubKeyEd25519 /
+     *                bstr .cbor PubKeyECDSA256,    ; Subject Public Key
+     *     -4670553 : bstr                          ; Key Usage
+     *
+     *     ; NOTE: All of the following fields may be omitted for a "Degenerate DICE Chain", as
+     *     ;       described above.
+     *     -4670545 : bstr,                         ; Code Hash
+     *     ? -4670546 : bstr,                       ; Code Descriptor
+     *     ? -4670547 : bstr,                       ; Configuration Hash
+     *     -4670548 : bstr .cbor {                  ; Configuration Descriptor
+     *         ? -70002 : tstr,                         ; Component name
+     *         ? -70003 : int,                          ; Firmware version
+     *         ? -70004 : null,                         ; Resettable
+     *     },
+     *     -4670549 : bstr,                         ; Authority Hash
+     *     ? -4670550 : bstr,                       ; Authority Descriptor
+     *     -4670551 : bstr,                         ; Mode
+     * }
+     *
+     * ; Each entry in the Dcc is a DiceChainEntryPayload signed by the key from the previous entry
+     * ; in the Dcc array.
+     * DiceChainEntry = [                            ; COSE_Sign1 (untagged)
+     *     protected : bstr .cbor { 1 : AlgorithmEdDSA / AlgorithmES256 },
+     *     unprotected: {},
+     *     payload: bstr .cbor DiceChainEntryPayload,
+     *     signature: bstr ; PureEd25519(SigningKey, bstr .cbor DiceChainEntryInput) /
+     *                     ; ECDSA(SigningKey, bstr .cbor DiceChainEntryInput)
+     *                     ; See RFC 8032 for details of how to encode the signature value
+     *                     ; for Ed25519.
+     * ]
+     *
+     * DiceChainEntryInput = [
+     *     context: "Signature1",
+     *     protected: bstr .cbor { 1 : AlgorithmEdDSA / AlgorithmES256 },
+     *     external_aad: bstr .size 0,
+     *     payload: bstr .cbor DiceChainEntryPayload
+     * ]
+     *
+     * ; The following section defines some types that are reused throughout the above
+     * ; data structures.
+     * PubKeyX25519 = {                 ; COSE_Key
+     *      1 : 1,                      ; Key type : Octet Key Pair
+     *     -1 : 4,                      ; Curve : X25519
+     *     -2 : bstr                    ; Sender X25519 public key
+     * }
+     *
+     * PubKeyEd25519 = {                ; COSE_Key
+     *     1 : 1,                       ; Key type : octet key pair
+     *     3 : AlgorithmEdDSA,          ; Algorithm : EdDSA
+     *     -1 : 6,                      ; Curve : Ed25519
+     *     -2 : bstr                    ; X coordinate, little-endian
+     * }
+     *
+     * PubKeyEcdhP256 = {               ; COSE_Key
+     *      1 : 2,                      ; Key type : EC2
+     *      -1 : 1,                     ; Curve : P256
+     *      -2 : bstr                   ; Sender X coordinate
+     *      -3 : bstr                   ; Sender Y coordinate
+     * }
+     *
+     * PubKeyECDSA256 = {               ; COSE_Key
+     *     1 : 2,                       ; Key type : EC2
+     *     3 : AlgorithmES256,          ; Algorithm : ECDSA w/ SHA-256
+     *     -1 : 1,                      ; Curve: P256
+     *     -2 : bstr,                   ; X coordinate
+     *     -3 : bstr                    ; Y coordinate
+     * }
+     *
+     * AlgorithmES256 = -7
+     * AlgorithmEdDSA = -8
+     */
+    byte[] generateCertificateRequestV2(in MacedPublicKey[] keysToSign, in byte[] challenge);
 }
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
index ae75579..4c2be89 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
@@ -158,12 +158,23 @@
      *     Failed                     (3),
      * }
      *
+     * -- Note that the AuthorizationList SEQUENCE is also used in IKeyMintDevice::importWrappedKey
+     * -- as a way of describing the authorizations associated with a key that is being securely
+     * -- imported.  As such, it includes the ability to describe tags that are only relevant for
+     * -- symmetric keys, and which will never appear in the attestation extension of an X.509
+     * -- certificate that holds the public key part of an asymmetric keypair. Importing a wrapped
+     * -- key also allows the use of Tag::USER_SECURE_ID, which is never included in an attestation
+     * -- extension because it has no meaning off-device.
+     *
      * AuthorizationList ::= SEQUENCE {
      *     purpose                    [1] EXPLICIT SET OF INTEGER OPTIONAL,
      *     algorithm                  [2] EXPLICIT INTEGER OPTIONAL,
      *     keySize                    [3] EXPLICIT INTEGER OPTIONAL,
+     *     blockMode                  [4] EXPLICIT SET OF INTEGER OPTIONAL, -- symmetric only
      *     digest                     [5] EXPLICIT SET OF INTEGER OPTIONAL,
      *     padding                    [6] EXPLICIT SET OF INTEGER OPTIONAL,
+     *     callerNonce                [7] EXPLICIT NULL OPTIONAL, -- symmetric only
+     *     minMacLength               [8] EXPLICIT INTEGER OPTIONAL, -- symmetric only
      *     ecCurve                    [10] EXPLICIT INTEGER OPTIONAL,
      *     rsaPublicExponent          [200] EXPLICIT INTEGER OPTIONAL,
      *     mgfDigest                  [203] EXPLICIT SET OF INTEGER OPTIONAL,
@@ -173,6 +184,7 @@
      *     originationExpireDateTime  [401] EXPLICIT INTEGER OPTIONAL,
      *     usageExpireDateTime        [402] EXPLICIT INTEGER OPTIONAL,
      *     usageCountLimit            [405] EXPLICIT INTEGER OPTIONAL,
+     *     userSecureId               [502] EXPLICIT INTEGER OPTIONAL, -- only used on import
      *     noAuthRequired             [503] EXPLICIT NULL OPTIONAL,
      *     userAuthType               [504] EXPLICIT INTEGER OPTIONAL,
      *     authTimeout                [505] EXPLICIT INTEGER OPTIONAL,
diff --git a/security/keymint/aidl/android/hardware/security/keymint/MacedPublicKey.aidl b/security/keymint/aidl/android/hardware/security/keymint/MacedPublicKey.aidl
index ad97443..275e322 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/MacedPublicKey.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/MacedPublicKey.aidl
@@ -28,21 +28,22 @@
      * only to the secure environment, as proof that the public key was generated by that
      * environment. In CDDL, assuming the contained key is a P-256 public key:
      *
-     *     MacedPublicKey = [                     // COSE_Mac0
-     *         protected: bstr .cbor { 1 : 5},    // Algorithm : HMAC-256
+     *     MacedPublicKey = [                     ; COSE_Mac0
+     *         protected: bstr .cbor { 1 : 5},    ; Algorithm : HMAC-256
      *         unprotected: { },
      *         payload : bstr .cbor PublicKey,
      *         tag : bstr HMAC-256(K_mac, MAC_structure)
      *     ]
      *
-     *     PublicKey = {               // COSE_Key
-     *         1 : 2,                  // Key type : EC2
-     *         3 : -7,                 // Algorithm : ES256
-     *         -1 : 1,                 // Curve : P256
-     *         -2 : bstr,              // X coordinate, little-endian
-     *         -3 : bstr,              // Y coordinate, little-endian
-     *         ? -70000 : nil          // Presence indicates this is a test key.  If set, K_mac is
-     *                                 // all zeros.
+     *     ; NOTE: -70000 is deprecated for v3 HAL implementations.
+     *     PublicKey = {               ; COSE_Key
+     *         1 : 2,                  ; Key type : EC2
+     *         3 : -7,                 ; Algorithm : ES256
+     *         -1 : 1,                 ; Curve : P256
+     *         -2 : bstr,              ; X coordinate, little-endian
+     *         -3 : bstr,              ; Y coordinate, little-endian
+     *         -70000 : nil            ; Presence indicates this is a test key. If set, K_mac is
+     *                                 ; all zeros.
      *     },
      *
      *     MAC_structure = [
@@ -51,9 +52,6 @@
      *         external_aad : bstr .size 0,
      *         payload : bstr .cbor PublicKey
      *     ]
-     *
-     * if a non-P256 public key were contained, the contents of the PublicKey map would change a
-     * little; see RFC 8152 for details.
      */
     byte[] macedKey;
 }
diff --git a/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl b/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl
index 8b3875b..d59508b 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl
@@ -17,9 +17,13 @@
 package android.hardware.security.keymint;
 
 /**
+ * NOTE: ProtectedData has been removed as of version 3, but is kept around for backwards
+ * compatibility reasons. For versions 1 and 2:
+ *
  * ProtectedData contains the encrypted BCC and the ephemeral MAC key used to
  * authenticate the keysToSign (see keysToSignMac output argument of
  * IRemotelyProvisionedComponent.generateCertificateRequest).
+ *
  * @hide
  */
 @VintfStability
@@ -33,201 +37,201 @@
      *   - None of the CBOR in ProtectedData uses CBOR tags. If an implementation includes
      *     tags, parsers may reject the data.
      *
-     *     ProtectedData = [               // COSE_Encrypt
+     *     ProtectedData = [               ; COSE_Encrypt
      *         protected: bstr .cbor {
-     *             1 : 3                   // Algorithm : AES-GCM 256
+     *             1 : 3                   ; Algorithm : AES-GCM 256
      *         },
      *         unprotected: {
-     *             5 : bstr .size 12       // IV
+     *             5 : bstr .size 12       ; IV
      *         },
-     *         ciphertext: bstr,           // AES-GCM-256(K, .cbor ProtectedDataPayload)
-     *                                     // Where the encryption key 'K' is derived as follows:
-     *                                     // ikm = ECDH(EEK_pub, Ephemeral_priv)
-     *                                     // salt = null
-     *                                     // info = .cbor Context (see below)
-     *                                     // K = HKDF-SHA-256(ikm, salt, info)
+     *         ciphertext: bstr,           ; AES-GCM-256(K, .cbor ProtectedDataPayload)
+     *                                     ; Where the encryption key 'K' is derived as follows:
+     *                                     ; ikm = ECDH(EEK_pub, Ephemeral_priv)
+     *                                     ; salt = null
+     *                                     ; info = .cbor Context (see below)
+     *                                     ; K = HKDF-SHA-256(ikm, salt, info)
      *         recipients : [
-     *             [                       // COSE_Recipient
+     *             [                       ; COSE_Recipient
      *                 protected : bstr .cbor {
-     *                     1 : -25         // Algorithm : ECDH-ES + HKDF-256
+     *                     1 : -25         ; Algorithm : ECDH-ES + HKDF-256
      *                 },
      *                 unprotected : {
-     *                     -1 : PubKeyX25519 / PubKeyEcdhP256  // Ephemeral_pub
-     *                     4 : bstr,       // KID : EEK ID
+     *                     -1 : PubKeyX25519 / PubKeyEcdhP256  ; Ephemeral_pub
+     *                     4 : bstr,       ; KID : EEK ID
      *                 },
      *                 ciphertext : nil
      *             ]
      *         ]
      *     ]
      *
-     *     // The COSE_KDF_Context that is used to derive the ProtectedData encryption key with
-     *     // HKDF. See details on use in ProtectedData comments above.
+     *     ; The COSE_KDF_Context that is used to derive the ProtectedData encryption key with
+     *     ; HKDF. See details on use in ProtectedData comments above.
      *     Context = [
-     *         AlgorithmID : 3             // AES-GCM 256
+     *         AlgorithmID : 3             ; AES-GCM 256
      *         PartyUInfo : [
      *             identity : bstr "client"
      *             nonce : bstr .size 0,
-     *             other : bstr            // Ephemeral_pub
+     *             other : bstr            ; Ephemeral_pub
      *         ],
      *         PartyVInfo : [
      *             identity : bstr "server",
      *             nonce : bstr .size 0,
-     *             other : bstr            // EEK pubkey
+     *             other : bstr            ; EEK pubkey
      *         ],
      *         SuppPubInfo : [
-     *             256,                    // Output key length
+     *             256,                    ; Output key length
      *             protected : bstr .size 0
      *         ]
      *     ]
      *
-     *     // The data that is encrypted and included in ProtectedData ciphertext (see above).
+     *     ; The data that is encrypted and included in ProtectedData ciphertext (see above).
      *     ProtectedDataPayload [
      *         SignedMac,
      *         Bcc,
      *         ? AdditionalDKSignatures,
      *     ]
      *
-     *     // AdditionalDKSignatures allows the platform to provide additional certifications
-     *     // for the DK_pub. For example, this could be provided by the hardware vendor, who
-     *     // certifies all of their devices. The SignerName is a free-form string describing
-     *     // who generated the signature.
+     *     ; AdditionalDKSignatures allows the platform to provide additional certifications
+     *     ; for the DK_pub. For example, this could be provided by the hardware vendor, who
+     *     ; certifies all of their devices. The SignerName is a free-form string describing
+     *     ; who generated the signature.
      *     AdditionalDKSignatures = {
      *         + SignerName => DKCertChain
      *     }
      *
-     *     // SignerName is a string identifier that indicates both the signing authority as
-     *     // well as the format of the DKCertChain
+     *     ; SignerName is a string identifier that indicates both the signing authority as
+     *     ; well as the format of the DKCertChain
      *     SignerName = tstr
      *
      *     DKCertChain = [
-     *         2* X509Certificate       // Root -> ... -> Leaf. "Root" is the vendor self-signed
-     *                                  // cert, "Leaf" contains DK_pub. There may also be
-     *                                  // intermediate certificates between Root and Leaf.
+     *         2* X509Certificate       ; Root -> ... -> Leaf. "Root" is the vendor self-signed
+     *                                  ; cert, "Leaf" contains DK_pub. There may also be
+     *                                  ; intermediate certificates between Root and Leaf.
      *     ]
      *
-     *     // A bstr containing a DER-encoded X.509 certificate (RSA, NIST P-curve, or edDSA)
+     *     ; A bstr containing a DER-encoded X.509 certificate (RSA, NIST P-curve, or edDSA)
      *     X509Certificate = bstr
      *
-     *     // The SignedMac, which authenticates the MAC key that is used to authenticate the
-     *     // keysToSign.
-     *     SignedMac = [                                // COSE_Sign1
-     *         bstr .cbor {                             // Protected params
-     *             1 : AlgorithmEdDSA / AlgorithmES256, // Algorithm
+     *     ; The SignedMac, which authenticates the MAC key that is used to authenticate the
+     *     ; keysToSign.
+     *     SignedMac = [                                ; COSE_Sign1
+     *         bstr .cbor {                             ; Protected params
+     *             1 : AlgorithmEdDSA / AlgorithmES256, ; Algorithm
      *         },
-     *         {},                                      // Unprotected params
-     *         bstr .size 32,                           // Payload: MAC key
-     *         bstr // PureEd25519(KM_priv, bstr .cbor SignedMac_structure) /
-     *              // ECDSA(KM_priv, bstr .cbor SignedMac_structure)
+     *         {},                                      ; Unprotected params
+     *         bstr .size 32,                           ; Payload: MAC key
+     *         bstr ; PureEd25519(KM_priv, bstr .cbor SignedMac_structure) /
+     *              ; ECDSA(KM_priv, bstr .cbor SignedMac_structure)
      *     ]
      *
-     *     SignedMac_structure = [                      //  COSE Sig_structure
+     *     SignedMac_structure = [                      ;  COSE Sig_structure
      *         "Signature1",
-     *         bstr .cbor {                             // Protected params
-     *             1 : AlgorithmEdDSA / AlgorithmES256, // Algorithm
+     *         bstr .cbor {                             ; Protected params
+     *             1 : AlgorithmEdDSA / AlgorithmES256, ; Algorithm
      *         },
      *         bstr .cbor SignedMacAad,
-     *         bstr .size 32                            // MAC key
+     *         bstr .size 32                            ; MAC key
      *     ]
      *
      *     SignedMacAad = [
-     *         challenge : bstr .size (32..64),   // Size between 32 - 64
-     *                                            // bytes inclusive
+     *         challenge : bstr .size (32..64),   ; Size between 32 - 64
+     *                                            ; bytes inclusive
      *         VerifiedDeviceInfo,
-     *         tag: bstr                 // This is the tag from COSE_Mac0 of
-     *                                   // KeysToCertify, to tie the key set to
-     *                                   // the signature.
+     *         tag: bstr                 ; This is the tag from COSE_Mac0 of
+     *                                   ; KeysToCertify, to tie the key set to
+     *                                   ; the signature.
      *     ]
      *
-     *     VerifiedDeviceInfo = DeviceInfo  // See DeviceInfo.aidl
+     *     VerifiedDeviceInfo = DeviceInfo  ; See DeviceInfo.aidl
      *
-     *     // The BCC is the boot certificate chain, containing measurements about the device
-     *     // boot chain. The BCC generally follows the Open Profile for DICE specification at
-     *     // https://pigweed.googlesource.com/open-dice/+/HEAD/docs/specification.md.
-     *     //
-     *     // The first entry in the Bcc is the DK_pub, encoded as a COSE_key. All entries after
-     *     // the first describe a link in the boot chain (e.g. bootloaders: BL1, BL2, ... BLN).
-     *     // Note that there is no BccEntry for DK_pub, only a "bare" COSE_key.
+     *     ; The BCC is the boot certificate chain, containing measurements about the device
+     *     ; boot chain. The BCC generally follows the Open Profile for DICE specification at
+     *     ; https:;pigweed.googlesource.com/open-dice/+/HEAD/docs/specification.md.
+     *     ;
+     *     ; The first entry in the Bcc is the DK_pub, encoded as a COSE_key. All entries after
+     *     ; the first describe a link in the boot chain (e.g. bootloaders: BL1, BL2, ... BLN).
+     *     ; Note that there is no BccEntry for DK_pub, only a "bare" COSE_key.
      *     Bcc = [
-     *         PubKeyEd25519 / PubKeyECDSA256, // DK_pub
-     *         + BccEntry,                     // Root -> leaf (KM_pub)
+     *         PubKeyEd25519 / PubKeyECDSA256, ; DK_pub
+     *         + BccEntry,                     ; Root -> leaf (KM_pub)
      *     ]
      *
-     *     // This is the signed payload for each entry in the Bcc. Note that the "Configuration
-     *     // Input Values" described by the Open Profile are not used here. Instead, the Bcc
-     *     // defines its own configuration values for the Configuration Descriptor field. See
-     *     // the Open Profile for DICE for more details on the fields. All hashes are SHA256.
-     *     BccPayload = {                               // CWT [RFC8392]
-     *         1 : tstr,                                // Issuer
-     *         2 : tstr,                                // Subject
+     *     ; This is the signed payload for each entry in the Bcc. Note that the "Configuration
+     *     ; Input Values" described by the Open Profile are not used here. Instead, the Bcc
+     *     ; defines its own configuration values for the Configuration Descriptor field. See
+     *     ; the Open Profile for DICE for more details on the fields. All hashes are SHA256.
+     *     BccPayload = {                               ; CWT [RFC8392]
+     *         1 : tstr,                                ; Issuer
+     *         2 : tstr,                                ; Subject
      *         -4670552 : bstr .cbor PubKeyEd25519 /
-     *                    bstr .cbor PubKeyECDSA256,    // Subject Public Key
-     *         -4670553 : bstr                          // Key Usage
+     *                    bstr .cbor PubKeyECDSA256,    ; Subject Public Key
+     *         -4670553 : bstr                          ; Key Usage
      *
-     *         // NOTE: All of the following fields may be omitted for a "Degenerate BCC", as
-     *         //       described by IRemotelyProvisionedComponent.aidl.
-     *         -4670545 : bstr,                         // Code Hash
-     *         ? -4670546 : bstr,                       // Code Descriptor
-     *         ? -4670547 : bstr,                       // Configuration Hash
-     *         -4670548 : bstr .cbor {                  // Configuration Descriptor
-     *             ? -70002 : tstr,                         // Component name
-     *             ? -70003 : int,                          // Firmware version
-     *             ? -70004 : null,                         // Resettable
+     *         ; NOTE: All of the following fields may be omitted for a "Degenerate BCC", as
+     *         ;       described by IRemotelyProvisionedComponent.aidl.
+     *         -4670545 : bstr,                         ; Code Hash
+     *         ? -4670546 : bstr,                       ; Code Descriptor
+     *         ? -4670547 : bstr,                       ; Configuration Hash
+     *         -4670548 : bstr .cbor {                  ; Configuration Descriptor
+     *             ? -70002 : tstr,                         ; Component name
+     *             ? -70003 : int,                          ; Firmware version
+     *             ? -70004 : null,                         ; Resettable
      *         },
-     *         -4670549 : bstr,                         // Authority Hash
-     *         ? -4670550 : bstr,                       // Authority Descriptor
-     *         -4670551 : bstr,                         // Mode
+     *         -4670549 : bstr,                         ; Authority Hash
+     *         ? -4670550 : bstr,                       ; Authority Descriptor
+     *         -4670551 : bstr,                         ; Mode
      *     }
      *
-     *     // Each entry in the Bcc is a BccPayload signed by the key from the previous entry
-     *     // in the Bcc array.
-     *     BccEntry = [                                  // COSE_Sign1 (untagged)
+     *     ; Each entry in the Bcc is a BccPayload signed by the key from the previous entry
+     *     ; in the Bcc array.
+     *     BccEntry = [                                  ; COSE_Sign1 (untagged)
      *         protected : bstr .cbor {
-     *             1 : AlgorithmEdDSA / AlgorithmES256,  // Algorithm
+     *             1 : AlgorithmEdDSA / AlgorithmES256,  ; Algorithm
      *         },
      *         unprotected: {},
      *         payload: bstr .cbor BccPayload,
-     *         signature: bstr // PureEd25519(SigningKey, bstr .cbor BccEntryInput) /
-     *                         // ECDSA(SigningKey, bstr .cbor BccEntryInput)
-     *         // See RFC 8032 for details of how to encode the signature value for Ed25519.
+     *         signature: bstr ; PureEd25519(SigningKey, bstr .cbor BccEntryInput) /
+     *                         ; ECDSA(SigningKey, bstr .cbor BccEntryInput)
+     *         ; See RFC 8032 for details of how to encode the signature value for Ed25519.
      *     ]
      *
      *     BccEntryInput = [
      *         context: "Signature1",
      *         protected: bstr .cbor {
-     *             1 : AlgorithmEdDSA / AlgorithmES256,  // Algorithm
+     *             1 : AlgorithmEdDSA / AlgorithmES256,  ; Algorithm
      *         },
      *         external_aad: bstr .size 0,
      *         payload: bstr .cbor BccPayload
      *     ]
      *
-     *     // The following section defines some types that are reused throughout the above
-     *     // data structures.
-     *     PubKeyX25519 = {                 // COSE_Key
-     *          1 : 1,                      // Key type : Octet Key Pair
-     *         -1 : 4,                      // Curve : X25519
-     *         -2 : bstr                    // Sender X25519 public key
+     *     ; The following section defines some types that are reused throughout the above
+     *     ; data structures.
+     *     PubKeyX25519 = {                 ; COSE_Key
+     *          1 : 1,                      ; Key type : Octet Key Pair
+     *         -1 : 4,                      ; Curve : X25519
+     *         -2 : bstr                    ; Sender X25519 public key
      *     }
      *
-     *     PubKeyEd25519 = {                // COSE_Key
-     *         1 : 1,                       // Key type : octet key pair
-     *         3 : AlgorithmEdDSA,          // Algorithm : EdDSA
-     *         -1 : 6,                      // Curve : Ed25519
-     *         -2 : bstr                    // X coordinate, little-endian
+     *     PubKeyEd25519 = {                ; COSE_Key
+     *         1 : 1,                       ; Key type : octet key pair
+     *         3 : AlgorithmEdDSA,          ; Algorithm : EdDSA
+     *         -1 : 6,                      ; Curve : Ed25519
+     *         -2 : bstr                    ; X coordinate, little-endian
      *     }
      *
-     *     PubKeyEcdhP256 = {               // COSE_Key
-     *          1 : 2,                      // Key type : EC2
-     *          -1 : 1,                     // Curve : P256
-     *          -2 : bstr                   // Sender X coordinate
-     *          -3 : bstr                   // Sender Y coordinate
+     *     PubKeyEcdhP256 = {               ; COSE_Key
+     *          1 : 2,                      ; Key type : EC2
+     *          -1 : 1,                     ; Curve : P256
+     *          -2 : bstr                   ; Sender X coordinate
+     *          -3 : bstr                   ; Sender Y coordinate
      *     }
      *
-     *     PubKeyECDSA256 = {               // COSE_Key
-     *         1 : 2,                       // Key type : EC2
-     *         3 : AlgorithmES256,          // Algorithm : ECDSA w/ SHA-256
-     *         -1 : 1,                      // Curve: P256
-     *         -2 : bstr,                   // X coordinate
-     *         -3 : bstr                    // Y coordinate
+     *     PubKeyECDSA256 = {               ; COSE_Key
+     *         1 : 2,                       ; Key type : EC2
+     *         3 : AlgorithmES256,          ; Algorithm : ECDSA w/ SHA-256
+     *         -1 : 1,                      ; Curve: P256
+     *         -2 : bstr,                   ; X coordinate
+     *         -3 : bstr                    ; Y coordinate
      *     }
      *
      *     AlgorithmES256 = -7
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
index 871a1ac..47361d5 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
@@ -274,25 +274,10 @@
     USAGE_EXPIRE_DATETIME = TagType.DATE | 402,
 
     /**
-     * Tag::MIN_SECONDS_BETWEEN_OPS specifies the minimum amount of time that elapses between
-     * allowed operations using a key.  This can be used to rate-limit uses of keys in contexts
-     * where unlimited use may enable brute force attacks.
+     * OBSOLETE: Do not use.
      *
-     * The value is a 32-bit integer representing seconds between allowed operations.
-     *
-     * When a key with this tag is used in an operation, the IKeyMintDevice must start a timer
-     * during the finish() or abort() call.  Any call to begin() that is received before the timer
-     * indicates that the interval specified by Tag::MIN_SECONDS_BETWEEN_OPS has elapsed must fail
-     * with ErrorCode::KEY_RATE_LIMIT_EXCEEDED.  This implies that the IKeyMintDevice must keep a
-     * table of use counters for keys with this tag.  Because memory is often limited, this table
-     * may have a fixed maximum size and KeyMint may fail operations that attempt to use keys with
-     * this tag when the table is full.  The table must accommodate at least 8 in-use keys and
-     * aggressively reuse table slots when key minimum-usage intervals expire.  If an operation
-     * fails because the table is full, KeyMint returns ErrorCode::TOO_MANY_OPERATIONS.
-     *
-     * Must be hardware-enforced.
-     *
-     * TODO(b/191738660): Remove in KeyMint V2. Currently only used for FDE.
+     * This tag value is included for historical reason, as it was present in Keymaster.
+     * KeyMint implementations do not need to support this tag.
      */
     MIN_SECONDS_BETWEEN_OPS = TagType.UINT | 403,
 
@@ -898,8 +883,12 @@
     STORAGE_KEY = TagType.BOOL | 722,
 
     /**
-     * OBSOLETE: Do not use. See IKeyMintOperation.updateAad instead.
-     * TODO(b/191738660): Remove in KeyMint v2.
+     * OBSOLETE: Do not use.
+     *
+     * This tag value is included for historical reasons -- in Keymaster it was used to hold
+     * associated data for AEAD encryption, as an additional parameter to
+     * IKeymasterDevice::finish().  In KeyMint the IKeyMintOperation::updateAad() method is used for
+     * this.
      */
     ASSOCIATED_DATA = TagType.BYTES | 1000,
 
@@ -938,10 +927,12 @@
     RESET_SINCE_ID_ROTATION = TagType.BOOL | 1004,
 
     /**
-     * OBSOLETE: Do not use. See the authToken parameter for IKeyMintDevice::begin and for
-     * IKeyMintOperation methods instead.
+     * OBSOLETE: Do not use.
      *
-     * TODO(b/191738660): Delete when keystore1 is deleted.
+     * This tag value is included for historical reasons -- in Keymaster it was used to hold
+     * a confirmation token as an additional parameter to
+     * IKeymasterDevice::finish().  In KeyMint the IKeyMintOperation::finish() method includes
+     * a confirmationToken argument for this.
      */
     CONFIRMATION_TOKEN = TagType.BYTES | 1005,
 
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
index 1caf8bc..5473062 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -1070,8 +1070,20 @@
         return "Failure";
     }
     X509_Ptr key_cert(parse_cert_blob(cert_chain_[0].encodedCertificate));
+    if (key_cert.get() == nullptr) {
+        ADD_FAILURE() << "Failed to parse cert";
+        return "Failure";
+    }
     EVP_PKEY_Ptr pub_key(X509_get_pubkey(key_cert.get()));
+    if (pub_key.get() == nullptr) {
+        ADD_FAILURE() << "Failed to retrieve public key";
+        return "Failure";
+    }
     RSA_Ptr rsa(EVP_PKEY_get1_RSA(const_cast<EVP_PKEY*>(pub_key.get())));
+    if (rsa.get() == nullptr) {
+        ADD_FAILURE() << "Failed to retrieve RSA public key";
+        return "Failure";
+    }
 
     // Retrieve relevant tags.
     Digest digest = Digest::NONE;
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index fa7dccb..4f5d821 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -741,6 +741,7 @@
 
     for (auto block_mode : ValidBlockModes(Algorithm::AES)) {
         for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+            SCOPED_TRACE(testing::Message() << "AES-unknown-" << block_mode << "-" << padding_mode);
             vector<uint8_t> key_blob;
             vector<KeyCharacteristics> key_characteristics;
             // No key size specified
@@ -994,6 +995,7 @@
  */
 TEST_P(NewKeyGenerationTest, Rsa) {
     for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
+        SCOPED_TRACE(testing::Message() << "RSA-" << key_size);
         vector<uint8_t> key_blob;
         vector<KeyCharacteristics> key_characteristics;
         ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
@@ -1066,6 +1068,7 @@
     vector<uint8_t> serial_blob(build_serial_blob(serial_int));
 
     for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
+        SCOPED_TRACE(testing::Message() << "RSA-" << key_size);
         vector<uint8_t> key_blob;
         vector<KeyCharacteristics> key_characteristics;
         auto builder = AuthorizationSetBuilder()
@@ -1148,6 +1151,7 @@
     attestation_key.issuerSubjectName = make_name_from_str("Android Keystore Key");
 
     for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
+        SCOPED_TRACE(testing::Message() << "RSA-" << key_size);
         auto challenge = "hello";
         auto app_id = "foo";
 
@@ -1295,6 +1299,7 @@
     vector<uint8_t> serial_blob(build_serial_blob(serial_int));
 
     for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
+        SCOPED_TRACE(testing::Message() << "RSA-" << key_size);
         vector<uint8_t> key_blob;
         vector<KeyCharacteristics> key_characteristics;
         ASSERT_EQ(ErrorCode::OK,
@@ -1416,6 +1421,7 @@
  */
 TEST_P(NewKeyGenerationTest, LimitedUsageRsa) {
     for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
+        SCOPED_TRACE(testing::Message() << "RSA-" << key_size);
         vector<uint8_t> key_blob;
         vector<KeyCharacteristics> key_characteristics;
         ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
@@ -1466,6 +1472,7 @@
     vector<uint8_t> serial_blob(build_serial_blob(serial_int));
 
     for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
+        SCOPED_TRACE(testing::Message() << "RSA-" << key_size);
         vector<uint8_t> key_blob;
         vector<KeyCharacteristics> key_characteristics;
         auto builder = AuthorizationSetBuilder()
@@ -1535,6 +1542,7 @@
  */
 TEST_P(NewKeyGenerationTest, NoInvalidRsaSizes) {
     for (auto key_size : InvalidKeySizes(Algorithm::RSA)) {
+        SCOPED_TRACE(testing::Message() << "RSA-" << key_size);
         vector<uint8_t> key_blob;
         vector<KeyCharacteristics> key_characteristics;
         ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
@@ -1569,6 +1577,7 @@
  */
 TEST_P(NewKeyGenerationTest, RsaMissingParams) {
     for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
+        SCOPED_TRACE(testing::Message() << "RSA-" << key_size);
         ASSERT_EQ(ErrorCode::OK,
                   GenerateKey(
                           AuthorizationSetBuilder().RsaKey(key_size, 65537).SetDefaultValidity()));
@@ -1584,6 +1593,7 @@
  */
 TEST_P(NewKeyGenerationTest, Ecdsa) {
     for (auto curve : ValidCurves()) {
+        SCOPED_TRACE(testing::Message() << "Curve::" << curve);
         vector<uint8_t> key_blob;
         vector<KeyCharacteristics> key_characteristics;
         ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
@@ -1709,6 +1719,7 @@
     vector<uint8_t> serial_blob(build_serial_blob(serial_int));
 
     for (auto curve : ValidCurves()) {
+        SCOPED_TRACE(testing::Message() << "Curve::" << curve);
         vector<uint8_t> key_blob;
         vector<KeyCharacteristics> key_characteristics;
         auto builder = AuthorizationSetBuilder()
@@ -2211,6 +2222,7 @@
     vector<uint8_t> serial_blob(build_serial_blob(serial_int));
 
     for (auto curve : ValidCurves()) {
+        SCOPED_TRACE(testing::Message() << "Curve::" << curve);
         vector<uint8_t> key_blob;
         vector<KeyCharacteristics> key_characteristics;
         ASSERT_EQ(ErrorCode::OK,
@@ -2282,6 +2294,7 @@
     auto app_id = "foo";
 
     for (auto curve : ValidCurves()) {
+        SCOPED_TRACE(testing::Message() << "Curve::" << curve);
         vector<uint8_t> key_blob;
         vector<KeyCharacteristics> key_characteristics;
         ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
@@ -2323,6 +2336,7 @@
     std::vector<uint32_t> app_id_lengths{143, 258};
 
     for (uint32_t length : app_id_lengths) {
+        SCOPED_TRACE(testing::Message() << "app_id_len=" << length);
         const string app_id(length, 'a');
         vector<uint8_t> key_blob;
         vector<KeyCharacteristics> key_characteristics;
@@ -2377,6 +2391,7 @@
  */
 TEST_P(NewKeyGenerationTest, LimitedUsageEcdsa) {
     for (auto curve : ValidCurves()) {
+        SCOPED_TRACE(testing::Message() << "Curve::" << curve);
         vector<uint8_t> key_blob;
         vector<KeyCharacteristics> key_characteristics;
         ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
@@ -2430,6 +2445,7 @@
  */
 TEST_P(NewKeyGenerationTest, EcdsaInvalidCurve) {
     for (auto curve : InvalidCurves()) {
+        SCOPED_TRACE(testing::Message() << "Curve::" << curve);
         vector<uint8_t> key_blob;
         vector<KeyCharacteristics> key_characteristics;
         auto result = GenerateKey(AuthorizationSetBuilder()
@@ -2507,6 +2523,7 @@
         digest = Digest::SHA_2_512;
     }
     for (auto curve : ValidCurves()) {
+        SCOPED_TRACE(testing::Message() << "Curve::" << curve);
         EXPECT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
                                                      .EcdsaSigningKey(curve)
                                                      .Digest(digest)
@@ -2524,6 +2541,7 @@
  */
 TEST_P(NewKeyGenerationTest, Hmac) {
     for (auto digest : ValidDigests(false /* withNone */, true /* withMD5 */)) {
+        SCOPED_TRACE(testing::Message() << "Digest::" << digest);
         vector<uint8_t> key_blob;
         vector<KeyCharacteristics> key_characteristics;
         constexpr size_t key_size = 128;
@@ -2557,6 +2575,7 @@
     auto app_id = "foo";
 
     for (auto digest : ValidDigests(false /* withNone */, true /* withMD5 */)) {
+        SCOPED_TRACE(testing::Message() << "Digest::" << digest);
         vector<uint8_t> key_blob;
         vector<KeyCharacteristics> key_characteristics;
         constexpr size_t key_size = 128;
@@ -2590,6 +2609,7 @@
  */
 TEST_P(NewKeyGenerationTest, LimitedUsageHmac) {
     for (auto digest : ValidDigests(false /* withNone */, true /* withMD5 */)) {
+        SCOPED_TRACE(testing::Message() << "Digest::" << digest);
         vector<uint8_t> key_blob;
         vector<KeyCharacteristics> key_characteristics;
         constexpr size_t key_size = 128;
@@ -2628,6 +2648,7 @@
  */
 TEST_P(NewKeyGenerationTest, HmacCheckKeySizes) {
     for (size_t key_size = 0; key_size <= 512; ++key_size) {
+        SCOPED_TRACE(testing::Message() << "HMAC-" << key_size);
         if (key_size < 64 || key_size % 8 != 0) {
             // To keep this test from being very slow, we only test a random fraction of
             // non-byte key sizes.  We test only ~10% of such cases. Since there are 392 of
@@ -2670,6 +2691,7 @@
  */
 TEST_P(NewKeyGenerationTest, HmacCheckMinMacLengths) {
     for (size_t min_mac_length = 0; min_mac_length <= 256; ++min_mac_length) {
+        SCOPED_TRACE(testing::Message() << "MIN_MAC_LENGTH=" << min_mac_length);
         if (min_mac_length < 64 || min_mac_length % 8 != 0) {
             // To keep this test from being very long, we only test a random fraction of
             // non-byte lengths.  We test only ~10% of such cases. Since there are 172 of them,
@@ -2826,6 +2848,7 @@
     for (auto padding :
          {PaddingMode::NONE, PaddingMode::RSA_PSS, PaddingMode::RSA_PKCS1_1_5_SIGN}) {
         for (auto digest : ValidDigests(true /* withNone */, true /* withMD5 */)) {
+            SCOPED_TRACE(testing::Message() << "RSA padding=" << padding << " digest=" << digest);
             if (padding == PaddingMode::NONE && digest != Digest::NONE) {
                 // Digesting only makes sense with padding.
                 continue;
@@ -3541,6 +3564,7 @@
  */
 TEST_P(SigningOperationsTest, HmacAllDigests) {
     for (auto digest : ValidDigests(false /* withNone */, false /* withMD5 */)) {
+        SCOPED_TRACE(testing::Message() << "Digest::" << digest);
         ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
                                                      .Authorization(TAG_NO_AUTH_REQUIRED)
                                                      .HmacKey(128)
@@ -4391,6 +4415,7 @@
     string key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     uint32_t bitlen = key.size() * 8;
     for (uint32_t key_size : {bitlen - 1, bitlen + 1, bitlen - 8, bitlen + 8}) {
+        SCOPED_TRACE(testing::Message() << "import-key-size=" << key_size);
         // Explicit key size doesn't match that of the provided key.
         auto result = ImportKey(AuthorizationSetBuilder()
                                         .Authorization(TAG_NO_AUTH_REQUIRED)
@@ -4458,6 +4483,7 @@
     string key = hex2str("a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f7358");
     uint32_t bitlen = key.size() * 7;
     for (uint32_t key_size : {bitlen - 1, bitlen + 1, bitlen - 8, bitlen + 8}) {
+        SCOPED_TRACE(testing::Message() << "import-key-size=" << key_size);
         // Explicit key size doesn't match that of the provided key.
         auto result = ImportKey(AuthorizationSetBuilder()
                                         .Authorization(TAG_NO_AUTH_REQUIRED)
@@ -4943,6 +4969,7 @@
  */
 TEST_P(EncryptionOperationsTest, RsaNoPaddingSuccess) {
     for (uint64_t exponent : ValidExponents()) {
+        SCOPED_TRACE(testing::Message() << "RSA exponent=" << exponent);
         ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
                                                      .Authorization(TAG_NO_AUTH_REQUIRED)
                                                      .RsaEncryptionKey(2048, exponent)
@@ -5133,6 +5160,7 @@
     string message = "Hello";
 
     for (auto digest : digests) {
+        SCOPED_TRACE(testing::Message() << "digest-" << digest);
         auto params = AuthorizationSetBuilder()
                               .Authorization(TAG_RSA_OAEP_MGF_DIGEST, digest)
                               .Digest(Digest::SHA_2_256)
@@ -5553,6 +5581,7 @@
  */
 TEST_P(EncryptionOperationsTest, AesEcbCbcNoPaddingWrongInputSize) {
     for (BlockMode blockMode : {BlockMode::ECB, BlockMode::CBC}) {
+        SCOPED_TRACE(testing::Message() << "AES-" << blockMode);
         ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
                                                      .Authorization(TAG_NO_AUTH_REQUIRED)
                                                      .AesEncryptionKey(128)
@@ -6249,6 +6278,7 @@
     // Zero input message
     string message = "";
     for (auto padding : {PaddingMode::NONE, PaddingMode::PKCS7}) {
+        SCOPED_TRACE(testing::Message() << "AES padding=" << padding);
         auto params = AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(padding);
         AuthorizationSet out_params;
         string ciphertext1 = EncryptMessage(message, params, &out_params);
@@ -6917,6 +6947,7 @@
                                                  .Padding(PaddingMode::PKCS7)));
 
     for (size_t i = 0; i < 32; ++i) {
+        SCOPED_TRACE(testing::Message() << "msg size=" << i);
         string message(i, 'a');
         auto inParams =
                 AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
@@ -7227,6 +7258,7 @@
  */
 TEST_P(EncryptionOperationsTest, TripleDesEcbCbcNoPaddingWrongInputSize) {
     for (BlockMode blockMode : {BlockMode::ECB, BlockMode::CBC}) {
+        SCOPED_TRACE(testing::Message() << "BlockMode::" << blockMode);
         ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
                                                      .TripleDesEncryptionKey(168)
                                                      .BlockMode(blockMode)
@@ -7285,6 +7317,7 @@
 
     // Try various message lengths; all should fail.
     for (size_t i = 0; i <= 32; i++) {
+        SCOPED_TRACE(testing::Message() << "i = " << i);
         auto begin_params =
                 AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::PKCS7);
         EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, begin_params));
@@ -7954,6 +7987,7 @@
                                                  .Padding(PaddingMode::NONE)));
 
     for (int msg_size = 8 /* 256 bytes */; msg_size <= 11 /* 2 KiB */; msg_size++) {
+        SCOPED_TRACE(testing::Message() << "msg_size = " << msg_size);
         auto cipher_params =
                 AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE);
 
@@ -8267,6 +8301,7 @@
     GenerateKeyMintEcKey(curve, &kmPubKey);
 
     for (auto localCurve : ValidCurves()) {
+        SCOPED_TRACE(testing::Message() << "local-curve-" << localCurve);
         if (localCurve == curve) {
             continue;
         }
diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
index 7184613..2e282e0 100644
--- a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
+++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
@@ -30,6 +30,7 @@
 #include <openssl/ec_key.h>
 #include <openssl/x509.h>
 #include <remote_prov/remote_prov_utils.h>
+#include <optional>
 #include <set>
 #include <vector>
 
@@ -73,6 +74,10 @@
     return {"locked", "open"};
 }
 
+std::set<std::string> getAttestationIdEntrySet() {
+    return {"brand", "manufacturer", "product", "model", "device"};
+}
+
 bytevec string_to_bytevec(const char* s) {
     const uint8_t* p = reinterpret_cast<const uint8_t*>(s);
     return bytevec(p, p + strlen(s));
@@ -431,7 +436,7 @@
         auto [deviceInfoMap, __2, deviceInfoErrMsg] = cppbor::parse(deviceInfo.deviceInfo);
         ASSERT_TRUE(deviceInfoMap) << "Failed to parse deviceInfo: " << deviceInfoErrMsg;
         ASSERT_TRUE(deviceInfoMap->asMap());
-        checkDeviceInfo(deviceInfoMap->asMap(), deviceInfo.deviceInfo);
+        checkDeviceInfo(*deviceInfoMap->asMap(), deviceInfo.deviceInfo);
         auto& signingKey = bccContents->back().pubKey;
         deviceInfoMap->asMap()->canonicalize();
         auto macKey = verifyAndParseCoseSign1(signedMac->asArray(), signingKey,
@@ -459,72 +464,92 @@
         }
     }
 
-    void checkType(const cppbor::Map* devInfo, uint8_t majorType, std::string entryName) {
-        const auto& val = devInfo->get(entryName);
-        ASSERT_TRUE(val) << entryName << " does not exist";
-        ASSERT_EQ(val->type(), majorType) << entryName << " has the wrong type.";
+    std::optional<std::string> assertAttribute(const cppbor::Map& devInfo,
+                                               cppbor::MajorType majorType, std::string entryName) {
+        const auto& val = devInfo.get(entryName);
+        if (!val) return entryName + " is missing.\n";
+        if (val->type() != majorType) return entryName + " has the wrong type.\n";
         switch (majorType) {
             case cppbor::TSTR:
-                EXPECT_GT(val->asTstr()->value().size(), 0);
+                if (val->asTstr()->value().size() <= 0) {
+                    return entryName + " is present but the value is empty.\n";
+                }
                 break;
             case cppbor::BSTR:
-                EXPECT_GT(val->asBstr()->value().size(), 0);
+                if (val->asBstr()->value().size() <= 0) {
+                    return entryName + " is present but the value is empty.\n";
+                }
                 break;
             default:
                 break;
         }
+        return {};
     }
 
-    void checkDeviceInfo(const cppbor::Map* deviceInfo, bytevec deviceInfoBytes) {
-        EXPECT_EQ(deviceInfo->clone()->asMap()->canonicalize().encode(), deviceInfoBytes)
+    void checkType(const cppbor::Map& devInfo, cppbor::MajorType majorType, std::string entryName) {
+        if (auto error = assertAttribute(devInfo, majorType, entryName)) {
+            FAIL() << *error;
+        }
+    }
+
+    void checkDeviceInfo(const cppbor::Map& deviceInfo, bytevec deviceInfoBytes) {
+        EXPECT_EQ(deviceInfo.clone()->asMap()->canonicalize().encode(), deviceInfoBytes)
                 << "DeviceInfo ordering is non-canonical.";
-        const auto& version = deviceInfo->get("version");
+        const auto& version = deviceInfo.get("version");
         ASSERT_TRUE(version);
         ASSERT_TRUE(version->asUint());
         RpcHardwareInfo info;
         provisionable_->getHardwareInfo(&info);
         ASSERT_EQ(version->asUint()->value(), info.versionNumber);
         std::set<std::string> allowList;
+        std::string problemEntries;
         switch (version->asUint()->value()) {
             // These fields became mandated in version 2.
             case 2:
-                checkType(deviceInfo, cppbor::TSTR, "brand");
-                checkType(deviceInfo, cppbor::TSTR, "manufacturer");
-                checkType(deviceInfo, cppbor::TSTR, "product");
-                checkType(deviceInfo, cppbor::TSTR, "model");
-                checkType(deviceInfo, cppbor::TSTR, "device");
+                for (auto attId : getAttestationIdEntrySet()) {
+                    if (auto errMsg = assertAttribute(deviceInfo, cppbor::TSTR, attId)) {
+                        problemEntries += *errMsg;
+                    }
+                }
+                EXPECT_EQ("", problemEntries)
+                        << problemEntries
+                        << "Attestation IDs are missing or malprovisioned. If this test is being "
+                           "run against an early proto or EVT build, this error is probably WAI "
+                           "and indicates that Device IDs were not provisioned in the factory. If "
+                           "this error is returned on a DVT or later build revision, then "
+                           "something is likely wrong with the factory provisioning process.";
                 // TODO: Refactor the KeyMint code that validates these fields and include it here.
                 checkType(deviceInfo, cppbor::TSTR, "vb_state");
                 allowList = getAllowedVbStates();
-                EXPECT_NE(allowList.find(deviceInfo->get("vb_state")->asTstr()->value()),
+                EXPECT_NE(allowList.find(deviceInfo.get("vb_state")->asTstr()->value()),
                           allowList.end());
                 checkType(deviceInfo, cppbor::TSTR, "bootloader_state");
                 allowList = getAllowedBootloaderStates();
-                EXPECT_NE(allowList.find(deviceInfo->get("bootloader_state")->asTstr()->value()),
+                EXPECT_NE(allowList.find(deviceInfo.get("bootloader_state")->asTstr()->value()),
                           allowList.end());
                 checkType(deviceInfo, cppbor::BSTR, "vbmeta_digest");
                 checkType(deviceInfo, cppbor::UINT, "system_patch_level");
                 checkType(deviceInfo, cppbor::UINT, "boot_patch_level");
                 checkType(deviceInfo, cppbor::UINT, "vendor_patch_level");
                 checkType(deviceInfo, cppbor::UINT, "fused");
-                EXPECT_LT(deviceInfo->get("fused")->asUint()->value(), 2);  // Must be 0 or 1.
+                EXPECT_LT(deviceInfo.get("fused")->asUint()->value(), 2);  // Must be 0 or 1.
                 checkType(deviceInfo, cppbor::TSTR, "security_level");
                 allowList = getAllowedSecurityLevels();
-                EXPECT_NE(allowList.find(deviceInfo->get("security_level")->asTstr()->value()),
+                EXPECT_NE(allowList.find(deviceInfo.get("security_level")->asTstr()->value()),
                           allowList.end());
-                if (deviceInfo->get("security_level")->asTstr()->value() == "tee") {
+                if (deviceInfo.get("security_level")->asTstr()->value() == "tee") {
                     checkType(deviceInfo, cppbor::TSTR, "os_version");
                 }
                 break;
             case 1:
                 checkType(deviceInfo, cppbor::TSTR, "security_level");
                 allowList = getAllowedSecurityLevels();
-                EXPECT_NE(allowList.find(deviceInfo->get("security_level")->asTstr()->value()),
+                EXPECT_NE(allowList.find(deviceInfo.get("security_level")->asTstr()->value()),
                           allowList.end());
                 if (version->asUint()->value() == 1) {
                     checkType(deviceInfo, cppbor::TSTR, "att_id_state");
                     allowList = getAllowedAttIdStates();
-                    EXPECT_NE(allowList.find(deviceInfo->get("att_id_state")->asTstr()->value()),
+                    EXPECT_NE(allowList.find(deviceInfo.get("att_id_state")->asTstr()->value()),
                               allowList.end());
                 }
                 break;
diff --git a/sensors/aidl/vts/AndroidTest.xml b/sensors/aidl/vts/AndroidTest.xml
new file mode 100644
index 0000000..99caf28
--- /dev/null
+++ b/sensors/aidl/vts/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?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 VtsAidlHalSensorsTargetTest.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-native" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.StopServicesSetup"/>
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="VtsAidlHalSensorsTargetTest->/data/local/tmp/VtsAidlHalSensorsTargetTest" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-timeout" value="900000" />
+        <option name="runtime-hint" value="300000"/>
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="VtsAidlHalSensorsTargetTest" />
+    </test>
+</configuration>
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/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlTestBase.h b/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlTestBase.h
index f3cbd78..f2c92ae 100644
--- a/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlTestBase.h
+++ b/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlTestBase.h
@@ -351,11 +351,11 @@
                   minDelayAverageInterval / 10);
 
         // fastest rate sampling time is close to spec
-        EXPECT_LT(std::abs(minDelayAverageInterval - minSamplingPeriodInNs),
+        EXPECT_LE(std::abs(minDelayAverageInterval - minSamplingPeriodInNs),
                   minSamplingPeriodInNs / 10);
 
         // slowest rate sampling time is close to spec
-        EXPECT_LT(std::abs(maxDelayAverageInterval - maxSamplingPeriodInNs),
+        EXPECT_LE(std::abs(maxDelayAverageInterval - maxSamplingPeriodInNs),
                   maxSamplingPeriodInNs / 10);
     }
 
diff --git a/soundtrigger/2.3/cli/Android.bp b/soundtrigger/2.3/cli/Android.bp
index 27d7b30..883bf2b 100644
--- a/soundtrigger/2.3/cli/Android.bp
+++ b/soundtrigger/2.3/cli/Android.bp
@@ -9,7 +9,7 @@
 
 java_binary {
     name: "sthal_cli_2.3",
-    wrapper: "sthal_cli_2.3",
+    wrapper: "sthal_cli_2.3.sh",
     srcs: ["java/**/*.java"],
     static_libs: [
         "android.hardware.soundtrigger-V2.3-java",
diff --git a/soundtrigger/2.3/cli/sthal_cli_2.3 b/soundtrigger/2.3/cli/sthal_cli_2.3.sh
similarity index 100%
rename from soundtrigger/2.3/cli/sthal_cli_2.3
rename to soundtrigger/2.3/cli/sthal_cli_2.3.sh
diff --git a/soundtrigger/aidl/Android.bp b/soundtrigger/aidl/Android.bp
index 0658519..27d43d3 100644
--- a/soundtrigger/aidl/Android.bp
+++ b/soundtrigger/aidl/Android.bp
@@ -39,6 +39,20 @@
             version: "1",
             imports: ["android.media.soundtrigger.types-V1"],
         },
+        // IMPORTANT: Update latest_android_hardware_soundtrigger3 every time
+        // you add the latest frozen version to versions_with_info
     ],
+}
 
+// Note: This should always be one version ahead of the last frozen version
+latest_android_hardware_soundtrigger3 = "android.hardware.soundtrigger3-V1"
+
+// Modules that depend on android.hardware.soundtrigger3 directly can include
+// the following java_defaults to avoid explicitly managing dependency versions
+// across many scattered files.
+java_defaults {
+    name: "latest_android_hardware_soundtrigger3_java_static",
+    static_libs: [
+        latest_android_hardware_soundtrigger3 + "-java",
+    ],
 }
diff --git a/soundtrigger/aidl/cli/Android.bp b/soundtrigger/aidl/cli/Android.bp
index e8999ff..935e438 100644
--- a/soundtrigger/aidl/cli/Android.bp
+++ b/soundtrigger/aidl/cli/Android.bp
@@ -9,7 +9,7 @@
 
 java_binary {
     name: "sthal_cli_3",
-    wrapper: "sthal_cli_3",
+    wrapper: "sthal_cli_3.sh",
     srcs: ["java/**/*.java"],
     static_libs: [
         "android.hardware.soundtrigger3-V1-java",
diff --git a/soundtrigger/aidl/cli/sthal_cli_3 b/soundtrigger/aidl/cli/sthal_cli_3.sh
similarity index 100%
rename from soundtrigger/aidl/cli/sthal_cli_3
rename to soundtrigger/aidl/cli/sthal_cli_3.sh
diff --git a/tv/input/OWNERS b/tv/input/OWNERS
new file mode 100644
index 0000000..a02291a
--- /dev/null
+++ b/tv/input/OWNERS
@@ -0,0 +1,4 @@
+hgchen@google.com
+shubang@google.com
+quxiangfang@google.com
+yixiaoluo@google.com
diff --git a/tv/input/aidl/Android.bp b/tv/input/aidl/Android.bp
new file mode 100644
index 0000000..1987174
--- /dev/null
+++ b/tv/input/aidl/Android.bp
@@ -0,0 +1,27 @@
+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.tv.input",
+    vendor_available: true,
+    srcs: ["android/hardware/tv/input/*.aidl"],
+    imports: [
+        "android.hardware.common-V2",
+        "android.media.audio.common.types-V1",
+    ],
+    stability: "vintf",
+    backend: {
+        java: {
+            sdk_version: "module_current",
+        },
+        cpp: {
+            enabled: false,
+        },
+    },
+}
diff --git a/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/CableConnectionStatus.aidl b/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/CableConnectionStatus.aidl
new file mode 100644
index 0000000..a48bdb1
--- /dev/null
+++ b/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/CableConnectionStatus.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.tv.input;
+@Backing(type="int") @VintfStability
+enum CableConnectionStatus {
+  UNKNOWN = 0,
+  CONNECTED = 1,
+  DISCONNECTED = 2,
+}
diff --git a/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/ITvInput.aidl b/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/ITvInput.aidl
new file mode 100644
index 0000000..f8d5e05
--- /dev/null
+++ b/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/ITvInput.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.tv.input;
+@VintfStability
+interface ITvInput {
+  void closeStream(in int deviceId, in int streamId);
+  android.hardware.tv.input.TvStreamConfig[] getStreamConfigurations(in int deviceId);
+  android.hardware.common.NativeHandle openStream(in int deviceId, in int streamId);
+  void setCallback(in android.hardware.tv.input.ITvInputCallback callback);
+  const int STATUS_UNKNOWN = 1;
+  const int STATUS_NO_RESOURCE = 2;
+  const int STATUS_INVALID_ARGUMENTS = 3;
+  const int STATUS_INVALID_STATE = 4;
+}
diff --git a/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/ITvInputCallback.aidl b/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/ITvInputCallback.aidl
new file mode 100644
index 0000000..7f2aff6
--- /dev/null
+++ b/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/ITvInputCallback.aidl
@@ -0,0 +1,38 @@
+/*
+ * 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.tv.input;
+@VintfStability
+interface ITvInputCallback {
+  void notify(in android.hardware.tv.input.TvInputEvent event);
+}
diff --git a/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/TvInputDeviceInfo.aidl b/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/TvInputDeviceInfo.aidl
new file mode 100644
index 0000000..d095146
--- /dev/null
+++ b/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/TvInputDeviceInfo.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.tv.input;
+@VintfStability
+parcelable TvInputDeviceInfo {
+  int deviceId;
+  android.hardware.tv.input.TvInputType type;
+  int portId;
+  android.hardware.tv.input.CableConnectionStatus cableConnectionStatus;
+  android.media.audio.common.AudioDevice audioDevice;
+}
diff --git a/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/TvInputEvent.aidl b/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/TvInputEvent.aidl
new file mode 100644
index 0000000..cfa8a34
--- /dev/null
+++ b/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/TvInputEvent.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.tv.input;
+@VintfStability
+parcelable TvInputEvent {
+  android.hardware.tv.input.TvInputEventType type;
+  android.hardware.tv.input.TvInputDeviceInfo deviceInfo;
+}
diff --git a/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/TvInputEventType.aidl b/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/TvInputEventType.aidl
new file mode 100644
index 0000000..a9f518a
--- /dev/null
+++ b/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/TvInputEventType.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.tv.input;
+@Backing(type="int") @VintfStability
+enum TvInputEventType {
+  DEVICE_AVAILABLE = 1,
+  DEVICE_UNAVAILABLE = 2,
+  STREAM_CONFIGURATIONS_CHANGED = 3,
+}
diff --git a/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/TvInputType.aidl b/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/TvInputType.aidl
new file mode 100644
index 0000000..7e44a7d
--- /dev/null
+++ b/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/TvInputType.aidl
@@ -0,0 +1,47 @@
+/*
+ * 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.tv.input;
+@Backing(type="int") @VintfStability
+enum TvInputType {
+  OTHER = 1,
+  TUNER = 2,
+  COMPOSITE = 3,
+  SVIDEO = 4,
+  SCART = 5,
+  COMPONENT = 6,
+  VGA = 7,
+  DVI = 8,
+  HDMI = 9,
+  DISPLAY_PORT = 10,
+}
diff --git a/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/TvStreamConfig.aidl b/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/TvStreamConfig.aidl
new file mode 100644
index 0000000..8378ff3
--- /dev/null
+++ b/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/TvStreamConfig.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.tv.input;
+@VintfStability
+parcelable TvStreamConfig {
+  int streamId;
+  int maxVideoWidth;
+  int maxVideoHeight;
+}
diff --git a/tv/input/aidl/android/hardware/tv/input/CableConnectionStatus.aidl b/tv/input/aidl/android/hardware/tv/input/CableConnectionStatus.aidl
new file mode 100644
index 0000000..64b79dd
--- /dev/null
+++ b/tv/input/aidl/android/hardware/tv/input/CableConnectionStatus.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.tv.input;
+
+/**
+ * Status of cable connection.
+ * This status is for devices having availability to detect the cable in a mechanical way,
+ * regardless of whether the connected external device is electrically on or not.
+ * If the device does not have such capability, you must use UNKNOWN.
+ */
+@VintfStability
+@Backing(type="int")
+enum CableConnectionStatus {
+    UNKNOWN = 0,
+    CONNECTED = 1,
+    DISCONNECTED = 2,
+}
diff --git a/tv/input/aidl/android/hardware/tv/input/ITvInput.aidl b/tv/input/aidl/android/hardware/tv/input/ITvInput.aidl
new file mode 100644
index 0000000..31d6586
--- /dev/null
+++ b/tv/input/aidl/android/hardware/tv/input/ITvInput.aidl
@@ -0,0 +1,76 @@
+/*
+ * 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.tv.input;
+
+import android.hardware.common.NativeHandle;
+import android.hardware.tv.input.ITvInputCallback;
+import android.hardware.tv.input.TvStreamConfig;
+
+@VintfStability
+interface ITvInput {
+    /**
+     * ServiceSpecificException values for ITvInput requests
+     */
+    const int STATUS_UNKNOWN = 1;
+    const int STATUS_NO_RESOURCE = 2;
+    const int STATUS_INVALID_ARGUMENTS = 3;
+    const int STATUS_INVALID_STATE = 4;
+
+    /**
+     * Closes a specific stream in a device.
+     *
+     * @param deviceId Device ID for the stream to close.
+     * @param streamId Stream ID for the stream to close.
+     * @throws ServiceSpecificException with values from the ITvInput::STATUS_* constants
+     */
+    void closeStream(in int deviceId, in int streamId);
+
+    /**
+     * Gets stream configurations for a specific device.
+     *
+     * The configs object is valid only until the next
+     * STREAM_CONFIGURATIONS_CHANGED event.
+     *
+     * @param deviceId Device ID for the configurations.
+     * @return the array of available configurations.
+     * @throws ServiceSpecificException with values from the ITvInput::STATUS_* constants
+     */
+    TvStreamConfig[] getStreamConfigurations(in int deviceId);
+
+    /**
+     * Opens a specific stream in a device.
+     *
+     * @param deviceId Device ID for the stream to open.
+     * @param streamId Stream ID for the stream to open. Must be one of the
+     *         stream IDs returned from getStreamConfigurations().
+     * @return the handle for sideband stream.
+     * @throws ServiceSpecificException with values from the ITvInput::STATUS_* constants
+     */
+    NativeHandle openStream(in int deviceId, in int streamId);
+
+    /**
+     * Sets a callback for events.
+     *
+     * Note that initially no device is available in the client side, so the
+     * implementation must notify all the currently available devices including
+     * static devices via callback once callback is set.
+     *
+     * @param callback Callback object to pass events.
+     * @throws ServiceSpecificException with values from the ITvInput::STATUS_* constants
+     */
+    void setCallback(in ITvInputCallback callback);
+}
diff --git a/tv/input/aidl/android/hardware/tv/input/ITvInputCallback.aidl b/tv/input/aidl/android/hardware/tv/input/ITvInputCallback.aidl
new file mode 100644
index 0000000..fc7492d
--- /dev/null
+++ b/tv/input/aidl/android/hardware/tv/input/ITvInputCallback.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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.tv.input;
+
+import android.hardware.tv.input.TvInputEvent;
+
+@VintfStability
+interface ITvInputCallback {
+    /**
+     * Notifies the client that an event has occurred. For possible event types,
+     * check TvInputEventType.
+     *
+     * @param event Event passed to the client.
+     */
+    void notify(in TvInputEvent event);
+}
diff --git a/tv/input/aidl/android/hardware/tv/input/TvInputDeviceInfo.aidl b/tv/input/aidl/android/hardware/tv/input/TvInputDeviceInfo.aidl
new file mode 100644
index 0000000..2782e90
--- /dev/null
+++ b/tv/input/aidl/android/hardware/tv/input/TvInputDeviceInfo.aidl
@@ -0,0 +1,36 @@
+/*
+ * 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.tv.input;
+
+import android.hardware.tv.input.CableConnectionStatus;
+import android.hardware.tv.input.TvInputType;
+import android.media.audio.common.AudioDevice;
+
+@VintfStability
+parcelable TvInputDeviceInfo {
+    int deviceId;
+    TvInputType type;
+
+    /* HDMI port ID number. e.g. 2 for HDMI 2. */
+    int portId;
+
+    /* Cable connection status. */
+    CableConnectionStatus cableConnectionStatus;
+
+    /* Audio device info. */
+    AudioDevice audioDevice;
+}
diff --git a/tv/input/aidl/android/hardware/tv/input/TvInputEvent.aidl b/tv/input/aidl/android/hardware/tv/input/TvInputEvent.aidl
new file mode 100644
index 0000000..66ca7aa
--- /dev/null
+++ b/tv/input/aidl/android/hardware/tv/input/TvInputEvent.aidl
@@ -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.
+ */
+
+package android.hardware.tv.input;
+
+import android.hardware.tv.input.TvInputDeviceInfo;
+import android.hardware.tv.input.TvInputEventType;
+
+@VintfStability
+parcelable TvInputEvent {
+    TvInputEventType type;
+
+    /**
+     * TvInputEventType::DEVICE_AVAILABLE: all fields are relevant.
+     * TvInputEventType::DEVICE_UNAVAILABLE: only deviceId is relevant.
+     * TvInputEventType::STREAM_CONFIGURATIONS_CHANGED: only deviceId is relevant.
+     */
+    TvInputDeviceInfo deviceInfo;
+}
diff --git a/tv/input/aidl/android/hardware/tv/input/TvInputEventType.aidl b/tv/input/aidl/android/hardware/tv/input/TvInputEventType.aidl
new file mode 100644
index 0000000..fcbc3c2
--- /dev/null
+++ b/tv/input/aidl/android/hardware/tv/input/TvInputEventType.aidl
@@ -0,0 +1,77 @@
+/*
+ * 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.tv.input;
+
+@VintfStability
+@Backing(type="int")
+enum TvInputEventType {
+    /**
+     * Hardware notifies the framework that a device is available.
+     *
+     * Note that DEVICE_AVAILABLE and DEVICE_UNAVAILABLE events do not represent
+     * hotplug events (i.e. plugging cable into or out of the physical port).
+     * These events notify the framework whether the port is available or not.
+     * For a concrete example, when a user plugs in or pulls out the HDMI cable
+     * from a HDMI port, it does not generate DEVICE_AVAILABLE and/or
+     * DEVICE_UNAVAILABLE events. However, if a user inserts a pluggable USB
+     * tuner into the Android device, it must generate a DEVICE_AVAILABLE event
+     * and when the port is removed, it must generate a DEVICE_UNAVAILABLE
+     * event.
+     *
+     * For hotplug events, please see STREAM_CONFIGURATION_CHANGED for more
+     * details.
+     *
+     * HAL implementation must register devices by using this event when the
+     * device boots up. The framework must recognize device reported via this
+     * event only.
+     */
+    DEVICE_AVAILABLE = 1,
+
+    /**
+     * Hardware notifies the framework that a device is unavailable.
+     *
+     * HAL implementation must generate this event when a device registered
+     * by DEVICE_AVAILABLE is no longer available. For example,
+     * the event can indicate that a USB tuner is plugged out from the Android
+     * device.
+     *
+     * Note that this event is not for indicating cable plugged out of the port;
+     * for that purpose, the implementation must use
+     * STREAM_CONFIGURATION_CHANGED event. This event represents the port itself
+     * being no longer available.
+     */
+    DEVICE_UNAVAILABLE = 2,
+
+    /**
+     * Stream configurations are changed. Client must regard all open streams
+     * at the specific device are closed, and must call
+     * getStreamConfigurations() again, opening some of them if necessary.
+     *
+     * HAL implementation must generate this event when the available stream
+     * configurations change for any reason. A typical use case of this event
+     * is to notify the framework that the input signal has changed resolution,
+     * or that the cable is plugged out so that the number of available streams
+     * is 0.
+     *
+     * The implementation must use this event to indicate hotplug status of the
+     * port. the framework regards input devices with no available streams as
+     * disconnected, so the implementation can generate this event with no
+     * available streams to indicate that this device is disconnected, and vice
+     * versa.
+     */
+    STREAM_CONFIGURATIONS_CHANGED = 3,
+}
diff --git a/tv/input/aidl/android/hardware/tv/input/TvInputType.aidl b/tv/input/aidl/android/hardware/tv/input/TvInputType.aidl
new file mode 100644
index 0000000..a0f8270
--- /dev/null
+++ b/tv/input/aidl/android/hardware/tv/input/TvInputType.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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.tv.input;
+
+/**
+ * Type of physical TV input.
+ */
+@VintfStability
+@Backing(type="int")
+enum TvInputType {
+    OTHER = 1,
+    TUNER = 2,
+    COMPOSITE = 3,
+    SVIDEO = 4,
+    SCART = 5,
+    COMPONENT = 6,
+    VGA = 7,
+    DVI = 8,
+    HDMI = 9,
+    DISPLAY_PORT = 10,
+}
diff --git a/tv/input/aidl/android/hardware/tv/input/TvStreamConfig.aidl b/tv/input/aidl/android/hardware/tv/input/TvStreamConfig.aidl
new file mode 100644
index 0000000..af86953
--- /dev/null
+++ b/tv/input/aidl/android/hardware/tv/input/TvStreamConfig.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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.tv.input;
+
+@VintfStability
+parcelable TvStreamConfig {
+    int streamId;
+
+    /* Maximum video width in pixel. */
+    int maxVideoWidth;
+
+    /* Maximum video height in pixel. */
+    int maxVideoHeight;
+}
diff --git a/tv/input/aidl/default/Android.bp b/tv/input/aidl/default/Android.bp
new file mode 100644
index 0000000..66148c8
--- /dev/null
+++ b/tv/input/aidl/default/Android.bp
@@ -0,0 +1,31 @@
+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: "android.hardware.tv.input-service.example",
+    relative_install_path: "hw",
+    init_rc: ["input-default.rc"],
+    vintf_fragments: ["input-default.xml"],
+    vendor: true,
+    srcs: [
+        "TvInput.cpp",
+        "service.cpp",
+    ],
+    static_libs: [
+        "libaidlcommonsupport",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libutils",
+        "libcutils",
+        "libbinder_ndk",
+        "android.hardware.tv.input-V1-ndk",
+    ],
+}
diff --git a/tv/input/aidl/default/TvInput.cpp b/tv/input/aidl/default/TvInput.cpp
new file mode 100644
index 0000000..6c3a138
--- /dev/null
+++ b/tv/input/aidl/default/TvInput.cpp
@@ -0,0 +1,143 @@
+/*
+ * 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.tv.input-service.example"
+
+#include <utils/Log.h>
+
+#include "TvInput.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace tv {
+namespace input {
+
+TvInput::TvInput() {}
+
+void TvInput::init() {
+    // Set up TvInputDeviceInfo and TvStreamConfig
+    mDeviceInfos[0] = shared_ptr<TvInputDeviceInfoWrapper>(
+            new TvInputDeviceInfoWrapper(0, TvInputType::TUNER, true));
+    mDeviceInfos[1] = shared_ptr<TvInputDeviceInfoWrapper>(
+            new TvInputDeviceInfoWrapper(1, TvInputType::HDMI, true));
+    mDeviceInfos[3] = shared_ptr<TvInputDeviceInfoWrapper>(
+            new TvInputDeviceInfoWrapper(3, TvInputType::DISPLAY_PORT, true));
+
+    mStreamConfigs[0] = {
+            {1, shared_ptr<TvStreamConfigWrapper>(new TvStreamConfigWrapper(1, 720, 1080, false))}};
+    mStreamConfigs[1] = {{11, shared_ptr<TvStreamConfigWrapper>(
+                                      new TvStreamConfigWrapper(11, 360, 480, false))}};
+    mStreamConfigs[3] = {{5, shared_ptr<TvStreamConfigWrapper>(
+                                     new TvStreamConfigWrapper(5, 1080, 1920, false))}};
+}
+
+::ndk::ScopedAStatus TvInput::setCallback(const shared_ptr<ITvInputCallback>& in_callback) {
+    ALOGV("%s", __FUNCTION__);
+
+    mCallback = in_callback;
+
+    TvInputEvent event;
+    event.type = TvInputEventType::DEVICE_AVAILABLE;
+
+    event.deviceInfo = mDeviceInfos[0]->deviceInfo;
+    mCallback->notify(event);
+
+    event.deviceInfo = mDeviceInfos[1]->deviceInfo;
+    mCallback->notify(event);
+
+    event.deviceInfo = mDeviceInfos[3]->deviceInfo;
+    mCallback->notify(event);
+
+    return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus TvInput::getStreamConfigurations(int32_t in_deviceId,
+                                                      vector<TvStreamConfig>* _aidl_return) {
+    ALOGV("%s", __FUNCTION__);
+
+    if (mStreamConfigs.count(in_deviceId) == 0) {
+        ALOGW("Device with id %d isn't available", in_deviceId);
+        return ::ndk::ScopedAStatus::fromServiceSpecificError(STATUS_INVALID_ARGUMENTS);
+    }
+
+    for (auto const& iconfig : mStreamConfigs[in_deviceId]) {
+        _aidl_return->push_back(iconfig.second->streamConfig);
+    }
+
+    return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus TvInput::openStream(int32_t in_deviceId, int32_t in_streamId,
+                                         NativeHandle* _aidl_return) {
+    ALOGV("%s", __FUNCTION__);
+
+    if (mStreamConfigs.count(in_deviceId) == 0 ||
+        mStreamConfigs[in_deviceId].count(in_streamId) == 0) {
+        ALOGW("Stream with device id %d, stream id %d isn't available", in_deviceId, in_streamId);
+        return ::ndk::ScopedAStatus::fromServiceSpecificError(STATUS_INVALID_ARGUMENTS);
+    }
+    if (mStreamConfigs[in_deviceId][in_streamId]->isOpen) {
+        ALOGW("Stream with device id %d, stream id %d is already opened", in_deviceId, in_streamId);
+        return ::ndk::ScopedAStatus::fromServiceSpecificError(STATUS_INVALID_STATE);
+    }
+    mStreamConfigs[in_deviceId][in_streamId]->handle = createNativeHandle(in_streamId);
+    mStreamConfigs[in_deviceId][in_streamId]->isOpen = true;
+    NativeHandle aidlHandle = makeToAidl(mStreamConfigs[in_deviceId][in_streamId]->handle);
+    _aidl_return = &aidlHandle;
+    return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus TvInput::closeStream(int32_t in_deviceId, int32_t in_streamId) {
+    ALOGV("%s", __FUNCTION__);
+
+    if (mStreamConfigs.count(in_deviceId) == 0 ||
+        mStreamConfigs[in_deviceId].count(in_streamId) == 0) {
+        ALOGW("Stream with device id %d, stream id %d isn't available", in_deviceId, in_streamId);
+        return ::ndk::ScopedAStatus::fromServiceSpecificError(STATUS_INVALID_ARGUMENTS);
+    }
+    if (!mStreamConfigs[in_deviceId][in_streamId]->isOpen) {
+        ALOGW("Stream with device id %d, stream id %d is already closed", in_deviceId, in_streamId);
+        return ::ndk::ScopedAStatus::fromServiceSpecificError(STATUS_INVALID_STATE);
+    }
+    releaseNativeHandle(mStreamConfigs[in_deviceId][in_streamId]->handle);
+    mStreamConfigs[in_deviceId][in_streamId]->handle = nullptr;
+    mStreamConfigs[in_deviceId][in_streamId]->isOpen = false;
+    return ::ndk::ScopedAStatus::ok();
+}
+
+native_handle_t* TvInput::createNativeHandle(int fd) {
+    native_handle_t* nativeHandle = native_handle_create(1, 0);
+    if (nativeHandle == nullptr) {
+        ALOGE("[TVInput] Failed to create native_handle %d", errno);
+        return nullptr;
+    }
+    if (nativeHandle->numFds > 0) {
+        nativeHandle->data[0] = dup(fd);
+    }
+    return nativeHandle;
+}
+
+void TvInput::releaseNativeHandle(native_handle_t* handle) {
+    native_handle_close(handle);
+    native_handle_delete(handle);
+}
+
+}  // namespace input
+}  // namespace tv
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/tv/input/aidl/default/TvInput.h b/tv/input/aidl/default/TvInput.h
new file mode 100644
index 0000000..a72bca3
--- /dev/null
+++ b/tv/input/aidl/default/TvInput.h
@@ -0,0 +1,62 @@
+/*
+ * 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/tv/input/BnTvInput.h>
+#include <utils/KeyedVector.h>
+
+#include <map>
+#include "TvInputDeviceInfoWrapper.h"
+#include "TvStreamConfigWrapper.h"
+
+using namespace android;
+using namespace std;
+using ::aidl::android::hardware::common::NativeHandle;
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace tv {
+namespace input {
+
+class TvInput : public BnTvInput {
+  public:
+    TvInput();
+
+    ::ndk::ScopedAStatus setCallback(const shared_ptr<ITvInputCallback>& in_callback) override;
+    ::ndk::ScopedAStatus getStreamConfigurations(int32_t in_deviceId,
+                                                 vector<TvStreamConfig>* _aidl_return) override;
+    ::ndk::ScopedAStatus openStream(int32_t in_deviceId, int32_t in_streamId,
+                                    NativeHandle* _aidl_return) override;
+    ::ndk::ScopedAStatus closeStream(int32_t in_deviceId, int32_t in_streamId) override;
+
+    void init();
+
+  private:
+    native_handle_t* createNativeHandle(int fd);
+    void releaseNativeHandle(native_handle_t* handle);
+
+    shared_ptr<ITvInputCallback> mCallback;
+    map<int32_t, shared_ptr<TvInputDeviceInfoWrapper>> mDeviceInfos;
+    map<int32_t, map<int32_t, shared_ptr<TvStreamConfigWrapper>>> mStreamConfigs;
+};
+
+}  // namespace input
+}  // namespace tv
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/tv/input/aidl/default/TvInputDeviceInfoWrapper.h b/tv/input/aidl/default/TvInputDeviceInfoWrapper.h
new file mode 100644
index 0000000..d844cc8
--- /dev/null
+++ b/tv/input/aidl/default/TvInputDeviceInfoWrapper.h
@@ -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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/tv/input/TvInputDeviceInfo.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace tv {
+namespace input {
+
+class TvInputDeviceInfoWrapper {
+  public:
+    TvInputDeviceInfoWrapper() {}
+    TvInputDeviceInfoWrapper(int32_t deviceId_, TvInputType type_, bool isAvailable_) {
+        deviceInfo.deviceId = deviceId_;
+        deviceInfo.type = type_;
+        isAvailable = isAvailable_;
+    }
+
+    TvInputDeviceInfo deviceInfo;
+    bool isAvailable;
+};
+}  // namespace input
+}  // namespace tv
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/tv/input/aidl/default/TvStreamConfigWrapper.h b/tv/input/aidl/default/TvStreamConfigWrapper.h
new file mode 100644
index 0000000..05c7ca3
--- /dev/null
+++ b/tv/input/aidl/default/TvStreamConfigWrapper.h
@@ -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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/tv/input/TvStreamConfig.h>
+#include <aidlcommonsupport/NativeHandle.h>
+
+using namespace std;
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace tv {
+namespace input {
+
+class TvStreamConfigWrapper {
+  public:
+    TvStreamConfigWrapper() {}
+    TvStreamConfigWrapper(int32_t streamId_, int32_t maxVideoWidth_, int32_t maxVideoHeight_,
+                          bool isOpen_) {
+        streamConfig.streamId = streamId_;
+        streamConfig.maxVideoWidth = maxVideoWidth_;
+        streamConfig.maxVideoHeight = maxVideoHeight_;
+        isOpen = isOpen_;
+        handle = nullptr;
+    }
+
+    TvStreamConfig streamConfig;
+    bool isOpen;
+    native_handle_t* handle;
+};
+}  // namespace input
+}  // namespace tv
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/tv/input/aidl/default/input-default.rc b/tv/input/aidl/default/input-default.rc
new file mode 100644
index 0000000..1958b5c
--- /dev/null
+++ b/tv/input/aidl/default/input-default.rc
@@ -0,0 +1,5 @@
+service vendor.input-default /vendor/bin/hw/android.hardware.tv.input-service.example
+    interface aidl android.hardware.tv.input.ITvInput/default
+    class hal
+    user system
+    group system
diff --git a/tv/input/aidl/default/input-default.xml b/tv/input/aidl/default/input-default.xml
new file mode 100644
index 0000000..38ba151
--- /dev/null
+++ b/tv/input/aidl/default/input-default.xml
@@ -0,0 +1,6 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.tv.input</name>
+        <fqname>ITvInput/default</fqname>
+    </hal>
+</manifest>
\ No newline at end of file
diff --git a/tv/input/aidl/default/service.cpp b/tv/input/aidl/default/service.cpp
new file mode 100644
index 0000000..1021206
--- /dev/null
+++ b/tv/input/aidl/default/service.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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.tv.input-service.example"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <utils/Log.h>
+
+#include "TvInput.h"
+
+using ::aidl::android::hardware::tv::input::TvInput;
+
+int main() {
+    ABinderProcess_setThreadPoolMaxThreadCount(8);
+    std::shared_ptr<TvInput> tvInput = ndk::SharedRefBase::make<TvInput>();
+    tvInput->init();
+
+    const std::string instance = std::string() + TvInput::descriptor + "/default";
+    binder_status_t status =
+            AServiceManager_addService(tvInput->asBinder().get(), instance.c_str());
+    CHECK(status == STATUS_OK);
+
+    ABinderProcess_joinThreadPool();
+    return EXIT_FAILURE;  // should not reached
+}
diff --git a/tv/input/aidl/vts/functional/Android.bp b/tv/input/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..9829b6d
--- /dev/null
+++ b/tv/input/aidl/vts/functional/Android.bp
@@ -0,0 +1,29 @@
+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: "VtsHalTvInputTargetTest",
+    defaults: ["VtsHalTargetTestDefaults","use_libaidlvintf_gtest_helper_static",],
+    srcs: ["VtsHalTvInputTargetTest.cpp"],
+    static_libs: [
+        "android.hardware.tv.input-V1-ndk",
+        "android.media.audio.common.types-V1-ndk",
+        "android.hardware.common-V2-ndk",
+        "libaidlcommonsupport",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+        "libvndksupport",
+    ],
+    require_root: true,
+}
diff --git a/tv/input/aidl/vts/functional/VtsHalTvInputTargetTest.cpp b/tv/input/aidl/vts/functional/VtsHalTvInputTargetTest.cpp
new file mode 100644
index 0000000..ec83e29
--- /dev/null
+++ b/tv/input/aidl/vts/functional/VtsHalTvInputTargetTest.cpp
@@ -0,0 +1,296 @@
+/*
+ * 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 "VtsHalTvInputTargetTest.h"
+
+#include <android-base/properties.h>
+#include <android/binder_ibinder.h>
+#include <android/binder_process.h>
+#include <android/binder_status.h>
+
+using namespace VtsHalTvInputTargetTest;
+
+TvInputAidlTest::TvInputCallback::TvInputCallback(shared_ptr<TvInputAidlTest> parent)
+    : parent_(parent) {}
+
+::ndk::ScopedAStatus TvInputAidlTest::TvInputCallback::notify(const TvInputEvent& in_event) {
+    unique_lock<mutex> lock(parent_->mutex_);
+
+    switch (in_event.type) {
+        case TvInputEventType::DEVICE_AVAILABLE:
+            parent_->onDeviceAvailable(in_event.deviceInfo);
+            break;
+        case TvInputEventType::DEVICE_UNAVAILABLE:
+            parent_->onDeviceUnavailable(in_event.deviceInfo.deviceId);
+            break;
+        case TvInputEventType::STREAM_CONFIGURATIONS_CHANGED:
+            parent_->onStreamConfigurationsChanged(in_event.deviceInfo.deviceId);
+            break;
+    }
+    return ::ndk::ScopedAStatus::ok();
+}
+
+void TvInputAidlTest::SetUp() {
+    if (AServiceManager_isDeclared(GetParam().c_str())) {
+        ::ndk::SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
+        tv_input_ = ITvInput::fromBinder(binder);
+    } else {
+        tv_input_ = nullptr;
+    }
+    ASSERT_NE(tv_input_, nullptr);
+
+    tv_input_callback_ =
+            ::ndk::SharedRefBase::make<TvInputCallback>(shared_ptr<TvInputAidlTest>(this));
+    ASSERT_NE(tv_input_callback_, nullptr);
+
+    tv_input_->setCallback(tv_input_callback_);
+    // All events received within the timeout should be handled.
+    sleep(WAIT_FOR_EVENT_TIMEOUT);
+}
+
+void TvInputAidlTest::TearDown() {
+    tv_input_ = nullptr;
+}
+
+void TvInputAidlTest::onDeviceAvailable(const TvInputDeviceInfo& deviceInfo) {
+    ALOGD("onDeviceAvailable for device id %d", deviceInfo.deviceId);
+    device_info_.add(deviceInfo.deviceId, deviceInfo);
+}
+
+void TvInputAidlTest::onDeviceUnavailable(int32_t deviceId) {
+    ALOGD("onDeviceUnavailable for device id %d", deviceId);
+    device_info_.removeItem(deviceId);
+    stream_config_.removeItem(deviceId);
+}
+
+::ndk::ScopedAStatus TvInputAidlTest::onStreamConfigurationsChanged(int32_t deviceId) {
+    ALOGD("onStreamConfigurationsChanged for device id %d", deviceId);
+    return updateStreamConfigurations(deviceId);
+}
+
+::ndk::ScopedAStatus TvInputAidlTest::updateStreamConfigurations(int32_t deviceId) {
+    stream_config_.removeItem(deviceId);
+    vector<TvStreamConfig> list;
+    ::ndk::ScopedAStatus status = tv_input_->getStreamConfigurations(deviceId, &list);
+    if (status.isOk()) {
+        stream_config_.add(deviceId, list);
+    }
+    return status;
+}
+
+void TvInputAidlTest::updateAllStreamConfigurations() {
+    for (size_t i = 0; i < device_info_.size(); i++) {
+        int32_t device_id = device_info_.keyAt(i);
+        updateStreamConfigurations(device_id);
+    }
+}
+
+vector<size_t> TvInputAidlTest::getConfigIndices() {
+    vector<size_t> indices;
+    for (size_t i = 0; i < stream_config_.size(); i++) {
+        if (stream_config_.valueAt(i).size() != 0) {
+            indices.push_back(i);
+        }
+    }
+    return indices;
+}
+
+int32_t TvInputAidlTest::getNumNotIn(vector<int32_t>& nums) {
+    int32_t result = DEFAULT_ID;
+    int32_t size = static_cast<int32_t>(nums.size());
+    for (int32_t i = 0; i < size; i++) {
+        // Put every element to its target position, if possible.
+        int32_t target_pos = nums[i];
+        while (target_pos >= 0 && target_pos < size && i != target_pos &&
+               nums[i] != nums[target_pos]) {
+            swap(nums[i], nums[target_pos]);
+            target_pos = nums[i];
+        }
+    }
+
+    for (int32_t i = 0; i < size; i++) {
+        if (nums[i] != i) {
+            return i;
+        }
+    }
+    return result;
+}
+
+/*
+ * GetStreamConfigTest:
+ * Calls updateStreamConfigurations() for each existing device
+ * Checks returned results
+ */
+TEST_P(TvInputAidlTest, GetStreamConfigTest) {
+    unique_lock<mutex> lock(mutex_);
+
+    for (size_t i = 0; i < device_info_.size(); i++) {
+        int32_t device_id = device_info_.keyAt(i);
+        ALOGD("GetStreamConfigTest: device_id=%d", device_id);
+        ASSERT_TRUE(updateStreamConfigurations(device_id).isOk());
+    }
+}
+
+/*
+ * OpenAndCloseStreamTest:
+ * Calls openStream() and then closeStream() for each existing stream
+ * Checks returned results
+ */
+TEST_P(TvInputAidlTest, OpenAndCloseStreamTest) {
+    unique_lock<mutex> lock(mutex_);
+
+    updateAllStreamConfigurations();
+
+    for (size_t j = 0; j < stream_config_.size(); j++) {
+        int32_t device_id = stream_config_.keyAt(j);
+        vector<TvStreamConfig> config = stream_config_.valueAt(j);
+        for (size_t i = 0; i < config.size(); i++) {
+            int32_t stream_id = config[i].streamId;
+            ALOGD("OpenAndCloseStreamTest: open stream, device_id=%d, stream_id=%d", device_id,
+                  stream_id);
+            ASSERT_TRUE(tv_input_->openStream(device_id, stream_id, &handle_).isOk());
+            ALOGD("OpenAndCloseStreamTest: close stream, device_id=%d, stream_id=%d", device_id,
+                  stream_id);
+            ASSERT_TRUE(tv_input_->closeStream(device_id, stream_id).isOk());
+        }
+    }
+}
+
+/*
+ * InvalidDeviceIdTest:
+ * Calls updateStreamConfigurations(), openStream(), and closeStream()
+ * for a non-existing device
+ * Checks returned results
+ * The results should be ITvInput::STATUS_INVALID_ARGUMENTS
+ */
+TEST_P(TvInputAidlTest, InvalidDeviceIdTest) {
+    unique_lock<mutex> lock(mutex_);
+
+    vector<int32_t> device_ids;
+    for (size_t i = 0; i < device_info_.size(); i++) {
+        device_ids.push_back(device_info_.keyAt(i));
+    }
+    // Get a non-existing device ID.
+    int32_t id = getNumNotIn(device_ids);
+    ALOGD("InvalidDeviceIdTest: update stream config, device_id=%d", id);
+    ASSERT_TRUE(updateStreamConfigurations(id).getServiceSpecificError() ==
+                ITvInput::STATUS_INVALID_ARGUMENTS);
+
+    int32_t stream_id = 0;
+
+    ALOGD("InvalidDeviceIdTest: open stream, device_id=%d, stream_id=%d", id, stream_id);
+    ASSERT_TRUE(tv_input_->openStream(id, stream_id, &handle_).getServiceSpecificError() ==
+                ITvInput::STATUS_INVALID_ARGUMENTS);
+
+    ALOGD("InvalidDeviceIdTest: close stream, device_id=%d, stream_id=%d", id, stream_id);
+    ASSERT_TRUE(tv_input_->closeStream(id, stream_id).getServiceSpecificError() ==
+                ITvInput::STATUS_INVALID_ARGUMENTS);
+}
+
+/*
+ * InvalidStreamIdTest:
+ * Calls openStream(), and closeStream() for a non-existing stream
+ * Checks returned results
+ * The results should be ITvInput::STATUS_INVALID_ARGUMENTS
+ */
+TEST_P(TvInputAidlTest, InvalidStreamIdTest) {
+    unique_lock<mutex> lock(mutex_);
+
+    if (device_info_.isEmpty()) {
+        return;
+    }
+    updateAllStreamConfigurations();
+
+    int32_t device_id = device_info_.keyAt(0);
+    // Get a non-existing stream ID.
+    int32_t id = DEFAULT_ID;
+    if (stream_config_.indexOfKey(device_id) >= 0) {
+        vector<int32_t> stream_ids;
+        vector<TvStreamConfig> config = stream_config_.valueFor(device_id);
+        for (size_t i = 0; i < config.size(); i++) {
+            stream_ids.push_back(config[i].streamId);
+        }
+        id = getNumNotIn(stream_ids);
+    }
+
+    ALOGD("InvalidStreamIdTest: open stream, device_id=%d, stream_id=%d", device_id, id);
+    ASSERT_TRUE(tv_input_->openStream(device_id, id, &handle_).getServiceSpecificError() ==
+                ITvInput::STATUS_INVALID_ARGUMENTS);
+
+    ALOGD("InvalidStreamIdTest: close stream, device_id=%d, stream_id=%d", device_id, id);
+    ASSERT_TRUE(tv_input_->closeStream(device_id, id).getServiceSpecificError() ==
+                ITvInput::STATUS_INVALID_ARGUMENTS);
+}
+
+/*
+ * OpenAnOpenedStreamsTest:
+ * Calls openStream() twice for a stream (if any)
+ * Checks returned results
+ * The result of the second call should be ITvInput::STATUS_INVALID_STATE
+ */
+TEST_P(TvInputAidlTest, OpenAnOpenedStreamsTest) {
+    unique_lock<mutex> lock(mutex_);
+
+    updateAllStreamConfigurations();
+    vector<size_t> indices = getConfigIndices();
+    if (indices.empty()) {
+        return;
+    }
+    int32_t device_id = stream_config_.keyAt(indices[0]);
+    int32_t stream_id = stream_config_.valueAt(indices[0])[0].streamId;
+
+    ALOGD("OpenAnOpenedStreamsTest: open stream, device_id=%d, stream_id=%d", device_id, stream_id);
+    ASSERT_TRUE(tv_input_->openStream(device_id, stream_id, &handle_).isOk());
+
+    ALOGD("OpenAnOpenedStreamsTest: open stream, device_id=%d, stream_id=%d", device_id, stream_id);
+    ASSERT_TRUE(tv_input_->openStream(device_id, stream_id, &handle_).getServiceSpecificError() ==
+                ITvInput::STATUS_INVALID_STATE);
+
+    // close stream as subsequent tests assume no open streams
+    ALOGD("OpenAnOpenedStreamsTest: close stream, device_id=%d, stream_id=%d", device_id,
+          stream_id);
+    ASSERT_TRUE(tv_input_->closeStream(device_id, stream_id).isOk());
+}
+
+/*
+ * CloseStreamBeforeOpenTest:
+ * Calls closeStream() without calling openStream() for a stream (if any)
+ * Checks the returned result
+ * The result should be ITvInput::STATUS_INVALID_STATE
+ */
+TEST_P(TvInputAidlTest, CloseStreamBeforeOpenTest) {
+    unique_lock<mutex> lock(mutex_);
+
+    updateAllStreamConfigurations();
+    vector<size_t> indices = getConfigIndices();
+    if (indices.empty()) {
+        return;
+    }
+    int32_t device_id = stream_config_.keyAt(indices[0]);
+    int32_t stream_id = stream_config_.valueAt(indices[0])[0].streamId;
+
+    ALOGD("CloseStreamBeforeOpenTest: close stream, device_id=%d, stream_id=%d", device_id,
+          stream_id);
+    ASSERT_TRUE(tv_input_->closeStream(device_id, stream_id).getServiceSpecificError() ==
+                ITvInput::STATUS_INVALID_STATE);
+}
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, TvInputAidlTest,
+                         testing::ValuesIn(android::getAidlHalInstanceNames(ITvInput::descriptor)),
+                         android::PrintInstanceNameToString);
+
+// TODO remove from the allow list once the cf tv target is enabled for testing
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TvInputAidlTest);
diff --git a/tv/input/aidl/vts/functional/VtsHalTvInputTargetTest.h b/tv/input/aidl/vts/functional/VtsHalTvInputTargetTest.h
new file mode 100644
index 0000000..c76e568
--- /dev/null
+++ b/tv/input/aidl/vts/functional/VtsHalTvInputTargetTest.h
@@ -0,0 +1,90 @@
+/*
+ * 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 <android/binder_manager.h>
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+
+#include <aidl/android/hardware/tv/input/BnTvInputCallback.h>
+#include <aidl/android/hardware/tv/input/ITvInput.h>
+#include <aidl/android/hardware/tv/input/TvInputDeviceInfo.h>
+#include <aidl/android/hardware/tv/input/TvInputEvent.h>
+#include <aidl/android/hardware/tv/input/TvStreamConfig.h>
+
+#include <log/log.h>
+#include <utils/KeyedVector.h>
+
+using namespace aidl::android::hardware::tv::input;
+using namespace std;
+
+using ::aidl::android::hardware::common::NativeHandle;
+
+#define WAIT_FOR_EVENT_TIMEOUT 5
+#define DEFAULT_ID INT32_MIN
+
+namespace VtsHalTvInputTargetTest {
+
+class TvInputAidlTest : public testing::TestWithParam<string> {
+  public:
+    class TvInputCallback : public BnTvInputCallback {
+      public:
+        TvInputCallback(shared_ptr<TvInputAidlTest> parent);
+        ::ndk::ScopedAStatus notify(const TvInputEvent& in_event) override;
+
+      private:
+        shared_ptr<TvInputAidlTest> parent_;
+    };
+
+    virtual void SetUp() override;
+    virtual void TearDown() override;
+
+    /* Called when a DEVICE_AVAILABLE event is received. */
+    void onDeviceAvailable(const TvInputDeviceInfo& deviceInfo);
+
+    /* Called when a DEVICE_UNAVAILABLE event is received. */
+    void onDeviceUnavailable(int32_t deviceId);
+
+    /* Called when a STREAM_CONFIGURATIONS_CHANGED event is received. */
+    ::ndk::ScopedAStatus onStreamConfigurationsChanged(int32_t deviceId);
+
+    /* Gets and updates the stream configurations for a device. */
+    ::ndk::ScopedAStatus updateStreamConfigurations(int32_t deviceId);
+
+    /* Gets and updates the stream configurations for all existing devices. */
+    void updateAllStreamConfigurations();
+
+    /* Returns a list of indices of stream_config_ whose corresponding values are not empty. */
+    vector<size_t> getConfigIndices();
+
+    /*
+     * Returns DEFAULT_ID if there is no missing integer in the range [0, the size of nums).
+     * Otherwise, returns the smallest missing non-negative integer.
+     */
+    int32_t getNumNotIn(vector<int32_t>& nums);
+
+  protected:
+    shared_ptr<ITvInput> tv_input_;
+    shared_ptr<TvInputCallback> tv_input_callback_;
+    android::KeyedVector<int32_t, TvInputDeviceInfo> device_info_;
+    android::KeyedVector<int32_t, vector<TvStreamConfig>> stream_config_;
+    mutex mutex_;
+    NativeHandle handle_;
+};
+
+}  // namespace VtsHalTvInputTargetTest
diff --git a/tv/tuner/aidl/default/Android.bp b/tv/tuner/aidl/default/Android.bp
index cb8f87b..c10ad22 100644
--- a/tv/tuner/aidl/default/Android.bp
+++ b/tv/tuner/aidl/default/Android.bp
@@ -7,10 +7,9 @@
     default_applicable_licenses: ["hardware_interfaces_license"],
 }
 
-cc_binary {
-    name: "android.hardware.tv.tuner-service.example",
+cc_defaults {
+    name: "tuner_hal_example_defaults",
     relative_install_path: "hw",
-    init_rc: ["tuner-default.rc"],
     vintf_fragments: ["tuner-default.xml"],
     vendor: true,
     compile_multilib: "first",
@@ -44,3 +43,18 @@
         "media_plugin_headers",
     ],
 }
+
+cc_binary {
+    name: "android.hardware.tv.tuner-service.example",
+    defaults: ["tuner_hal_example_defaults"],
+    init_rc: ["tuner-default.rc"],
+}
+
+cc_binary {
+    name: "android.hardware.tv.tuner-service.example-lazy",
+    defaults: ["tuner_hal_example_defaults"],
+    init_rc: ["tuner-default-lazy.rc"],
+    cflags: [
+        "-DLAZY_HAL",
+    ],
+}
diff --git a/tv/tuner/aidl/default/Demux.cpp b/tv/tuner/aidl/default/Demux.cpp
index a94b4ad..60fd899 100644
--- a/tv/tuner/aidl/default/Demux.cpp
+++ b/tv/tuner/aidl/default/Demux.cpp
@@ -37,6 +37,7 @@
 }
 
 Demux::~Demux() {
+    ALOGV("%s", __FUNCTION__);
     close();
 }
 
@@ -180,7 +181,10 @@
     mRecordFilterIds.clear();
     mFilters.clear();
     mLastUsedFilterId = -1;
-    mTuner->removeDemux(mDemuxId);
+    if (mTuner != nullptr) {
+        mTuner->removeDemux(mDemuxId);
+        mTuner = nullptr;
+    }
 
     return ::ndk::ScopedAStatus::ok();
 }
diff --git a/tv/tuner/aidl/default/Filter.h b/tv/tuner/aidl/default/Filter.h
index b638f0c..f2d9248 100644
--- a/tv/tuner/aidl/default/Filter.h
+++ b/tv/tuner/aidl/default/Filter.h
@@ -50,8 +50,7 @@
 using ::android::hardware::EventFlag;
 
 using FilterMQ = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
-// Large buffer size can lead to sudden crashes due to being de-allocated
-// by the memory management system. Change the buffer size when needed.
+
 const uint32_t BUFFER_SIZE = 0x800000;  // 8 MB
 
 class Demux;
diff --git a/tv/tuner/aidl/default/Frontend.cpp b/tv/tuner/aidl/default/Frontend.cpp
index 3f7797c..0573d08 100644
--- a/tv/tuner/aidl/default/Frontend.cpp
+++ b/tv/tuner/aidl/default/Frontend.cpp
@@ -28,10 +28,10 @@
 namespace tv {
 namespace tuner {
 
-Frontend::Frontend(FrontendType type, int32_t id, std::shared_ptr<Tuner> tuner) {
+Frontend::Frontend(FrontendType type, int32_t id) {
     mType = type;
     mId = id;
-    mTuner = tuner;
+    mTuner = nullptr;
     // Init callback to nullptr
     mCallback = nullptr;
 
@@ -170,7 +170,12 @@
     }
 }
 
-Frontend::~Frontend() {}
+Frontend::~Frontend() {
+    ALOGV("%s", __FUNCTION__);
+    mCallback = nullptr;
+    mIsLocked = false;
+    mTuner = nullptr;
+}
 
 ::ndk::ScopedAStatus Frontend::close() {
     ALOGV("%s", __FUNCTION__);
@@ -178,6 +183,7 @@
     mCallback = nullptr;
     mIsLocked = false;
     mTuner->removeFrontend(mId);
+    mTuner = nullptr;
 
     return ::ndk::ScopedAStatus::ok();
 }
@@ -233,6 +239,10 @@
     return ::ndk::ScopedAStatus::ok();
 }
 
+void Frontend::setTunerService(std::shared_ptr<Tuner> tuner) {
+    mTuner = tuner;
+}
+
 void Frontend::scanThreadLoop() {
     if (mIsLocked) {
         FrontendScanMessage msg;
diff --git a/tv/tuner/aidl/default/Frontend.h b/tv/tuner/aidl/default/Frontend.h
index 1d9ab53..85bd636 100644
--- a/tv/tuner/aidl/default/Frontend.h
+++ b/tv/tuner/aidl/default/Frontend.h
@@ -34,7 +34,7 @@
 
 class Frontend : public BnFrontend {
   public:
-    Frontend(FrontendType type, int32_t id, std::shared_ptr<Tuner> tuner);
+    Frontend(FrontendType type, int32_t id);
 
     ::ndk::ScopedAStatus setCallback(
             const std::shared_ptr<IFrontendCallback>& in_callback) override;
@@ -62,6 +62,7 @@
     string getSourceFile();
     bool isLocked();
     void getFrontendInfo(FrontendInfo* _aidl_return);
+    void setTunerService(std::shared_ptr<Tuner> tuner);
 
   private:
     virtual ~Frontend();
diff --git a/tv/tuner/aidl/default/Tuner.cpp b/tv/tuner/aidl/default/Tuner.cpp
index fa74288..591f936 100644
--- a/tv/tuner/aidl/default/Tuner.cpp
+++ b/tv/tuner/aidl/default/Tuner.cpp
@@ -38,16 +38,16 @@
     // Static Frontends array to maintain local frontends information
     // Array index matches their FrontendId in the default impl
     mFrontendSize = 10;
-    mFrontends[0] = ndk::SharedRefBase::make<Frontend>(FrontendType::ISDBS, 0, this->ref<Tuner>());
-    mFrontends[1] = ndk::SharedRefBase::make<Frontend>(FrontendType::ATSC3, 1, this->ref<Tuner>());
-    mFrontends[2] = ndk::SharedRefBase::make<Frontend>(FrontendType::DVBC, 2, this->ref<Tuner>());
-    mFrontends[3] = ndk::SharedRefBase::make<Frontend>(FrontendType::DVBS, 3, this->ref<Tuner>());
-    mFrontends[4] = ndk::SharedRefBase::make<Frontend>(FrontendType::DVBT, 4, this->ref<Tuner>());
-    mFrontends[5] = ndk::SharedRefBase::make<Frontend>(FrontendType::ISDBT, 5, this->ref<Tuner>());
-    mFrontends[6] = ndk::SharedRefBase::make<Frontend>(FrontendType::ANALOG, 6, this->ref<Tuner>());
-    mFrontends[7] = ndk::SharedRefBase::make<Frontend>(FrontendType::ATSC, 7, this->ref<Tuner>());
-    mFrontends[8] = ndk::SharedRefBase::make<Frontend>(FrontendType::ISDBS3, 8, this->ref<Tuner>());
-    mFrontends[9] = ndk::SharedRefBase::make<Frontend>(FrontendType::DTMB, 9, this->ref<Tuner>());
+    mFrontends[0] = ndk::SharedRefBase::make<Frontend>(FrontendType::ISDBS, 0);
+    mFrontends[1] = ndk::SharedRefBase::make<Frontend>(FrontendType::ATSC3, 1);
+    mFrontends[2] = ndk::SharedRefBase::make<Frontend>(FrontendType::DVBC, 2);
+    mFrontends[3] = ndk::SharedRefBase::make<Frontend>(FrontendType::DVBS, 3);
+    mFrontends[4] = ndk::SharedRefBase::make<Frontend>(FrontendType::DVBT, 4);
+    mFrontends[5] = ndk::SharedRefBase::make<Frontend>(FrontendType::ISDBT, 5);
+    mFrontends[6] = ndk::SharedRefBase::make<Frontend>(FrontendType::ANALOG, 6);
+    mFrontends[7] = ndk::SharedRefBase::make<Frontend>(FrontendType::ATSC, 7);
+    mFrontends[8] = ndk::SharedRefBase::make<Frontend>(FrontendType::ISDBS3, 8);
+    mFrontends[9] = ndk::SharedRefBase::make<Frontend>(FrontendType::DTMB, 9);
 
     mMaxUsableFrontends[FrontendType::ISDBS] = 1;
     mMaxUsableFrontends[FrontendType::ATSC3] = 1;
@@ -89,6 +89,7 @@
                 static_cast<int32_t>(Result::INVALID_ARGUMENT));
     }
 
+    mFrontends[in_frontendId]->setTunerService(this->ref<Tuner>());
     *_aidl_return = mFrontends[in_frontendId];
     return ::ndk::ScopedAStatus::ok();
 }
diff --git a/tv/tuner/aidl/default/service.cpp b/tv/tuner/aidl/default/service.cpp
index ac8d779..f980f4a 100644
--- a/tv/tuner/aidl/default/service.cpp
+++ b/tv/tuner/aidl/default/service.cpp
@@ -30,8 +30,15 @@
     std::shared_ptr<Tuner> tuner = ndk::SharedRefBase::make<Tuner>();
     tuner->init();
 
+    binder_status_t status;
     const std::string instance = std::string() + Tuner::descriptor + "/default";
-    binder_status_t status = AServiceManager_addService(tuner->asBinder().get(), instance.c_str());
+#ifdef LAZY_HAL
+    ALOGD("Start as a lazy HAL");
+    status = AServiceManager_registerLazyService(tuner->asBinder().get(), instance.c_str());
+#else
+    ALOGD("Start as a normal HAL");
+    status = AServiceManager_addService(tuner->asBinder().get(), instance.c_str());
+#endif
     CHECK(status == STATUS_OK);
 
     ABinderProcess_joinThreadPool();
diff --git a/tv/tuner/aidl/default/tuner-default-lazy.rc b/tv/tuner/aidl/default/tuner-default-lazy.rc
new file mode 100644
index 0000000..6b3b183
--- /dev/null
+++ b/tv/tuner/aidl/default/tuner-default-lazy.rc
@@ -0,0 +1,9 @@
+service vendor.tuner-default-lazy /vendor/bin/hw/android.hardware.tv.tuner-service.example-lazy
+    class hal
+    user media
+    group mediadrm drmrpc
+    ioprio rt 4
+    task_profiles ProcessCapacityHigh
+    interface aidl android.hardware.tv.tuner.ITuner/default
+    oneshot
+    disabled
diff --git a/tv/tuner/aidl/default/tuner-default.rc b/tv/tuner/aidl/default/tuner-default.rc
index 661c219..f9315ae 100644
--- a/tv/tuner/aidl/default/tuner-default.rc
+++ b/tv/tuner/aidl/default/tuner-default.rc
@@ -5,3 +5,4 @@
     ioprio rt 4
     task_profiles ProcessCapacityHigh
     onrestart restart media.tuner
+    interface aidl android.hardware.tv.tuner.ITuner/default
diff --git a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
index b8e2912..9a17d05 100644
--- a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
+++ b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
@@ -170,7 +170,9 @@
     ASSERT_TRUE(mLnbTests.setVoltage(lnbConf.voltage));
     ASSERT_TRUE(mLnbTests.setTone(lnbConf.tone));
     ASSERT_TRUE(mLnbTests.setSatellitePosition(lnbConf.position));
-    broadcastSingleFilterTest(filterConf, frontendConf);
+    if (!frontendConf.isSoftwareFe) {
+        broadcastSingleFilterTest(filterConf, frontendConf);
+    }
     ASSERT_TRUE(mLnbTests.closeLnb());
     mLnbId = INVALID_LNB_ID;
 }
@@ -258,7 +260,9 @@
     for (auto msgName : lnbRecord.diseqcMsgs) {
         ASSERT_TRUE(mLnbTests.sendDiseqcMessage(diseqcMsgMap[msgName]));
     }
-    recordSingleFilterTest(filterConf, frontendConf, dvrConf);
+    if (!frontendConf.isSoftwareFe) {
+        recordSingleFilterTest(filterConf, frontendConf, dvrConf, Dataflow_Context::LNBRECORD);
+    }
     ASSERT_TRUE(mLnbTests.closeLnb());
     mLnbId = INVALID_LNB_ID;
 }
@@ -316,29 +320,47 @@
 }
 
 void TunerRecordAidlTest::recordSingleFilterTest(FilterConfig filterConf,
-                                                 FrontendConfig frontendConf, DvrConfig dvrConf) {
+                                                 FrontendConfig frontendConf, DvrConfig dvrConf,
+                                                 Dataflow_Context context) {
     int32_t demuxId;
     std::shared_ptr<IDemux> demux;
     ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
     mDvrTests.setDemux(demux);
 
     DvrConfig dvrSourceConfig;
-    if (record.hasFrontendConnection) {
+    if (context == Dataflow_Context::RECORD) {
+        if (record.hasFrontendConnection) {
+            int32_t feId;
+            mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+            ASSERT_TRUE(feId != INVALID_ID);
+            ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+            ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+            if (frontendConf.isSoftwareFe) {
+                mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[record.dvrSoftwareFeId]);
+            }
+            ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+            mFrontendTests.setDvrTests(&mDvrTests);
+        } else {
+            dvrSourceConfig = dvrMap[record.dvrSourceId];
+            ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrSourceConfig.type, dvrSourceConfig.bufferSize));
+            ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrSourceConfig.settings));
+            ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
+        }
+    } else if (context == Dataflow_Context::LNBRECORD) {
+        // If function arrives here, frontend should not be software, so no need to configure a dvr
+        // source or dvr fe connection that might be used for recording without an Lnb
         int32_t feId;
         mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
         ASSERT_TRUE(feId != INVALID_ID);
         ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
         ASSERT_TRUE(mFrontendTests.setFrontendCallback());
-        if (frontendConf.isSoftwareFe) {
-            mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[record.dvrSoftwareFeId]);
+        if (mLnbId != INVALID_LNB_ID) {
+            ASSERT_TRUE(mFrontendTests.setLnb(mLnbId));
+        } else {
+            FAIL();
         }
         ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
         mFrontendTests.setDvrTests(&mDvrTests);
-    } else {
-        dvrSourceConfig = dvrMap[record.dvrSourceId];
-        ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrSourceConfig.type, dvrSourceConfig.bufferSize));
-        ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrSourceConfig.settings));
-        ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
     }
 
     int64_t filterId;
@@ -358,24 +380,31 @@
     ASSERT_TRUE(mDvrTests.startDvrRecord());
     ASSERT_TRUE(mFilterTests.startFilter(filterId));
 
-    if (record.hasFrontendConnection) {
+    if (context == Dataflow_Context::RECORD) {
+        if (record.hasFrontendConnection) {
+            ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+        } else {
+            // Start DVR Source
+            mDvrTests.startPlaybackInputThread(
+                    dvrSourceConfig.playbackInputFile,
+                    dvrSourceConfig.settings.get<DvrSettings::Tag::playback>());
+            ASSERT_TRUE(mDvrTests.startDvrPlayback());
+        }
+    } else if (context == Dataflow_Context::LNBRECORD) {
         ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
-    } else {
-        // Start DVR Source
-        mDvrTests.startPlaybackInputThread(
-                dvrSourceConfig.playbackInputFile,
-                dvrSourceConfig.settings.get<DvrSettings::Tag::playback>());
-        ASSERT_TRUE(mDvrTests.startDvrPlayback());
     }
-
     mDvrTests.testRecordOutput();
     mDvrTests.stopRecordThread();
 
-    if (record.hasFrontendConnection) {
+    if (context == Dataflow_Context::RECORD) {
+        if (record.hasFrontendConnection) {
+            ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+        } else {
+            mDvrTests.stopPlaybackThread();
+            ASSERT_TRUE(mDvrTests.stopDvrPlayback());
+        }
+    } else if (context == Dataflow_Context::LNBRECORD) {
         ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
-    } else {
-        mDvrTests.stopPlaybackThread();
-        ASSERT_TRUE(mDvrTests.stopDvrPlayback());
     }
 
     ASSERT_TRUE(mFilterTests.stopFilter(filterId));
@@ -384,10 +413,14 @@
     ASSERT_TRUE(mFilterTests.closeFilter(filterId));
     mDvrTests.closeDvrRecord();
 
-    if (record.hasFrontendConnection) {
+    if (context == Dataflow_Context::RECORD) {
+        if (record.hasFrontendConnection) {
+            ASSERT_TRUE(mFrontendTests.closeFrontend());
+        } else {
+            mDvrTests.closeDvrPlayback();
+        }
+    } else if (context == Dataflow_Context::LNBRECORD) {
         ASSERT_TRUE(mFrontendTests.closeFrontend());
-    } else {
-        mDvrTests.closeDvrPlayback();
     }
 
     ASSERT_TRUE(mDemuxTests.closeDemux());
@@ -395,28 +428,45 @@
 
 void TunerDescramblerAidlTest::scrambledBroadcastTest(set<struct FilterConfig> mediaFilterConfs,
                                                       FrontendConfig frontendConf,
-                                                      DescramblerConfig descConfig) {
+                                                      DescramblerConfig descConfig,
+                                                      Dataflow_Context context) {
     int32_t demuxId;
     std::shared_ptr<IDemux> demux;
     ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
 
     DvrConfig dvrSourceConfig;
-    if (descrambling.hasFrontendConnection) {
+    if (context == Dataflow_Context::DESCRAMBLING) {
+        if (descrambling.hasFrontendConnection) {
+            int32_t feId;
+            mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+            ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+            ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+            if (frontendConf.isSoftwareFe) {
+                mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[descrambling.dvrSoftwareFeId]);
+            }
+            ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+            mFrontendTests.setDemux(demux);
+        } else {
+            dvrSourceConfig = dvrMap[descrambling.dvrSourceId];
+            mDvrTests.setDemux(demux);
+            ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrSourceConfig.type, dvrSourceConfig.bufferSize));
+            ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrSourceConfig.settings));
+            ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
+        }
+    } else if (context == Dataflow_Context::LNBDESCRAMBLING) {
         int32_t feId;
         mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
         ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
         ASSERT_TRUE(mFrontendTests.setFrontendCallback());
-        if (frontendConf.isSoftwareFe) {
-            mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[descrambling.dvrSoftwareFeId]);
+        if (mLnbId != INVALID_LNB_ID) {
+            ASSERT_TRUE(mFrontendTests.setLnb(mLnbId));
+        } else {
+            // If, for some reason, the test got here without failing. We fail it here.
+            ALOGD("mLnbId is null. Something went wrong. Exiting ScrambledBroadcastWithLnbId.");
+            FAIL();
         }
         ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
         mFrontendTests.setDemux(demux);
-    } else {
-        dvrSourceConfig = dvrMap[descrambling.dvrSourceId];
-        mDvrTests.setDemux(demux);
-        ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrSourceConfig.type, dvrSourceConfig.bufferSize));
-        ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrSourceConfig.settings));
-        ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
     }
 
     set<int64_t> filterIds;
@@ -447,24 +497,32 @@
         ASSERT_TRUE(mFilterTests.startFilter(*id));
     }
 
-    if (descrambling.hasFrontendConnection) {
-        // tune test
+    if (context == Dataflow_Context::DESCRAMBLING) {
+        if (descrambling.hasFrontendConnection) {
+            // tune test
+            ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+        } else {
+            // Start DVR Source
+            mDvrTests.startPlaybackInputThread(
+                    dvrSourceConfig.playbackInputFile,
+                    dvrSourceConfig.settings.get<DvrSettings::Tag::playback>());
+            ASSERT_TRUE(mDvrTests.startDvrPlayback());
+        }
+    } else if (context == Dataflow_Context::LNBDESCRAMBLING) {
         ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
-    } else {
-        // Start DVR Source
-        mDvrTests.startPlaybackInputThread(
-                dvrSourceConfig.playbackInputFile,
-                dvrSourceConfig.settings.get<DvrSettings::Tag::playback>());
-        ASSERT_TRUE(mDvrTests.startDvrPlayback());
     }
 
     ASSERT_TRUE(filterDataOutputTest());
 
-    if (descrambling.hasFrontendConnection) {
+    if (context == Dataflow_Context::DESCRAMBLING) {
+        if (descrambling.hasFrontendConnection) {
+            ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+        } else {
+            mDvrTests.stopPlaybackThread();
+            ASSERT_TRUE(mDvrTests.stopDvrPlayback());
+        }
+    } else if (context == Dataflow_Context::LNBDESCRAMBLING) {
         ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
-    } else {
-        mDvrTests.stopPlaybackThread();
-        ASSERT_TRUE(mDvrTests.stopDvrPlayback());
     }
 
     for (id = filterIds.begin(); id != filterIds.end(); id++) {
@@ -478,15 +536,50 @@
         ASSERT_TRUE(mFilterTests.closeFilter(*id));
     }
 
-    if (descrambling.hasFrontendConnection) {
+    if (context == Dataflow_Context::DESCRAMBLING) {
+        if (descrambling.hasFrontendConnection) {
+            ASSERT_TRUE(mFrontendTests.closeFrontend());
+        } else {
+            mDvrTests.closeDvrPlayback();
+        }
+    } else if (context == Dataflow_Context::LNBDESCRAMBLING) {
         ASSERT_TRUE(mFrontendTests.closeFrontend());
-    } else {
-        mDvrTests.closeDvrPlayback();
     }
 
     ASSERT_TRUE(mDemuxTests.closeDemux());
 }
 
+void TunerDescramblerAidlTest::scrambledBroadcastTestWithLnb(
+        set<struct FilterConfig>& mediaFilterConfs, FrontendConfig& frontendConf,
+        DescramblerConfig& descConfig, LnbConfig& lnbConfig) {
+    // We can test the Lnb individually and make sure it functions properly. If the frontend is
+    // software, we cannot test the whole dataflow. If the frontend is hardware, we can
+    if (lnbConfig.name.compare(emptyHardwareId) == 0) {
+        vector<int32_t> ids;
+        ASSERT_TRUE(mLnbTests.getLnbIds(ids));
+        ASSERT_TRUE(ids.size() > 0);
+        ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
+        mLnbId = ids[0];
+    } else {
+        ASSERT_TRUE(mLnbTests.openLnbByName(lnbConfig.name, mLnbId));
+    }
+    // Once Lnb is opened, test some of its basic functionality
+    ASSERT_TRUE(mLnbTests.setLnbCallback());
+    ASSERT_TRUE(mLnbTests.setVoltage(lnbConfig.voltage));
+    ASSERT_TRUE(mLnbTests.setTone(lnbConfig.tone));
+    ASSERT_TRUE(mLnbTests.setSatellitePosition(lnbConfig.position));
+    if (!frontendConf.isSoftwareFe) {
+        ALOGD("Frontend is not software, testing entire dataflow.");
+        scrambledBroadcastTest(mediaFilterConfs, frontendConf, descConfig,
+                               Dataflow_Context::LNBDESCRAMBLING);
+    } else {
+        ALOGD("Frontend is software, did not test the entire dataflow, but tested the Lnb "
+              "individually.");
+    }
+    ASSERT_TRUE(mLnbTests.closeLnb());
+    mLnbId = INVALID_LNB_ID;
+}
+
 TEST_P(TunerLnbAidlTest, SendDiseqcMessageToLnb) {
     description("Open and configure an Lnb with specific settings then send a diseqc msg to it.");
     if (!lnbLive.support) {
@@ -499,7 +592,6 @@
     }
     for (auto& combination : lnbLive_configs) {
         lnbLive = combination;
-
         if (lnbMap[lnbLive.lnbId].name.compare(emptyHardwareId) == 0) {
             vector<int32_t> ids;
             ASSERT_TRUE(mLnbTests.getLnbIds(ids));
@@ -525,17 +617,21 @@
     if (!live.hasFrontendConnection) {
         return;
     }
-    int32_t feId;
-    int32_t demuxId;
-    std::shared_ptr<IDemux> demux;
-    mFrontendTests.getFrontendIdByType(frontendMap[live.frontendId].type, feId);
-    ASSERT_TRUE(feId != INVALID_ID);
-    ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
-    ASSERT_TRUE(mFrontendTests.setFrontendCallback());
-    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
-    ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
-    ASSERT_TRUE(mDemuxTests.closeDemux());
-    ASSERT_TRUE(mFrontendTests.closeFrontend());
+    auto live_configs = generateLiveConfigurations();
+    for (auto& configuration : live_configs) {
+        live = configuration;
+        int32_t feId;
+        int32_t demuxId;
+        std::shared_ptr<IDemux> demux;
+        mFrontendTests.getFrontendIdByType(frontendMap[live.frontendId].type, feId);
+        ASSERT_TRUE(feId != INVALID_ID);
+        ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+        ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+        ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+        ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+        ASSERT_TRUE(mDemuxTests.closeDemux());
+        ASSERT_TRUE(mFrontendTests.closeFrontend());
+    }
 }
 
 TEST_P(TunerDemuxAidlTest, getAvSyncTime) {
@@ -543,40 +639,45 @@
     if (!live.hasFrontendConnection) {
         return;
     }
-    if (live.pcrFilterId.compare(emptyHardwareId) == 0) {
-        return;
-    }
-    int32_t feId;
-    int32_t demuxId;
-    std::shared_ptr<IDemux> demux;
-    int64_t mediaFilterId;
-    int64_t pcrFilterId;
-    int32_t avSyncHwId;
-    std::shared_ptr<IFilter> mediaFilter;
+    auto live_configs = generateLiveConfigurations();
+    for (auto& configuration : live_configs) {
+        live = configuration;
+        if (live.pcrFilterId.compare(emptyHardwareId) == 0) {
+            continue;
+        }
+        int32_t feId;
+        int32_t demuxId;
+        std::shared_ptr<IDemux> demux;
+        int64_t mediaFilterId;
+        int64_t pcrFilterId;
+        int32_t avSyncHwId;
+        std::shared_ptr<IFilter> mediaFilter;
 
-    mFrontendTests.getFrontendIdByType(frontendMap[live.frontendId].type, feId);
-    ASSERT_TRUE(feId != INVALID_ID);
-    ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
-    ASSERT_TRUE(mFrontendTests.setFrontendCallback());
-    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
-    ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
-    mFilterTests.setDemux(demux);
-    ASSERT_TRUE(mFilterTests.openFilterInDemux(filterMap[live.videoFilterId].type,
-                                               filterMap[live.videoFilterId].bufferSize));
-    ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(mediaFilterId));
-    ASSERT_TRUE(mFilterTests.configFilter(filterMap[live.videoFilterId].settings, mediaFilterId));
-    mediaFilter = mFilterTests.getFilterById(mediaFilterId);
-    ASSERT_TRUE(mFilterTests.openFilterInDemux(filterMap[live.pcrFilterId].type,
-                                               filterMap[live.pcrFilterId].bufferSize));
-    ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(pcrFilterId));
-    ASSERT_TRUE(mFilterTests.configFilter(filterMap[live.pcrFilterId].settings, pcrFilterId));
-    ASSERT_TRUE(mDemuxTests.getAvSyncId(mediaFilter, avSyncHwId));
-    ASSERT_TRUE(pcrFilterId == avSyncHwId);
-    ASSERT_TRUE(mDemuxTests.getAvSyncTime(pcrFilterId));
-    ASSERT_TRUE(mFilterTests.closeFilter(pcrFilterId));
-    ASSERT_TRUE(mFilterTests.closeFilter(mediaFilterId));
-    ASSERT_TRUE(mDemuxTests.closeDemux());
-    ASSERT_TRUE(mFrontendTests.closeFrontend());
+        mFrontendTests.getFrontendIdByType(frontendMap[live.frontendId].type, feId);
+        ASSERT_TRUE(feId != INVALID_ID);
+        ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+        ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+        ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+        ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+        mFilterTests.setDemux(demux);
+        ASSERT_TRUE(mFilterTests.openFilterInDemux(filterMap[live.videoFilterId].type,
+                                                   filterMap[live.videoFilterId].bufferSize));
+        ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(mediaFilterId));
+        ASSERT_TRUE(
+                mFilterTests.configFilter(filterMap[live.videoFilterId].settings, mediaFilterId));
+        mediaFilter = mFilterTests.getFilterById(mediaFilterId);
+        ASSERT_TRUE(mFilterTests.openFilterInDemux(filterMap[live.pcrFilterId].type,
+                                                   filterMap[live.pcrFilterId].bufferSize));
+        ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(pcrFilterId));
+        ASSERT_TRUE(mFilterTests.configFilter(filterMap[live.pcrFilterId].settings, pcrFilterId));
+        ASSERT_TRUE(mDemuxTests.getAvSyncId(mediaFilter, avSyncHwId));
+        ASSERT_TRUE(pcrFilterId == avSyncHwId);
+        ASSERT_TRUE(mDemuxTests.getAvSyncTime(pcrFilterId));
+        ASSERT_TRUE(mFilterTests.closeFilter(pcrFilterId));
+        ASSERT_TRUE(mFilterTests.closeFilter(mediaFilterId));
+        ASSERT_TRUE(mDemuxTests.closeDemux());
+        ASSERT_TRUE(mFrontendTests.closeFrontend());
+    }
 }
 
 TEST_P(TunerFilterAidlTest, StartFilterInDemux) {
@@ -585,7 +686,11 @@
         return;
     }
     // TODO use parameterized tests
-    configSingleFilterInDemuxTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
+    auto live_configs = generateLiveConfigurations();
+    for (auto& configuration : live_configs) {
+        live = configuration;
+        configSingleFilterInDemuxTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
+    }
 }
 
 TEST_P(TunerFilterAidlTest, ConfigIpFilterInDemuxWithCid) {
@@ -594,10 +699,14 @@
     if (!live.hasFrontendConnection) {
         return;
     }
-    if (live.ipFilterId.compare(emptyHardwareId) == 0) {
-        return;
+    auto live_configs = generateLiveConfigurations();
+    for (auto& configuration : live_configs) {
+        live = configuration;
+        if (live.ipFilterId.compare(emptyHardwareId) == 0) {
+            continue;
+        }
+        configSingleFilterInDemuxTest(filterMap[live.ipFilterId], frontendMap[live.frontendId]);
     }
-    configSingleFilterInDemuxTest(filterMap[live.ipFilterId], frontendMap[live.frontendId]);
 }
 
 TEST_P(TunerFilterAidlTest, ReconfigFilterToReceiveStartId) {
@@ -606,8 +715,13 @@
         return;
     }
     // TODO use parameterized tests
-    reconfigSingleFilterInDemuxTest(filterMap[live.videoFilterId], filterMap[live.videoFilterId],
-                                    frontendMap[live.frontendId]);
+    auto live_configs = generateLiveConfigurations();
+    for (auto& configuration : live_configs) {
+        live = configuration;
+        reconfigSingleFilterInDemuxTest(filterMap[live.videoFilterId],
+                                        filterMap[live.videoFilterId],
+                                        frontendMap[live.frontendId]);
+    }
 }
 
 TEST_P(TunerFilterAidlTest, SetFilterLinkage) {
@@ -644,7 +758,11 @@
         return;
     }
     // TODO use parameterized tests
-    testTimeFilter(timeFilterMap[timeFilter.timeFilterId]);
+    auto timeFilter_configs = generateTimeFilterConfigurations();
+    for (auto& configuration : timeFilter_configs) {
+        timeFilter = configuration;
+        testTimeFilter(timeFilterMap[timeFilter.timeFilterId]);
+    }
 }
 
 static bool isEventProducingFilter(const FilterConfig& filterConfig) {
@@ -887,8 +1005,12 @@
     if (!record.support) {
         return;
     }
-    recordSingleFilterTest(filterMap[record.recordFilterId], frontendMap[record.frontendId],
-                           dvrMap[record.dvrRecordId]);
+    auto record_configs = generateRecordConfigurations();
+    for (auto& configuration : record_configs) {
+        record = configuration;
+        recordSingleFilterTest(filterMap[record.recordFilterId], frontendMap[record.frontendId],
+                               dvrMap[record.dvrRecordId], Dataflow_Context::RECORD);
+    }
 }
 
 TEST_P(TunerRecordAidlTest, AttachFiltersToRecordTest) {
@@ -897,8 +1019,13 @@
     if (!record.support) {
         return;
     }
-    attachSingleFilterToRecordDvrTest(filterMap[record.recordFilterId],
-                                      frontendMap[record.frontendId], dvrMap[record.dvrRecordId]);
+    auto record_configs = generateRecordConfigurations();
+    for (auto& configuration : record_configs) {
+        record = configuration;
+        attachSingleFilterToRecordDvrTest(filterMap[record.recordFilterId],
+                                          frontendMap[record.frontendId],
+                                          dvrMap[record.dvrRecordId]);
+    }
 }
 
 TEST_P(TunerRecordAidlTest, LnbRecordDataFlowWithTsRecordFilterTest) {
@@ -924,7 +1051,11 @@
     if (!live.hasFrontendConnection) {
         return;
     }
-    mFrontendTests.tuneTest(frontendMap[live.frontendId]);
+    auto live_configs = generateLiveConfigurations();
+    for (auto& configuration : live_configs) {
+        live = configuration;
+        mFrontendTests.tuneTest(frontendMap[live.frontendId]);
+    }
 }
 
 TEST_P(TunerFrontendAidlTest, AutoScanFrontend) {
@@ -956,7 +1087,11 @@
     if (!live.hasFrontendConnection) {
         return;
     }
-    mFrontendTests.tuneTest(frontendMap[live.frontendId]);
+    auto live_configs = generateLiveConfigurations();
+    for (auto& configuration : live_configs) {
+        live = configuration;
+        mFrontendTests.tuneTest(frontendMap[live.frontendId]);
+    }
 }
 
 TEST_P(TunerFrontendAidlTest, BlindScanFrontendWithEndFrequency) {
@@ -976,10 +1111,14 @@
     if (!live.hasFrontendConnection) {
         return;
     }
-    if (!frontendMap[live.frontendId].canConnectToCiCam) {
-        return;
+    auto live_configs = generateLiveConfigurations();
+    for (auto& configuration : live_configs) {
+        live = configuration;
+        if (!frontendMap[live.frontendId].canConnectToCiCam) {
+            continue;
+        }
+        mFrontendTests.tuneTest(frontendMap[live.frontendId]);
     }
-    mFrontendTests.tuneTest(frontendMap[live.frontendId]);
 }
 
 TEST_P(TunerFrontendAidlTest, getHardwareInfo) {
@@ -987,7 +1126,11 @@
     if (!live.hasFrontendConnection) {
         return;
     }
-    mFrontendTests.debugInfoTest(frontendMap[live.frontendId]);
+    auto live_configs = generateLiveConfigurations();
+    for (auto& configuration : live_configs) {
+        live = configuration;
+        mFrontendTests.debugInfoTest(frontendMap[live.frontendId]);
+    }
 }
 
 TEST_P(TunerFrontendAidlTest, maxNumberOfFrontends) {
@@ -1003,7 +1146,11 @@
     if (!live.hasFrontendConnection) {
         return;
     }
-    mFrontendTests.statusReadinessTest(frontendMap[live.frontendId]);
+    auto live_configs = generateLiveConfigurations();
+    for (auto& configuration : live_configs) {
+        live = configuration;
+        mFrontendTests.statusReadinessTest(frontendMap[live.frontendId]);
+    }
 }
 
 TEST_P(TunerBroadcastAidlTest, BroadcastDataFlowVideoFilterTest) {
@@ -1011,7 +1158,11 @@
     if (!live.hasFrontendConnection) {
         return;
     }
-    broadcastSingleFilterTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
+    auto live_configs = generateLiveConfigurations();
+    for (auto& configuration : live_configs) {
+        live = configuration;
+        broadcastSingleFilterTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
+    }
 }
 
 TEST_P(TunerBroadcastAidlTest, BroadcastDataFlowAudioFilterTest) {
@@ -1019,7 +1170,11 @@
     if (!live.hasFrontendConnection) {
         return;
     }
-    broadcastSingleFilterTest(filterMap[live.audioFilterId], frontendMap[live.frontendId]);
+    auto live_configs = generateLiveConfigurations();
+    for (auto& configuration : live_configs) {
+        live = configuration;
+        broadcastSingleFilterTest(filterMap[live.audioFilterId], frontendMap[live.frontendId]);
+    }
 }
 
 TEST_P(TunerBroadcastAidlTest, BroadcastDataFlowSectionFilterTest) {
@@ -1027,10 +1182,14 @@
     if (!live.hasFrontendConnection) {
         return;
     }
-    if (live.sectionFilterId.compare(emptyHardwareId) == 0) {
-        return;
+    auto live_configs = generateLiveConfigurations();
+    for (auto& configuration : live_configs) {
+        live = configuration;
+        if (live.sectionFilterId.compare(emptyHardwareId) == 0) {
+            continue;
+        }
+        broadcastSingleFilterTest(filterMap[live.sectionFilterId], frontendMap[live.frontendId]);
     }
-    broadcastSingleFilterTest(filterMap[live.sectionFilterId], frontendMap[live.frontendId]);
 }
 
 TEST_P(TunerBroadcastAidlTest, IonBufferTest) {
@@ -1038,7 +1197,11 @@
     if (!live.hasFrontendConnection) {
         return;
     }
-    broadcastSingleFilterTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
+    auto live_configs = generateLiveConfigurations();
+    for (auto& configuration : live_configs) {
+        live = configuration;
+        broadcastSingleFilterTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
+    }
 }
 
 TEST_P(TunerBroadcastAidlTest, LnbBroadcastDataFlowVideoFilterTest) {
@@ -1063,7 +1226,12 @@
     if (!live.hasFrontendConnection) {
         return;
     }
-    mediaFilterUsingSharedMemoryTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
+    auto live_configs = generateLiveConfigurations();
+    for (auto& configuration : live_configs) {
+        live = configuration;
+        mediaFilterUsingSharedMemoryTest(filterMap[live.videoFilterId],
+                                         frontendMap[live.frontendId]);
+    }
 }
 
 TEST_P(TunerDescramblerAidlTest, CreateDescrambler) {
@@ -1119,7 +1287,29 @@
         filterConfs.insert(static_cast<FilterConfig>(filterMap[descrambling.audioFilterId]));
         filterConfs.insert(static_cast<FilterConfig>(filterMap[descrambling.videoFilterId]));
         scrambledBroadcastTest(filterConfs, frontendMap[descrambling.frontendId],
-                               descramblerMap[descrambling.descramblerId]);
+                               descramblerMap[descrambling.descramblerId],
+                               Dataflow_Context::DESCRAMBLING);
+    }
+}
+
+TEST_P(TunerDescramblerAidlTest, ScrambledBroadcastDataFlowMediaFiltersTestWithLnb) {
+    description("Test media filters in scrambled broadcast use case with Lnb");
+    if (!lnbDescrambling.support) {
+        return;
+    }
+    auto lnbDescrambling_configs = generateLnbDescramblingConfigurations();
+    if (lnbDescrambling_configs.empty()) {
+        ALOGD("No frontends that support satellites.");
+        return;
+    }
+    for (auto& configuration : lnbDescrambling_configs) {
+        lnbDescrambling = configuration;
+        set<FilterConfig> filterConfs;
+        filterConfs.insert(static_cast<FilterConfig>(filterMap[lnbDescrambling.audioFilterId]));
+        filterConfs.insert(static_cast<FilterConfig>(filterMap[lnbDescrambling.videoFilterId]));
+        scrambledBroadcastTestWithLnb(filterConfs, frontendMap[lnbDescrambling.frontendId],
+                                      descramblerMap[lnbDescrambling.descramblerId],
+                                      lnbMap[lnbDescrambling.lnbId]);
     }
 }
 
diff --git a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.h b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.h
index 2e69821..f68e1ec 100644
--- a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.h
+++ b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.h
@@ -68,7 +68,10 @@
     lnbIds.clear();
     diseqcMsgs.clear();
     frontendIds.clear();
+    ipFilterIds.clear();
+    pcrFilterIds.clear();
     recordDvrIds.clear();
+    timeFilterIds.clear();
     descramblerIds.clear();
     audioFilterIds.clear();
     videoFilterIds.clear();
@@ -77,6 +80,8 @@
     sectionFilterIds.clear();
 }
 
+enum class Dataflow_Context { LNBRECORD, RECORD, DESCRAMBLING, LNBDESCRAMBLING };
+
 class TunerLnbAidlTest : public testing::TestWithParam<std::string> {
   public:
     virtual void SetUp() override {
@@ -153,7 +158,7 @@
             mService = nullptr;
         }
         ASSERT_NE(mService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mFrontendTests.setService(mService);
         mDemuxTests.setService(mService);
@@ -264,7 +269,7 @@
             mService = nullptr;
         }
         ASSERT_NE(mService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mFrontendTests.setService(mService);
         mDemuxTests.setService(mService);
@@ -288,7 +293,7 @@
     void recordSingleFilterTestWithLnb(FilterConfig filterConf, FrontendConfig frontendConf,
                                        DvrConfig dvrConf, LnbConfig lnbConf);
     void recordSingleFilterTest(FilterConfig filterConf, FrontendConfig frontendConf,
-                                DvrConfig dvrConf);
+                                DvrConfig dvrConf, Dataflow_Context context);
 
     std::shared_ptr<ITuner> mService;
     FrontendTests mFrontendTests;
@@ -313,7 +318,7 @@
             mService = nullptr;
         }
         ASSERT_NE(mService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mFrontendTests.setService(mService);
     }
@@ -344,7 +349,7 @@
             mService = nullptr;
         }
         ASSERT_NE(mService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mFrontendTests.setService(mService);
         mDemuxTests.setService(mService);
@@ -402,6 +407,7 @@
         mDvrTests.setService(mService);
         mDescramblerTests.setService(mService);
         mDescramblerTests.setCasService(mCasService);
+        mLnbTests.setService(mService);
     }
 
     virtual void TearDown() override {
@@ -415,7 +421,11 @@
     }
 
     void scrambledBroadcastTest(set<struct FilterConfig> mediaFilterConfs,
-                                FrontendConfig frontendConf, DescramblerConfig descConfig);
+                                FrontendConfig frontendConf, DescramblerConfig descConfig,
+                                Dataflow_Context context);
+    void scrambledBroadcastTestWithLnb(set<struct FilterConfig>& mediaFilterConfs,
+                                       FrontendConfig& frontendConf, DescramblerConfig& descConfig,
+                                       LnbConfig& lnbConfig);
     AssertionResult filterDataOutputTest();
 
     std::shared_ptr<ITuner> mService;
@@ -425,6 +435,10 @@
     FilterTests mFilterTests;
     DescramblerTests mDescramblerTests;
     DvrTests mDvrTests;
+    LnbTests mLnbTests;
+
+  private:
+    int32_t mLnbId = INVALID_LNB_ID;
 };
 
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerDescramblerAidlTest);
diff --git a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTestConfigurations.h b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTestConfigurations.h
index de12dc0..fea5f83 100644
--- a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTestConfigurations.h
+++ b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTestConfigurations.h
@@ -74,6 +74,7 @@
 static LnbLiveHardwareConnections lnbLive;
 static LnbRecordHardwareConnections lnbRecord;
 static TimeFilterHardwareConnections timeFilter;
+static LnbDescramblingHardwareConnections lnbDescrambling;
 
 /*
  * This function takes in a 2d vector of device Id's
@@ -131,28 +132,28 @@
 /*
  * index 0 - playback dvr
  * index 1 - audio filters
- * index 2 - video filters
- * index 3 - optional section filters
+ * index 2 - optional section filters
  */
 static inline vector<DvrPlaybackHardwareConnections> generatePlaybackCombinations() {
     vector<DvrPlaybackHardwareConnections> combinations;
     vector<string> sectionFilterIds_optional = sectionFilterIds;
     sectionFilterIds_optional.push_back(emptyHardwareId);
-    vector<vector<string>> deviceIds{playbackDvrIds, audioFilterIds, videoFilterIds,
-                                     sectionFilterIds_optional};
+    vector<vector<string>> deviceIds{playbackDvrIds, audioFilterIds, sectionFilterIds_optional};
 
     const int dvrIndex = 0;
     const int audioFilterIndex = 1;
-    const int videoFilterIndex = 2;
-    const int sectionFilterIndex = 3;
+    const int sectionFilterIndex = 2;
 
     auto idCombinations = generateIdCombinations(deviceIds);
     for (auto& combo : idCombinations) {
         DvrPlaybackHardwareConnections mPlayback;
         mPlayback.dvrId = combo[dvrIndex];
         mPlayback.audioFilterId = combo[audioFilterIndex];
-        mPlayback.videoFilterId = combo[videoFilterIndex];
         mPlayback.sectionFilterId = combo[sectionFilterIndex];
+        const int videoFilterIndex =
+                find(audioFilterIds.begin(), audioFilterIds.end(), mPlayback.audioFilterId) -
+                audioFilterIds.begin();
+        mPlayback.videoFilterId = videoFilterIds[videoFilterIndex];
         combinations.push_back(mPlayback);
     }
 
@@ -176,17 +177,15 @@
 /*
  * index 0 - frontends
  * index 1 - audio filters
- * index 2 - video filters
- * index 3 - lnbs
+ * index 2 - lnbs
  */
 static inline vector<LnbLiveHardwareConnections> generateLnbLiveCombinations() {
     vector<LnbLiveHardwareConnections> combinations;
-    vector<vector<string>> deviceIds{frontendIds, audioFilterIds, videoFilterIds, lnbIds};
+    vector<vector<string>> deviceIds{frontendIds, audioFilterIds, lnbIds};
 
     const int frontendIndex = 0;
     const int audioFilterIndex = 1;
-    const int videoFilterIndex = 2;
-    const int lnbIndex = 3;
+    const int lnbIndex = 2;
 
     // TODO: Find a better way to vary diseqcMsgs, if at all
     auto idCombinations = generateIdCombinations(deviceIds);
@@ -198,7 +197,10 @@
             LnbLiveHardwareConnections mLnbLive;
             mLnbLive.frontendId = feId;
             mLnbLive.audioFilterId = combo[audioFilterIndex];
-            mLnbLive.videoFilterId = combo[videoFilterIndex];
+            const int videoFilterIndex =
+                    find(audioFilterIds.begin(), audioFilterIds.end(), mLnbLive.audioFilterId) -
+                    audioFilterIds.begin();
+            mLnbLive.videoFilterId = videoFilterIds[videoFilterIndex];
             mLnbLive.lnbId = combo[lnbIndex];
             mLnbLive.diseqcMsgs = diseqcMsgs;
             combinations.push_back(mLnbLive);
@@ -301,9 +303,8 @@
  * index 0 - decramblers
  * index 1 - frontends
  * index 2 - audio filters
- * index 3 - video filters
- * index 4 - Dvr SW Fe Connections
- * index 5 - DVR Source Connections
+ * index 3 - Dvr SW Fe Connections
+ * index 4 - DVR Source Connections
  */
 static inline vector<DescramblingHardwareConnections> generateDescramblingCombinations() {
     vector<DescramblingHardwareConnections> combinations;
@@ -320,12 +321,11 @@
     const int descramblerIndex = 0;
     const int frontendIndex = 1;
     const int audioFilterIndex = 2;
-    const int videoFilterIndex = 3;
-    const int dvrFeIdIndex = 4;
-    const int dvrSourceIdIndex = 5;
+    const int dvrFeIdIndex = 3;
+    const int dvrSourceIdIndex = 4;
 
-    vector<vector<string>> deviceIds{descramblerIds, mfrontendIds,        audioFilterIds,
-                                     videoFilterIds, mDvrFeConnectionIds, mDvrSourceConnectionIds};
+    vector<vector<string>> deviceIds{descramblerIds, mfrontendIds, audioFilterIds,
+                                     mDvrFeConnectionIds, mDvrSourceConnectionIds};
     auto idCombinations = generateIdCombinations(deviceIds);
     for (auto& combo : idCombinations) {
         DescramblingHardwareConnections mDescrambling;
@@ -353,7 +353,10 @@
         }
         mDescrambling.frontendId = feId;
         mDescrambling.audioFilterId = combo[audioFilterIndex];
-        mDescrambling.videoFilterId = combo[videoFilterIndex];
+        const int videoFilterIndex =
+                find(audioFilterIds.begin(), audioFilterIds.end(), mDescrambling.audioFilterId) -
+                audioFilterIds.begin();
+        mDescrambling.videoFilterId = videoFilterIds[videoFilterIndex];
         mDescrambling.dvrSoftwareFeId = dvrSwFeId;
         mDescrambling.dvrSourceId = dvrSourceId;
         mDescrambling.descramblerId = combo[descramblerIndex];
@@ -378,6 +381,206 @@
     return descrambling_configs;
 }
 
+static inline vector<TimeFilterHardwareConnections> generateTimeFilterCombinations() {
+    vector<TimeFilterHardwareConnections> combinations;
+
+    for (auto& id : timeFilterIds) {
+        TimeFilterHardwareConnections mTimeFilter;
+        mTimeFilter.timeFilterId = id;
+        combinations.push_back(mTimeFilter);
+    }
+
+    return combinations;
+}
+
+static inline vector<TimeFilterHardwareConnections> generateTimeFilterConfigurations() {
+    vector<TimeFilterHardwareConnections> timeFilter_configs;
+    if (configuredTimeFilter) {
+        ALOGD("Using TimeFilter configuration provided.");
+        timeFilter_configs = {timeFilter};
+    } else {
+        ALOGD("TimeFilter not provided. Generating possible combinations. Consider adding it to "
+              "the "
+              "configuration file.");
+        timeFilter_configs = generateTimeFilterCombinations();
+    }
+
+    return timeFilter_configs;
+}
+
+/*
+ * index 0 - frontends
+ * index 1 - record dvrs
+ * index 2 - record filters
+ */
+static inline vector<DvrRecordHardwareConnections> generateRecordCombinations() {
+    vector<DvrRecordHardwareConnections> combinations;
+
+    const int frontendIdIndex = 0;
+    const int recordDvrIndex = 1;
+    const int recordFilterIndex = 2;
+
+    vector<vector<string>> deviceIds{frontendIds, recordDvrIds, recordFilterIds};
+
+    auto idCombinations = generateIdCombinations(deviceIds);
+    for (auto& combo : idCombinations) {
+        DvrRecordHardwareConnections mRecord;
+        const string feId = combo[frontendIdIndex];
+        mRecord.hasFrontendConnection = true;
+        if (frontendMap[feId].isSoftwareFe) {
+            // If we have a software frontend, do not include configuration for testing.
+            continue;
+        }
+        mRecord.frontendId = feId;
+        mRecord.support = true;
+        mRecord.dvrSourceId = emptyHardwareId;
+        mRecord.dvrSoftwareFeId = emptyHardwareId;
+        mRecord.recordFilterId = combo[recordFilterIndex];
+        mRecord.dvrRecordId = combo[recordDvrIndex];
+        combinations.push_back(mRecord);
+    }
+
+    return combinations;
+}
+
+static inline vector<DvrRecordHardwareConnections> generateRecordConfigurations() {
+    vector<DvrRecordHardwareConnections> record_configs;
+    if (configuredRecord) {
+        ALOGD("Using Record configuration provided.");
+        record_configs = {record};
+    } else {
+        ALOGD("Record not provided. Generating possible combinations. Consider adding it to "
+              "the "
+              "configuration file.");
+        record_configs = generateRecordCombinations();
+    }
+
+    return record_configs;
+}
+
+/*
+ * index 0 - frontends
+ * index 1 - audio filters
+ * index 2 - playback dvrs
+ * index 3 - section Filters
+ */
+static inline vector<LiveBroadcastHardwareConnections> generateLiveCombinations() {
+    vector<LiveBroadcastHardwareConnections> combinations;
+    vector<string> mSectionFilterIds = sectionFilterIds;
+    vector<string> mDvrSwConnectionIds = playbackDvrIds;
+
+    // Adding the empty hardware id to cover cases where fields are optional
+    mSectionFilterIds.push_back(emptyHardwareId);
+    mDvrSwConnectionIds.push_back(emptyHardwareId);
+
+    const int frontendIdIndex = 0;
+    const int audioFilterIdIndex = 1;
+    const int dvrSwConnectionIdIndex = 2;
+    const int sectionFilterIdIndex = 3;
+
+    vector<vector<string>> deviceIds{frontendIds, audioFilterIds, mDvrSwConnectionIds,
+                                     mSectionFilterIds};
+
+    auto idCombinations = generateIdCombinations(deviceIds);
+    for (auto& combo : idCombinations) {
+        LiveBroadcastHardwareConnections mLive;
+        const string feId = combo[frontendIdIndex];
+        const string dvrSwConnectionId = combo[dvrSwConnectionIdIndex];
+        mLive.hasFrontendConnection = true;
+
+        if (frontendMap[feId].isSoftwareFe && dvrSwConnectionId.compare(emptyHardwareId) == 0) {
+            // If the frontend is a software frontend and there is no dvr playback connected, do not
+            // include configuration
+            continue;
+        }
+        mLive.frontendId = feId;
+        mLive.dvrSoftwareFeId = dvrSwConnectionId;
+        mLive.audioFilterId = combo[audioFilterIdIndex];
+        const int videoFilterIdIndex =
+                find(audioFilterIds.begin(), audioFilterIds.end(), mLive.audioFilterId) -
+                audioFilterIds.begin();
+        mLive.videoFilterId = videoFilterIds[videoFilterIdIndex];
+        mLive.sectionFilterId = combo[sectionFilterIdIndex];
+
+        if (pcrFilterIds.empty()) {
+            // If pcr Filters have not been provided, set it to empty hardware id
+            mLive.pcrFilterId = emptyHardwareId;
+        } else {
+            // If pcr Filters have been provided, use the first index if there is only 1, or choose
+            // the filter that corresponds to the correct audio and video filter pair
+            const int pcrFilterIdIndex = pcrFilterIds.size() == 1 ? 0 : videoFilterIdIndex;
+            mLive.pcrFilterId = pcrFilterIds[pcrFilterIdIndex];
+        }
+
+        combinations.push_back(mLive);
+    }
+
+    return combinations;
+}
+
+static inline vector<LiveBroadcastHardwareConnections> generateLiveConfigurations() {
+    vector<LiveBroadcastHardwareConnections> live_configs;
+    if (configuredLive) {
+        ALOGD("Using Live configuration provided.");
+        live_configs = {live};
+    } else {
+        ALOGD("Live not provided. Generating possible combinations. Consider adding it to "
+              "the "
+              "configuration file.");
+        live_configs = generateLiveCombinations();
+    }
+
+    return live_configs;
+}
+
+static inline vector<LnbDescramblingHardwareConnections> generateLnbDescramblingCombinations() {
+    vector<LnbDescramblingHardwareConnections> combinations;
+    vector<vector<string>> deviceIds{frontendIds, audioFilterIds, lnbIds, descramblerIds};
+
+    const int frontendIdIndex = 0;
+    const int audioFilterIdIndex = 1;
+    const int lnbIdIndex = 2;
+    const int descramblerIdIndex = 3;
+
+    auto idCombinations = generateIdCombinations(deviceIds);
+    // TODO : Find a better way to vary diseqcMsgs, if at all
+    for (auto& combo : idCombinations) {
+        const string feId = combo[frontendIdIndex];
+        auto type = frontendMap[feId].type;
+        if (type == FrontendType::DVBS || type == FrontendType::ISDBS ||
+            type == FrontendType::ISDBS3) {
+            LnbDescramblingHardwareConnections mLnbDescrambling;
+            mLnbDescrambling.support = true;
+            mLnbDescrambling.frontendId = feId;
+            mLnbDescrambling.audioFilterId = combo[audioFilterIdIndex];
+            const int videoFilterIdIndex = find(audioFilterIds.begin(), audioFilterIds.end(),
+                                                mLnbDescrambling.audioFilterId) -
+                                           audioFilterIds.begin();
+            mLnbDescrambling.videoFilterId = videoFilterIds[videoFilterIdIndex];
+            mLnbDescrambling.lnbId = combo[lnbIdIndex];
+            mLnbDescrambling.descramblerId = combo[descramblerIdIndex];
+            mLnbDescrambling.diseqcMsgs = diseqcMsgs;
+            combinations.push_back(mLnbDescrambling);
+        }
+    }
+
+    return combinations;
+}
+
+static inline vector<LnbDescramblingHardwareConnections> generateLnbDescramblingConfigurations() {
+    vector<LnbDescramblingHardwareConnections> lnbDescrambling_configs;
+    if (configuredLnbDescrambling) {
+        ALOGD("Using LnbDescrambling configuration provided");
+        lnbDescrambling_configs = {lnbDescrambling};
+    } else {
+        ALOGD("LnbDescrambling not provided. Generating possible combinations. Consider adding it "
+              "to the configuration file.");
+        lnbDescrambling_configs = generateLnbDescramblingCombinations();
+    }
+
+    return lnbDescrambling_configs;
+}
+
 /** Config all the frontends that would be used in the tests */
 inline void initFrontendConfig() {
     // The test will use the internal default fe when default fe is connected to any data flow
@@ -566,6 +769,15 @@
     record.support = true;
 }
 
+inline void determineLnbDescrambling() {
+    if (frontendIds.empty() || audioFilterIds.empty() || videoFilterIds.empty() || lnbIds.empty() ||
+        descramblerIds.empty()) {
+        return;
+    }
+    ALOGD("Can support LnbDescrambling.");
+    lnbDescrambling.support = true;
+}
+
 /** Read the vendor configurations of which hardware to use for each test cases/data flows */
 inline void connectHardwaresToTestCases() {
     TunerTestingConfigAidlReader1_0::connectLiveBroadcast(live);
@@ -576,6 +788,7 @@
     TunerTestingConfigAidlReader1_0::connectLnbLive(lnbLive);
     TunerTestingConfigAidlReader1_0::connectLnbRecord(lnbRecord);
     TunerTestingConfigAidlReader1_0::connectDvrPlayback(playback);
+    TunerTestingConfigAidlReader1_0::connectLnbDescrambling(lnbDescrambling);
 };
 
 inline void determineDataFlows() {
@@ -587,6 +800,7 @@
     determineLive();
     determineDescrambling();
     determineDvrRecord();
+    determineLnbDescrambling();
 }
 
 inline bool validateConnections() {
@@ -612,6 +826,10 @@
     feIsValid &=
             lnbRecord.support ? frontendMap.find(lnbRecord.frontendId) != frontendMap.end() : true;
 
+    feIsValid &= lnbDescrambling.support
+                         ? frontendMap.find(lnbDescrambling.frontendId) != frontendMap.end()
+                         : true;
+
     if (!feIsValid) {
         ALOGW("[vts config] dynamic config fe connection is invalid.");
         return false;
@@ -696,11 +914,29 @@
                 playback.hasExtraFilters ? filterMap.find(filterId) != filterMap.end() : true;
     }
 
+    filterIsValid &=
+            lnbDescrambling.support
+                    ? filterMap.find(lnbDescrambling.audioFilterId) != filterMap.end() &&
+                              filterMap.find(lnbDescrambling.videoFilterId) != filterMap.end()
+                    : true;
+
     if (!filterIsValid) {
         ALOGW("[vts config] dynamic config filter connection is invalid.");
         return false;
     }
 
+    if (audioFilterIds.size() != videoFilterIds.size()) {
+        ALOGW("[vts config] the number of audio and video filters should be equal");
+        return false;
+    }
+
+    if (!pcrFilterIds.empty() && pcrFilterIds.size() != 1 &&
+        pcrFilterIds.size() != audioFilterIds.size()) {
+        ALOGW("[vts config] When more than 1 pcr filter is configured, the number of pcr filters "
+              "must equal the number of audio and video filters.");
+        return false;
+    }
+
     bool timeFilterIsValid =
             timeFilter.support ? timeFilterMap.find(timeFilter.timeFilterId) != timeFilterMap.end()
                                : true;
@@ -714,6 +950,11 @@
                     ? descramblerMap.find(descrambling.descramblerId) != descramblerMap.end()
                     : true;
 
+    descramblerIsValid &=
+            lnbDescrambling.support
+                    ? descramblerMap.find(lnbDescrambling.descramblerId) != descramblerMap.end()
+                    : true;
+
     if (!descramblerIsValid) {
         ALOGW("[vts config] dynamic config descrambler connection is invalid.");
         return false;
@@ -723,6 +964,9 @@
 
     lnbIsValid &= lnbRecord.support ? lnbMap.find(lnbRecord.lnbId) != lnbMap.end() : true;
 
+    lnbIsValid &=
+            lnbDescrambling.support ? lnbMap.find(lnbDescrambling.lnbId) != lnbMap.end() : true;
+
     if (!lnbIsValid) {
         ALOGW("[vts config] dynamic config lnb connection is invalid.");
         return false;
@@ -738,6 +982,10 @@
         diseqcMsgsIsValid &= diseqcMsgMap.find(msg) != diseqcMsgMap.end();
     }
 
+    for (auto& msg : lnbDescrambling.diseqcMsgs) {
+        diseqcMsgsIsValid &= diseqcMsgMap.find(msg) != diseqcMsgMap.end();
+    }
+
     if (!diseqcMsgsIsValid) {
         ALOGW("[vts config] dynamic config diseqcMsg is invalid.");
         return false;
diff --git a/tv/tuner/config/TunerTestingConfigAidlReaderV1_0.h b/tv/tuner/config/TunerTestingConfigAidlReaderV1_0.h
index 6fafd59..9517520 100644
--- a/tv/tuner/config/TunerTestingConfigAidlReaderV1_0.h
+++ b/tv/tuner/config/TunerTestingConfigAidlReaderV1_0.h
@@ -73,13 +73,17 @@
 static bool configuredLnbRecord = false;
 static bool configuredTimeFilter = false;
 static bool configuredDescrambling = false;
+static bool configuredLnbDescrambling = false;
 
 const string emptyHardwareId = "";
 
 static string mConfigFilePath;
 
 static vector<string> playbackDvrIds;
+static vector<string> ipFilterIds;
 static vector<string> recordDvrIds;
+static vector<string> pcrFilterIds;
+static vector<string> timeFilterIds;
 static vector<string> audioFilterIds;
 static vector<string> videoFilterIds;
 static vector<string> recordFilterIds;
@@ -226,6 +230,16 @@
     string timeFilterId;
 };
 
+struct LnbDescramblingHardwareConnections {
+    bool support;
+    string frontendId;
+    string audioFilterId;
+    string videoFilterId;
+    string lnbId;
+    string descramblerId;
+    vector<string> diseqcMsgs;
+};
+
 struct TunerTestingConfigAidlReader1_0 {
   public:
     static void setConfigFilePath(string path) { mConfigFilePath = path; }
@@ -503,6 +517,7 @@
             auto timeFilters = *hardwareConfig.getFirstTimeFilters();
             for (auto timeFilterConfig : timeFilters.getTimeFilter()) {
                 string id = timeFilterConfig.getId();
+                timeFilterIds.push_back(id);
                 timeFilterMap[id].timeStamp = static_cast<int64_t>(timeFilterConfig.getTimeStamp());
             }
         }
@@ -701,6 +716,28 @@
         timeFilter.timeFilterId = timeFilterConfig.getTimeFilterConnection();
     }
 
+    static void connectLnbDescrambling(LnbDescramblingHardwareConnections& lnbDescrambling) {
+        auto dataFlow = getDataFlowConfiguration();
+        if (dataFlow.hasLnbDescrambling()) {
+            lnbDescrambling.support = true;
+            configuredLnbDescrambling = true;
+        } else {
+            lnbDescrambling.support = false;
+            return;
+        }
+        auto lnbDescramblingConfig = *dataFlow.getFirstLnbDescrambling();
+        lnbDescrambling.frontendId = lnbDescramblingConfig.getFrontendConnection();
+        lnbDescrambling.audioFilterId = lnbDescramblingConfig.getAudioFilterConnection();
+        lnbDescrambling.videoFilterId = lnbDescramblingConfig.getVideoFilterConnection();
+        lnbDescrambling.lnbId = lnbDescramblingConfig.getLnbConnection();
+        lnbDescrambling.descramblerId = lnbDescramblingConfig.getDescramblerConnection();
+        if (lnbDescramblingConfig.hasDiseqcMsgSender()) {
+            for (auto& msgName : lnbDescramblingConfig.getDiseqcMsgSender()) {
+                lnbDescrambling.diseqcMsgs.push_back(msgName);
+            }
+        }
+    }
+
   private:
     static FrontendDvbtSettings readDvbtFrontendSettings(Frontend feConfig) {
         ALOGW("[ConfigReader] fe type is dvbt");
@@ -851,6 +888,10 @@
             recordFilterIds.push_back(filterConfig.getId());
         } else if (subType == FilterSubTypeEnum::SECTION) {
             sectionFilterIds.push_back(filterConfig.getId());
+        } else if (subType == FilterSubTypeEnum::PCR) {
+            pcrFilterIds.push_back(filterConfig.getId());
+        } else if (subType == FilterSubTypeEnum::IP) {
+            ipFilterIds.push_back(filterConfig.getId());
         }
 
         switch (mainType) {
diff --git a/tv/tuner/config/api/current.txt b/tv/tuner/config/api/current.txt
index 79efb1b..dbd3486 100644
--- a/tv/tuner/config/api/current.txt
+++ b/tv/tuner/config/api/current.txt
@@ -27,6 +27,7 @@
     method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.Descrambling getDescrambling();
     method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.DvrPlayback getDvrPlayback();
     method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.DvrRecord getDvrRecord();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.LnbDescrambling getLnbDescrambling();
     method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.LnbLive getLnbLive();
     method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.LnbRecord getLnbRecord();
     method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.Scan getScan();
@@ -35,6 +36,7 @@
     method public void setDescrambling(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.Descrambling);
     method public void setDvrPlayback(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.DvrPlayback);
     method public void setDvrRecord(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.DvrRecord);
+    method public void setLnbDescrambling(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.LnbDescrambling);
     method public void setLnbLive(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.LnbLive);
     method public void setLnbRecord(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.LnbRecord);
     method public void setScan(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.Scan);
@@ -111,6 +113,22 @@
     method public void setRecordFilterConnection(@Nullable String);
   }
 
+  public static class DataFlowConfiguration.LnbDescrambling {
+    ctor public DataFlowConfiguration.LnbDescrambling();
+    method @Nullable public String getAudioFilterConnection();
+    method @Nullable public String getDescramblerConnection();
+    method @Nullable public java.util.List<java.lang.String> getDiseqcMsgSender();
+    method @Nullable public String getFrontendConnection();
+    method @Nullable public String getLnbConnection();
+    method @Nullable public String getVideoFilterConnection();
+    method public void setAudioFilterConnection(@Nullable String);
+    method public void setDescramblerConnection(@Nullable String);
+    method public void setDiseqcMsgSender(@Nullable java.util.List<java.lang.String>);
+    method public void setFrontendConnection(@Nullable String);
+    method public void setLnbConnection(@Nullable String);
+    method public void setVideoFilterConnection(@Nullable String);
+  }
+
   public static class DataFlowConfiguration.LnbLive {
     ctor public DataFlowConfiguration.LnbLive();
     method @Nullable public String getAudioFilterConnection();
diff --git a/tv/tuner/config/sample_tuner_vts_config_aidl_V1.xml b/tv/tuner/config/sample_tuner_vts_config_aidl_V1.xml
index 1a148a4..f74af7e 100644
--- a/tv/tuner/config/sample_tuner_vts_config_aidl_V1.xml
+++ b/tv/tuner/config/sample_tuner_vts_config_aidl_V1.xml
@@ -196,7 +196,6 @@
                       videoFilterConnection="FILTER_VIDEO_DEFAULT"
                       dvrSoftwareFeConnection="DVR_PLAYBACK_0"
                       dvrSourceConnection="DVR_PLAYBACK_1">
-                      <optionalFilters>FILTER_TS_RECORD_0 FILTER_IP_IP_0</optionalFilters>
         </descrambling>
         <timeFilter timeFilterConnection="TIME_FILTER_0"/>
         <lnbLive frontendConnection="FE_DVBS_0"
@@ -209,5 +208,11 @@
                    dvrRecordConnection="DVR_RECORD_0"
                    lnbConnection="LNB_0"
                    diseqcMsgSender="DISEQC_POWER_ON"/>
+        <lnbDescrambling frontendConnection="FE_DVBS_0"
+                        audioFilterConnection="FILTER_AUDIO_DEFAULT"
+                        videoFilterConnection="FILTER_VIDEO_DEFAULT"
+                        lnbConnection="LNB_1"
+                        descramblerConnection="DESCRAMBLER_0"/>
+
     </dataFlowConfiguration>
 </TunerConfiguration>
diff --git a/tv/tuner/config/tuner_testing_dynamic_configuration.xsd b/tv/tuner/config/tuner_testing_dynamic_configuration.xsd
index e0f2579..c51ac51 100644
--- a/tv/tuner/config/tuner_testing_dynamic_configuration.xsd
+++ b/tv/tuner/config/tuner_testing_dynamic_configuration.xsd
@@ -726,6 +726,16 @@
                     <xs:attribute name="timeFilterConnection" type="timeFilterId" use="required"/>
                 </xs:complexType>
             </xs:element>
+            <xs:element name="lnbDescrambling" minOccurs="0" maxOccurs="1">
+                <xs:complexType>
+                    <xs:attribute name="frontendConnection" type="frontendId" use="required"/>
+                    <xs:attribute name="audioFilterConnection" type="filterId" use="required"/>
+                    <xs:attribute name="videoFilterConnection" type="filterId" use="required"/>
+                    <xs:attribute name="lnbConnection" type="lnbId" use="required"/>
+                    <xs:attribute name="descramblerConnection" type="descramblerId" use="required"/>
+                    <xs:attribute name="diseqcMsgSender" type="diseqcMsgSender" use="optional"/>
+                </xs:complexType>
+            </xs:element>
         </xs:sequence>
     </xs:complexType>
 
diff --git a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
index 42d52f1..4768f55 100644
--- a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
+++ b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
@@ -30,6 +30,8 @@
     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
diff --git a/vibrator/aidl/default/Android.bp b/vibrator/aidl/default/Android.bp
index c4140be..78bb4ed 100644
--- a/vibrator/aidl/default/Android.bp
+++ b/vibrator/aidl/default/Android.bp
@@ -57,33 +57,12 @@
 cc_fuzz {
     name: "android.hardware.vibrator-service.example_fuzzer",
     host_supported: true,
+    defaults: ["service_fuzzer_defaults"],
     static_libs: [
         "android.hardware.vibrator-V2-ndk",
-        "libbase",
-        "libbinder_random_parcel",
-        "libcutils",
         "liblog",
         "libvibratorexampleimpl",
     ],
-    target: {
-        android: {
-            shared_libs: [
-                "libbinder_ndk",
-                "libbinder",
-                "libutils",
-            ],
-        },
-        host: {
-            static_libs: [
-                "libbinder_ndk",
-                "libbinder",
-                "libutils",
-            ],
-        },
-        darwin: {
-            enabled: false,
-        },
-    },
     srcs: ["fuzzer.cpp"],
     fuzz_config: {
         cc: [
diff --git a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
index 791d7e8..f50a5e7 100644
--- a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
+++ b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
@@ -444,7 +444,7 @@
 
             effect.delayMs = std::rand() % (maxDelay + 1);
             effect.primitive = primitive;
-            effect.scale = static_cast<float>(std::rand()) / RAND_MAX;
+            effect.scale = static_cast<float>(std::rand()) / static_cast<float>(RAND_MAX);
             composite.emplace_back(effect);
 
             if (composite.size() == maxSize) {
diff --git a/wifi/1.6/default/Android.bp b/wifi/1.6/default/Android.bp
index d48d183..a132dee 100644
--- a/wifi/1.6/default/Android.bp
+++ b/wifi/1.6/default/Android.bp
@@ -16,42 +16,61 @@
     default_applicable_licenses: ["hardware_interfaces_license"],
 }
 
-filegroup {
-    name: "android.hardware.wifi@1.0-service_srcs",
-    srcs: ["service.cpp"],
+soong_config_module_type {
+    name: "wifi_hal_cc_defaults",
+    module_type: "cc_defaults",
+    config_namespace: "wifi",
+    bool_variables: [
+        "hidl_feature_aware", // WIFI_HIDL_FEATURE_AWARE
+        "hidl_feature_dual_interface", // WIFI_HIDL_FEATURE_DUAL_INTERFACE
+        "hidl_feature_disable_ap", // WIFI_HIDL_FEATURE_DISABLE_AP
+        "hidl_feature_disable_ap_mac_randomization", // WIFI_HIDL_FEATURE_DISABLE_AP_MAC_RANDOMIZATION
+        "avoid_iface_reset_mac_change", // WIFI_AVOID_IFACE_RESET_MAC_CHANGE
+    ],
+    value_variables: [
+        "hal_interface_combinations", // WIFI_HAL_INTERFACE_COMBINATIONS
+    ],
+    properties: [
+        "cppflags",
+    ],
 }
 
-cc_defaults {
-    name: "android.hardware.wifi@1.0-service_default",
-    srcs: [":android.hardware.wifi@1.0-service_srcs"],
-    relative_install_path: "hw",
-    soc_specific: true,
-    shared_libs: [
-        "android.hardware.wifi@1.0",
-        "android.hardware.wifi@1.1",
-        "android.hardware.wifi@1.2",
-        "android.hardware.wifi@1.3",
-        "android.hardware.wifi@1.4",
-        "android.hardware.wifi@1.5",
-        "android.hardware.wifi@1.6",
-        "libbase",
-        "libcutils",
-        "libhidlbase",
-        "liblog",
-        "libnl",
-        "libutils",
-        "libwifi-system-iface",
-        "libxml2",
-    ],
+wifi_hal_cc_defaults {
+    name: "android.hardware.wifi@1.0-service-cppflags-defaults",
+    soong_config_variables: {
+        hidl_feature_aware: {
+            cppflags: ["-DWIFI_HIDL_FEATURE_AWARE"],
+        },
+        hidl_feature_dual_interface: {
+            cppflags: ["-DWIFI_HIDL_FEATURE_DUAL_INTERFACE"],
+        },
+        hidl_feature_disable_ap: {
+            cppflags: ["-DWIFI_HIDL_FEATURE_DISABLE_AP"],
+        },
+        hidl_feature_disable_ap_mac_randomization: {
+            cppflags: ["-DWIFI_HIDL_FEATURE_DISABLE_AP_MAC_RANDOMIZATION"],
+        },
+        avoid_iface_reset_mac_change: {
+            cppflags: ["-DWIFI_AVOID_IFACE_RESET_MAC_CHANGE"],
+        },
+        hal_interface_combinations: {
+            cppflags: ["-DWIFI_HAL_INTERFACE_COMBINATIONS=%s"],
+        },
+    },
+}
+
+cc_library_static {
+    name: "android.hardware.wifi@1.0-service-lib",
+    defaults: ["android.hardware.wifi@1.0-service-cppflags-defaults"],
+    proprietary: true,
+    compile_multilib: "first",
     cppflags: [
         "-Wall",
         "-Werror",
         "-Wextra",
     ],
-}
-
-filegroup {
-    name: "android.hardware.wifi@1.0-service-lib_srcs",
+    // Allow implicit fallthroughs in wifi_legacy_hal.cpp until they are fixed.
+    cflags: ["-Wno-error=implicit-fallthrough"],
     srcs: [
         "hidl_struct_util.cpp",
         "hidl_sync_util.cpp",
@@ -71,14 +90,17 @@
         "wifi_sta_iface.cpp",
         "wifi_status_util.cpp",
     ],
-}
 
-cc_defaults {
-    name: "android.hardware.wifi@1.0-service-lib_defaults",
-    srcs: [":android.hardware.wifi@1.0-service-lib_srcs"],
-    relative_install_path: "hw",
-    soc_specific: true,
     shared_libs: [
+        "libbase",
+        "libcutils",
+        "libhidlbase",
+        "liblog",
+        "libnl",
+        "libutils",
+        "libwifi-hal",
+        "libwifi-system-iface",
+        "libxml2",
         "android.hardware.wifi@1.0",
         "android.hardware.wifi@1.1",
         "android.hardware.wifi@1.2",
@@ -86,22 +108,121 @@
         "android.hardware.wifi@1.4",
         "android.hardware.wifi@1.5",
         "android.hardware.wifi@1.6",
+    ],
+
+    export_include_dirs: ["."],
+}
+
+cc_binary {
+    name: "android.hardware.wifi@1.0-service",
+    vintf_fragments: ["android.hardware.wifi@1.0-service.xml"],
+    relative_install_path: "hw",
+    proprietary: true,
+    cppflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+    srcs: ["service.cpp"],
+    shared_libs: [
         "libbase",
         "libcutils",
         "libhidlbase",
         "liblog",
         "libnl",
         "libutils",
+        "libwifi-hal",
         "libwifi-system-iface",
         "libxml2",
+        "android.hardware.wifi@1.0",
+        "android.hardware.wifi@1.1",
+        "android.hardware.wifi@1.2",
+        "android.hardware.wifi@1.3",
+        "android.hardware.wifi@1.4",
+        "android.hardware.wifi@1.5",
+        "android.hardware.wifi@1.6",
     ],
-    // Generated by building android.hardware.wifi@1.0-service-lib and printing LOCAL_CPPFLAGS.
+    static_libs: ["android.hardware.wifi@1.0-service-lib"],
+    init_rc: ["android.hardware.wifi@1.0-service.rc"],
+}
+
+cc_binary {
+    name: "android.hardware.wifi@1.0-service-lazy",
+    vintf_fragments: ["android.hardware.wifi@1.0-service.xml"],
+    overrides: ["android.hardware.wifi@1.0-service"],
+    cflags: ["-DLAZY_SERVICE"],
+    relative_install_path: "hw",
+    proprietary: true,
     cppflags: [
         "-Wall",
         "-Werror",
         "-Wextra",
-        "-DWIFI_HIDL_FEATURE_DUAL_INTERFACE",
     ],
-    export_include_dirs: ["."],
-    include_dirs: ["external/libxml2/include"],
+    srcs: ["service.cpp"],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libhidlbase",
+        "liblog",
+        "libnl",
+        "libutils",
+        "libwifi-hal",
+        "libwifi-system-iface",
+        "libxml2",
+        "android.hardware.wifi@1.0",
+        "android.hardware.wifi@1.1",
+        "android.hardware.wifi@1.2",
+        "android.hardware.wifi@1.3",
+        "android.hardware.wifi@1.4",
+        "android.hardware.wifi@1.5",
+        "android.hardware.wifi@1.6",
+    ],
+    static_libs: ["android.hardware.wifi@1.0-service-lib"],
+    init_rc: ["android.hardware.wifi@1.0-service-lazy.rc"],
+}
+
+cc_test {
+    name: "android.hardware.wifi@1.0-service-tests",
+    proprietary: true,
+    compile_multilib: "first",
+    cppflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+    srcs: [
+        "tests/hidl_struct_util_unit_tests.cpp",
+        "tests/main.cpp",
+        "tests/mock_interface_tool.cpp",
+        "tests/mock_wifi_feature_flags.cpp",
+        "tests/mock_wifi_iface_util.cpp",
+        "tests/mock_wifi_legacy_hal.cpp",
+        "tests/mock_wifi_mode_controller.cpp",
+        "tests/ringbuffer_unit_tests.cpp",
+        "tests/wifi_nan_iface_unit_tests.cpp",
+        "tests/wifi_chip_unit_tests.cpp",
+        "tests/wifi_iface_util_unit_tests.cpp",
+    ],
+    static_libs: [
+        "libgmock",
+        "libgtest",
+        "android.hardware.wifi@1.0",
+        "android.hardware.wifi@1.1",
+        "android.hardware.wifi@1.2",
+        "android.hardware.wifi@1.3",
+        "android.hardware.wifi@1.4",
+        "android.hardware.wifi@1.5",
+        "android.hardware.wifi@1.6",
+        "android.hardware.wifi@1.0-service-lib",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libhidlbase",
+        "liblog",
+        "libnl",
+        "libutils",
+        "libwifi-hal",
+        "libwifi-system-iface",
+    ],
 }
diff --git a/wifi/1.6/default/Android.mk b/wifi/1.6/default/Android.mk
deleted file mode 100644
index ca1c022..0000000
--- a/wifi/1.6/default/Android.mk
+++ /dev/null
@@ -1,202 +0,0 @@
-# Copyright (C) 2016 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.
-LOCAL_PATH := $(call my-dir)
-
-###
-### android.hardware.wifi static library
-###
-include $(CLEAR_VARS)
-LOCAL_MODULE := android.hardware.wifi@1.0-service-lib
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../NOTICE
-LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_PROPRIETARY_MODULE := true
-LOCAL_CPPFLAGS := -Wall -Werror -Wextra
-ifdef WIFI_HAL_INTERFACE_COMBINATIONS
-LOCAL_CPPFLAGS += -DWIFI_HAL_INTERFACE_COMBINATIONS="$(WIFI_HAL_INTERFACE_COMBINATIONS)"
-endif
-ifdef WIFI_HIDL_FEATURE_AWARE
-LOCAL_CPPFLAGS += -DWIFI_HIDL_FEATURE_AWARE
-endif
-ifdef WIFI_HIDL_FEATURE_DUAL_INTERFACE
-LOCAL_CPPFLAGS += -DWIFI_HIDL_FEATURE_DUAL_INTERFACE
-endif
-ifdef WIFI_HIDL_FEATURE_DISABLE_AP
-LOCAL_CPPFLAGS += -DWIFI_HIDL_FEATURE_DISABLE_AP
-endif
-ifdef WIFI_HIDL_FEATURE_DISABLE_AP_MAC_RANDOMIZATION
-LOCAL_CPPFLAGS += -DWIFI_HIDL_FEATURE_DISABLE_AP_MAC_RANDOMIZATION
-endif
-ifdef WIFI_AVOID_IFACE_RESET_MAC_CHANGE
-LOCAL_CPPFLAGS += -DWIFI_AVOID_IFACE_RESET_MAC_CHANGE
-endif
-# Allow implicit fallthroughs in wifi_legacy_hal.cpp until they are fixed.
-LOCAL_CFLAGS += -Wno-error=implicit-fallthrough
-LOCAL_SRC_FILES := \
-    hidl_struct_util.cpp \
-    hidl_sync_util.cpp \
-    ringbuffer.cpp \
-    wifi.cpp \
-    wifi_ap_iface.cpp \
-    wifi_chip.cpp \
-    wifi_feature_flags.cpp \
-    wifi_iface_util.cpp \
-    wifi_legacy_hal.cpp \
-    wifi_legacy_hal_factory.cpp \
-    wifi_legacy_hal_stubs.cpp \
-    wifi_mode_controller.cpp \
-    wifi_nan_iface.cpp \
-    wifi_p2p_iface.cpp \
-    wifi_rtt_controller.cpp \
-    wifi_sta_iface.cpp \
-    wifi_status_util.cpp
-LOCAL_SHARED_LIBRARIES := \
-    libbase \
-    libcutils \
-    libhidlbase \
-    liblog \
-    libnl \
-    libutils \
-    libwifi-hal \
-    libwifi-system-iface \
-    libxml2 \
-    android.hardware.wifi@1.0 \
-    android.hardware.wifi@1.1 \
-    android.hardware.wifi@1.2 \
-    android.hardware.wifi@1.3 \
-    android.hardware.wifi@1.4 \
-    android.hardware.wifi@1.5 \
-    android.hardware.wifi@1.6
-LOCAL_C_INCLUDES += $(TOP)/external/libxml2/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
-include $(BUILD_STATIC_LIBRARY)
-
-###
-### android.hardware.wifi daemon
-###
-include $(CLEAR_VARS)
-LOCAL_MODULE := android.hardware.wifi@1.0-service
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../NOTICE
-LOCAL_VINTF_FRAGMENTS := android.hardware.wifi@1.0-service.xml
-LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_PROPRIETARY_MODULE := true
-LOCAL_CPPFLAGS := -Wall -Werror -Wextra
-LOCAL_SRC_FILES := \
-    service.cpp
-LOCAL_SHARED_LIBRARIES := \
-    libbase \
-    libcutils \
-    libhidlbase \
-    liblog \
-    libnl \
-    libutils \
-    libwifi-hal \
-    libwifi-system-iface \
-    libxml2 \
-    android.hardware.wifi@1.0 \
-    android.hardware.wifi@1.1 \
-    android.hardware.wifi@1.2 \
-    android.hardware.wifi@1.3 \
-    android.hardware.wifi@1.4 \
-    android.hardware.wifi@1.5 \
-    android.hardware.wifi@1.6
-LOCAL_STATIC_LIBRARIES := \
-    android.hardware.wifi@1.0-service-lib
-LOCAL_INIT_RC := android.hardware.wifi@1.0-service.rc
-include $(BUILD_EXECUTABLE)
-
-###
-### android.hardware.wifi daemon
-###
-include $(CLEAR_VARS)
-LOCAL_MODULE := android.hardware.wifi@1.0-service-lazy
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../NOTICE
-LOCAL_VINTF_FRAGMENTS := android.hardware.wifi@1.0-service.xml
-LOCAL_OVERRIDES_MODULES := android.hardware.wifi@1.0-service
-LOCAL_CFLAGS := -DLAZY_SERVICE
-LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_PROPRIETARY_MODULE := true
-LOCAL_CPPFLAGS := -Wall -Werror -Wextra
-LOCAL_SRC_FILES := \
-    service.cpp
-LOCAL_SHARED_LIBRARIES := \
-    libbase \
-    libcutils \
-    libhidlbase \
-    liblog \
-    libnl \
-    libutils \
-    libwifi-hal \
-    libwifi-system-iface \
-    libxml2 \
-    android.hardware.wifi@1.0 \
-    android.hardware.wifi@1.1 \
-    android.hardware.wifi@1.2 \
-    android.hardware.wifi@1.3 \
-    android.hardware.wifi@1.4 \
-    android.hardware.wifi@1.5 \
-    android.hardware.wifi@1.6
-LOCAL_STATIC_LIBRARIES := \
-    android.hardware.wifi@1.0-service-lib
-LOCAL_INIT_RC := android.hardware.wifi@1.0-service-lazy.rc
-include $(BUILD_EXECUTABLE)
-
-###
-### android.hardware.wifi unit tests.
-###
-include $(CLEAR_VARS)
-LOCAL_MODULE := android.hardware.wifi@1.0-service-tests
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../NOTICE
-LOCAL_PROPRIETARY_MODULE := true
-LOCAL_CPPFLAGS := -Wall -Werror -Wextra
-LOCAL_SRC_FILES := \
-    tests/hidl_struct_util_unit_tests.cpp \
-    tests/main.cpp \
-    tests/mock_interface_tool.cpp \
-    tests/mock_wifi_feature_flags.cpp \
-    tests/mock_wifi_iface_util.cpp \
-    tests/mock_wifi_legacy_hal.cpp \
-    tests/mock_wifi_mode_controller.cpp \
-    tests/ringbuffer_unit_tests.cpp \
-    tests/wifi_nan_iface_unit_tests.cpp \
-    tests/wifi_chip_unit_tests.cpp \
-    tests/wifi_iface_util_unit_tests.cpp
-LOCAL_STATIC_LIBRARIES := \
-    libgmock \
-    libgtest \
-    android.hardware.wifi@1.0 \
-    android.hardware.wifi@1.1 \
-    android.hardware.wifi@1.2 \
-    android.hardware.wifi@1.3 \
-    android.hardware.wifi@1.4 \
-    android.hardware.wifi@1.5 \
-    android.hardware.wifi@1.6 \
-    android.hardware.wifi@1.0-service-lib
-LOCAL_SHARED_LIBRARIES := \
-    libbase \
-    libcutils \
-    libhidlbase \
-    liblog \
-    libnl \
-    libutils \
-    libwifi-hal \
-    libwifi-system-iface
-include $(BUILD_NATIVE_TEST)
diff --git a/wifi/1.6/default/wifi_chip.cpp b/wifi/1.6/default/wifi_chip.cpp
index c7c00b1..920beb8 100644
--- a/wifi/1.6/default/wifi_chip.cpp
+++ b/wifi/1.6/default/wifi_chip.cpp
@@ -1926,9 +1926,10 @@
 // concurrent STA and not dual AP, else start with idx 0.
 std::string WifiChip::allocateApIfaceName() {
     // Check if we have a dedicated iface for AP.
-    std::vector<std::string> ifnames = getPredefinedApIfaceNames(false);
-    if (!ifnames.empty()) {
-        return ifnames[0];
+    std::vector<std::string> ifnames = getPredefinedApIfaceNames(true);
+    for (auto const& ifname : ifnames) {
+        if (findUsingName(ap_ifaces_, ifname)) continue;
+        return ifname;
     }
     return allocateApOrStaIfaceName(IfaceType::AP, startIdxOfApIface());
 }
diff --git a/wifi/1.6/default/wifi_legacy_hal.cpp b/wifi/1.6/default/wifi_legacy_hal.cpp
index 2211897..c9bcdc5 100644
--- a/wifi/1.6/default/wifi_legacy_hal.cpp
+++ b/wifi/1.6/default/wifi_legacy_hal.cpp
@@ -555,7 +555,7 @@
 
     status = global_func_table_.wifi_read_packet_filter(
             getIfaceHandle(iface_name), /*src_offset=*/0, buffer.data(), buffer.size());
-    return {status, move(buffer)};
+    return {status, std::move(buffer)};
 }
 
 std::pair<wifi_error, wifi_gscan_capabilities> WifiLegacyHal::getGscanCapabilities(