audio: Provide operations common to input and output streams

Introduce IStreamCommon which contains methods that apply
both to input and output streams. Move the 'close' method
to it. Add 'IStreamIn/Out.getStreamCommon' method to retrieve
the common interface.

Add methods for dealing with HW AV Sync and vendor parameters.

Bug: 205884982
Test: atest VtsHalAudioCoreTargetTest
Change-Id: Ie1a7b32e28425f5398afc95192fcbc5403b5c332
diff --git a/audio/aidl/Android.bp b/audio/aidl/Android.bp
index 8f0286f..674b644 100644
--- a/audio/aidl/Android.bp
+++ b/audio/aidl/Android.bp
@@ -115,6 +115,7 @@
         "android/hardware/audio/core/IModule.aidl",
         "android/hardware/audio/core/ISoundDose.aidl",
         "android/hardware/audio/core/IStreamCallback.aidl",
+        "android/hardware/audio/core/IStreamCommon.aidl",
         "android/hardware/audio/core/IStreamIn.aidl",
         "android/hardware/audio/core/IStreamOut.aidl",
         "android/hardware/audio/core/ITelephony.aidl",
@@ -124,6 +125,7 @@
         "android/hardware/audio/core/ModuleDebug.aidl",
         "android/hardware/audio/core/StreamDescriptor.aidl",
         "android/hardware/audio/core/SurroundSoundConfig.aidl",
+        "android/hardware/audio/core/VendorParameter.aidl",
     ],
     imports: [
         "android.hardware.common-V2",
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 42b12d9..ebfa94b 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
@@ -61,6 +61,9 @@
   void updateScreenRotation(android.hardware.audio.core.IModule.ScreenRotation rotation);
   void updateScreenState(boolean isTurnedOn);
   @nullable android.hardware.audio.core.ISoundDose getSoundDose();
+  int generateHwAvSyncId();
+  android.hardware.audio.core.VendorParameter[] getVendorParameters(in @utf8InCpp String[] ids);
+  void setVendorParameters(in android.hardware.audio.core.VendorParameter[] parameters, boolean async);
   @VintfStability
   parcelable OpenInputStreamArguments {
     int portConfigId;
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamCommon.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamCommon.aidl
new file mode 100644
index 0000000..8471c79
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamCommon.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;
+@VintfStability
+interface IStreamCommon {
+  void close();
+  void updateHwAvSyncId(int hwAvSyncId);
+  android.hardware.audio.core.VendorParameter[] getVendorParameters(in @utf8InCpp String[] ids);
+  void setVendorParameters(in android.hardware.audio.core.VendorParameter[] parameters, boolean async);
+}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamIn.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamIn.aidl
index e9c727f..98acf5f 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamIn.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamIn.aidl
@@ -34,7 +34,7 @@
 package android.hardware.audio.core;
 @VintfStability
 interface IStreamIn {
-  void close();
+  android.hardware.audio.core.IStreamCommon getStreamCommon();
   android.hardware.audio.core.MicrophoneDynamicInfo[] getActiveMicrophones();
   android.hardware.audio.core.IStreamIn.MicrophoneDirection getMicrophoneDirection();
   void setMicrophoneDirection(android.hardware.audio.core.IStreamIn.MicrophoneDirection direction);
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamOut.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamOut.aidl
index 3021d94..5ea2a12 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamOut.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamOut.aidl
@@ -34,6 +34,6 @@
 package android.hardware.audio.core;
 @VintfStability
 interface IStreamOut {
-  void close();
+  android.hardware.audio.core.IStreamCommon getStreamCommon();
   void updateMetadata(in android.hardware.audio.common.SourceMetadata sourceMetadata);
 }
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/VendorParameter.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/VendorParameter.aidl
new file mode 100644
index 0000000..bfe33ee
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/VendorParameter.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.audio.core;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable VendorParameter {
+  @utf8InCpp String id;
+  ParcelableHolder ext;
+}
diff --git a/audio/aidl/android/hardware/audio/core/IModule.aidl b/audio/aidl/android/hardware/audio/core/IModule.aidl
index 2d34df6..7facc6c 100644
--- a/audio/aidl/android/hardware/audio/core/IModule.aidl
+++ b/audio/aidl/android/hardware/audio/core/IModule.aidl
@@ -29,6 +29,7 @@
 import android.hardware.audio.core.MicrophoneInfo;
 import android.hardware.audio.core.ModuleDebug;
 import android.hardware.audio.core.StreamDescriptor;
+import android.hardware.audio.core.VendorParameter;
 import android.media.audio.common.AudioOffloadInfo;
 import android.media.audio.common.AudioPort;
 import android.media.audio.common.AudioPortConfig;
@@ -684,4 +685,47 @@
      * @throws EX_ILLEGAL_STATE If there was an error creating an instance.
      */
     @nullable ISoundDose getSoundDose();
+
+    /**
+     * Generate a HW AV Sync identifier for a new audio session.
+     *
+     * Creates a new unique identifier which can be further used by the client
+     * for tagging input / output streams that belong to the same audio
+     * session and thus must use the same HW AV Sync timestamps sequence.
+     *
+     * HW AV Sync timestamps are used for "tunneled" I/O modes and thus
+     * are not mandatory.
+     *
+     * @throws EX_ILLEGAL_STATE If the identifier can not be provided at the moment.
+     * @throws EX_UNSUPPORTED_OPERATION If synchronization with HW AV Sync markers
+     *                                  is not supported.
+     */
+    int generateHwAvSyncId();
+
+    /**
+     * Get current values of vendor parameters.
+     *
+     * Return current values for the parameters corresponding to the provided ids.
+     *
+     * @param ids Ids of the parameters to retrieve values of.
+     * @return Current values of parameters, one per each id.
+     * @throws EX_ILLEGAL_ARGUMENT If the module does not recognize provided ids.
+     * @throws EX_ILLEGAL_STATE If parameter values can not be retrieved at the moment.
+     * @throws EX_UNSUPPORTED_OPERATION If the module does not support vendor parameters.
+     */
+    VendorParameter[] getVendorParameters(in @utf8InCpp String[] ids);
+    /**
+     * Set vendor parameters.
+     *
+     * Update values for provided vendor parameters. If the 'async' parameter
+     * is set to 'true', the implementation must return the control back without
+     * waiting for the application of parameters to complete.
+     *
+     * @param parameters Ids and values of parameters to set.
+     * @param async Whether to return from the method as early as possible.
+     * @throws EX_ILLEGAL_ARGUMENT If the module does not recognize provided parameters.
+     * @throws EX_ILLEGAL_STATE If parameters can not be set at the moment.
+     * @throws EX_UNSUPPORTED_OPERATION If the module does not support vendor parameters.
+     */
+    void setVendorParameters(in VendorParameter[] parameters, boolean async);
 }
diff --git a/audio/aidl/android/hardware/audio/core/IStreamCommon.aidl b/audio/aidl/android/hardware/audio/core/IStreamCommon.aidl
new file mode 100644
index 0000000..84f7309
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/core/IStreamCommon.aidl
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.audio.core;
+
+import android.hardware.audio.core.VendorParameter;
+
+/**
+ * This interface contains operations that are common to input and output
+ * streams (IStreamIn and IStreamOut). The lifetime of the server-side
+ * implementation object is the same as of the "parent" IStreamIn/Out object.
+ * The client must release all references to this object together with
+ * references to the "parent" object.
+ */
+@VintfStability
+interface IStreamCommon {
+    /**
+     * Close the stream.
+     *
+     * Releases any resources allocated for this stream on the HAL module side.
+     * 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 IStream* interfaces throw EX_ILLEGAL_STATE for a closed stream.
+     *
+     * @throws EX_ILLEGAL_STATE If the stream has already been closed.
+     */
+    void close();
+
+    /**
+     * Update the HW AV Sync identifier for the stream.
+     *
+     * The argument to this method must be one of the identifiers previously
+     * returned by the 'IModule.generateHwAvSyncId' method. By tagging streams
+     * with the same identifier, the client indicates to the HAL that they all
+     * use the same HW AV Sync timestamps sequence.
+     *
+     * HW AV Sync timestamps are used for "tunneled" I/O modes and thus
+     * are not mandatory.
+     *
+     * @throws EX_ILLEGAL_ARGUMENT If the provided ID is unknown to the HAL module.
+     * @throws EX_ILLEGAL_STATE If the stream is closed.
+     * @throws EX_UNSUPPORTED_OPERATION If synchronization with HW AV Sync markers
+     *                                  is not supported.
+     */
+    void updateHwAvSyncId(int hwAvSyncId);
+
+    /**
+     * Get current values of vendor parameters.
+     *
+     * Return current values for the parameters corresponding to the provided ids.
+     *
+     * @param ids Ids of the parameters to retrieve values of.
+     * @return Current values of parameters.
+     * @throws EX_ILLEGAL_ARGUMENT If the stream does not recognize provided ids.
+     * @throws EX_ILLEGAL_STATE If parameter values can not be retrieved at the moment.
+     * @throws EX_UNSUPPORTED_OPERATION If the stream does not support vendor parameters.
+     */
+    VendorParameter[] getVendorParameters(in @utf8InCpp String[] ids);
+    /**
+     * Set vendor parameters.
+     *
+     * Update values for provided vendor parameters. If the 'async' parameter
+     * is set to 'true', the implementation must return the control back without
+     * waiting for the application of parameters to complete.
+     *
+     * @param parameters Ids and values of parameters to set.
+     * @param async Whether to return from the method as early as possible.
+     * @throws EX_ILLEGAL_ARGUMENT If the stream does not recognize provided parameters.
+     * @throws EX_ILLEGAL_STATE If parameters can not be set at the moment.
+     * @throws EX_UNSUPPORTED_OPERATION If the stream does not support vendor parameters.
+     */
+    void setVendorParameters(in VendorParameter[] parameters, boolean async);
+}
diff --git a/audio/aidl/android/hardware/audio/core/IStreamIn.aidl b/audio/aidl/android/hardware/audio/core/IStreamIn.aidl
index 0b6e02c..92788a6 100644
--- a/audio/aidl/android/hardware/audio/core/IStreamIn.aidl
+++ b/audio/aidl/android/hardware/audio/core/IStreamIn.aidl
@@ -17,6 +17,7 @@
 package android.hardware.audio.core;
 
 import android.hardware.audio.common.SinkMetadata;
+import android.hardware.audio.core.IStreamCommon;
 import android.hardware.audio.core.MicrophoneDynamicInfo;
 
 /**
@@ -25,19 +26,15 @@
 @VintfStability
 interface IStreamIn {
     /**
-     * Close the stream.
+     * Return the interface for common stream operations.
      *
-     * Releases any resources allocated for this stream on the HAL module side.
-     * 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.
+     * This method must always succeed. The implementation must
+     * return the same instance object for all subsequent calls to
+     * this method.
      *
-     * Methods of this interface throw EX_ILLEGAL_STATE for a closed stream.
-     *
-     * @throws EX_ILLEGAL_STATE If the stream has already been closed.
+     * @return The interface for common operations.
      */
-    void close();
+    IStreamCommon getStreamCommon();
 
     /**
      * Provides information on the microphones that are active for this stream.
diff --git a/audio/aidl/android/hardware/audio/core/IStreamOut.aidl b/audio/aidl/android/hardware/audio/core/IStreamOut.aidl
index 9fdb37d..f7fc77a 100644
--- a/audio/aidl/android/hardware/audio/core/IStreamOut.aidl
+++ b/audio/aidl/android/hardware/audio/core/IStreamOut.aidl
@@ -17,6 +17,7 @@
 package android.hardware.audio.core;
 
 import android.hardware.audio.common.SourceMetadata;
+import android.hardware.audio.core.IStreamCommon;
 
 /**
  * This interface provides means for sending audio data to output devices.
@@ -24,19 +25,15 @@
 @VintfStability
 interface IStreamOut {
     /**
-     * Close the stream.
+     * Return the interface for common stream operations.
      *
-     * Releases any resources allocated for this stream on the HAL module side.
-     * 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.
+     * This method must always succeed. The implementation must
+     * return the same instance object for all subsequent calls to
+     * this method.
      *
-     * Methods of this interface throw EX_ILLEGAL_STATE for a closed stream.
-     *
-     * @throws EX_ILLEGAL_STATE If the stream has already been closed.
+     * @return The interface for common operations.
      */
-    void close();
+    IStreamCommon getStreamCommon();
 
     /**
      * Update stream metadata.
diff --git a/audio/aidl/android/hardware/audio/core/VendorParameter.aidl b/audio/aidl/android/hardware/audio/core/VendorParameter.aidl
new file mode 100644
index 0000000..206bd9d
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/core/VendorParameter.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.audio.core;
+
+/**
+ * Vendor parameters are used as a lightweight way to pass vendor-specific
+ * configuration data back and forth between the HAL and vendor's extension
+ * to the Android framework, without the need to extend audio interfaces
+ * from AOSP.
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable VendorParameter {
+    /**
+     * Vendor-generated unique ID of the parameter. In order to avoid
+     * collisions, vendors must use a vendor-specific prefix for parameter
+     * ids. The Android framework always passes ids as-is, without any attempt
+     * to parse their content.
+     */
+    @utf8InCpp String id;
+    /**
+     * The payload of the parameter.
+     */
+    ParcelableHolder ext;
+}
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index 38a8cc4..d52e328 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -532,9 +532,10 @@
         return status;
     }
     context.fillDescriptor(&_aidl_return->desc);
-    auto stream = ndk::SharedRefBase::make<StreamIn>(in_args.sinkMetadata, std::move(context),
-                                                     mConfig->microphones);
-    if (auto status = stream->init(); !status.isOk()) {
+    std::shared_ptr<StreamIn> stream;
+    if (auto status = StreamIn::createInstance(in_args.sinkMetadata, std::move(context),
+                                               mConfig->microphones, &stream);
+        !status.isOk()) {
         return status;
     }
     StreamWrapper streamWrapper(stream);
@@ -584,9 +585,10 @@
         return status;
     }
     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()) {
+    std::shared_ptr<StreamOut> stream;
+    if (auto status = StreamOut::createInstance(in_args.sourceMetadata, std::move(context),
+                                                in_args.offloadInfo, &stream);
+        !status.isOk()) {
         return status;
     }
     StreamWrapper streamWrapper(stream);
@@ -947,4 +949,24 @@
     return ndk::ScopedAStatus::ok();
 }
 
+ndk::ScopedAStatus Module::generateHwAvSyncId(int32_t* _aidl_return) {
+    LOG(DEBUG) << __func__;
+    (void)_aidl_return;
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus Module::getVendorParameters(const std::vector<std::string>& in_ids,
+                                               std::vector<VendorParameter>* _aidl_return) {
+    LOG(DEBUG) << __func__ << ": id count: " << in_ids.size();
+    (void)_aidl_return;
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus Module::setVendorParameters(const std::vector<VendorParameter>& in_parameters,
+                                               bool in_async) {
+    LOG(DEBUG) << __func__ << ": parameter count " << in_parameters.size()
+               << ", async: " << in_async;
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
 }  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index be5887c..424c3e4 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "AHAL_Stream"
 #include <android-base/logging.h>
+#include <android/binder_ibinder_platform.h>
 #include <utils/SystemClock.h>
 
 #include <Utils.h>
@@ -486,7 +487,7 @@
 }
 
 template <class Metadata, class StreamWorker>
-StreamCommon<Metadata, StreamWorker>::~StreamCommon() {
+StreamCommonImpl<Metadata, StreamWorker>::~StreamCommonImpl() {
     if (!isClosed()) {
         LOG(ERROR) << __func__ << ": stream was not closed prior to destruction, resource leak";
         stopWorker();
@@ -495,7 +496,52 @@
 }
 
 template <class Metadata, class StreamWorker>
-ndk::ScopedAStatus StreamCommon<Metadata, StreamWorker>::close() {
+void StreamCommonImpl<Metadata, StreamWorker>::createStreamCommon(
+        const std::shared_ptr<StreamCommonInterface>& delegate) {
+    if (mCommon != nullptr) {
+        LOG(FATAL) << __func__ << ": attempting to create the common interface twice";
+    }
+    mCommon = ndk::SharedRefBase::make<StreamCommon>(delegate);
+    mCommonBinder = mCommon->asBinder();
+    AIBinder_setMinSchedulerPolicy(mCommonBinder.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
+}
+
+template <class Metadata, class StreamWorker>
+ndk::ScopedAStatus StreamCommonImpl<Metadata, StreamWorker>::getStreamCommon(
+        std::shared_ptr<IStreamCommon>* _aidl_return) {
+    if (mCommon == nullptr) {
+        LOG(FATAL) << __func__ << ": the common interface was not created";
+    }
+    *_aidl_return = mCommon;
+    LOG(DEBUG) << __func__ << ": returning " << _aidl_return->get()->asBinder().get();
+    return ndk::ScopedAStatus::ok();
+}
+
+template <class Metadata, class StreamWorker>
+ndk::ScopedAStatus StreamCommonImpl<Metadata, StreamWorker>::updateHwAvSyncId(
+        int32_t in_hwAvSyncId) {
+    LOG(DEBUG) << __func__ << ": id " << in_hwAvSyncId;
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+template <class Metadata, class StreamWorker>
+ndk::ScopedAStatus StreamCommonImpl<Metadata, StreamWorker>::getVendorParameters(
+        const std::vector<std::string>& in_ids, std::vector<VendorParameter>* _aidl_return) {
+    LOG(DEBUG) << __func__ << ": id count: " << in_ids.size();
+    (void)_aidl_return;
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+template <class Metadata, class StreamWorker>
+ndk::ScopedAStatus StreamCommonImpl<Metadata, StreamWorker>::setVendorParameters(
+        const std::vector<VendorParameter>& in_parameters, bool in_async) {
+    LOG(DEBUG) << __func__ << ": parameters count " << in_parameters.size()
+               << ", async: " << in_async;
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+template <class Metadata, class StreamWorker>
+ndk::ScopedAStatus StreamCommonImpl<Metadata, StreamWorker>::close() {
     LOG(DEBUG) << __func__;
     if (!isClosed()) {
         stopWorker();
@@ -512,7 +558,7 @@
 }
 
 template <class Metadata, class StreamWorker>
-void StreamCommon<Metadata, StreamWorker>::stopWorker() {
+void StreamCommonImpl<Metadata, StreamWorker>::stopWorker() {
     if (auto commandMQ = mContext.getCommandMQ(); commandMQ != nullptr) {
         LOG(DEBUG) << __func__ << ": asking the worker to exit...";
         auto cmd = StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::halReservedExit>(
@@ -529,7 +575,8 @@
 }
 
 template <class Metadata, class StreamWorker>
-ndk::ScopedAStatus StreamCommon<Metadata, StreamWorker>::updateMetadata(const Metadata& metadata) {
+ndk::ScopedAStatus StreamCommonImpl<Metadata, StreamWorker>::updateMetadata(
+        const Metadata& metadata) {
     LOG(DEBUG) << __func__;
     if (!isClosed()) {
         mMetadata = metadata;
@@ -539,6 +586,20 @@
     return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
 }
 
+// static
+ndk::ScopedAStatus StreamIn::createInstance(const common::SinkMetadata& sinkMetadata,
+                                            StreamContext context,
+                                            const std::vector<MicrophoneInfo>& microphones,
+                                            std::shared_ptr<StreamIn>* result) {
+    auto stream = ndk::SharedRefBase::make<StreamIn>(sinkMetadata, std::move(context), microphones);
+    if (auto status = stream->init(); !status.isOk()) {
+        return status;
+    }
+    stream->createStreamCommon(stream);
+    *result = std::move(stream);
+    return ndk::ScopedAStatus::ok();
+}
+
 namespace {
 static std::map<AudioDevice, std::string> transformMicrophones(
         const std::vector<MicrophoneInfo>& microphones) {
@@ -549,9 +610,9 @@
 }
 }  // namespace
 
-StreamIn::StreamIn(const SinkMetadata& sinkMetadata, StreamContext context,
+StreamIn::StreamIn(const SinkMetadata& sinkMetadata, StreamContext&& context,
                    const std::vector<MicrophoneInfo>& microphones)
-    : StreamCommon<SinkMetadata, StreamInWorker>(sinkMetadata, std::move(context)),
+    : StreamCommonImpl<SinkMetadata, StreamInWorker>(sinkMetadata, std::move(context)),
       mMicrophones(transformMicrophones(microphones)) {
     LOG(DEBUG) << __func__;
 }
@@ -597,9 +658,24 @@
     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 }
 
-StreamOut::StreamOut(const SourceMetadata& sourceMetadata, StreamContext context,
+// static
+ndk::ScopedAStatus StreamOut::createInstance(const SourceMetadata& sourceMetadata,
+                                             StreamContext context,
+                                             const std::optional<AudioOffloadInfo>& offloadInfo,
+                                             std::shared_ptr<StreamOut>* result) {
+    auto stream =
+            ndk::SharedRefBase::make<StreamOut>(sourceMetadata, std::move(context), offloadInfo);
+    if (auto status = stream->init(); !status.isOk()) {
+        return status;
+    }
+    stream->createStreamCommon(stream);
+    *result = std::move(stream);
+    return ndk::ScopedAStatus::ok();
+}
+
+StreamOut::StreamOut(const SourceMetadata& sourceMetadata, StreamContext&& context,
                      const std::optional<AudioOffloadInfo>& offloadInfo)
-    : StreamCommon<SourceMetadata, StreamOutWorker>(sourceMetadata, std::move(context)),
+    : StreamCommonImpl<SourceMetadata, StreamOutWorker>(sourceMetadata, std::move(context)),
       mOffloadInfo(offloadInfo) {
     LOG(DEBUG) << __func__;
 }
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index 6272083..6baaa76 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -87,6 +87,11 @@
             ::aidl::android::hardware::audio::core::IModule::ScreenRotation in_rotation) override;
     ndk::ScopedAStatus updateScreenState(bool in_isTurnedOn) override;
     ndk::ScopedAStatus getSoundDose(std::shared_ptr<ISoundDose>* _aidl_return) override;
+    ndk::ScopedAStatus generateHwAvSyncId(int32_t* _aidl_return) override;
+    ndk::ScopedAStatus getVendorParameters(const std::vector<std::string>& in_ids,
+                                           std::vector<VendorParameter>* _aidl_return) override;
+    ndk::ScopedAStatus setVendorParameters(const std::vector<VendorParameter>& in_parameters,
+                                           bool in_async) override;
 
     void cleanUpPatch(int32_t patchId);
     ndk::ScopedAStatus createStreamContext(
diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h
index 9509a05..5746f9d 100644
--- a/audio/aidl/default/include/core-impl/Stream.h
+++ b/audio/aidl/default/include/core-impl/Stream.h
@@ -27,6 +27,7 @@
 #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/BnStreamCommon.h>
 #include <aidl/android/hardware/audio/core/BnStreamIn.h>
 #include <aidl/android/hardware/audio/core/BnStreamOut.h>
 #include <aidl/android/hardware/audio/core/IStreamCallback.h>
@@ -197,10 +198,67 @@
 };
 using StreamOutWorker = ::android::hardware::audio::common::StreamWorker<StreamOutWorkerLogic>;
 
-template <class Metadata, class StreamWorker>
-class StreamCommon {
+// This provides a C++ interface with methods of the IStreamCommon Binder interface,
+// but intentionally does not inherit from it. This is needed to avoid inheriting
+// StreamIn and StreamOut from two Binder interface classes, as these parts of the class
+// will be reference counted separately.
+//
+// The implementation of these common methods is in the StreamCommonImpl template class.
+struct StreamCommonInterface {
+    virtual ~StreamCommonInterface() = default;
+    virtual ndk::ScopedAStatus close() = 0;
+    virtual ndk::ScopedAStatus updateHwAvSyncId(int32_t in_hwAvSyncId) = 0;
+    virtual ndk::ScopedAStatus getVendorParameters(const std::vector<std::string>& in_ids,
+                                                   std::vector<VendorParameter>* _aidl_return) = 0;
+    virtual ndk::ScopedAStatus setVendorParameters(
+            const std::vector<VendorParameter>& in_parameters, bool in_async) = 0;
+};
+
+class StreamCommon : public BnStreamCommon {
   public:
-    ndk::ScopedAStatus close();
+    explicit StreamCommon(const std::shared_ptr<StreamCommonInterface>& delegate)
+        : mDelegate(delegate) {}
+
+  private:
+    ndk::ScopedAStatus close() override {
+        auto delegate = mDelegate.lock();
+        return delegate != nullptr ? delegate->close()
+                                   : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    ndk::ScopedAStatus updateHwAvSyncId(int32_t in_hwAvSyncId) override {
+        auto delegate = mDelegate.lock();
+        return delegate != nullptr ? delegate->updateHwAvSyncId(in_hwAvSyncId)
+                                   : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    ndk::ScopedAStatus getVendorParameters(const std::vector<std::string>& in_ids,
+                                           std::vector<VendorParameter>* _aidl_return) override {
+        auto delegate = mDelegate.lock();
+        return delegate != nullptr ? delegate->getVendorParameters(in_ids, _aidl_return)
+                                   : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    ndk::ScopedAStatus setVendorParameters(const std::vector<VendorParameter>& in_parameters,
+                                           bool in_async) override {
+        auto delegate = mDelegate.lock();
+        return delegate != nullptr ? delegate->setVendorParameters(in_parameters, in_async)
+                                   : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    // It is possible that on the client side the proxy for IStreamCommon will outlive
+    // the IStream* instance, and the server side IStream* instance will get destroyed
+    // while this IStreamCommon instance is still alive.
+    std::weak_ptr<StreamCommonInterface> mDelegate;
+};
+
+template <class Metadata, class StreamWorker>
+class StreamCommonImpl : public StreamCommonInterface {
+  public:
+    ndk::ScopedAStatus close() override;
+    ndk::ScopedAStatus updateHwAvSyncId(int32_t in_hwAvSyncId) override;
+    ndk::ScopedAStatus getVendorParameters(const std::vector<std::string>& in_ids,
+                                           std::vector<VendorParameter>* _aidl_return) override;
+    ndk::ScopedAStatus setVendorParameters(const std::vector<VendorParameter>& in_parameters,
+                                           bool in_async) override;
+
+    ndk::ScopedAStatus getStreamCommon(std::shared_ptr<IStreamCommon>* _aidl_return);
     ndk::ScopedAStatus init() {
         return mWorker.start(StreamWorker::kThreadName, ANDROID_PRIORITY_AUDIO)
                        ? ndk::ScopedAStatus::ok()
@@ -215,23 +273,26 @@
     ndk::ScopedAStatus updateMetadata(const Metadata& metadata);
 
   protected:
-    StreamCommon(const Metadata& metadata, StreamContext context)
+    StreamCommonImpl(const Metadata& metadata, StreamContext&& context)
         : mMetadata(metadata), mContext(std::move(context)), mWorker(mContext) {}
-    ~StreamCommon();
+    ~StreamCommonImpl();
     void stopWorker();
+    void createStreamCommon(const std::shared_ptr<StreamCommonInterface>& delegate);
 
+    std::shared_ptr<StreamCommon> mCommon;
+    ndk::SpAIBinder mCommonBinder;
     Metadata mMetadata;
     StreamContext mContext;
     StreamWorker mWorker;
     std::vector<::aidl::android::media::audio::common::AudioDevice> mConnectedDevices;
 };
 
-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();
+class StreamIn : public StreamCommonImpl<::aidl::android::hardware::audio::common::SinkMetadata,
+                                         StreamInWorker>,
+                 public BnStreamIn {
+    ndk::ScopedAStatus getStreamCommon(std::shared_ptr<IStreamCommon>* _aidl_return) override {
+        return StreamCommonImpl<::aidl::android::hardware::audio::common::SinkMetadata,
+                                StreamInWorker>::getStreamCommon(_aidl_return);
     }
     ndk::ScopedAStatus getActiveMicrophones(
             std::vector<MicrophoneDynamicInfo>* _aidl_return) override;
@@ -241,39 +302,61 @@
     ndk::ScopedAStatus setMicrophoneFieldDimension(float in_zoom) override;
     ndk::ScopedAStatus updateMetadata(const ::aidl::android::hardware::audio::common::SinkMetadata&
                                               in_sinkMetadata) override {
-        return StreamCommon<::aidl::android::hardware::audio::common::SinkMetadata,
-                            StreamInWorker>::updateMetadata(in_sinkMetadata);
+        return StreamCommonImpl<::aidl::android::hardware::audio::common::SinkMetadata,
+                                StreamInWorker>::updateMetadata(in_sinkMetadata);
     }
 
   public:
-    StreamIn(const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
-             StreamContext context, const std::vector<MicrophoneInfo>& microphones);
+    static ndk::ScopedAStatus createInstance(
+            const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
+            StreamContext context, const std::vector<MicrophoneInfo>& microphones,
+            std::shared_ptr<StreamIn>* result);
 
   private:
+    friend class ndk::SharedRefBase;
+    StreamIn(const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
+             StreamContext&& context, const std::vector<MicrophoneInfo>& microphones);
+    void createStreamCommon(const std::shared_ptr<StreamIn>& myPtr) {
+        StreamCommonImpl<::aidl::android::hardware::audio::common::SinkMetadata,
+                         StreamInWorker>::createStreamCommon(myPtr);
+    }
+
     const std::map<::aidl::android::media::audio::common::AudioDevice, std::string> mMicrophones;
 };
 
-class StreamOut : public StreamCommon<::aidl::android::hardware::audio::common::SourceMetadata,
-                                      StreamOutWorker>,
+class StreamOut : public StreamCommonImpl<::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 getStreamCommon(std::shared_ptr<IStreamCommon>* _aidl_return) override {
+        return StreamCommonImpl<::aidl::android::hardware::audio::common::SourceMetadata,
+                                StreamOutWorker>::getStreamCommon(_aidl_return);
     }
     ndk::ScopedAStatus updateMetadata(
             const ::aidl::android::hardware::audio::common::SourceMetadata& in_sourceMetadata)
             override {
-        return StreamCommon<::aidl::android::hardware::audio::common::SourceMetadata,
-                            StreamOutWorker>::updateMetadata(in_sourceMetadata);
+        return StreamCommonImpl<::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);
+    static ndk::ScopedAStatus createInstance(
+            const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
+            StreamContext context,
+            const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
+                    offloadInfo,
+            std::shared_ptr<StreamOut>* result);
 
   private:
+    friend class ndk::SharedRefBase;
+    StreamOut(const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
+              StreamContext&& context,
+              const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
+                      offloadInfo);
+    void createStreamCommon(const std::shared_ptr<StreamOut>& myPtr) {
+        StreamCommonImpl<::aidl::android::hardware::audio::common::SourceMetadata,
+                         StreamOutWorker>::createStreamCommon(myPtr);
+    }
+
     std::optional<::aidl::android::media::audio::common::AudioOffloadInfo> mOffloadInfo;
 };
 
diff --git a/audio/aidl/vts/TestUtils.h b/audio/aidl/vts/TestUtils.h
index 5e4d56a..4c1d42c 100644
--- a/audio/aidl/vts/TestUtils.h
+++ b/audio/aidl/vts/TestUtils.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <algorithm>
+#include <initializer_list>
 #include <iostream>
 
 #include <android/binder_auto_utils.h>
@@ -45,6 +47,19 @@
            << "\n  but is has completed with: " << status;
 }
 
+template <typename T>
+inline ::testing::AssertionResult assertResult(const char* exp_expr, const char* act_expr,
+                                               const std::initializer_list<T>& expected,
+                                               const ::ndk::ScopedAStatus& status) {
+    if (std::find(expected.begin(), expected.end(), status.getExceptionCode()) != expected.end()) {
+        return ::testing::AssertionSuccess();
+    }
+    return ::testing::AssertionFailure() << "Expected the transaction \'" << act_expr
+                                         << "\' to complete with one of: " << exp_expr
+                                         << "\n  which is: " << ::testing::PrintToString(expected)
+                                         << "\n  but is has completed with: " << status;
+}
+
 }  // namespace detail
 
 }  // namespace android::hardware::audio::common::testing
diff --git a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
index 5dadea3..1a97004 100644
--- a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
@@ -58,6 +58,7 @@
 using aidl::android::hardware::audio::core::AudioRoute;
 using aidl::android::hardware::audio::core::IModule;
 using aidl::android::hardware::audio::core::ISoundDose;
+using aidl::android::hardware::audio::core::IStreamCommon;
 using aidl::android::hardware::audio::core::IStreamIn;
 using aidl::android::hardware::audio::core::IStreamOut;
 using aidl::android::hardware::audio::core::ITelephony;
@@ -65,6 +66,7 @@
 using aidl::android::hardware::audio::core::MicrophoneInfo;
 using aidl::android::hardware::audio::core::ModuleDebug;
 using aidl::android::hardware::audio::core::StreamDescriptor;
+using aidl::android::hardware::audio::core::VendorParameter;
 using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
 using aidl::android::media::audio::common::AudioContentType;
 using aidl::android::media::audio::common::AudioDevice;
@@ -211,6 +213,56 @@
     EXPECT_IS_OK((inst->*setter)(initialValue)) << "Failed to restore the initial value";
 }
 
+template <class Instance>
+void TestGetVendorParameters(Instance* inst, bool* isSupported) {
+    static const std::vector<std::vector<std::string>> kIdsLists = {{}, {"zero"}, {"one", "two"}};
+    static const auto kStatuses = {EX_ILLEGAL_ARGUMENT, EX_ILLEGAL_STATE, EX_UNSUPPORTED_OPERATION};
+    for (const auto& ids : kIdsLists) {
+        std::vector<VendorParameter> params;
+        if (ndk::ScopedAStatus status = inst->getVendorParameters(ids, &params); status.isOk()) {
+            EXPECT_EQ(ids.size(), params.size()) << "Size of the returned parameters list must "
+                                                 << "match the size of the provided ids list";
+            for (const auto& param : params) {
+                EXPECT_NE(ids.end(), std::find(ids.begin(), ids.end(), param.id))
+                        << "Returned parameter id \"" << param.id << "\" is unexpected";
+            }
+            for (const auto& id : ids) {
+                EXPECT_NE(params.end(),
+                          std::find_if(params.begin(), params.end(),
+                                       [&](const auto& param) { return param.id == id; }))
+                        << "Requested parameter with id \"" << id << "\" was not returned";
+            }
+        } else {
+            EXPECT_STATUS(kStatuses, status);
+            if (status.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+                *isSupported = false;
+                return;
+            }
+        }
+    }
+    *isSupported = true;
+}
+
+template <class Instance>
+void TestSetVendorParameters(Instance* inst, bool* isSupported) {
+    static const auto kStatuses = {EX_NONE, EX_ILLEGAL_ARGUMENT, EX_ILLEGAL_STATE,
+                                   EX_UNSUPPORTED_OPERATION};
+    static const std::vector<std::vector<VendorParameter>> kParamsLists = {
+            {}, {VendorParameter{"zero"}}, {VendorParameter{"one"}, VendorParameter{"two"}}};
+    for (const auto& params : kParamsLists) {
+        ndk::ScopedAStatus status = inst->setVendorParameters(params, false);
+        if (status.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+            *isSupported = false;
+            return;
+        }
+        EXPECT_STATUS(kStatuses, status)
+                << ::android::internal::ToString(params) << ", async: false";
+        EXPECT_STATUS(kStatuses, inst->setVendorParameters(params, true))
+                << ::android::internal::ToString(params) << ", async: true";
+    }
+    *isSupported = true;
+}
+
 // Can be used as a base for any test here, does not depend on the fixture GTest parameters.
 class AudioCoreModuleBase {
   public:
@@ -837,6 +889,13 @@
 template <typename Stream>
 class WithStream {
   public:
+    static ndk::ScopedAStatus callClose(std::shared_ptr<Stream> stream) {
+        std::shared_ptr<IStreamCommon> common;
+        ndk::ScopedAStatus status = stream->getStreamCommon(&common);
+        if (!status.isOk()) return status;
+        return common->close();
+    }
+
     WithStream() {}
     explicit WithStream(const AudioPortConfig& portConfig) : mPortConfig(portConfig) {}
     WithStream(const WithStream&) = delete;
@@ -844,7 +903,7 @@
     ~WithStream() {
         if (mStream != nullptr) {
             mContext.reset();
-            EXPECT_IS_OK(mStream->close()) << "port config id " << getPortId();
+            EXPECT_IS_OK(callClose(mStream)) << "port config id " << getPortId();
         }
     }
     void SetUpPortConfig(IModule* module) { ASSERT_NO_FATAL_FAILURE(mPortConfig.SetUp(module)); }
@@ -1628,6 +1687,40 @@
     EXPECT_IS_OK(module->updateScreenState(true));
 }
 
+TEST_P(AudioCoreModule, GenerateHwAvSyncId) {
+    const auto kStatuses = {EX_NONE, EX_ILLEGAL_STATE};
+    int32_t id1;
+    ndk::ScopedAStatus status = module->generateHwAvSyncId(&id1);
+    if (status.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+        GTEST_SKIP() << "HW AV Sync is not supported";
+    }
+    EXPECT_STATUS(kStatuses, status);
+    if (status.isOk()) {
+        int32_t id2;
+        ASSERT_IS_OK(module->generateHwAvSyncId(&id2));
+        EXPECT_NE(id1, id2) << "HW AV Sync IDs must be unique";
+    }
+}
+
+TEST_P(AudioCoreModule, GetVendorParameters) {
+    bool isGetterSupported = false;
+    EXPECT_NO_FATAL_FAILURE(TestGetVendorParameters(module.get(), &isGetterSupported));
+    ndk::ScopedAStatus status = module->setVendorParameters({}, false);
+    EXPECT_EQ(isGetterSupported, status.getExceptionCode() != EX_UNSUPPORTED_OPERATION)
+            << "Support for getting and setting of vendor parameters must be consistent";
+    if (!isGetterSupported) {
+        GTEST_SKIP() << "Vendor parameters are not supported";
+    }
+}
+
+TEST_P(AudioCoreModule, SetVendorParameters) {
+    bool isSupported = false;
+    EXPECT_NO_FATAL_FAILURE(TestSetVendorParameters(module.get(), &isSupported));
+    if (!isSupported) {
+        GTEST_SKIP() << "Vendor parameters are not supported";
+    }
+}
+
 class AudioCoreTelephony : public AudioCoreModuleBase, public testing::TestWithParam<std::string> {
   public:
     void SetUp() override {
@@ -1733,6 +1826,23 @@
         ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
     }
 
+    void GetStreamCommon() {
+        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(), kDefaultBufferSizeFrames));
+        std::shared_ptr<IStreamCommon> streamCommon1;
+        EXPECT_IS_OK(stream.get()->getStreamCommon(&streamCommon1));
+        EXPECT_NE(nullptr, streamCommon1);
+        std::shared_ptr<IStreamCommon> streamCommon2;
+        EXPECT_IS_OK(stream.get()->getStreamCommon(&streamCommon2));
+        EXPECT_NE(nullptr, streamCommon2);
+        EXPECT_EQ(streamCommon1->asBinder(), streamCommon2->asBinder())
+                << "getStreamCommon must return the same interface instance across invocations";
+    }
+
     void CloseTwice() {
         const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits<Stream>::is_input);
         if (!portConfig.has_value()) {
@@ -1744,7 +1854,8 @@
             ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
             heldStream = stream.getSharedPointer();
         }
-        EXPECT_STATUS(EX_ILLEGAL_STATE, heldStream->close()) << "when closing the stream twice";
+        EXPECT_STATUS(EX_ILLEGAL_STATE, WithStream<Stream>::callClose(heldStream))
+                << "when closing the stream twice";
     }
 
     void OpenAllConfigs() {
@@ -1849,6 +1960,65 @@
         EXPECT_NO_FATAL_FAILURE(SendInvalidCommandImpl(portConfig.value()));
     }
 
+    void UpdateHwAvSyncId() {
+        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(), kDefaultBufferSizeFrames));
+        std::shared_ptr<IStreamCommon> streamCommon;
+        ASSERT_IS_OK(stream.get()->getStreamCommon(&streamCommon));
+        ASSERT_NE(nullptr, streamCommon);
+        const auto kStatuses = {EX_NONE, EX_ILLEGAL_ARGUMENT, EX_ILLEGAL_STATE};
+        for (const auto id : {-100, -1, 0, 1, 100}) {
+            ndk::ScopedAStatus status = streamCommon->updateHwAvSyncId(id);
+            if (status.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+                GTEST_SKIP() << "HW AV Sync is not supported";
+            }
+            EXPECT_STATUS(kStatuses, status) << "id: " << id;
+        }
+    }
+
+    void GetVendorParameters() {
+        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(), kDefaultBufferSizeFrames));
+        std::shared_ptr<IStreamCommon> streamCommon;
+        ASSERT_IS_OK(stream.get()->getStreamCommon(&streamCommon));
+        ASSERT_NE(nullptr, streamCommon);
+
+        bool isGetterSupported = false;
+        EXPECT_NO_FATAL_FAILURE(TestGetVendorParameters(module.get(), &isGetterSupported));
+        ndk::ScopedAStatus status = module->setVendorParameters({}, false);
+        EXPECT_EQ(isGetterSupported, status.getExceptionCode() != EX_UNSUPPORTED_OPERATION)
+                << "Support for getting and setting of vendor parameters must be consistent";
+        if (!isGetterSupported) {
+            GTEST_SKIP() << "Vendor parameters are not supported";
+        }
+    }
+
+    void SetVendorParameters() {
+        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(), kDefaultBufferSizeFrames));
+        std::shared_ptr<IStreamCommon> streamCommon;
+        ASSERT_IS_OK(stream.get()->getStreamCommon(&streamCommon));
+        ASSERT_NE(nullptr, streamCommon);
+
+        bool isSupported = false;
+        EXPECT_NO_FATAL_FAILURE(TestSetVendorParameters(module.get(), &isSupported));
+        if (!isSupported) {
+            GTEST_SKIP() << "Vendor parameters are not supported";
+        }
+    }
+
     void OpenTwiceSamePortConfigImpl(const AudioPortConfig& portConfig) {
         WithStream<Stream> stream1(portConfig);
         ASSERT_NO_FATAL_FAILURE(stream1.SetUp(module.get(), kDefaultBufferSizeFrames));
@@ -1914,6 +2084,7 @@
     }
 
 TEST_IN_AND_OUT_STREAM(CloseTwice);
+TEST_IN_AND_OUT_STREAM(GetStreamCommon);
 TEST_IN_AND_OUT_STREAM(OpenAllConfigs);
 TEST_IN_AND_OUT_STREAM(OpenInvalidBufferSize);
 TEST_IN_AND_OUT_STREAM(OpenInvalidDirection);
@@ -1921,6 +2092,9 @@
 TEST_IN_AND_OUT_STREAM(OpenTwiceSamePortConfig);
 TEST_IN_AND_OUT_STREAM(ResetPortConfigWithOpenStream);
 TEST_IN_AND_OUT_STREAM(SendInvalidCommand);
+TEST_IN_AND_OUT_STREAM(UpdateHwAvSyncId);
+TEST_IN_AND_OUT_STREAM(GetVendorParameters);
+TEST_IN_AND_OUT_STREAM(SetVendorParameters);
 
 namespace aidl::android::hardware::audio::core {
 std::ostream& operator<<(std::ostream& os, const IStreamIn::MicrophoneDirection& md) {