Merge "Refactored camera_c2OutputConfiguration_fuzzer" into main
diff --git a/camera/CameraMetadata.cpp b/camera/CameraMetadata.cpp
index 2e808d1..424923a 100644
--- a/camera/CameraMetadata.cpp
+++ b/camera/CameraMetadata.cpp
@@ -880,7 +880,7 @@
     return OK;
 }
 
-metadata_vendor_id_t CameraMetadata::getVendorId() {
+metadata_vendor_id_t CameraMetadata::getVendorId() const {
     return get_camera_metadata_vendor_id(mBuffer);
 }
 
diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp
index fb26f83..c12a1a1 100644
--- a/camera/VendorTagDescriptor.cpp
+++ b/camera/VendorTagDescriptor.cpp
@@ -466,7 +466,7 @@
 
 int VendorTagDescriptorCache::getTagType(uint32_t tag,
         metadata_vendor_id_t id) const {
-    int ret = 0;
+    int ret = -1;
     auto desc = mVendorMap.find(id);
     if (desc != mVendorMap.end()) {
         ret = desc->second->getTagType(tag);
diff --git a/camera/aidl/android/hardware/ICameraService.aidl b/camera/aidl/android/hardware/ICameraService.aidl
index 8078cea..0eeeb7f 100644
--- a/camera/aidl/android/hardware/ICameraService.aidl
+++ b/camera/aidl/android/hardware/ICameraService.aidl
@@ -149,6 +149,18 @@
     void remapCameraIds(in CameraIdRemapping cameraIdRemapping);
 
     /**
+     * Inject Session Params into an existing camera session.
+     *
+     * @param cameraId the camera id session to inject session params into. Note that
+     *                 if there is no active session for the input cameraid, this operation
+     *                 will be a no-op. In addition, future camera sessions for cameraid will
+     *                 not be affected.
+     * @param sessionParams the session params to override for the existing session.
+     */
+    void injectSessionParams(@utf8InCpp String cameraId,
+            in CameraMetadataNative sessionParams);
+
+    /**
      * Remove listener for changes to camera device and flashlight state.
      */
     void removeListener(ICameraServiceListener listener);
diff --git a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
index 8e1fcc0..843e0d4 100644
--- a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
+++ b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
@@ -99,6 +99,15 @@
       */
     boolean isSessionConfigurationSupported(in SessionConfiguration sessionConfiguration);
 
+    /**
+     * Get the camera characteristics for a particular session configuration
+     *
+     * @param sessionConfiguration Specific session configuration for which the characteristics
+     * are fetched.
+     * @return - characteristics associated with the given session.
+     */
+    CameraMetadataNative getSessionCharacteristics(in SessionConfiguration sessionConfiguration);
+
     void deleteStream(int streamId);
 
     /**
diff --git a/camera/camera_platform.aconfig b/camera/camera_platform.aconfig
index f7deda1..5d2a263 100644
--- a/camera/camera_platform.aconfig
+++ b/camera/camera_platform.aconfig
@@ -62,3 +62,17 @@
     description: "Enable session parameter injection via reconfiguration"
     bug: "308984721"
 }
+
+flag {
+     namespace: "camera_platform"
+     name: "camera_ae_mode_low_light_boost"
+     description: "An AE mode that enables increased brightening in low light scenes"
+     bug: "312803148"
+}
+
+flag {
+     namespace: "camera_platform"
+     name: "multiresolution_imagereader_usage_config"
+     description: "Enable creating MultiResolutionImageReader with usage flag configuration"
+     bug: "301588215"
+}
diff --git a/camera/include/camera/CameraMetadata.h b/camera/include/camera/CameraMetadata.h
index c56ee6d..2903dfb 100644
--- a/camera/include/camera/CameraMetadata.h
+++ b/camera/include/camera/CameraMetadata.h
@@ -245,7 +245,7 @@
     /**
      * Return the current vendor tag id associated with this metadata.
      */
-    metadata_vendor_id_t getVendorId();
+    metadata_vendor_id_t getVendorId() const;
 
   private:
     camera_metadata_t *mBuffer;
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 3a483cc..2c68cef 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -2244,6 +2244,39 @@
      */
     ACAMERA_CONTROL_AUTOFRAMING_STATE =                         // byte (acamera_metadata_enum_android_control_autoframing_state_t)
             ACAMERA_CONTROL_START + 54,
+    /**
+     * <p>The operating luminance range of low light boost measured in lux (lx).</p>
+     *
+     * <p>Type: float[2]</p>
+     *
+     * <p>This tag may appear in:
+     * <ul>
+     *   <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+     * </ul></p>
+     *
+     */
+    ACAMERA_CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE =      // float[2]
+            ACAMERA_CONTROL_START + 55,
+    /**
+     * <p>Current state of the low light boost AE mode.</p>
+     *
+     * <p>Type: byte (acamera_metadata_enum_android_control_low_light_boost_state_t)</p>
+     *
+     * <p>This tag may appear in:
+     * <ul>
+     *   <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
+     * </ul></p>
+     *
+     * <p>When low light boost is enabled by setting the AE mode to
+     * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY', it can dynamically apply a low light
+     * boost when the light level threshold is exceeded.</p>
+     * <p>This state indicates when low light boost is 'ACTIVE' and applied. Similarly, it can
+     * indicate when it is not being applied by returning 'INACTIVE'.</p>
+     * <p>This key will be absent from the CaptureResult if AE mode is not set to
+     * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY.</p>
+     */
+    ACAMERA_CONTROL_LOW_LIGHT_BOOST_STATE =                     // byte (acamera_metadata_enum_android_control_low_light_boost_state_t)
+            ACAMERA_CONTROL_START + 56,
     ACAMERA_CONTROL_END,
 
     /**
@@ -4625,8 +4658,8 @@
      * <p>The guaranteed stream combinations related to stream use case for a camera device with
      * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE">CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE</a>
      * capability is documented in the camera device
-     * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice.html#stream-use-case-capability-additional-guaranteed-configurations">guideline</a>. The application is strongly recommended to use one of the guaranteed stream
-     * combinations.
+     * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#stream-use-case-capability-additional-guaranteed-configurations">guideline</a>.
+     * The application is strongly recommended to use one of the guaranteed stream combinations.
      * If the application creates a session with a stream combination not in the guaranteed
      * list, or with mixed DEFAULT and non-DEFAULT use cases within the same session,
      * the camera device may ignore some stream use cases due to hardware constraints
@@ -8214,6 +8247,44 @@
      */
     ACAMERA_CONTROL_AE_MODE_ON_EXTERNAL_FLASH                        = 5,
 
+    /**
+     * <p>Like 'ON' but applies additional brightness boost in low light scenes.</p>
+     * <p>When the scene lighting conditions are within the range defined by
+     * ACAMERA_CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE this mode will apply additional
+     * brightness boost.</p>
+     * <p>This mode will automatically adjust the intensity of low light boost applied
+     * according to the scene lighting conditions. A darker scene will receive more boost
+     * while a brighter scene will receive less boost.</p>
+     * <p>This mode can ignore the set target frame rate to allow more light to be captured
+     * which can result in choppier motion. The frame rate can extend to lower than the
+     * ACAMERA_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES but will not go below 10 FPS. This mode
+     * can also increase the sensor sensitivity gain which can result in increased luma
+     * and chroma noise. The sensor sensitivity gain can extend to higher values beyond
+     * ACAMERA_SENSOR_INFO_SENSITIVITY_RANGE. This mode may also apply additional
+     * processing to recover details in dark and bright areas of the image,and noise
+     * reduction at high sensitivity gain settings to manage the trade-off between light
+     * sensitivity and capture noise.</p>
+     * <p>This mode is restricted to two output surfaces. One output surface type can either
+     * be SurfaceView or TextureView. Another output surface type can either be MediaCodec
+     * or MediaRecorder. This mode cannot be used with a target FPS range higher than 30
+     * FPS.</p>
+     * <p>If the session configuration is not supported, the AE mode reported in the
+     * CaptureResult will be 'ON' instead of 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY'.</p>
+     * <p>The application can observe the CapturerResult field
+     * ACAMERA_CONTROL_LOW_LIGHT_BOOST_STATE to determine when low light boost is 'ACTIVE' or
+     * 'INACTIVE'.</p>
+     * <p>The low light boost is 'ACTIVE' once the scene lighting condition is less than the
+     * upper bound lux value defined by ACAMERA_CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE.
+     * This mode will be 'INACTIVE' once the scene lighting condition is greater than the
+     * upper bound lux value defined by ACAMERA_CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE.</p>
+     *
+     * @see ACAMERA_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES
+     * @see ACAMERA_CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE
+     * @see ACAMERA_CONTROL_LOW_LIGHT_BOOST_STATE
+     * @see ACAMERA_SENSOR_INFO_SENSITIVITY_RANGE
+     */
+    ACAMERA_CONTROL_AE_MODE_ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY   = 6,
+
 } acamera_metadata_enum_android_control_ae_mode_t;
 
 // ACAMERA_CONTROL_AE_PRECAPTURE_TRIGGER
@@ -9215,6 +9286,20 @@
 
 } acamera_metadata_enum_android_control_autoframing_state_t;
 
+// ACAMERA_CONTROL_LOW_LIGHT_BOOST_STATE
+typedef enum acamera_metadata_enum_acamera_control_low_light_boost_state {
+    /**
+     * <p>The AE mode 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY' is enabled but not applied.</p>
+     */
+    ACAMERA_CONTROL_LOW_LIGHT_BOOST_STATE_INACTIVE                   = 0,
+
+    /**
+     * <p>The AE mode 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY' is enabled and applied.</p>
+     */
+    ACAMERA_CONTROL_LOW_LIGHT_BOOST_STATE_ACTIVE                     = 1,
+
+} acamera_metadata_enum_android_control_low_light_boost_state_t;
+
 
 
 // ACAMERA_EDGE_MODE
@@ -10090,8 +10175,8 @@
      * </ul>
      * <p><a href="https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html#SCALER_AVAILABLE_STREAM_USE_CASES">CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES</a>
      * lists all of the supported stream use cases.</p>
-     * <p>Refer to
-     * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice.html#stream-use-case-capability-additional-guaranteed-configurations">CameraDevice#stream-use-case-capability-additional-guaranteed-configurations</a>
+     * <p>Refer to the
+     * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#stream-use-case-capability-additional-guaranteed-configurations">guideline</a>
      * for the mandatory stream combinations involving stream use cases, which can also be
      * queried via <a href="https://developer.android.com/reference/android/hardware/camera2/params/MandatoryStreamCombination.html">MandatoryStreamCombination</a>.</p>
      */
@@ -10952,9 +11037,9 @@
     /**
      * <p>This camera device does not have enough capabilities to qualify as a <code>FULL</code> device or
      * better.</p>
-     * <p>Only the stream configurations listed in the <code>LEGACY</code> and <code>LIMITED</code> tables in the
-     * {@link ACameraDevice_createCaptureSession }
-     * documentation are guaranteed to be supported.</p>
+     * <p>Only the stream configurations listed in the <code>LEGACY</code> and <code>LIMITED</code>
+     * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#limited-level-additional-guaranteed-configurations">tables</a>
+     * in the documentation are guaranteed to be supported.</p>
      * <p>All <code>LIMITED</code> devices support the <code>BACKWARDS_COMPATIBLE</code> capability, indicating basic
      * support for color image capture. The only exception is that the device may
      * alternatively support only the <code>DEPTH_OUTPUT</code> capability, if it can only output depth
@@ -10979,9 +11064,9 @@
 
     /**
      * <p>This camera device is capable of supporting advanced imaging applications.</p>
-     * <p>The stream configurations listed in the <code>FULL</code>, <code>LEGACY</code> and <code>LIMITED</code> tables in the
-     * {@link ACameraDevice_createCaptureSession }
-     * documentation are guaranteed to be supported.</p>
+     * <p>The stream configurations listed in the <code>FULL</code>, <code>LEGACY</code> and <code>LIMITED</code>
+     * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#full-level-additional-guaranteed-configurations">tables</a>
+     * in the documentation are guaranteed to be supported.</p>
      * <p>A <code>FULL</code> device will support below capabilities:</p>
      * <ul>
      * <li><code>BURST_CAPTURE</code> capability (ACAMERA_REQUEST_AVAILABLE_CAPABILITIES contains
@@ -11008,9 +11093,9 @@
 
     /**
      * <p>This camera device is running in backward compatibility mode.</p>
-     * <p>Only the stream configurations listed in the <code>LEGACY</code> table in the
-     * {@link ACameraDevice_createCaptureSession }
-     * documentation are supported.</p>
+     * <p>Only the stream configurations listed in the <code>LEGACY</code>
+     * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#legacy-level-guaranteed-configurations">table</a>
+     * in the documentation are supported.</p>
      * <p>A <code>LEGACY</code> device does not support per-frame control, manual sensor control, manual
      * post-processing, arbitrary cropping regions, and has relaxed performance constraints.
      * No additional capabilities beyond <code>BACKWARD_COMPATIBLE</code> will ever be listed by a
@@ -11032,9 +11117,9 @@
      * <p>This camera device is capable of YUV reprocessing and RAW data capture, in addition to
      * FULL-level capabilities.</p>
      * <p>The stream configurations listed in the <code>LEVEL_3</code>, <code>RAW</code>, <code>FULL</code>, <code>LEGACY</code> and
-     * <code>LIMITED</code> tables in the
-     * {@link ACameraDevice_createCaptureSession }
-     * documentation are guaranteed to be supported.</p>
+     * <code>LIMITED</code>
+     * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#level-3-additional-guaranteed-configurations">tables</a>
+     * in the documentation are guaranteed to be supported.</p>
      * <p>The following additional capabilities are guaranteed to be supported:</p>
      * <ul>
      * <li><code>YUV_REPROCESSING</code> capability (ACAMERA_REQUEST_AVAILABLE_CAPABILITIES contains
diff --git a/media/audioaidlconversion/AidlConversionCppNdk.cpp b/media/audioaidlconversion/AidlConversionCppNdk.cpp
index 60b814e..1a6c7f1 100644
--- a/media/audioaidlconversion/AidlConversionCppNdk.cpp
+++ b/media/audioaidlconversion/AidlConversionCppNdk.cpp
@@ -28,7 +28,6 @@
 
 #include "media/AidlConversionCppNdk.h"
 
-#include <media/ShmemCompat.h>
 #include <media/stagefright/foundation/MediaDefs.h>
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/media/audioaidlconversion/Android.bp b/media/audioaidlconversion/Android.bp
index d3a5755..07c59c7 100644
--- a/media/audioaidlconversion/Android.bp
+++ b/media/audioaidlconversion/Android.bp
@@ -56,6 +56,19 @@
 }
 
 cc_defaults {
+    name: "audio_aidl_conversion_common_default_cpp",
+    shared_libs: [
+        "libbinder",
+        "libshmemcompat",
+        "shared-file-region-aidl-cpp",
+        "framework-permission-aidl-cpp",
+    ],
+    export_shared_lib_headers: [
+        "shared-file-region-aidl-cpp",
+    ],
+}
+
+cc_defaults {
     name: "audio_aidl_conversion_common_default",
     export_include_dirs: ["include"],
     host_supported: true,
@@ -67,17 +80,12 @@
     ],
     shared_libs: [
         "libbase",
-        "libbinder",
         "liblog",
-        "libshmemcompat",
         "libstagefright_foundation",
         "libutils",
-        "shared-file-region-aidl-cpp",
-        "framework-permission-aidl-cpp",
     ],
     export_shared_lib_headers: [
         "libbase",
-        "shared-file-region-aidl-cpp",
     ],
     cflags: [
         "-Wall",
@@ -113,6 +121,7 @@
     ],
     defaults: [
         "audio_aidl_conversion_common_default",
+        "audio_aidl_conversion_common_default_cpp",
         "latest_android_media_audio_common_types_cpp_export_shared",
     ],
     min_sdk_version: "29",
@@ -223,6 +232,7 @@
     ],
     defaults: [
         "audio_aidl_conversion_common_default",
+        "audio_aidl_conversion_common_default_cpp",
         "audio_aidl_conversion_common_util_default",
         "latest_android_media_audio_common_types_cpp_shared",
         "latest_android_media_audio_common_types_ndk_shared",
diff --git a/media/audioaidlconversion/include/media/AidlConversionUtil-impl.h b/media/audioaidlconversion/include/media/AidlConversionUtil-impl.h
index 656d76a..53f529e 100644
--- a/media/audioaidlconversion/include/media/AidlConversionUtil-impl.h
+++ b/media/audioaidlconversion/include/media/AidlConversionUtil-impl.h
@@ -30,7 +30,6 @@
 #include <utility>
 
 #include <android-base/expected.h>
-#include <binder/Status.h>
 
 #if defined(BACKEND_NDK_IMPL)
 #include <android/binder_auto_utils.h>
@@ -40,6 +39,7 @@
 namespace aidl {
 #else
 #include <binder/Enums.h>
+#include <binder/Status.h>
 #endif  // BACKEND_NDK_IMPL
 namespace android {
 
@@ -374,6 +374,30 @@
  * Note: for EX_TRANSACTION_FAILED and EX_SERVICE_SPECIFIC a more detailed error code
  * can be found from transactionError() or serviceSpecificErrorCode().
  */
+#if defined(BACKEND_NDK_IMPL)
+static inline ::android::status_t statusTFromExceptionCode(binder_exception_t exception) {
+    switch (exception) {
+        case EX_NONE:
+            return ::android::OK;
+        case EX_SECURITY:  // Java SecurityException, rethrows locally in Java
+            return ::android::PERMISSION_DENIED;
+        case EX_BAD_PARCELABLE:  // Java BadParcelableException, rethrows in Java
+        case EX_ILLEGAL_ARGUMENT:  // Java IllegalArgumentException, rethrows in Java
+        case EX_NULL_POINTER:  // Java NullPointerException, rethrows in Java
+            return ::android::BAD_VALUE;
+        case EX_ILLEGAL_STATE:  // Java IllegalStateException, rethrows in Java
+        case EX_UNSUPPORTED_OPERATION:  // Java UnsupportedOperationException, rethrows
+            return ::android::INVALID_OPERATION;
+        case EX_PARCELABLE:  // Java bootclass loader (not standard exception), rethrows
+        case EX_NETWORK_MAIN_THREAD:  // Java NetworkOnMainThreadException, rethrows
+        case EX_TRANSACTION_FAILED: // Native - see error code
+        case EX_SERVICE_SPECIFIC:   // Java ServiceSpecificException,
+                                            // rethrows in Java with integer error code
+            return ::android::UNKNOWN_ERROR;
+    }
+    return ::android::UNKNOWN_ERROR;
+}
+#else
 static inline ::android::status_t statusTFromExceptionCode(int32_t exceptionCode) {
     using namespace ::android::binder;
     switch (exceptionCode) {
@@ -398,6 +422,7 @@
     }
     return ::android::UNKNOWN_ERROR;
 }
+#endif  // BACKEND_NDK_IMPL
 
 /**
  * Return the equivalent Android ::android::status_t from a binder status.
@@ -410,6 +435,7 @@
  *
  * return_type method(type0 param0, ...)
  */
+#if !defined(BACKEND_NDK_IMPL)
 static inline ::android::status_t statusTFromBinderStatus(const ::android::binder::Status &status) {
     return status.isOk() ? ::android::OK // check ::android::OK,
         : status.serviceSpecificErrorCode() // service-side error, not standard Java exception
@@ -418,6 +444,7 @@
         ?: statusTFromExceptionCode(status.exceptionCode()); // a service-side error with a
                                                     // standard Java exception (fromExceptionCode)
 }
+#endif
 
 #if defined(BACKEND_NDK_IMPL)
 static inline ::android::status_t statusTFromBinderStatus(const ::ndk::ScopedAStatus &status) {
@@ -443,6 +470,7 @@
  * This is used for methods not returning an explicit status_t,
  * where Java callers expect an exception, not an integer return value.
  */
+#if !defined(BACKEND_NDK_IMPL)
 static inline ::android::binder::Status binderStatusFromStatusT(
         ::android::status_t status, const char *optionalMessage = nullptr) {
     const char * const emptyIfNull = optionalMessage == nullptr ? "" : optionalMessage;
@@ -470,6 +498,7 @@
     // throw a ServiceSpecificException.
     return Status::fromServiceSpecificError(status, emptyIfNull);
 }
+#endif
 
 } // namespace aidl_utils
 
diff --git a/media/audioaidlconversion/tests/audio_aidl_ndk_conversion_tests.cpp b/media/audioaidlconversion/tests/audio_aidl_ndk_conversion_tests.cpp
index 60727b4..f78243e 100644
--- a/media/audioaidlconversion/tests/audio_aidl_ndk_conversion_tests.cpp
+++ b/media/audioaidlconversion/tests/audio_aidl_ndk_conversion_tests.cpp
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
+#define LOG_TAG "AidlConversionNdkTests"
 #include <iostream>
 #include <type_traits>
 
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp b/media/codec2/hal/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
index ce9fc39..c7c04c5 100644
--- a/media/codec2/hal/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
+++ b/media/codec2/hal/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
@@ -90,8 +90,8 @@
 
         std::shared_ptr<C2AllocatorStore> store = android::GetCodec2PlatformAllocatorStore();
         CHECK_EQ(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &mLinearAllocator), C2_OK);
-        mLinearPool = std::make_shared<C2PooledBlockPool>(
-                mLinearAllocator, mBlockPoolId++, getBufferPoolVer());
+        mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, mBlockPoolId++,
+                                                          getBufferPoolVer());
         ASSERT_NE(mLinearPool, nullptr);
 
         std::vector<std::unique_ptr<C2Param>> queried;
@@ -336,7 +336,9 @@
             ASSERT_TRUE(false) << "Wait for generating C2Work exceeded timeout";
         }
         int64_t timestamp = (*Info)[frameID].timestamp;
-        flags = ((*Info)[frameID].flags == FLAG_CONFIG_DATA) ? C2FrameData::FLAG_CODEC_CONFIG : 0;
+        flags = ((*Info)[frameID].vtsFlags & (1 << VTS_BIT_FLAG_CSD_FRAME))
+                        ? C2FrameData::FLAG_CODEC_CONFIG
+                        : 0;
         if (signalEOS && ((frameID == (int)Info->size() - 1) || (frameID == (offset + range - 1))))
             flags |= C2FrameData::FLAG_END_OF_STREAM;
 
@@ -531,14 +533,10 @@
 
     // request EOS for thumbnail
     // signal EOS flag with last frame
-    size_t i = -1;
-    uint32_t flags;
-    do {
-        i++;
-        flags = 0;
-        if (Info[i].flags) flags = 1u << (Info[i].flags - 1);
-
-    } while (!(flags & SYNC_FRAME));
+    size_t i;
+    for (i = 0; i < Info.size(); i++) {
+        if (Info[i].vtsFlags & (1 << VTS_BIT_FLAG_SYNC_FRAME)) break;
+    }
     std::ifstream eleStream;
     eleStream.open(mInputFile, std::ifstream::binary);
     ASSERT_EQ(eleStream.is_open(), true);
@@ -643,14 +641,11 @@
     mFlushedIndices.clear();
     int index = numFramesFlushed;
     bool keyFrame = false;
-    uint32_t flags = 0;
     while (index < (int)Info.size()) {
-        if (Info[index].flags) flags = 1u << (Info[index].flags - 1);
-        if ((flags & SYNC_FRAME) == SYNC_FRAME) {
+        if (Info[index].vtsFlags & (1 << VTS_BIT_FLAG_SYNC_FRAME)) {
             keyFrame = true;
             break;
         }
-        flags = 0;
         eleStream.ignore(Info[index].bytesCount);
         index++;
     }
@@ -684,24 +679,24 @@
     int bytesCount = 0;
     uint32_t frameId = 0;
     uint32_t flags = 0;
+    uint32_t vtsFlags = 0;
     uint32_t timestamp = 0;
     bool codecConfig = false;
     // This test introduces empty CSD after every 20th frame
     // and empty input frames at an interval of 5 frames.
     while (1) {
         if (!(frameId % 5)) {
-            if (!(frameId % 20))
-                flags = 32;
-            else
-                flags = 0;
+            vtsFlags = !(frameId % 20) ? (1 << VTS_BIT_FLAG_CSD_FRAME) : 0;
             bytesCount = 0;
         } else {
             if (!(eleInfo >> bytesCount)) break;
             eleInfo >> flags;
+            vtsFlags = mapInfoFlagstoVtsFlags(flags);
+            ASSERT_NE(vtsFlags, 0xFF) << "unrecognized flag entry in info file: " << mInfoFile;
             eleInfo >> timestamp;
-            codecConfig = flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
+            codecConfig = (vtsFlags & (1 << VTS_BIT_FLAG_CSD_FRAME)) != 0;
         }
-        Info.push_back({bytesCount, flags, timestamp});
+        Info.push_back({bytesCount, vtsFlags, timestamp});
         frameId++;
     }
     eleInfo.close();
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp b/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
index f36bc41..a72f7bd 100644
--- a/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
+++ b/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
@@ -233,18 +233,24 @@
     int32_t numCsds = 0;
     int32_t bytesCount = 0;
     uint32_t flags = 0;
+    uint32_t vtsFlags = 0;
     uint32_t timestamp = 0;
     while (1) {
         if (!(eleInfo >> bytesCount)) break;
         eleInfo >> flags;
+        vtsFlags = mapInfoFlagstoVtsFlags(flags);
+        if (vtsFlags == 0xFF) {
+            ALOGE("unrecognized flag entry in info file %s", info.c_str());
+            return -1;
+        }
         eleInfo >> timestamp;
-        bool codecConfig = flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
+        bool codecConfig = (vtsFlags & (1 << VTS_BIT_FLAG_CSD_FRAME)) != 0 ;
         if (codecConfig) numCsds++;
-        bool nonDisplayFrame = ((flags & FLAG_NON_DISPLAY_FRAME) != 0);
+        bool nonDisplayFrame = (vtsFlags & (1 << VTS_BIT_FLAG_NO_SHOW_FRAME)) != 0;
         if (timestampDevTest && !codecConfig && !nonDisplayFrame) {
             timestampUslist->push_back(timestamp);
         }
-        frameInfo->push_back({bytesCount, flags, timestamp});
+        frameInfo->push_back({bytesCount, vtsFlags, timestamp});
     }
     ALOGV("numCsds : %d", numCsds);
     eleInfo.close();
@@ -273,3 +279,11 @@
     ASSERT_EQ(flushedIndices.empty(), true);
     flushedWork.clear();
 }
+
+int mapInfoFlagstoVtsFlags(int infoFlags) {
+    if (infoFlags == 0) return 0;
+    else if (infoFlags == 0x1) return (1 << VTS_BIT_FLAG_SYNC_FRAME);
+    else if (infoFlags == 0x10) return (1 << VTS_BIT_FLAG_NO_SHOW_FRAME);
+    else if (infoFlags == 0x20) return (1 << VTS_BIT_FLAG_CSD_FRAME);
+    return 0xFF;
+}
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h b/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
index 48e80a4..eda7b99 100644
--- a/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
+++ b/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
@@ -29,9 +29,6 @@
 #include <chrono>
 #include <fstream>
 
-#define FLAG_NON_DISPLAY_FRAME (1 << 4)
-#define FLAG_CONFIG_DATA (1 << 5)
-
 #define MAX_RETRY 20
 #define TIME_OUT 400ms
 #define MAX_INPUT_BUFFERS 8
@@ -53,9 +50,15 @@
 // Component name prefix
 extern std::string sComponentNamePrefix;
 
+enum c2_vts_flags_t {
+    VTS_BIT_FLAG_SYNC_FRAME = 1,
+    VTS_BIT_FLAG_NO_SHOW_FRAME = 2,
+    VTS_BIT_FLAG_CSD_FRAME = 3,
+};
+
 struct FrameInfo {
     int bytesCount;
-    uint32_t flags;
+    uint32_t vtsFlags;
     int64_t timestamp;
 };
 
@@ -165,4 +168,7 @@
 void verifyFlushOutput(std::list<std::unique_ptr<C2Work>>& flushedWork,
                        std::list<std::unique_ptr<C2Work>>& workQueue,
                        std::list<uint64_t>& flushedIndices, std::mutex& queueLock);
+
+int mapInfoFlagstoVtsFlags(int infoFlags);
+
 #endif  // MEDIA_C2_HIDL_TEST_COMMON_H
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp b/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
index 2cf0d6e..fdb28f4 100644
--- a/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
+++ b/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
@@ -120,8 +120,8 @@
 
         std::shared_ptr<C2AllocatorStore> store = android::GetCodec2PlatformAllocatorStore();
         CHECK_EQ(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &mLinearAllocator), C2_OK);
-        mLinearPool = std::make_shared<C2PooledBlockPool>(
-                mLinearAllocator, mBlockPoolId++, getBufferPoolVer());
+        mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, mBlockPoolId++,
+                                                          getBufferPoolVer());
         ASSERT_NE(mLinearPool, nullptr);
 
         std::vector<std::unique_ptr<C2Param>> queried;
@@ -463,7 +463,9 @@
         }
         int64_t timestamp = (*Info)[frameID].timestamp;
 
-        flags = ((*Info)[frameID].flags == FLAG_CONFIG_DATA) ? C2FrameData::FLAG_CODEC_CONFIG : 0;
+        flags = ((*Info)[frameID].vtsFlags & (1 << VTS_BIT_FLAG_CSD_FRAME))
+                        ? C2FrameData::FLAG_CODEC_CONFIG
+                        : 0;
         if (signalEOS && ((frameID == (int)Info->size() - 1) || (frameID == (offset + range - 1))))
             flags |= C2FrameData::FLAG_END_OF_STREAM;
 
@@ -711,17 +713,19 @@
         ASSERT_EQ(eleInfo.is_open(), true) << mInputFile << " - file not found";
         int bytesCount = 0;
         uint32_t flags = 0;
+        uint32_t vtsFlags = 0;
         uint32_t timestamp = 0;
         uint32_t timestampMax = 0;
         while (1) {
             if (!(eleInfo >> bytesCount)) break;
             eleInfo >> flags;
+            vtsFlags = mapInfoFlagstoVtsFlags(flags);
+            ASSERT_NE(vtsFlags, 0xFF) << "unrecognized flag entry in info file: " << mInfoFile;
             eleInfo >> timestamp;
             timestamp += timestampOffset;
-            Info.push_back({bytesCount, flags, timestamp});
-            bool codecConfig =
-                    flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
-            bool nonDisplayFrame = ((flags & FLAG_NON_DISPLAY_FRAME) != 0);
+            Info.push_back({bytesCount, vtsFlags, timestamp});
+            bool codecConfig = (vtsFlags & (1 << VTS_BIT_FLAG_CSD_FRAME)) != 0;
+            bool nonDisplayFrame = (vtsFlags & (1 << VTS_BIT_FLAG_NO_SHOW_FRAME)) != 0;
 
             {
                 ULock l(mQueueLock);
@@ -795,20 +799,15 @@
     int32_t numCsds = populateInfoVector(mInfoFile, &Info, mTimestampDevTest, &mTimestampUslist);
     ASSERT_GE(numCsds, 0) << "Error in parsing input info file: " << mInfoFile;
 
-    uint32_t flags = 0;
     for (size_t i = 0; i < MAX_ITERATIONS; i++) {
         ASSERT_EQ(mComponent->start(), C2_OK);
 
         // request EOS for thumbnail
         // signal EOS flag with last frame
         size_t j = -1;
-        do {
-            j++;
-            flags = 0;
-            if (Info[j].flags) flags = 1u << (Info[j].flags - 1);
-
-        } while (!(flags & SYNC_FRAME));
-
+        for (j = 0; j < Info.size(); j++) {
+            if (Info[j].vtsFlags & (1 << VTS_BIT_FLAG_SYNC_FRAME)) break;
+        }
         std::ifstream eleStream;
         eleStream.open(mInputFile, std::ifstream::binary);
         ASSERT_EQ(eleStream.is_open(), true);
@@ -908,14 +907,11 @@
     // Seek to next key frame and start decoding till the end
     int index = numFramesFlushed;
     bool keyFrame = false;
-    uint32_t flags = 0;
     while (index < (int)Info.size()) {
-        if (Info[index].flags) flags = 1u << (Info[index].flags - 1);
-        if ((flags & SYNC_FRAME) == SYNC_FRAME) {
+        if (Info[index].vtsFlags & (1 << VTS_BIT_FLAG_SYNC_FRAME)) {
             keyFrame = true;
             break;
         }
-        flags = 0;
         eleStream.ignore(Info[index].bytesCount);
         index++;
     }
@@ -949,24 +945,24 @@
     int bytesCount = 0;
     uint32_t frameId = 0;
     uint32_t flags = 0;
+    uint32_t vtsFlags = 0;
     uint32_t timestamp = 0;
     bool codecConfig = false;
     // This test introduces empty CSD after every 20th frame
     // and empty input frames at an interval of 5 frames.
     while (1) {
         if (!(frameId % 5)) {
-            if (!(frameId % 20))
-                flags = 32;
-            else
-                flags = 0;
+            vtsFlags = !(frameId % 20) ? (1 << VTS_BIT_FLAG_CSD_FRAME) : 0;
             bytesCount = 0;
         } else {
             if (!(eleInfo >> bytesCount)) break;
             eleInfo >> flags;
+            vtsFlags = mapInfoFlagstoVtsFlags(flags);
+            ASSERT_NE(vtsFlags, 0xFF) << "unrecognized flag entry in info file: " << mInfoFile;
             eleInfo >> timestamp;
-            codecConfig = flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
+            codecConfig = (vtsFlags & (1 << VTS_BIT_FLAG_CSD_FRAME)) != 0;
         }
-        Info.push_back({bytesCount, flags, timestamp});
+        Info.push_back({bytesCount, vtsFlags, timestamp});
         frameId++;
     }
     eleInfo.close();
@@ -1046,12 +1042,9 @@
     }
 
     int offset = framesToDecode;
-    uint32_t flags = 0;
     while (1) {
         while (offset < (int)Info.size()) {
-            flags = 0;
-            if (Info[offset].flags) flags = 1u << (Info[offset].flags - 1);
-            if (flags & SYNC_FRAME) {
+            if (Info[offset].vtsFlags & (1 << VTS_BIT_FLAG_SYNC_FRAME)) {
                 keyFrame = true;
                 break;
             }
diff --git a/media/codec2/vndk/include/C2SurfaceSyncObj.h b/media/codec2/vndk/include/C2SurfaceSyncObj.h
index b193b4a..7c1a405 100644
--- a/media/codec2/vndk/include/C2SurfaceSyncObj.h
+++ b/media/codec2/vndk/include/C2SurfaceSyncObj.h
@@ -117,6 +117,16 @@
      */
     void notifyAll();
 
+    /**
+     * Invalide current sync variables on the death of the other process.
+     */
+    void invalidate();
+
+    /**
+     * If a dead process holds the lock, clear the lock.
+     */
+    void clearLockIfNecessary();
+
     C2SyncVariables() {}
 
 private:
@@ -135,6 +145,11 @@
      */
     int wait();
 
+    /**
+     * try lock for the specified duration.
+     */
+    bool tryLockFor(size_t ms);
+
     std::atomic<uint32_t> mLock;
     std::atomic<uint32_t> mCond;
     int32_t mMaxDequeueCount;
diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp
index 62b0ab5..48157c8 100644
--- a/media/codec2/vndk/platform/C2BqBuffer.cpp
+++ b/media/codec2/vndk/platform/C2BqBuffer.cpp
@@ -396,6 +396,12 @@
                     if (c2Fence) {
                         *c2Fence = _C2FenceFactory::CreateSurfaceFence(mSyncMem, waitId);
                     }
+                    if (mInvalidated) {
+                        if (c2Fence) {
+                            *c2Fence = C2Fence();
+                        }
+                        return C2_BAD_STATE;
+                    }
                     return C2_BLOCKING;
                 }
                 if (syncVar->getSyncStatusLocked() != C2SyncVariables::STATUS_ACTIVE) {
@@ -404,6 +410,12 @@
                     if (c2Fence) {
                         *c2Fence = _C2FenceFactory::CreateSurfaceFence(mSyncMem, waitId);
                     }
+                    if (mInvalidated) {
+                        if (c2Fence) {
+                            *c2Fence = C2Fence();
+                        }
+                        return C2_BAD_STATE;
+                    }
                     return C2_BLOCKING;
                 }
                 syncVar->notifyDequeuedLocked();
@@ -690,7 +702,6 @@
             }
         }
         int migrated = 0;
-        std::shared_ptr<C2SurfaceSyncMemory> oldMem;
         // poolDatas dtor should not be called during lock is held.
         std::shared_ptr<C2BufferQueueBlockPoolData>
                 poolDatas[NUM_BUFFER_SLOTS];
@@ -708,8 +719,22 @@
                 mGeneration = 0;
                 ALOGD("configuring null producer: igbp_information(%d)", bqInformation);
             }
-            oldMem = mSyncMem; // preven destruction while locked.
-            mSyncMem = c2SyncMem;
+            if (mInvalidated) {
+                return;
+            }
+            {
+                std::unique_lock<std::mutex> memLock(mSyncMemMutex);
+                mOldMem = mSyncMem; // prevent destruction while locked.
+                                    // The waiters from the old memory will be
+                                    // woken up by the client after this
+                                    // configuration from HAL being finished.
+                                    // But we will keep this in case of the
+                                    // client being dead in between.
+                                    // In the case the death listener will wake
+                                    // up the wiators for the old memory using
+                                    // mOldMem here.
+                mSyncMem = c2SyncMem;
+            }
             C2SyncVariables *syncVar = mSyncMem ? mSyncMem->mem() : nullptr;
             if (syncVar) {
                 syncVar->lock();
@@ -736,6 +761,9 @@
                 // is no longer valid.
                 mIgbpValidityToken = std::make_shared<int>(0);
             }
+            if (mInvalidated) {
+                mIgbpValidityToken = std::make_shared<int>(0);
+            }
             for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) {
                 mBuffers[i] = buffers[i];
                 mPoolDatas[i] = poolDatas[i];
@@ -754,8 +782,33 @@
     }
 
     void invalidate() {
-        mInvalidated = true;
+        std::shared_ptr<C2SurfaceSyncMemory> syncMem;
+        std::shared_ptr<C2SurfaceSyncMemory> oldMem;
+        {
+            std::unique_lock<std::mutex> l(mSyncMemMutex);
+            bool old = mInvalidated.exchange(true);
+            if (old) {
+                return;
+            }
+            syncMem = mSyncMem;
+            oldMem = mOldMem;
+        }
         mIgbpValidityToken.reset();
+        C2SyncVariables *syncVar = syncMem ? syncMem->mem(): nullptr;
+        if (syncVar) {
+            syncVar->invalidate();
+        }
+        C2SyncVariables *oldVar = oldMem ? oldMem->mem(): nullptr;
+        if (oldVar) {
+            oldVar->invalidate();
+        }
+        // invalidate pending lock from a dead process if any
+        if (syncVar) {
+            syncVar->clearLockIfNecessary();
+        }
+        if (oldVar) {
+            oldVar->clearLockIfNecessary();
+        }
     }
 
 private:
@@ -780,7 +833,9 @@
     sp<GraphicBuffer> mBuffers[NUM_BUFFER_SLOTS];
     std::weak_ptr<C2BufferQueueBlockPoolData> mPoolDatas[NUM_BUFFER_SLOTS];
 
+    std::mutex mSyncMemMutex;
     std::shared_ptr<C2SurfaceSyncMemory> mSyncMem;
+    std::shared_ptr<C2SurfaceSyncMemory> mOldMem;
 
     // IGBP invalidation notification token.
     // The buffers(C2BufferQueueBlockPoolData) has the reference to the IGBP where
diff --git a/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp b/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp
index d8c2292..41d16b5 100644
--- a/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp
+++ b/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp
@@ -26,6 +26,33 @@
 #include <chrono>
 #include <C2SurfaceSyncObj.h>
 
+namespace {
+static inline void timespec_add_ms(timespec& ts, size_t ms) {
+    constexpr int kNanoSecondsPerSec = 1000000000;
+    ts.tv_sec  += ms / 1000;
+    ts.tv_nsec += (ms % 1000) * 1000000;
+    if (ts.tv_nsec >= kNanoSecondsPerSec) {
+        ts.tv_sec++;
+        ts.tv_nsec -= kNanoSecondsPerSec;
+    }
+}
+
+/*
+ * lhs < rhs:  return <0
+ * lhs == rhs: return 0
+ * lhs > rhs:  return >0
+ */
+static inline int timespec_compare(const timespec& lhs, const timespec& rhs) {
+    if (lhs.tv_sec < rhs.tv_sec) {
+        return -1;
+    }
+    if (lhs.tv_sec > rhs.tv_sec) {
+        return 1;
+    }
+    return lhs.tv_nsec - rhs.tv_nsec;
+}
+}
+
 const native_handle_t C2SurfaceSyncMemory::HandleSyncMem::cHeader = {
     C2SurfaceSyncMemory::HandleSyncMem::version,
     C2SurfaceSyncMemory::HandleSyncMem::numFds,
@@ -284,6 +311,26 @@
     this->unlock();
 }
 
+void C2SyncVariables::invalidate() {
+    mCond++;
+    (void) syscall(__NR_futex, &mCond, FUTEX_REQUEUE, INT_MAX, (void *)INT_MAX, &mLock, 0);
+}
+
+void C2SyncVariables::clearLockIfNecessary() {
+    // Note: After waiting for 30ms without acquiring the lock,
+    // we will consider the lock is dangling.
+    // Since the lock duration is very brief to manage the counter,
+    // waiting for 30ms should be more than enough.
+    constexpr size_t kTestLockDurationMs = 30;
+
+    bool locked = tryLockFor(kTestLockDurationMs);
+    unlock();
+
+    if (!locked) {
+        ALOGW("A dead process might be holding the lock");
+    }
+}
+
 int C2SyncVariables::signal() {
     mCond++;
 
@@ -308,3 +355,35 @@
     }
     return 0;
 }
+
+bool C2SyncVariables::tryLockFor(size_t ms) {
+    uint32_t old = FUTEX_UNLOCKED;
+
+    if (mLock.compare_exchange_strong(old, FUTEX_LOCKED_UNCONTENDED)) {
+        return true;
+    }
+
+    if (old == FUTEX_LOCKED_UNCONTENDED) {
+        old = mLock.exchange(FUTEX_LOCKED_CONTENDED);
+    }
+
+    struct timespec wait{
+            static_cast<time_t>(ms / 1000),
+            static_cast<long>((ms % 1000) * 1000000)};
+    struct timespec end;
+    clock_gettime(CLOCK_REALTIME, &end);
+    timespec_add_ms(end, ms);
+
+    while (old != FUTEX_UNLOCKED) { // case of EINTR being returned;
+        (void)syscall(__NR_futex, &mLock, FUTEX_WAIT, FUTEX_LOCKED_CONTENDED, &wait, NULL, 0);
+        old = mLock.exchange(FUTEX_LOCKED_CONTENDED);
+
+        struct timespec now;
+        clock_gettime(CLOCK_REALTIME, &now);
+        if (timespec_compare(now, end) >= 0) {
+            break;
+        }
+    }
+
+    return old == FUTEX_UNLOCKED;
+}
diff --git a/media/libaaudio/src/Android.bp b/media/libaaudio/src/Android.bp
index 886603d..fcb376c 100644
--- a/media/libaaudio/src/Android.bp
+++ b/media/libaaudio/src/Android.bp
@@ -41,10 +41,10 @@
     "modernize-use-emplace",
     "modernize-use-equals-default",
     "modernize-use-equals-delete",
-    // "modernize-use-nodiscard", // found in aidl generated files
+    "modernize-use-nodiscard",
     "modernize-use-noexcept",
     "modernize-use-nullptr",
-    // "modernize-use-override", // found in aidl generated files
+    "modernize-use-override",
     // "modernize-use-trailing-return-type", // not necessarily more readable
     "modernize-use-transparent-functors",
     "modernize-use-uncaught-exceptions",
@@ -225,10 +225,12 @@
         "flowgraph/SinkI16.cpp",
         "flowgraph/SinkI24.cpp",
         "flowgraph/SinkI32.cpp",
+        "flowgraph/SinkI8_24.cpp",
         "flowgraph/SourceFloat.cpp",
         "flowgraph/SourceI16.cpp",
         "flowgraph/SourceI24.cpp",
         "flowgraph/SourceI32.cpp",
+        "flowgraph/SourceI8_24.cpp",
         "flowgraph/resampler/IntegerRatio.cpp",
         "flowgraph/resampler/LinearResampler.cpp",
         "flowgraph/resampler/MultiChannelResampler.cpp",
diff --git a/media/libaaudio/src/binding/AudioEndpointParcelable.h b/media/libaaudio/src/binding/AudioEndpointParcelable.h
index 722dd14..12cc42e 100644
--- a/media/libaaudio/src/binding/AudioEndpointParcelable.h
+++ b/media/libaaudio/src/binding/AudioEndpointParcelable.h
@@ -53,7 +53,7 @@
     int32_t addFileDescriptor(const android::base::unique_fd& fd, int32_t sizeInBytes);
 
     /**
-     * Close current data file descriptor. The duplicated file descriptor will be close.
+     * Close current data file descriptor. The duplicated file descriptor will be closed.
      */
     void closeDataFileDescriptor();
 
diff --git a/media/libaaudio/src/client/AAudioFlowGraph.cpp b/media/libaaudio/src/client/AAudioFlowGraph.cpp
index b7e0ae6..14e2007 100644
--- a/media/libaaudio/src/client/AAudioFlowGraph.cpp
+++ b/media/libaaudio/src/client/AAudioFlowGraph.cpp
@@ -30,10 +30,12 @@
 #include <flowgraph/SinkI16.h>
 #include <flowgraph/SinkI24.h>
 #include <flowgraph/SinkI32.h>
+#include <flowgraph/SinkI8_24.h>
 #include <flowgraph/SourceFloat.h>
 #include <flowgraph/SourceI16.h>
 #include <flowgraph/SourceI24.h>
 #include <flowgraph/SourceI32.h>
+#include <flowgraph/SourceI8_24.h>
 
 using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph;
 
@@ -68,6 +70,9 @@
         case AUDIO_FORMAT_PCM_32_BIT:
             mSource = std::make_unique<SourceI32>(sourceChannelCount);
             break;
+        case AUDIO_FORMAT_PCM_8_24_BIT:
+            mSource = std::make_unique<SourceI8_24>(sourceChannelCount);
+            break;
         default:
             ALOGE("%s() Unsupported source format = %d", __func__, sourceFormat);
             return AAUDIO_ERROR_UNIMPLEMENTED;
@@ -139,6 +144,9 @@
         case AUDIO_FORMAT_PCM_32_BIT:
             mSink = std::make_unique<SinkI32>(sinkChannelCount);
             break;
+        case AUDIO_FORMAT_PCM_8_24_BIT:
+            mSink = std::make_unique<SinkI8_24>(sinkChannelCount);
+            break;
         default:
             ALOGE("%s() Unsupported sink format = %d", __func__, sinkFormat);
             return AAUDIO_ERROR_UNIMPLEMENTED;
diff --git a/media/libaaudio/src/client/AudioEndpoint.h b/media/libaaudio/src/client/AudioEndpoint.h
index 2c23e1d..b117572 100644
--- a/media/libaaudio/src/client/AudioEndpoint.h
+++ b/media/libaaudio/src/client/AudioEndpoint.h
@@ -107,7 +107,7 @@
      */
     void eraseDataMemory();
 
-    void freeDataQueue();
+    void freeDataQueue() { mDataQueue.reset(); }
 
     void dump() const;
 
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index f431da8..52925d9 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -399,6 +399,12 @@
     uint8_t buffer[getDeviceBufferCapacity() * getBytesPerFrame()];
     android::fifo_frames_t fullFramesAvailable = mAudioEndpoint->read(buffer,
             getDeviceBufferCapacity());
+    // Before releasing the data queue, update the frames read and written.
+    getFramesRead();
+    getFramesWritten();
+    // Call freeDataQueue() here because the following call to
+    // closeDataFileDescriptor() will invalidate the pointers used by the data queue.
+    mAudioEndpoint->freeDataQueue();
     mEndPointParcelable.closeDataFileDescriptor();
     aaudio_result_t result = mServiceInterface.exitStandby(
             mServiceStreamHandleInfo, endpointParcelable);
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
index d9b75da..68c9156 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
@@ -273,7 +273,8 @@
 
 int64_t AudioStreamInternalCapture::getFramesRead() {
     if (mAudioEndpoint) {
-        mLastFramesRead = mAudioEndpoint->getDataReadCounter() + mFramesOffsetFromService;
+        mLastFramesRead = std::max(mLastFramesRead,
+                                   mAudioEndpoint->getDataReadCounter() + mFramesOffsetFromService);
     }
     return mLastFramesRead;
 }
@@ -295,8 +296,10 @@
         if ((result != mCallbackFrames)) {
             ALOGE("callbackLoop: read() returned %d", result);
             if (result >= 0) {
-                // Only read some of the frames requested. Must have timed out.
-                result = AAUDIO_ERROR_TIMEOUT;
+                // Only read some of the frames requested. The stream can be disconnected
+                // or timed out.
+                processCommands();
+                result = isDisconnected() ? AAUDIO_ERROR_DISCONNECTED : AAUDIO_ERROR_TIMEOUT;
             }
             maybeCallErrorCallback(result);
             break;
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index 3badb0b..5bac2ca 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -329,8 +329,9 @@
 
 int64_t AudioStreamInternalPlay::getFramesWritten() {
     if (mAudioEndpoint) {
-        mLastFramesWritten = mAudioEndpoint->getDataWriteCounter()
-                             + mFramesOffsetFromService;
+        mLastFramesWritten = std::max(
+                mLastFramesWritten,
+                mAudioEndpoint->getDataWriteCounter() + mFramesOffsetFromService);
     }
     return mLastFramesWritten;
 }
@@ -353,8 +354,10 @@
             result = write(mCallbackBuffer.get(), mCallbackFrames, timeoutNanos);
             if ((result != mCallbackFrames)) {
                 if (result >= 0) {
-                    // Only wrote some of the frames requested. Must have timed out.
-                    result = AAUDIO_ERROR_TIMEOUT;
+                    // Only wrote some of the frames requested. The stream can be disconnected
+                    // or timed out.
+                    processCommands();
+                    result = isDisconnected() ? AAUDIO_ERROR_DISCONNECTED : AAUDIO_ERROR_TIMEOUT;
                 }
                 maybeCallErrorCallback(result);
                 break;
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp
index 1db62f3..3e51575 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.cpp
+++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp
@@ -61,6 +61,7 @@
         case AUDIO_FORMAT_PCM_32_BIT:
         case AUDIO_FORMAT_PCM_FLOAT:
         case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+        case AUDIO_FORMAT_PCM_8_24_BIT:
         case AUDIO_FORMAT_IEC61937:
             break; // valid
         default:
diff --git a/media/libaaudio/src/flowgraph/FlowgraphUtilities.h b/media/libaaudio/src/flowgraph/FlowgraphUtilities.h
index 5e90588..e277d6e 100644
--- a/media/libaaudio/src/flowgraph/FlowgraphUtilities.h
+++ b/media/libaaudio/src/flowgraph/FlowgraphUtilities.h
@@ -17,6 +17,7 @@
 #ifndef FLOWGRAPH_UTILITIES_H
 #define FLOWGRAPH_UTILITIES_H
 
+#include <math.h>
 #include <unistd.h>
 
 using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph;
@@ -50,6 +51,20 @@
     return f > 0 ? f + 0.5 : f - 0.5;
 }
 
+/**
+ * Convert a single-precision floating point value to a Q0.23 integer value, stored in a
+ * 32 bit signed integer (technically stored as Q8.23, but clamped to Q0.23).
+ *
+ * Values outside the range [-1.0, 1.0) are properly clamped to -8388608 and 8388607,
+ * including -Inf and +Inf. NaN values are considered undefined, and behavior may change
+ * depending on hardware and future implementation of this function.
+ */
+static int32_t clamp24FromFloat(float f)
+{
+    static const float scale = 1 << 23;
+    return (int32_t) lroundf(fmaxf(fminf(f * scale, scale - 1.f), -scale));
+}
+
 };
 
 #endif // FLOWGRAPH_UTILITIES_H
diff --git a/media/libaaudio/src/flowgraph/SinkI8_24.cpp b/media/libaaudio/src/flowgraph/SinkI8_24.cpp
new file mode 100644
index 0000000..d5e4b80
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SinkI8_24.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FlowGraphNode.h"
+#include "FlowgraphUtilities.h"
+#include "SinkI8_24.h"
+
+#if FLOWGRAPH_ANDROID_INTERNAL
+#include <audio_utils/primitives.h>
+#endif
+
+using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph;
+
+SinkI8_24::SinkI8_24(int32_t channelCount)
+        : FlowGraphSink(channelCount) {}
+
+int32_t SinkI8_24::read(void *data, int32_t numFrames) {
+    int32_t *intData = (int32_t *) data;
+    const int32_t channelCount = input.getSamplesPerFrame();
+
+    int32_t framesLeft = numFrames;
+    while (framesLeft > 0) {
+        // Run the graph and pull data through the input port.
+        int32_t framesRead = pullData(framesLeft);
+        if (framesRead <= 0) {
+            break;
+        }
+        const float *signal = input.getBuffer();
+        int32_t numSamples = framesRead * channelCount;
+#if FLOWGRAPH_ANDROID_INTERNAL
+        memcpy_to_q8_23_from_float_with_clamp(intData, signal, numSamples);
+        intData += numSamples;
+        signal += numSamples;
+#else
+        for (int i = 0; i < numSamples; i++) {
+            *intData++ = FlowgraphUtilities::clamp24FromFloat(*signal++);
+        }
+#endif
+        framesLeft -= framesRead;
+    }
+    return numFrames - framesLeft;
+}
diff --git a/media/libaaudio/src/flowgraph/SinkI8_24.h b/media/libaaudio/src/flowgraph/SinkI8_24.h
new file mode 100644
index 0000000..aa96918
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SinkI8_24.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FLOWGRAPH_SINK_I8_24_H
+#define FLOWGRAPH_SINK_I8_24_H
+
+#include <stdint.h>
+
+#include "FlowGraphNode.h"
+
+namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph {
+
+class SinkI8_24 : public FlowGraphSink {
+public:
+    explicit SinkI8_24(int32_t channelCount);
+    ~SinkI8_24() override = default;
+
+    int32_t read(void *data, int32_t numFrames) override;
+
+    const char *getName() override {
+        return "SinkI8_24";
+    }
+};
+
+} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */
+
+#endif //FLOWGRAPH_SINK_I8_24_H
diff --git a/media/libaaudio/src/flowgraph/SourceI8_24.cpp b/media/libaaudio/src/flowgraph/SourceI8_24.cpp
new file mode 100644
index 0000000..684446c
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SourceI8_24.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <unistd.h>
+
+#include "FlowGraphNode.h"
+#include "SourceI8_24.h"
+
+#if FLOWGRAPH_ANDROID_INTERNAL
+#include <audio_utils/primitives.h>
+#endif
+
+using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph;
+
+SourceI8_24::SourceI8_24(int32_t channelCount)
+        : FlowGraphSourceBuffered(channelCount) {
+}
+
+int32_t SourceI8_24::onProcess(int32_t numFrames) {
+    float *floatData = output.getBuffer();
+    const int32_t channelCount = output.getSamplesPerFrame();
+
+    const int32_t framesLeft = mSizeInFrames - mFrameIndex;
+    const int32_t framesToProcess = std::min(numFrames, framesLeft);
+    const int32_t numSamples = framesToProcess * channelCount;
+
+    const int32_t *intBase = static_cast<const int32_t *>(mData);
+    const int32_t *intData = &intBase[mFrameIndex * channelCount];
+
+#if FLOWGRAPH_ANDROID_INTERNAL
+    memcpy_to_float_from_q8_23(floatData, intData, numSamples);
+#else
+    for (int i = 0; i < numSamples; i++) {
+        *floatData++ = *intData++ * kScale;
+    }
+#endif
+
+    mFrameIndex += framesToProcess;
+    return framesToProcess;
+}
diff --git a/media/libaaudio/src/flowgraph/SourceI8_24.h b/media/libaaudio/src/flowgraph/SourceI8_24.h
new file mode 100644
index 0000000..91c756c
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SourceI8_24.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FLOWGRAPH_SOURCE_I8_24_H
+#define FLOWGRAPH_SOURCE_I8_24_H
+
+#include <stdint.h>
+
+#include "FlowGraphNode.h"
+
+namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph {
+
+class SourceI8_24 : public FlowGraphSourceBuffered {
+public:
+    explicit SourceI8_24(int32_t channelCount);
+    ~SourceI8_24() override = default;
+
+    int32_t onProcess(int32_t numFrames) override;
+
+    const char *getName() override {
+        return "SourceI8_24";
+    }
+private:
+    static constexpr float kScale = 1.0 / (1UL << 23);
+};
+
+} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */
+
+#endif //FLOWGRAPH_SOURCE_I8_24_H
diff --git a/media/libaaudio/tests/Android.bp b/media/libaaudio/tests/Android.bp
index 1129ced..d59afef 100644
--- a/media/libaaudio/tests/Android.bp
+++ b/media/libaaudio/tests/Android.bp
@@ -241,3 +241,10 @@
         "libaaudio_internal",
     ],
 }
+
+cc_binary {
+    name: "test_idle_disconnected_shared_stream",
+    defaults: ["libaaudio_tests_defaults"],
+    srcs: ["test_idle_disconnected_shared_stream.cpp"],
+    shared_libs: ["libaaudio"],
+}
diff --git a/media/libaaudio/tests/test_flowgraph.cpp b/media/libaaudio/tests/test_flowgraph.cpp
index 7eb8b0d..f20a4bb 100644
--- a/media/libaaudio/tests/test_flowgraph.cpp
+++ b/media/libaaudio/tests/test_flowgraph.cpp
@@ -31,14 +31,16 @@
 #include "flowgraph/Limiter.h"
 #include "flowgraph/MonoBlend.h"
 #include "flowgraph/MonoToMultiConverter.h"
-#include "flowgraph/SourceFloat.h"
 #include "flowgraph/RampLinear.h"
 #include "flowgraph/SinkFloat.h"
 #include "flowgraph/SinkI16.h"
 #include "flowgraph/SinkI24.h"
 #include "flowgraph/SinkI32.h"
+#include "flowgraph/SinkI8_24.h"
+#include "flowgraph/SourceFloat.h"
 #include "flowgraph/SourceI16.h"
 #include "flowgraph/SourceI24.h"
+#include "flowgraph/SourceI8_24.h"
 #include "flowgraph/resampler/IntegerRatio.h"
 
 using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph;
@@ -52,6 +54,9 @@
     PARAM_RESAMPLER_QUALITY
 };
 
+constexpr int kInt24Min = 0xff800000;
+constexpr int kInt24Max = 0x007fffff;
+
 constexpr int kBytesPerI24Packed = 3;
 
 constexpr int kNumSamples = 8;
@@ -59,15 +64,19 @@
     1.0f, 0.5f, -0.25f, -1.0f,
     0.0f, 53.9f, -87.2f, -1.02f};
 
-// Corresponding PCM values  as integers.
-constexpr std::array<int16_t, kNumSamples>  kExpectedI16 = {
+// Corresponding PCM values as integers.
+constexpr std::array<int16_t, kNumSamples> kExpectedI16 = {
     INT16_MAX, 1 << 14, INT16_MIN / 4, INT16_MIN,
     0, INT16_MAX, INT16_MIN, INT16_MIN};
 
-constexpr std::array<int32_t, kNumSamples>  kExpectedI32 = {
+constexpr std::array<int32_t, kNumSamples> kExpectedI32 = {
     INT32_MAX, 1 << 30, INT32_MIN / 4, INT32_MIN,
     0, INT32_MAX, INT32_MIN, INT32_MIN};
 
+constexpr std::array<int32_t, kNumSamples> kExpectedI8_24 = {
+    kInt24Max, 1 << 22, kInt24Min / 4, kInt24Min,
+    0, kInt24Max, kInt24Min, kInt24Min};
+
 // =================================== FLOAT to I16 ==============
 
 // Simple test that tries to reproduce a Clang compiler bug.
@@ -215,6 +224,8 @@
     EXPECT_EQ(input[0], output[1]);
     EXPECT_EQ(input[1], output[2]);
     EXPECT_EQ(input[1], output[3]);
+    EXPECT_EQ(input[2], output[4]);
+    EXPECT_EQ(input[2], output[5]);
 }
 
 TEST(test_flowgraph, module_ramp_linear) {
@@ -434,6 +445,70 @@
     }
 }
 
+// =================================== FLOAT to Q8.23 ==============
+__attribute__((noinline))
+static int32_t clamp24FromFloat(float f)
+{
+    static const float scale = 1 << 23;
+    return (int32_t) lroundf(fmaxf(fminf(f * scale, scale - 1.f), -scale));
+}
+
+void local_convert_float_to_i8_24(const float *input,
+                                  int32_t *output,
+                                  int count) {
+    for (int i = 0; i < count; i++) {
+        *output++ = clamp24FromFloat(*input++);
+    }
+}
+
+TEST(test_flowgraph, local_convert_float_to_i8_24) {
+    std::array<int32_t, kNumSamples> output;
+    // Convert audio signal using the function.
+    output.fill(777);
+    local_convert_float_to_i8_24(kInputFloat.data(), output.data(), kNumSamples);
+    for (int i = 0; i < kNumSamples; i++) {
+        EXPECT_EQ(kExpectedI8_24.at(i), output.at(i)) << ", i = " << i;
+    }
+}
+
+TEST(test_flowgraph, module_sinkI8_24) {
+    std::array<int32_t, kNumSamples + 10> output; // larger than input
+
+    SourceFloat sourceFloat{2};
+    SinkI8_24 sinkI8_24{2};
+
+    sourceFloat.setData(kInputFloat.data(), kNumSamples);
+    sourceFloat.output.connect(&sinkI8_24.input);
+
+    output.fill(777);
+    int32_t numRead = sinkI8_24.read(output.data(), output.size());
+    ASSERT_EQ(kNumSamples, numRead);
+    for (int i = 0; i < numRead; i++) {
+        EXPECT_EQ(kExpectedI8_24.at(i), output.at(i)) << ", i = " << i;
+    }
+}
+
+TEST(test_flowgraph, module_sourceI8_24) {
+    static const int32_t input[] = {1 << 23, 1 << 22, -(1 << 21), -(1 << 23), 0, 1 << 25,
+            -(1 << 25)};
+    static const float expected[] = {1.0f, 0.5f, -0.25f, -1.0f, 0.0f, 4.0f, -4.0f};
+    float output[100];
+
+    SourceI8_24 sourceI8_24{1};
+    SinkFloat sinkFloat{1};
+
+    int numSamples = std::size(input);
+
+    sourceI8_24.setData(input, numSamples);
+    sourceI8_24.output.connect(&sinkFloat.input);
+
+    int32_t numRead = sinkFloat.read(output, numSamples);
+    ASSERT_EQ(numSamples, numRead);
+    for (int i = 0; i < numRead; i++) {
+        EXPECT_EQ(expected[i], output[i]) << ", i = " << i;
+    }
+}
+
 void checkSampleRateConversionVariedSizes(int32_t sourceSampleRate,
                     int32_t sinkSampleRate,
                     MultiChannelResampler::Quality resamplerQuality) {
diff --git a/media/libaaudio/tests/test_idle_disconnected_shared_stream.cpp b/media/libaaudio/tests/test_idle_disconnected_shared_stream.cpp
new file mode 100644
index 0000000..2e24b6b
--- /dev/null
+++ b/media/libaaudio/tests/test_idle_disconnected_shared_stream.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// When receive disconnect event, ignore it and leave the shared stream at OPEN
+// state. It should be possible to open another shared stream and start it.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <aaudio/AAudio.h>
+#include <aaudio/AAudioTesting.h>
+
+static constexpr unsigned int ONE_SECOND = 1e6;
+static constexpr unsigned int WAIT_TIME_MS = 10 * ONE_SECOND;
+#define MMAP_POLICY              AAUDIO_POLICY_ALWAYS
+
+AAudioStream* openStream() {
+    AAudioStreamBuilder *aaudioBuilder = nullptr;
+    aaudio_result_t result = AAudio_createStreamBuilder(&aaudioBuilder);
+    if (result != AAUDIO_OK) {
+        printf("Failed to create stream builder, result=%d, %s\n",
+               result, AAudio_convertResultToText(result));
+        return nullptr;
+    }
+    AAudioStreamBuilder_setSharingMode(aaudioBuilder, AAUDIO_SHARING_MODE_SHARED);
+    AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+    AAudioStream* aaudioStream;
+    result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
+    if (result != AAUDIO_OK) {
+        printf("ERROR could not open AAudio stream, %d %s\n",
+               result, AAudio_convertResultToText(result));
+    }
+    AAudioStreamBuilder_delete(aaudioBuilder);
+    return aaudioStream;
+}
+
+aaudio_result_t testNoCloseSharedStreamAfterRoutingChanged(bool stopFirstStream) {
+    aaudio_result_t result = AAUDIO_OK;
+    printf("Please connect external device that supports MMAP playback, will wait 10 seconds\n");
+    usleep(WAIT_TIME_MS);
+
+    // Open first shared stream
+    printf("Open first shared stream\n");
+    AAudioStream* firstStream = openStream();
+    if (firstStream == nullptr) {
+        return 1;
+    }
+    result = AAudioStream_requestStart(firstStream);
+    if (result != AAUDIO_OK) {
+        return result;
+    }
+
+    if (stopFirstStream) {
+        printf("Stop first shared stream\n");
+        result = AAudioStream_requestStop(firstStream);
+        if (result != AAUDIO_OK) {
+            return result;
+        }
+        printf("Wait to make sure the stream is stopped\n");
+        usleep(ONE_SECOND * 3);
+    }
+
+    printf("Please disconnect and reconnect the external device, will wait 10 second\n");
+    usleep(WAIT_TIME_MS);
+
+    // Open second stream after the first shared stream was reconnected
+    printf("Open second shared stream\n");
+    AAudioStream* secondStream = openStream();
+    if (secondStream == nullptr) {
+        result = 1;
+        goto exit;
+    }
+
+    // Starting second stream should be successful
+    printf("Start second shared stream\n");
+    result = AAudioStream_requestStart(secondStream);
+    if (result != AAUDIO_OK) {
+        printf("ERROR could not start second stream, %d %s\n",
+               result, AAudio_convertResultToText(result));
+    }
+
+exit:
+    // Close all streams
+    AAudioStream_close(firstStream);
+    AAudioStream_close(secondStream);
+    return result;
+}
+
+int main(int argc, char **argv) {
+    (void) argc; // unused
+    (void) argv; // unused
+
+    aaudio_policy_t originalPolicy = AAudio_getMMapPolicy();
+    AAudio_setMMapPolicy(MMAP_POLICY);
+
+    printf("Run first test. The first stream is started when routing changed.\n");
+    aaudio_result_t result = testNoCloseSharedStreamAfterRoutingChanged(false /*stopFirstStream*/);
+
+    if (result != AAUDIO_OK) {
+        goto exit;
+    }
+
+    printf("First test passed\n");
+    printf("----------------------------------------------------------------\n");
+    printf("Run second test. The first stream is stopped when routing changed.\n");
+    if (testNoCloseSharedStreamAfterRoutingChanged(true /*stopFirstStream*/) == AAUDIO_OK) {
+        printf("Second test passed\n");
+    }
+
+exit:
+    AAudio_setMMapPolicy(originalPolicy);
+
+    return result != AAUDIO_OK ? EXIT_FAILURE : EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index 658bf63..51a679b 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -217,6 +217,7 @@
     ],
     defaults: [
         "audio_aidl_conversion_common_default",
+        "audio_aidl_conversion_common_default_cpp",
         "latest_android_media_audio_common_types_cpp_export_shared",
     ],
 }
diff --git a/media/libaudiohal/impl/DeviceHalAidl.cpp b/media/libaudiohal/impl/DeviceHalAidl.cpp
index 6a6557c..86fa63f 100644
--- a/media/libaudiohal/impl/DeviceHalAidl.cpp
+++ b/media/libaudiohal/impl/DeviceHalAidl.cpp
@@ -451,6 +451,7 @@
     }
     *config = VALUE_OR_RETURN_STATUS(
             ::aidl::android::aidl2legacy_AudioConfig_audio_config_t(aidlConfig, isInput));
+    if (mixPortConfig.id == 0) return BAD_VALUE;  // HAL suggests a different config.
     ::aidl::android::hardware::audio::core::IModule::OpenOutputStreamArguments args;
     args.portConfigId = mixPortConfig.id;
     const bool isOffload = isBitPositionFlagSet(
@@ -523,6 +524,7 @@
     }
     *config = VALUE_OR_RETURN_STATUS(
             ::aidl::android::aidl2legacy_AudioConfig_audio_config_t(aidlConfig, isInput));
+    if (mixPortConfig.id == 0) return BAD_VALUE;  // HAL suggests a different config.
     ::aidl::android::hardware::audio::core::IModule::OpenInputStreamArguments args;
     args.portConfigId = mixPortConfig.id;
     RecordTrackMetadata aidlTrackMetadata{
@@ -707,8 +709,7 @@
                     *config, isInput, 0 /*portId*/));
     AudioPortConfig portConfig;
     std::lock_guard l(mLock);
-    return mMapper.findOrCreatePortConfig(
-            requestedPortConfig, std::set<int32_t>(), &portConfig);
+    return mMapper.setPortConfig(requestedPortConfig, std::set<int32_t>(), &portConfig);
 }
 
 MicrophoneInfoProvider::Info const* DeviceHalAidl::getMicrophoneInfo() {
@@ -780,7 +781,7 @@
     Hal2AidlMapper::Cleanups cleanups(mMapperAccessor);
     {
         std::lock_guard l(mLock);
-        RETURN_STATUS_IF_ERROR(mMapper.findOrCreatePortConfig(
+        RETURN_STATUS_IF_ERROR(mMapper.setPortConfig(
                     requestedPortConfig, {} /*destinationPortIds*/, &devicePortConfig, &cleanups));
     }
     auto aidlEffect = sp<effect::EffectHalAidl>::cast(effect);
diff --git a/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp b/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp
index a8f9f7e..96a3e60 100644
--- a/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp
+++ b/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp
@@ -24,7 +24,6 @@
 
 #include <aidl/android/hardware/audio/core/IModule.h>
 #include <android/binder_manager.h>
-#include <binder/IServiceManager.h>
 #include <media/AidlConversionNdkCpp.h>
 #include <media/AidlConversionUtil.h>
 #include <utils/Log.h>
@@ -119,23 +118,9 @@
     if (pids == nullptr) {
         return BAD_VALUE;
     }
-    // The functionality for retrieving debug infos of services is not exposed via the NDK.
-    sp<IServiceManager> sm = defaultServiceManager();
-    if (sm == nullptr) {
-        return NO_INIT;
-    }
-    std::set<pid_t> pidsSet;
-    const auto moduleServiceName = std::string(IModule::descriptor) + "/";
-    auto debugInfos = sm->getServiceDebugInfo();
-    for (const auto& info : debugInfos) {
-        if (info.pid > 0 &&
-                info.name.size() > moduleServiceName.size() && // '>' as there must be instance name
-                info.name.substr(0, moduleServiceName.size()) == moduleServiceName) {
-            pidsSet.insert(info.pid);
-        }
-    }
-    *pids = {pidsSet.begin(), pidsSet.end()};
-    return NO_ERROR;
+    // Retrieval of HAL pids requires "list services" permission which is not granted
+    // to the audio server. This job is performed by AudioService (in Java) instead.
+    return PERMISSION_DENIED;
 }
 
 status_t DevicesFactoryHalAidl::setCallbackOnce(sp<DevicesFactoryHalCallback> callback) {
diff --git a/media/libaudiohal/impl/EffectProxy.cpp b/media/libaudiohal/impl/EffectProxy.cpp
index aee42a9..182d68a 100644
--- a/media/libaudiohal/impl/EffectProxy.cpp
+++ b/media/libaudiohal/impl/EffectProxy.cpp
@@ -131,6 +131,7 @@
 }
 
 ndk::ScopedAStatus EffectProxy::close() {
+    command(CommandId::STOP);
     return runWithAllSubEffects([&](std::shared_ptr<IEffect>& effect) {
         return effect->close();
     });
diff --git a/media/libaudiohal/impl/Hal2AidlMapper.cpp b/media/libaudiohal/impl/Hal2AidlMapper.cpp
index 47fcd27..413a1f8 100644
--- a/media/libaudiohal/impl/Hal2AidlMapper.cpp
+++ b/media/libaudiohal/impl/Hal2AidlMapper.cpp
@@ -30,11 +30,13 @@
 using aidl::android::aidl_utils::statusTFromBinderStatus;
 using aidl::android::media::audio::common::AudioChannelLayout;
 using aidl::android::media::audio::common::AudioConfig;
+using aidl::android::media::audio::common::AudioConfigBase;
 using aidl::android::media::audio::common::AudioDevice;
 using aidl::android::media::audio::common::AudioDeviceAddress;
 using aidl::android::media::audio::common::AudioDeviceDescription;
 using aidl::android::media::audio::common::AudioDeviceType;
 using aidl::android::media::audio::common::AudioFormatDescription;
+using aidl::android::media::audio::common::AudioFormatType;
 using aidl::android::media::audio::common::AudioInputFlags;
 using aidl::android::media::audio::common::AudioIoFlags;
 using aidl::android::media::audio::common::AudioOutputFlags;
@@ -64,10 +66,11 @@
             portConfig.format.value() == config.base.format;
 }
 
-void setConfigFromPortConfig(AudioConfig* config, const AudioPortConfig& portConfig) {
+AudioConfig* setConfigFromPortConfig(AudioConfig* config, const AudioPortConfig& portConfig) {
     config->base.sampleRate = portConfig.sampleRate.value().value;
     config->base.channelMask = portConfig.channelMask.value();
     config->base.format = portConfig.format.value();
+    return config;
 }
 
 void setPortConfigFromConfig(AudioPortConfig* portConfig, const AudioConfig& config) {
@@ -142,8 +145,11 @@
             std::vector<int32_t>* ids, std::set<int32_t>* portIds) -> status_t {
         for (const auto& s : configs) {
             AudioPortConfig portConfig;
-            RETURN_STATUS_IF_ERROR(findOrCreatePortConfig(
+            RETURN_STATUS_IF_ERROR(setPortConfig(
                             s, destinationPortIds, &portConfig, cleanups));
+            LOG_ALWAYS_FATAL_IF(portConfig.id == 0,
+                    "fillPortConfigs: initial config: %s, port config: %s",
+                    s.toString().c_str(), portConfig.toString().c_str());
             ids->push_back(portConfig.id);
             if (portIds != nullptr) {
                 portIds->insert(portConfig.portId);
@@ -189,30 +195,23 @@
 }
 
 status_t Hal2AidlMapper::createOrUpdatePortConfig(
-        const AudioPortConfig& requestedPortConfig, PortConfigs::iterator* result, bool* created) {
-    AudioPortConfig appliedPortConfig;
+        const AudioPortConfig& requestedPortConfig, AudioPortConfig* result, bool* created) {
     bool applied = false;
     RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->setAudioPortConfig(
-                            requestedPortConfig, &appliedPortConfig, &applied)));
+                            requestedPortConfig, result, &applied)));
     if (!applied) {
-        RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->setAudioPortConfig(
-                                appliedPortConfig, &appliedPortConfig, &applied)));
-        if (!applied) {
-            ALOGE("%s: module %s did not apply suggested config %s",
-                    __func__, mInstance.c_str(), appliedPortConfig.toString().c_str());
-            return NO_INIT;
-        }
+        result->id = 0;
+        *created = false;
+        return OK;
     }
 
-    int32_t id = appliedPortConfig.id;
+    int32_t id = result->id;
     if (requestedPortConfig.id != 0 && requestedPortConfig.id != id) {
         LOG_ALWAYS_FATAL("%s: requested port config id %d changed to %d", __func__,
                 requestedPortConfig.id, id);
     }
 
-    auto [it, inserted] = mPortConfigs.insert_or_assign(std::move(id),
-            std::move(appliedPortConfig));
-    *result = it;
+    auto [_, inserted] = mPortConfigs.insert_or_assign(id, *result);
     *created = inserted;
     return OK;
 }
@@ -258,11 +257,10 @@
     return OK;
 }
 
-status_t Hal2AidlMapper::findOrCreatePortConfig(
+status_t Hal2AidlMapper::findOrCreateDevicePortConfig(
         const AudioDevice& device, const AudioConfig* config, AudioPortConfig* portConfig,
         bool* created) {
-    auto portConfigIt = findPortConfig(device);
-    if (portConfigIt == mPortConfigs.end()) {
+    if (auto portConfigIt = findPortConfig(device); portConfigIt == mPortConfigs.end()) {
         auto portsIt = findPort(device);
         if (portsIt == mPorts.end()) {
             ALOGE("%s: device port for device %s is not found in the module %s",
@@ -274,24 +272,40 @@
         if (config != nullptr) {
             setPortConfigFromConfig(&requestedPortConfig, *config);
         }
-        RETURN_STATUS_IF_ERROR(createOrUpdatePortConfig(requestedPortConfig, &portConfigIt,
-                created));
+        AudioPortConfig suggestedOrAppliedPortConfig;
+        RETURN_STATUS_IF_ERROR(createOrUpdatePortConfig(requestedPortConfig,
+                        &suggestedOrAppliedPortConfig, created));
+        if (suggestedOrAppliedPortConfig.id == 0) {
+            // Try again with the suggested config
+            suggestedOrAppliedPortConfig.id = requestedPortConfig.id;
+            AudioPortConfig appliedPortConfig;
+            RETURN_STATUS_IF_ERROR(createOrUpdatePortConfig(suggestedOrAppliedPortConfig,
+                            &appliedPortConfig, created));
+            if (appliedPortConfig.id == 0) {
+                ALOGE("%s: module %s did not apply suggested config %s", __func__,
+                        mInstance.c_str(), suggestedOrAppliedPortConfig.toString().c_str());
+                return NO_INIT;
+            }
+            *portConfig = appliedPortConfig;
+        } else {
+            *portConfig = suggestedOrAppliedPortConfig;
+        }
     } else {
+        *portConfig = portConfigIt->second;
         *created = false;
     }
-    *portConfig = portConfigIt->second;
     return OK;
 }
 
-status_t Hal2AidlMapper::findOrCreatePortConfig(
+status_t Hal2AidlMapper::findOrCreateMixPortConfig(
         const AudioConfig& config, const std::optional<AudioIoFlags>& flags, int32_t ioHandle,
         AudioSource source, const std::set<int32_t>& destinationPortIds,
         AudioPortConfig* portConfig, bool* created) {
     // These flags get removed one by one in this order when retrying port finding.
     static const std::vector<AudioInputFlags> kOptionalInputFlags{
         AudioInputFlags::FAST, AudioInputFlags::RAW, AudioInputFlags::VOIP_TX };
-    auto portConfigIt = findPortConfig(config, flags, ioHandle);
-    if (portConfigIt == mPortConfigs.end() && flags.has_value()) {
+    if (auto portConfigIt = findPortConfig(config, flags, ioHandle);
+            portConfigIt == mPortConfigs.end() && flags.has_value()) {
         auto optionalInputFlagsIt = kOptionalInputFlags.begin();
         AudioIoFlags matchFlags = flags.value();
         auto portsIt = findPort(config, matchFlags, destinationPortIds);
@@ -319,14 +333,14 @@
         AudioPortConfig requestedPortConfig;
         requestedPortConfig.portId = portsIt->first;
         setPortConfigFromConfig(&requestedPortConfig, config);
+        requestedPortConfig.flags = portsIt->second.flags;
         requestedPortConfig.ext = AudioPortMixExt{ .handle = ioHandle };
         if (matchFlags.getTag() == AudioIoFlags::Tag::input
                 && source != AudioSource::SYS_RESERVED_INVALID) {
             requestedPortConfig.ext.get<AudioPortExt::Tag::mix>().usecase =
                     AudioPortMixExtUseCase::make<AudioPortMixExtUseCase::Tag::source>(source);
         }
-        RETURN_STATUS_IF_ERROR(createOrUpdatePortConfig(requestedPortConfig, &portConfigIt,
-                created));
+        return createOrUpdatePortConfig(requestedPortConfig, portConfig, created);
     } else if (portConfigIt == mPortConfigs.end() && !flags.has_value()) {
         ALOGW("%s: mix port config for %s, handle %d not found in the module %s, "
                 "and was not created as flags are not specified",
@@ -343,13 +357,12 @@
         }
 
         if (requestedPortConfig != portConfigIt->second) {
-            RETURN_STATUS_IF_ERROR(createOrUpdatePortConfig(requestedPortConfig, &portConfigIt,
-                    created));
+            return createOrUpdatePortConfig(requestedPortConfig, portConfig, created);
         } else {
+            *portConfig = portConfigIt->second;
             *created = false;
         }
     }
-    *portConfig = portConfigIt->second;
     return OK;
 }
 
@@ -371,11 +384,11 @@
                 AudioPortMixExtUseCase::Tag::source ?
                 requestedPortConfig.ext.get<Tag::mix>().usecase.
                 get<AudioPortMixExtUseCase::Tag::source>() : AudioSource::SYS_RESERVED_INVALID;
-        return findOrCreatePortConfig(config, requestedPortConfig.flags,
+        return findOrCreateMixPortConfig(config, requestedPortConfig.flags,
                 requestedPortConfig.ext.get<Tag::mix>().handle, source, destinationPortIds,
                 portConfig, created);
     } else if (requestedPortConfig.ext.getTag() == Tag::device) {
-        return findOrCreatePortConfig(
+        return findOrCreateDevicePortConfig(
                 requestedPortConfig.ext.get<Tag::device>().device, nullptr /*config*/,
                 portConfig, created);
     }
@@ -384,18 +397,6 @@
     return BAD_VALUE;
 }
 
-status_t Hal2AidlMapper::findOrCreatePortConfig(
-        const AudioPortConfig& requestedPortConfig, const std::set<int32_t>& destinationPortIds,
-        AudioPortConfig* portConfig, Cleanups* cleanups) {
-    bool created = false;
-    RETURN_STATUS_IF_ERROR(findOrCreatePortConfig(
-                    requestedPortConfig, destinationPortIds, portConfig, &created));
-    if (created && cleanups != nullptr) {
-        cleanups->add(&Hal2AidlMapper::resetPortConfig, portConfig->id);
-    }
-    return OK;
-}
-
 status_t Hal2AidlMapper::findPortConfig(const AudioDevice& device, AudioPortConfig* portConfig) {
     if (auto it = findPortConfig(device); it != mPortConfigs.end()) {
         *portConfig = it->second;
@@ -675,21 +676,56 @@
             config->toString().c_str(), mixPortConfig->toString().c_str());
     resetUnusedPatchesAndPortConfigs();
     const bool isInput = flags.getTag() == AudioIoFlags::Tag::input;
+    const AudioConfig initialConfig = *config;
     // Find / create AudioPortConfigs for the device port and the mix port,
     // then find / create a patch between them, and open a stream on the mix port.
     AudioPortConfig devicePortConfig;
     bool created = false;
-    RETURN_STATUS_IF_ERROR(findOrCreatePortConfig(device, config,
-                                                  &devicePortConfig, &created));
+    RETURN_STATUS_IF_ERROR(findOrCreateDevicePortConfig(device, config,
+                    &devicePortConfig, &created));
+    LOG_ALWAYS_FATAL_IF(devicePortConfig.id == 0);
     if (created) {
         cleanups->add(&Hal2AidlMapper::resetPortConfig, devicePortConfig.id);
     }
-    RETURN_STATUS_IF_ERROR(findOrCreatePortConfig(*config, flags, ioHandle, source,
+    RETURN_STATUS_IF_ERROR(findOrCreateMixPortConfig(*config, flags, ioHandle, source,
                     std::set<int32_t>{devicePortConfig.portId}, mixPortConfig, &created));
     if (created) {
         cleanups->add(&Hal2AidlMapper::resetPortConfig, mixPortConfig->id);
     }
     setConfigFromPortConfig(config, *mixPortConfig);
+    bool retryWithSuggestedConfig = false;   // By default, let the framework to retry.
+    if (mixPortConfig->id == 0 && config->base == AudioConfigBase{}) {
+        // The HAL proposes a default config, can retry here.
+        retryWithSuggestedConfig = true;
+    } else if (isInput && config->base != initialConfig.base) {
+        // If the resulting config is different, we must stop and provide the config to the
+        // framework so that it can retry.
+        mixPortConfig->id = 0;
+    } else if (!isInput && mixPortConfig->id == 0 &&
+                    (initialConfig.base.format.type == AudioFormatType::PCM ||
+                            !isBitPositionFlagSet(flags.get<AudioIoFlags::output>(),
+                                    AudioOutputFlags::DIRECT) ||
+                            isBitPositionFlagSet(flags.get<AudioIoFlags::output>(),
+                                    AudioOutputFlags::COMPRESS_OFFLOAD))) {
+        // The framework does not retry opening non-direct PCM and IEC61937 outputs, need to retry
+        // here (see 'AudioHwDevice::openOutputStream').
+        retryWithSuggestedConfig = true;
+    }
+    if (mixPortConfig->id == 0 && retryWithSuggestedConfig) {
+        ALOGD("%s: retrying to find/create a mix port config using config %s", __func__,
+                config->toString().c_str());
+        RETURN_STATUS_IF_ERROR(findOrCreateMixPortConfig(*config, flags, ioHandle, source,
+                        std::set<int32_t>{devicePortConfig.portId}, mixPortConfig, &created));
+        if (created) {
+            cleanups->add(&Hal2AidlMapper::resetPortConfig, mixPortConfig->id);
+        }
+        setConfigFromPortConfig(config, *mixPortConfig);
+    }
+    if (mixPortConfig->id == 0) {
+        ALOGD("%p %s: returning suggested config for the stream: %s", this, __func__,
+                config->toString().c_str());
+        return OK;
+    }
     if (isInput) {
         RETURN_STATUS_IF_ERROR(findOrCreatePatch(
                         {devicePortConfig.id}, {mixPortConfig->id}, patch, &created));
@@ -706,6 +742,18 @@
     return OK;
 }
 
+status_t Hal2AidlMapper::setPortConfig(
+        const AudioPortConfig& requestedPortConfig, const std::set<int32_t>& destinationPortIds,
+        AudioPortConfig* portConfig, Cleanups* cleanups) {
+    bool created = false;
+    RETURN_STATUS_IF_ERROR(findOrCreatePortConfig(
+                    requestedPortConfig, destinationPortIds, portConfig, &created));
+    if (created && cleanups != nullptr) {
+        cleanups->add(&Hal2AidlMapper::resetPortConfig, portConfig->id);
+    }
+    return OK;
+}
+
 status_t Hal2AidlMapper::releaseAudioPatch(int32_t patchId) {
     return releaseAudioPatches({patchId});
 }
diff --git a/media/libaudiohal/impl/Hal2AidlMapper.h b/media/libaudiohal/impl/Hal2AidlMapper.h
index 70a2bd7..ee55b22 100644
--- a/media/libaudiohal/impl/Hal2AidlMapper.h
+++ b/media/libaudiohal/impl/Hal2AidlMapper.h
@@ -36,6 +36,11 @@
 // structures directly. Mapper does the job of translating the "legacy" way of identifying ports
 // and port configs (by device addresses and I/O handles) into AIDL IDs. Once the framework will
 // be updated to provide these IDs directly to libaudiohal, the need for the mapper will cease.
+//
+// Note that unlike DeviceHalInterface, which sometimes allows a method to return an error,
+// but still consider some of the outputs to be valid (for example, in 'open{Input|Output}Stream'),
+// 'Hal2AidlMapper' follows the Binder convention. It means that if a method returns an error,
+// the outputs may not be initialized at all and should not be considered by the caller.
 class Hal2AidlMapper {
   public:
     using Cleanups = Cleanups<Hal2AidlMapper>;
@@ -49,27 +54,6 @@
             const std::vector<::aidl::android::media::audio::common::AudioPortConfig>& sources,
             const std::vector<::aidl::android::media::audio::common::AudioPortConfig>& sinks,
             int32_t* patchId, Cleanups* cleanups);
-    status_t findOrCreatePortConfig(
-            const ::aidl::android::media::audio::common::AudioDevice& device,
-            const ::aidl::android::media::audio::common::AudioConfig* config,
-            ::aidl::android::media::audio::common::AudioPortConfig* portConfig,
-            bool* created);
-    status_t findOrCreatePortConfig(
-            const ::aidl::android::media::audio::common::AudioConfig& config,
-            const std::optional<::aidl::android::media::audio::common::AudioIoFlags>& flags,
-            int32_t ioHandle,
-            ::aidl::android::media::audio::common::AudioSource source,
-            const std::set<int32_t>& destinationPortIds,
-            ::aidl::android::media::audio::common::AudioPortConfig* portConfig, bool* created);
-    status_t findOrCreatePortConfig(
-        const ::aidl::android::media::audio::common::AudioPortConfig& requestedPortConfig,
-        const std::set<int32_t>& destinationPortIds,
-        ::aidl::android::media::audio::common::AudioPortConfig* portConfig, bool* created);
-    status_t findOrCreatePortConfig(
-        const ::aidl::android::media::audio::common::AudioPortConfig& requestedPortConfig,
-        const std::set<int32_t>& destinationPortIds,
-        ::aidl::android::media::audio::common::AudioPortConfig* portConfig,
-        Cleanups* cleanups = nullptr);
     status_t findPortConfig(
             const ::aidl::android::media::audio::common::AudioDevice& device,
             ::aidl::android::media::audio::common::AudioPortConfig* portConfig);
@@ -88,6 +72,8 @@
         return ::aidl::android::convertContainer(mRoutes, routes, converter);
     }
     status_t initialize();
+    // If the resulting 'mixPortConfig->id' is 0, that means the stream was not created,
+    // and 'config' is a suggested config.
     status_t prepareToOpenStream(
         int32_t ioHandle,
         const ::aidl::android::media::audio::common::AudioDevice& device,
@@ -97,6 +83,11 @@
         ::aidl::android::media::audio::common::AudioConfig* config,
         ::aidl::android::media::audio::common::AudioPortConfig* mixPortConfig,
         ::aidl::android::hardware::audio::core::AudioPatch* patch);
+    status_t setPortConfig(
+        const ::aidl::android::media::audio::common::AudioPortConfig& requestedPortConfig,
+        const std::set<int32_t>& destinationPortIds,
+        ::aidl::android::media::audio::common::AudioPortConfig* portConfig,
+        Cleanups* cleanups = nullptr);
     status_t releaseAudioPatch(int32_t patchId);
     void resetUnusedPatchesAndPortConfigs();
     status_t setDevicePortConnectedState(
@@ -128,9 +119,11 @@
             const ::aidl::android::media::audio::common::AudioPort& p);
     bool audioDeviceMatches(const ::aidl::android::media::audio::common::AudioDevice& device,
             const ::aidl::android::media::audio::common::AudioPortConfig& p);
+    // If the 'result->id' is 0, that means, the config was not created/updated,
+    // and the 'result' is a suggestion from the HAL.
     status_t createOrUpdatePortConfig(
             const ::aidl::android::media::audio::common::AudioPortConfig& requestedPortConfig,
-            PortConfigs::iterator* result, bool *created);
+            ::aidl::android::media::audio::common::AudioPortConfig* result, bool *created);
     void eraseConnectedPort(int32_t portId);
     status_t findOrCreatePatch(
         const std::set<int32_t>& sourcePortConfigIds,
@@ -139,6 +132,24 @@
     status_t findOrCreatePatch(
         const ::aidl::android::hardware::audio::core::AudioPatch& requestedPatch,
         ::aidl::android::hardware::audio::core::AudioPatch* patch, bool* created);
+    status_t findOrCreateDevicePortConfig(
+            const ::aidl::android::media::audio::common::AudioDevice& device,
+            const ::aidl::android::media::audio::common::AudioConfig* config,
+            ::aidl::android::media::audio::common::AudioPortConfig* portConfig,
+            bool* created);
+    // If the resulting 'portConfig->id' is 0, that means the config was not created,
+    // and 'portConfig' is a suggested config.
+    status_t findOrCreateMixPortConfig(
+            const ::aidl::android::media::audio::common::AudioConfig& config,
+            const std::optional<::aidl::android::media::audio::common::AudioIoFlags>& flags,
+            int32_t ioHandle,
+            ::aidl::android::media::audio::common::AudioSource source,
+            const std::set<int32_t>& destinationPortIds,
+            ::aidl::android::media::audio::common::AudioPortConfig* portConfig, bool* created);
+    status_t findOrCreatePortConfig(
+        const ::aidl::android::media::audio::common::AudioPortConfig& requestedPortConfig,
+        const std::set<int32_t>& destinationPortIds,
+        ::aidl::android::media::audio::common::AudioPortConfig* portConfig, bool* created);
     Patches::iterator findPatch(const std::set<int32_t>& sourcePortConfigIds,
             const std::set<int32_t>& sinkPortConfigIds);
     Ports::iterator findPort(const ::aidl::android::media::audio::common::AudioDevice& device);
diff --git a/media/libaudiohal/tests/CoreAudioHalAidl_test.cpp b/media/libaudiohal/tests/CoreAudioHalAidl_test.cpp
index adff110..1204a3b 100644
--- a/media/libaudiohal/tests/CoreAudioHalAidl_test.cpp
+++ b/media/libaudiohal/tests/CoreAudioHalAidl_test.cpp
@@ -201,6 +201,9 @@
     ndk::ScopedAStatus getAAudioHardwareBurstMinUsec(int32_t*) override {
         return ndk::ScopedAStatus::ok();
     }
+    ndk::ScopedAStatus prepareToDisconnectExternalDevice(int32_t) override {
+        return ndk::ScopedAStatus::ok();
+    }
 
     bool mIsScreenTurnedOn = false;
     ScreenRotation mScreenRotation = ScreenRotation::DEG_0;
diff --git a/media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp b/media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp
index 578f58a..33f6779 100644
--- a/media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp
+++ b/media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp
@@ -89,8 +89,7 @@
   } else {
     state_ = alpha_release_ * state_ + (1.0f - alpha_release_) * cv;
   }
-  compressor_gain_ *=
-      math::ExpApproximationViaTaylorExpansionOrder5(state_ - prev_state);
+  compressor_gain_ *= expf(state_ - prev_state);
   x *= compressor_gain_;
   if (x > kFixedPointLimit) {
     return kFixedPointLimit;
@@ -118,8 +117,7 @@
   } else {
     state_ = alpha_release_ * state_ + (1.0f - alpha_release_) * cv;
   }
-  compressor_gain_ *=
-      math::ExpApproximationViaTaylorExpansionOrder5(state_ - prev_state);
+  compressor_gain_ *= expf(state_ - prev_state);
   *x1 *= compressor_gain_;
   if (*x1 > kFixedPointLimit) {
     *x1 = kFixedPointLimit;
diff --git a/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp b/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp
index 0db7a73..a163f4b 100644
--- a/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp
+++ b/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp
@@ -32,6 +32,20 @@
 using ::aidl::android::media::audio::common::AudioDeviceDescription;
 using ::aidl::android::media::audio::common::AudioDeviceType;
 
+BundleContext::BundleContext(int statusDepth, const Parameter::Common& common,
+              const lvm::BundleEffectType& type)
+        : EffectContext(statusDepth, common), mType(type) {
+    LOG(DEBUG) << __func__ << type;
+    int channelCount = ::aidl::android::hardware::audio::common::getChannelCount(
+            common.input.base.channelMask);
+    mSamplesPerSecond = common.input.base.sampleRate * channelCount;
+}
+
+BundleContext::~BundleContext() {
+    LOG(DEBUG) << __func__;
+    deInit();
+}
+
 RetCode BundleContext::init() {
     std::lock_guard lg(mMutex);
     // init with pre-defined preset NORMAL
diff --git a/media/libeffects/lvm/wrapper/Aidl/BundleContext.h b/media/libeffects/lvm/wrapper/Aidl/BundleContext.h
index 3f31b8b..779d53a 100644
--- a/media/libeffects/lvm/wrapper/Aidl/BundleContext.h
+++ b/media/libeffects/lvm/wrapper/Aidl/BundleContext.h
@@ -29,14 +29,8 @@
 class BundleContext final : public EffectContext {
   public:
     BundleContext(int statusDepth, const Parameter::Common& common,
-                  const lvm::BundleEffectType& type)
-        : EffectContext(statusDepth, common), mType(type) {
-        LOG(DEBUG) << __func__ << type;
-    }
-    ~BundleContext() override {
-        LOG(DEBUG) << __func__;
-        deInit();
-    }
+                  const lvm::BundleEffectType& type);
+    ~BundleContext();
 
     RetCode init();
     void deInit();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index a625893..bb49b5a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -1977,6 +1977,8 @@
         if (rate > 0) {
             format->setFloat("operating-rate", rate * mPlaybackSettings.mSpeed);
         }
+
+        format->setInt32("android._video-scaling", mVideoScalingMode);
     }
 
     Mutex::Autolock autoLock(mDecoderLock);
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index ee66622..46a3587 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -1197,6 +1197,7 @@
       mTunneledInputHeight(0),
       mTunneled(false),
       mTunnelPeekState(TunnelPeekState::kLegacyMode),
+      mTunnelPeekEnabled(false),
       mHaveInputSurface(false),
       mHavePendingInputBuffers(false),
       mCpuBoostRequested(false),
@@ -1742,6 +1743,7 @@
 
     TunnelPeekState previousState = mTunnelPeekState;
     if(tunnelPeek == 0){
+        mTunnelPeekEnabled = false;
         switch (mTunnelPeekState) {
             case TunnelPeekState::kLegacyMode:
                 msg->setInt32("android._tunnel-peek-set-legacy", 0);
@@ -1757,6 +1759,7 @@
                 return;
         }
     } else {
+        mTunnelPeekEnabled = true;
         switch (mTunnelPeekState) {
             case TunnelPeekState::kLegacyMode:
                 msg->setInt32("android._tunnel-peek-set-legacy", 0);
@@ -4943,10 +4946,11 @@
             CHECK(msg->senderAwaitsResponse(&replyID));
             TunnelPeekState previousState = mTunnelPeekState;
             if (previousState != TunnelPeekState::kLegacyMode) {
-                mTunnelPeekState = TunnelPeekState::kEnabledNoBuffer;
+                mTunnelPeekState = mTunnelPeekEnabled ? TunnelPeekState::kEnabledNoBuffer :
+                    TunnelPeekState::kDisabledNoBuffer;
                 ALOGV("TunnelPeekState: %s -> %s",
                         asString(previousState),
-                        asString(TunnelPeekState::kEnabledNoBuffer));
+                        asString(mTunnelPeekState));
             }
 
             mReplyID = replyID;
@@ -5445,10 +5449,11 @@
             returnBuffersToCodec();
             TunnelPeekState previousState = mTunnelPeekState;
             if (previousState != TunnelPeekState::kLegacyMode) {
-                mTunnelPeekState = TunnelPeekState::kEnabledNoBuffer;
+                mTunnelPeekState = mTunnelPeekEnabled ? TunnelPeekState::kEnabledNoBuffer :
+                    TunnelPeekState::kDisabledNoBuffer;
                 ALOGV("TunnelPeekState: %s -> %s",
                         asString(previousState),
-                        asString(TunnelPeekState::kEnabledNoBuffer));
+                        asString(mTunnelPeekState));
             }
             break;
         }
@@ -5991,7 +5996,7 @@
         if (isBufferDecodeOnly) {
             buffer->meta()->setInt32("decode-only", true);
         }
-        if (mTunneled && !isBufferDecodeOnly) {
+        if (mTunneled && !isBufferDecodeOnly && !(flags & BUFFER_FLAG_CODECCONFIG)) {
             TunnelPeekState previousState = mTunnelPeekState;
             switch(mTunnelPeekState){
                 case TunnelPeekState::kEnabledNoBuffer:
diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
index e091cb8..800e2e1 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
@@ -314,8 +314,11 @@
         outHeader->nFilledLen = frameSize;
 
         List<BufferInfo *>::iterator it = outQueue.begin();
-        while ((*it)->mHeader != outHeader) {
-            ++it;
+        while (it != outQueue.end() && (*it)->mHeader != outHeader) {
+             ++it;
+        }
+        if (it == outQueue.end()) {
+            return;
         }
 
         BufferInfo *outInfo = *it;
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index f99a78b..baa5b7e 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -560,6 +560,7 @@
     int32_t mTunneledInputHeight;
     bool mTunneled;
     TunnelPeekState mTunnelPeekState;
+    bool mTunnelPeekEnabled;
 
     sp<IDescrambler> mDescrambler;
 
diff --git a/media/module/codecs/mp3dec/src/pvmp3_framedecoder.cpp b/media/module/codecs/mp3dec/src/pvmp3_framedecoder.cpp
index 5cf1ed3..e8fea73 100644
--- a/media/module/codecs/mp3dec/src/pvmp3_framedecoder.cpp
+++ b/media/module/codecs/mp3dec/src/pvmp3_framedecoder.cpp
@@ -154,6 +154,184 @@
 ; FUNCTION CODE
 ----------------------------------------------------------------------------*/
 
+/* The below code is borrowed from ./test/mp3reader.cpp */
+static bool parseHeader(
+        uint32_t header, size_t *frame_size,
+        uint32_t *out_sampling_rate = NULL, uint32_t *out_channels = NULL ,
+        uint32_t *out_bitrate = NULL, uint32_t *out_num_samples = NULL) {
+    *frame_size = 0;
+
+    if (out_sampling_rate) {
+        *out_sampling_rate = 0;
+    }
+
+    if (out_channels) {
+        *out_channels = 0;
+    }
+
+    if (out_bitrate) {
+        *out_bitrate = 0;
+    }
+
+    if (out_num_samples) {
+        *out_num_samples = 1152;
+    }
+
+    if ((header & 0xffe00000) != 0xffe00000) {
+        return false;
+    }
+
+    unsigned version = (header >> 19) & 3;
+
+    if (version == 0x01) {
+        return false;
+    }
+
+    unsigned layer = (header >> 17) & 3;
+
+    if (layer == 0x00) {
+        return false;
+    }
+
+    unsigned bitrate_index = (header >> 12) & 0x0f;
+
+    if (bitrate_index == 0 || bitrate_index == 0x0f) {
+        // Disallow "free" bitrate.
+        return false;
+    }
+
+    unsigned sampling_rate_index = (header >> 10) & 3;
+
+    if (sampling_rate_index == 3) {
+        return false;
+    }
+
+    static const int kSamplingRateV1[] = { 44100, 48000, 32000 };
+    int sampling_rate = kSamplingRateV1[sampling_rate_index];
+    if (version == 2 /* V2 */) {
+        sampling_rate /= 2;
+    } else if (version == 0 /* V2.5 */) {
+        sampling_rate /= 4;
+    }
+
+    unsigned padding = (header >> 9) & 1;
+
+    if (layer == 3) {
+        // layer I
+
+        static const int kBitrateV1[] = {
+            32, 64, 96, 128, 160, 192, 224, 256,
+            288, 320, 352, 384, 416, 448
+        };
+
+        static const int kBitrateV2[] = {
+            32, 48, 56, 64, 80, 96, 112, 128,
+            144, 160, 176, 192, 224, 256
+        };
+
+        int bitrate =
+            (version == 3 /* V1 */)
+                ? kBitrateV1[bitrate_index - 1]
+                : kBitrateV2[bitrate_index - 1];
+
+        if (out_bitrate) {
+            *out_bitrate = bitrate;
+        }
+
+        *frame_size = (12000 * bitrate / sampling_rate + padding) * 4;
+
+        if (out_num_samples) {
+            *out_num_samples = 384;
+        }
+    } else {
+        // layer II or III
+
+        static const int kBitrateV1L2[] = {
+            32, 48, 56, 64, 80, 96, 112, 128,
+            160, 192, 224, 256, 320, 384
+        };
+
+        static const int kBitrateV1L3[] = {
+            32, 40, 48, 56, 64, 80, 96, 112,
+            128, 160, 192, 224, 256, 320
+        };
+
+        static const int kBitrateV2[] = {
+            8, 16, 24, 32, 40, 48, 56, 64,
+            80, 96, 112, 128, 144, 160
+        };
+
+        int bitrate;
+        if (version == 3 /* V1 */) {
+            bitrate = (layer == 2 /* L2 */)
+                ? kBitrateV1L2[bitrate_index - 1]
+                : kBitrateV1L3[bitrate_index - 1];
+
+            if (out_num_samples) {
+                *out_num_samples = 1152;
+            }
+        } else {
+            // V2 (or 2.5)
+
+            bitrate = kBitrateV2[bitrate_index - 1];
+            if (out_num_samples) {
+                *out_num_samples = (layer == 1 /* L3 */) ? 576 : 1152;
+            }
+        }
+
+        if (out_bitrate) {
+            *out_bitrate = bitrate;
+        }
+
+        if (version == 3 /* V1 */) {
+            *frame_size = 144000 * bitrate / sampling_rate + padding;
+        } else {
+            // V2 or V2.5
+            size_t tmp = (layer == 1 /* L3 */) ? 72000 : 144000;
+            *frame_size = tmp * bitrate / sampling_rate + padding;
+        }
+    }
+
+    if (out_sampling_rate) {
+        *out_sampling_rate = sampling_rate;
+    }
+
+    if (out_channels) {
+        int channel_mode = (header >> 6) & 3;
+
+        *out_channels = (channel_mode == 3) ? 1 : 2;
+    }
+
+    return true;
+}
+
+static uint32_t U32_AT(const uint8_t *ptr) {
+    return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
+}
+
+// Check if the input is valid by checking if it contains a sync word
+static bool isInputValid(uint8 *buf, uint32 inSize)
+{
+    // Buffer needs to contain at least 4 bytes which is the size of
+    // the header
+    if (inSize < 4) return false;
+
+    size_t totalInSize = 0;
+    size_t frameSize = 0;
+    while (totalInSize <= (inSize - 4)) {
+        if (!parseHeader(U32_AT(buf + totalInSize), &frameSize)) {
+            return false;
+        }
+        // Buffer needs to be large enough to include complete frame
+        if ((frameSize > inSize) || (totalInSize > (inSize - frameSize))) {
+            return false;
+        }
+        totalInSize += frameSize;
+    }
+
+    return true;
+}
+
 ERROR_CODE pvmp3_framedecoder(tPVMP3DecoderExternal *pExt,
                               void              *pMem)
 {
@@ -170,6 +348,12 @@
     mp3Header info_data;
     mp3Header *info = &info_data;
 
+    if (!isInputValid(pExt->pInputBuffer, pExt->inputBufferCurrentLength))
+    {
+        pExt->outputFrameSize = 0;
+        return SYNCH_LOST_ERROR;
+    }
+
     pVars->inputStream.pBuffer  = pExt->pInputBuffer;
 
 
diff --git a/media/module/extractors/mp4/ItemTable.cpp b/media/module/extractors/mp4/ItemTable.cpp
index cf3df62..c6586fc 100644
--- a/media/module/extractors/mp4/ItemTable.cpp
+++ b/media/module/extractors/mp4/ItemTable.cpp
@@ -1501,9 +1501,9 @@
                     info.isExif(), (long long)offset, (long long)size);
             if ((info.isExif() && size > 4) || (info.isXmp() && size > 0)) {
                 ExternalMetaItem metaItem = {
-                        .isExif = info.isExif(),
                         .offset = offset,
                         .size = size,
+                        .isExif = info.isExif(),
                 };
                 mItemIdToMetaMap.add(info.itemId, metaItem);
             }
diff --git a/media/module/extractors/mp4/MPEG4Extractor.cpp b/media/module/extractors/mp4/MPEG4Extractor.cpp
index 559927a..b3707c8 100644
--- a/media/module/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/module/extractors/mp4/MPEG4Extractor.cpp
@@ -522,7 +522,7 @@
         return AMEDIA_ERROR_UNKNOWN;
     }
 
-    [=] {
+    [this, &track] {
         int64_t duration;
         int32_t samplerate;
         // Only for audio track.
diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp
index 42e9078..a2b1c5e 100644
--- a/media/mtp/MtpDataPacket.cpp
+++ b/media/mtp/MtpDataPacket.cpp
@@ -427,6 +427,9 @@
 }
 
 void MtpDataPacket::putString(const uint16_t* string) {
+    if (string == NULL) {
+        return;
+    }
     int count = 0;
     for (int i = 0; i <= MTP_STRING_MAX_CHARACTER_NUMBER; i++) {
         if (string[i])
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index b87b373..9420bf1 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -31,6 +31,7 @@
 #include <afutils/Permission.h>
 #include <afutils/PropertyUtils.h>
 #include <afutils/TypedLogger.h>
+#include <android-base/errors.h>
 #include <android-base/stringprintf.h>
 #include <android/media/IAudioPolicyService.h>
 #include <audiomanager/IAudioManager.h>
@@ -38,6 +39,7 @@
 #include <binder/IServiceManager.h>
 #include <binder/Parcel.h>
 #include <cutils/properties.h>
+#include <com_android_media_audioserver.h>
 #include <media/AidlConversion.h>
 #include <media/AudioParameter.h>
 #include <media/AudioValidator.h>
@@ -210,6 +212,20 @@
     return methodStatistics;
 }
 
+namespace base {
+template <typename T>
+struct OkOrFail<std::optional<T>> {
+    using opt_t = std::optional<T>;
+    OkOrFail() = delete;
+    OkOrFail(const opt_t&) = delete;
+
+    static bool IsOk(const opt_t& opt) { return opt.has_value(); }
+    static T Unwrap(opt_t&& opt) { return std::move(opt.value()); }
+    static std::string ErrorMessage(const opt_t&) { return "Empty optional"; }
+    static void Fail(opt_t&&) {}
+};
+}
+
 class DevicesFactoryHalCallbackImpl : public DevicesFactoryHalCallback {
   public:
     void onNewDevicesAvailable() override {
@@ -3597,11 +3613,20 @@
 void AudioFlinger::dumpToThreadLog_l(const sp<IAfThreadBase> &thread)
 {
     constexpr int THREAD_DUMP_TIMEOUT_MS = 2;
-    audio_utils::FdToStringOldImpl fdToString("- ", THREAD_DUMP_TIMEOUT_MS);
-    const int fd = fdToString.borrowFdUnsafe();
-    if (fd >= 0) {
-        thread->dump(fd, {} /* args */);
-        mThreadLog.logs(-1 /* time */, fdToString.closeAndGetString());
+    constexpr auto PREFIX = "- ";
+    if (com::android::media::audioserver::fdtostring_timeout_fix()) {
+        using ::android::audio_utils::FdToString;
+
+        auto writer = OR_RETURN(FdToString::createWriter(PREFIX));
+        thread->dump(writer.borrowFdUnsafe(), {} /* args */);
+        mThreadLog.logs(-1 /* time */, FdToString::closeWriterAndGetString(std::move(writer)));
+    } else {
+        audio_utils::FdToStringOldImpl fdToString("- ", THREAD_DUMP_TIMEOUT_MS);
+        const int fd = fdToString.borrowFdUnsafe();
+        if (fd >= 0) {
+            thread->dump(fd, {} /* args */);
+            mThreadLog.logs(-1 /* time */, fdToString.closeAndGetString());
+        }
     }
 }
 
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 54880f8..18bb173 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -7608,6 +7608,23 @@
     }
 }
 
+void DuplicatingThread::threadLoop_exit()
+{
+    // Prevent calling the OutputTrack dtor in the DuplicatingThread dtor
+    // where other mutexes (i.e. AudioPolicyService_Mutex) may be held.
+    // Do so here in the threadLoop_exit().
+
+    SortedVector <sp<IAfOutputTrack>> localTracks;
+    {
+        audio_utils::lock_guard l(mutex());
+        localTracks = std::move(mOutputTracks);
+        mOutputTracks.clear();
+    }
+    localTracks.clear();
+    outputTracks.clear();
+    PlaybackThread::threadLoop_exit();
+}
+
 void DuplicatingThread::dumpInternals_l(int fd, const Vector<String16>& args)
 {
     MixerThread::dumpInternals_l(fd, args);
@@ -7881,6 +7898,15 @@
     }
 }
 
+void SpatializerThread::threadLoop_exit()
+{
+    // The Spatializer EffectHandle must be released on the PlaybackThread
+    // threadLoop() to prevent lock inversion in the SpatializerThread dtor.
+    mFinalDownMixer.clear();
+
+    PlaybackThread::threadLoop_exit();
+}
+
 // ----------------------------------------------------------------------------
 //      Record
 // ----------------------------------------------------------------------------
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index a5afdd8..ea994a5 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1849,6 +1849,7 @@
     void threadLoop_sleepTime() final REQUIRES(ThreadBase_ThreadLoop);
     ssize_t threadLoop_write() final REQUIRES(ThreadBase_ThreadLoop);
     void threadLoop_standby() final REQUIRES(ThreadBase_ThreadLoop);
+    void threadLoop_exit() final REQUIRES(ThreadBase_ThreadLoop);
     void cacheParameters_l() final REQUIRES(mutex(), ThreadBase_ThreadLoop);
 
 private:
@@ -1900,6 +1901,8 @@
             REQUIRES(ThreadBase_ThreadLoop) EXCLUDES_ThreadBase_Mutex;
     void setHalLatencyMode_l() final REQUIRES(mutex());
 
+    void threadLoop_exit() final REQUIRES(ThreadBase_ThreadLoop);
+
 private:
             // Do not request a specific mode by default
             audio_latency_mode_t mRequestedLatencyMode = AUDIO_LATENCY_MODE_FREE;
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
index b560bc4..f066c09 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
@@ -61,7 +61,7 @@
 
     status_t updateMix(const AudioMix& mix, const std::vector<AudioMixMatchCriterion>& newCriteria);
 
-    void closeOutput(sp<SwAudioOutputDescriptor> &desc);
+    void closeOutput(sp<SwAudioOutputDescriptor> &desc, const SwAudioOutputCollection& allOutputs);
 
     /**
      * Tries to find the best matching audio policy mix
diff --git a/services/audiopolicy/common/managerdefinitions/include/HwModule.h b/services/audiopolicy/common/managerdefinitions/include/HwModule.h
index cf20260..d206637 100644
--- a/services/audiopolicy/common/managerdefinitions/include/HwModule.h
+++ b/services/audiopolicy/common/managerdefinitions/include/HwModule.h
@@ -89,10 +89,12 @@
     status_t addProfile(const sp<IOProfile> &profile);
 
     status_t addOutputProfile(const std::string& name, const audio_config_t *config,
-            audio_devices_t device, const String8& address);
+            audio_devices_t device, const String8& address,
+            audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE);
     status_t removeOutputProfile(const std::string& name);
     status_t addInputProfile(const std::string& name, const audio_config_t *config,
-            audio_devices_t device, const String8& address);
+            audio_devices_t device, const String8& address,
+            audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE);
     status_t removeInputProfile(const std::string& name);
 
     audio_module_handle_t getHandle() const { return mHandle; }
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index 7ee75c7..dc0f466 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -272,12 +272,33 @@
     return BAD_VALUE;
 }
 
-void AudioPolicyMixCollection::closeOutput(sp<SwAudioOutputDescriptor> &desc)
+void AudioPolicyMixCollection::closeOutput(sp<SwAudioOutputDescriptor> &desc,
+                                           const SwAudioOutputCollection& allOutputs)
 {
     for (size_t i = 0; i < size(); i++) {
         sp<AudioPolicyMix> policyMix = itemAt(i);
-        if (policyMix->getOutput() == desc) {
-            policyMix->clearOutput();
+        if (policyMix->getOutput() != desc) {
+            continue;
+        }
+        policyMix->clearOutput();
+        if (policyMix->mRouteFlags != MIX_ROUTE_FLAG_RENDER) {
+            continue;
+        }
+        auto device = desc->supportedDevices().getDevice(
+                policyMix->mDeviceType, policyMix->mDeviceAddress, AUDIO_FORMAT_DEFAULT);
+        if (device == nullptr) {
+            // This must not happen
+            ALOGE("%s, the rerouted device is not found", __func__);
+            continue;
+        }
+        // Restore the policy mix mix output to the first opened output supporting a route to
+        // the mix device. This is because the current mix output can be changed to a direct output.
+        for (size_t j = 0; j < allOutputs.size(); ++j) {
+            if (allOutputs[i] != desc && !allOutputs[i]->isDuplicated() &&
+                allOutputs[i]->supportedDevices().contains(device)) {
+                policyMix->setOutput(allOutputs[i]);
+                break;
+            }
         }
     }
 }
@@ -335,13 +356,6 @@
                       "audio policy mix.", __func__);
                 return INVALID_OPERATION;
             }
-            if (mixDevice != nullptr) {
-                // TODO(b/301619865): Only disallow the device that doesn't support MMAP.
-                ALOGD("%s: Rejecting MMAP_NOIRQ request matched to dynamic audio policy "
-                      "mix pointing to device %s which the mmap support is unknown at this moment",
-                      __func__, mixDevice->toString(false).c_str());
-                return INVALID_OPERATION;
-            }
         }
 
         if (mixDevice != nullptr && mixDevice->equals(requestedDevice)) {
diff --git a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
index cb45f54..6696b45 100644
--- a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
@@ -59,12 +59,13 @@
 }
 
 status_t HwModule::addOutputProfile(const std::string& name, const audio_config_t *config,
-                                    audio_devices_t device, const String8& address)
+                                    audio_devices_t device, const String8& address,
+                                    audio_output_flags_t flags)
 {
     sp<IOProfile> profile = new OutputProfile(name);
-
     profile->addAudioProfile(new AudioProfile(config->format, config->channel_mask,
                                               config->sample_rate));
+    profile->setFlags(flags);
 
     sp<DeviceDescriptor> devDesc =
             new DeviceDescriptor(device, getTagForDevice(device), address.c_str());
@@ -128,11 +129,13 @@
 }
 
 status_t HwModule::addInputProfile(const std::string& name, const audio_config_t *config,
-                                   audio_devices_t device, const String8& address)
+                                   audio_devices_t device, const String8& address,
+                                   audio_input_flags_t flags)
 {
     sp<IOProfile> profile = new InputProfile(name);
     profile->addAudioProfile(new AudioProfile(config->format, config->channel_mask,
                                               config->sample_rate));
+    profile->setFlags(flags);
 
     sp<DeviceDescriptor> devDesc =
             new DeviceDescriptor(device, getTagForDevice(device), address.c_str());
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 69d3b5d..3bffcd8 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1217,13 +1217,14 @@
         return BAD_VALUE;
     }
     if (usePrimaryOutputFromPolicyMixes) {
-        sp<DeviceDescriptor> deviceDesc =
+        sp<DeviceDescriptor> policyMixDevice =
                 mAvailableOutputDevices.getDevice(primaryMix->mDeviceType,
                                                   primaryMix->mDeviceAddress,
                                                   AUDIO_FORMAT_DEFAULT);
         sp<SwAudioOutputDescriptor> policyDesc = primaryMix->getOutput();
         bool tryDirectForFlags = policyDesc == nullptr ||
-                (policyDesc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT);
+                (policyDesc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) ||
+                (*flags & (AUDIO_OUTPUT_FLAG_HW_AV_SYNC | AUDIO_OUTPUT_FLAG_MMAP_NOIRQ));
         // if a direct output can be opened to deliver the track's multi-channel content to the
         // output rather than being downmixed by the primary output, then use this direct
         // output by by-passing the primary mix if possible, otherwise fall-through to primary
@@ -1231,23 +1232,29 @@
         bool tryDirectForChannelMask = policyDesc != nullptr
                     && (audio_channel_count_from_out_mask(policyDesc->getConfig().channel_mask) <
                         audio_channel_count_from_out_mask(config->channel_mask));
-        if (deviceDesc != nullptr && (tryDirectForFlags || tryDirectForChannelMask)) {
+        if (policyMixDevice != nullptr && (tryDirectForFlags || tryDirectForChannelMask)) {
             audio_io_handle_t newOutput;
             status = openDirectOutput(
                     *stream, session, config,
                     (audio_output_flags_t)(*flags | AUDIO_OUTPUT_FLAG_DIRECT),
-                    DeviceVector(deviceDesc), &newOutput);
+                    DeviceVector(policyMixDevice), &newOutput);
             if (status == NO_ERROR) {
                 policyDesc = mOutputs.valueFor(newOutput);
                 primaryMix->setOutput(policyDesc);
             } else if (tryDirectForFlags) {
+                ALOGW("%s, failed open direct, status: %d", __func__, status);
                 policyDesc = nullptr;
             } // otherwise use primary if available.
         }
         if (policyDesc != nullptr) {
             policyDesc->mPolicyMix = primaryMix;
             *output = policyDesc->mIoHandle;
-            *selectedDeviceId = deviceDesc != 0 ? deviceDesc->getId() : AUDIO_PORT_HANDLE_NONE;
+            *selectedDeviceId = policyMixDevice != nullptr ? policyMixDevice->getId()
+                                                           : AUDIO_PORT_HANDLE_NONE;
+            if ((policyDesc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != AUDIO_OUTPUT_FLAG_DIRECT) {
+                // Remove direct flag as it is not on a direct output.
+                *flags = (audio_output_flags_t) (*flags & ~AUDIO_OUTPUT_FLAG_DIRECT);
+            }
 
             ALOGV("getOutputForAttr() returns output %d", *output);
             if (resultAttr->usage == AUDIO_USAGE_VIRTUAL_SOURCE) {
@@ -1256,6 +1263,13 @@
                 *outputType = API_OUTPUT_LEGACY;
             }
             return NO_ERROR;
+        } else {
+            if (policyMixDevice != nullptr) {
+                ALOGE("%s, try to use primary mix but no output found", __func__);
+                return INVALID_OPERATION;
+            }
+            // Fallback to default engine selection as the selected primary mix device is not
+            // available.
         }
     }
     // Virtual sources must always be dynamicaly or explicitly routed
@@ -3659,9 +3673,13 @@
             outputConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
             inputConfig.channel_mask = AUDIO_CHANNEL_IN_STEREO;
             rSubmixModule->addOutputProfile(address.c_str(), &outputConfig,
-                    AUDIO_DEVICE_OUT_REMOTE_SUBMIX, address);
+                    AUDIO_DEVICE_OUT_REMOTE_SUBMIX, address,
+                    audio_is_linear_pcm(outputConfig.format)
+                        ? AUDIO_OUTPUT_FLAG_NONE : AUDIO_OUTPUT_FLAG_DIRECT);
             rSubmixModule->addInputProfile(address.c_str(), &inputConfig,
-                    AUDIO_DEVICE_IN_REMOTE_SUBMIX, address);
+                    AUDIO_DEVICE_IN_REMOTE_SUBMIX, address,
+                    audio_is_linear_pcm(inputConfig.format)
+                        ? AUDIO_INPUT_FLAG_NONE : AUDIO_INPUT_FLAG_DIRECT);
 
             if ((res = setDeviceConnectionStateInt(deviceTypeToMakeAvailable,
                     AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
@@ -6689,7 +6707,7 @@
         return;
     }
     const bool closingOutputWasActive = closingOutput->isActive();
-    mPolicyMixes.closeOutput(closingOutput);
+    mPolicyMixes.closeOutput(closingOutput, mOutputs);
 
     // look for duplicated outputs connected to the output being removed.
     for (size_t i = 0; i < mOutputs.size(); i++) {
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 7241597..5d3788d 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -299,7 +299,11 @@
         AudioDeviceTypeAddrVector devices;
         bool hasSpatializer = mAudioPolicyManager->canBeSpatialized(&attr, nullptr, devices);
         if (hasSpatializer) {
+            // Unlock as Spatializer::create() will use the callback and acquire the
+            // AudioPolicyService_Mutex.
+            mLock.unlock();
             mSpatializer = Spatializer::create(this, effectsFactoryHal);
+            mLock.lock();
         }
         if (mSpatializer == nullptr) {
             // No spatializer created, signal the reason: NO_INIT a failure, OK means intended.
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index 8dbf471..74d3474 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -211,7 +211,7 @@
     std::unique_ptr<AudioPolicyManagerTestClient> mClient;
     std::unique_ptr<AudioPolicyTestManager> mManager;
 
-    const uint32_t k48000SamplingRate = 48000;
+    constexpr static const uint32_t k48000SamplingRate = 48000;
 };
 
 void AudioPolicyManagerTest::SetUp() {
@@ -1274,13 +1274,30 @@
             std::string mixAddress, const audio_config_t& audioConfig,
             const std::vector<AudioMixMatchCriterion>& matchCriteria);
     void clearPolicyMix();
+    void addPolicyMixAndStartInputForLoopback(
+            int mixType, int mixFlag, audio_devices_t deviceType, std::string mixAddress,
+            const audio_config_t& audioConfig,
+            const std::vector<AudioMixMatchCriterion>& matchCriteria,
+            audio_session_t session=AUDIO_SESSION_NONE,
+            audio_config_base_t config=DEFAULT_INPUT_CONFIG,
+            audio_input_flags_t inputFlags=AUDIO_INPUT_FLAG_NONE);
 
     Vector<AudioMix> mAudioMixes;
     const std::string mMixAddress = "remote_submix_media";
+
+    audio_port_handle_t mLoopbackInputPortId = AUDIO_PORT_HANDLE_NONE;
+    std::unique_ptr<RecordingActivityTracker> mTracker;
+    struct audio_port_v7 mInjectionPort;
+
+    constexpr static const audio_config_base_t DEFAULT_INPUT_CONFIG = {
+            .sample_rate = k48000SamplingRate,
+            .channel_mask = AUDIO_CHANNEL_IN_STEREO,
+            .format = AUDIO_FORMAT_PCM_16_BIT
+    };
 };
 
 void AudioPolicyManagerTestDynamicPolicy::TearDown() {
-    mManager->unregisterPolicyMixes(mAudioMixes);
+    clearPolicyMix();
     AudioPolicyManagerTestWithConfigurationFile::TearDown();
 }
 
@@ -1302,11 +1319,45 @@
 
 void AudioPolicyManagerTestDynamicPolicy::clearPolicyMix() {
     if (mManager != nullptr) {
+        mManager->stopInput(mLoopbackInputPortId);
         mManager->unregisterPolicyMixes(mAudioMixes);
     }
     mAudioMixes.clear();
 }
 
+void AudioPolicyManagerTestDynamicPolicy::addPolicyMixAndStartInputForLoopback(
+        int mixType, int mixFlag, audio_devices_t deviceType, std::string mixAddress,
+        const audio_config_t& audioConfig,
+        const std::vector<AudioMixMatchCriterion>& matchCriteria, audio_session_t session,
+        audio_config_base_t config, audio_input_flags_t inputFlags) {
+    ASSERT_EQ(NO_ERROR,
+              addPolicyMix(mixType, mixFlag, deviceType, mixAddress, audioConfig, matchCriteria));
+    if ((mixFlag & MIX_ROUTE_FLAG_LOOP_BACK) != MIX_ROUTE_FLAG_LOOP_BACK) {
+        return;
+    }
+
+    mTracker.reset(new RecordingActivityTracker());
+    struct audio_port_v7 extractionPort;
+    ASSERT_TRUE(findDevicePort(AUDIO_PORT_ROLE_SOURCE, AUDIO_DEVICE_IN_REMOTE_SUBMIX,
+                               mixAddress, &extractionPort));
+    audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+    audio_source_t source = AUDIO_SOURCE_REMOTE_SUBMIX;
+    audio_attributes_t attr = {
+            AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, source, AUDIO_FLAG_NONE, ""};
+    std::string tags = "addr=" + mMixAddress;
+    audio_io_handle_t input = AUDIO_PORT_HANDLE_NONE;
+    strncpy(attr.tags, tags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
+    ASSERT_NO_FATAL_FAILURE(
+            getInputForAttr(attr, &input, session, mTracker->getRiid(),
+                            &selectedDeviceId, config.format, config.channel_mask,
+                            config.sample_rate, inputFlags, &mLoopbackInputPortId));
+    ASSERT_EQ(NO_ERROR, mManager->startInput(mLoopbackInputPortId));
+    ASSERT_EQ(extractionPort.id, selectedDeviceId);
+
+    ASSERT_TRUE(findDevicePort(AUDIO_PORT_ROLE_SINK, AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
+                               mMixAddress, &mInjectionPort));
+}
+
 TEST_F(AudioPolicyManagerTestDynamicPolicy, InitSuccess) {
     // SetUp must finish with no assertions
 }
@@ -1684,11 +1735,6 @@
         public testing::WithParamInterface<DPTestParam> {
 protected:
     void SetUp() override;
-    void TearDown() override;
-
-    std::unique_ptr<RecordingActivityTracker> mTracker;
-    struct audio_port_v7 mInjectionPort;
-    audio_port_handle_t mPortId = AUDIO_PORT_HANDLE_NONE;
 };
 
 void AudioPolicyManagerTestDPPlaybackReRouting::SetUp() {
@@ -1702,34 +1748,10 @@
     audioConfig.sample_rate = k48000SamplingRate;
 
     DPTestParam param = GetParam();
-    status_t ret = addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_LOOP_BACK,
-            AUDIO_DEVICE_OUT_REMOTE_SUBMIX, mMixAddress, audioConfig, param.mixCriteria);
-    ASSERT_EQ(NO_ERROR, ret);
-
-    struct audio_port_v7 extractionPort;
-    ASSERT_TRUE(findDevicePort(AUDIO_PORT_ROLE_SOURCE, AUDIO_DEVICE_IN_REMOTE_SUBMIX,
-                    mMixAddress, &extractionPort));
-
-    audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
-    audio_source_t source = AUDIO_SOURCE_REMOTE_SUBMIX;
-    audio_attributes_t attr = {
-        AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, source, AUDIO_FLAG_NONE, ""};
-    std::string tags = "addr=" + mMixAddress;
-    audio_io_handle_t input = AUDIO_PORT_HANDLE_NONE;
-    strncpy(attr.tags, tags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
-    getInputForAttr(attr, &input, param.session, mTracker->getRiid(),
-                    &selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
-                    k48000SamplingRate, AUDIO_INPUT_FLAG_NONE, &mPortId);
-    ASSERT_EQ(NO_ERROR, mManager->startInput(mPortId));
-    ASSERT_EQ(extractionPort.id, selectedDeviceId);
-
-    ASSERT_TRUE(findDevicePort(AUDIO_PORT_ROLE_SINK, AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
-                    mMixAddress, &mInjectionPort));
-}
-
-void AudioPolicyManagerTestDPPlaybackReRouting::TearDown() {
-    mManager->stopInput(mPortId);
-    AudioPolicyManagerTestDynamicPolicy::TearDown();
+    ASSERT_NO_FATAL_FAILURE(
+            addPolicyMixAndStartInputForLoopback(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_LOOP_BACK,
+            AUDIO_DEVICE_OUT_REMOTE_SUBMIX, mMixAddress, audioConfig, param.mixCriteria,
+            param.session));
 }
 
 TEST_P(AudioPolicyManagerTestDPPlaybackReRouting, PlaybackReRouting) {
@@ -1924,12 +1946,14 @@
     // Add mix matching the test uid.
     const int testUid = 12345;
     const auto param = GetParam();
-    status_t ret = addPolicyMix(MIX_TYPE_PLAYERS, param.mixRouteFlags, param.deviceType,
-                                param.deviceAddress, audioConfig, {createUidCriterion(testUid)});
-    ASSERT_EQ(NO_ERROR, ret);
+    ASSERT_NO_FATAL_FAILURE(
+            addPolicyMixAndStartInputForLoopback(MIX_TYPE_PLAYERS, param.mixRouteFlags,
+                                                 param.deviceType, param.deviceAddress, audioConfig,
+                                                 {createUidCriterion(testUid)}));
 
-    // Geting output for matching uid and mmap-ed stream should fail.
-    audio_output_flags_t outputFlags = AUDIO_OUTPUT_FLAG_MMAP_NOIRQ;
+    // Getting output for matching uid and mmap-ed stream should fail.
+    audio_output_flags_t outputFlags =
+            (audio_output_flags_t) (AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT);
     ASSERT_EQ(INVALID_OPERATION,
               mManager->getOutputForAttr(&attr, &mOutput, AUDIO_SESSION_NONE, &mStream,
                                          createAttributionSourceState(testUid), &audioConfig,
@@ -1942,11 +1966,12 @@
     // Add mix matching the test uid.
     const int testUid = 12345;
     const auto param = GetParam();
-    status_t ret = addPolicyMix(MIX_TYPE_PLAYERS, param.mixRouteFlags, param.deviceType,
-                                param.deviceAddress, audioConfig, {createUidCriterion(testUid)});
-    ASSERT_EQ(NO_ERROR, ret);
+    ASSERT_NO_FATAL_FAILURE(
+            addPolicyMixAndStartInputForLoopback(MIX_TYPE_PLAYERS, param.mixRouteFlags,
+                                                 param.deviceType,param.deviceAddress, audioConfig,
+                                                 {createUidCriterion(testUid)}));
 
-    // Geting output for matching uid should succeed for non-mmaped stream.
+    // Getting output for matching uid should succeed for non-mmaped stream.
     audio_output_flags_t outputFlags = AUDIO_OUTPUT_FLAG_NONE;
     ASSERT_EQ(NO_ERROR,
               mManager->getOutputForAttr(&attr, &mOutput, AUDIO_SESSION_NONE, &mStream,
@@ -1957,23 +1982,57 @@
 
 TEST_F(AudioPolicyManagerTestMMapPlaybackRerouting,
         MmapPlaybackStreamMatchingRenderDapMixSupportingMmapSucceeds) {
+    const std::string usbAddress = "card=1;device=0";
+    ASSERT_EQ(NO_ERROR, mManager->setDeviceConnectionState(
+            AUDIO_DEVICE_OUT_USB_DEVICE, AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+            usbAddress.c_str(), "", AUDIO_FORMAT_DEFAULT));
+    audio_port_v7 usbDevicePort;
+    ASSERT_TRUE(findDevicePort(AUDIO_PORT_ROLE_SINK, AUDIO_DEVICE_OUT_USB_DEVICE,
+                               usbAddress, &usbDevicePort));
+
     // Add render-only mix matching the test uid.
     const int testUid = 12345;
     // test_audio_policy_configuration.xml declares mmap-capable mix port
     // for AUDIO_DEVICE_OUT_USB_DEVICE.
-    status_t ret = addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_RENDER,
-                                AUDIO_DEVICE_OUT_USB_DEVICE, /*mixAddress=*/"",
-                                audioConfig, {createUidCriterion(testUid)});
-    ASSERT_EQ(NO_ERROR, ret);
+    ASSERT_EQ(NO_ERROR,
+              addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_RENDER,
+                           AUDIO_DEVICE_OUT_USB_DEVICE, /*mixAddress=*/"",
+                           audioConfig, {createUidCriterion(testUid)}));
 
-    // Geting output for matching uid should succeed for mmaped stream, because matched mix
+    static const audio_output_flags_t mmapDirectFlags =
+            (audio_output_flags_t) (AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT);
+    // Getting output for matching uid should succeed for mmaped stream, because matched mix
     // redirects to mmap capable device.
-    audio_output_flags_t outputFlags = AUDIO_OUTPUT_FLAG_MMAP_NOIRQ;
+    audio_output_flags_t outputFlags = mmapDirectFlags;
     ASSERT_EQ(NO_ERROR,
               mManager->getOutputForAttr(&attr, &mOutput, AUDIO_SESSION_NONE, &mStream,
                                          createAttributionSourceState(testUid), &audioConfig,
                                          &outputFlags, &mSelectedDeviceId, &mPortId, {},
                                          &mOutputType, &mIsSpatialized, &mIsBitPerfect));
+    ASSERT_EQ(usbDevicePort.id, mSelectedDeviceId);
+    auto outputDesc = mManager->getOutputs().valueFor(mOutput);
+    ASSERT_NE(nullptr, outputDesc);
+    ASSERT_EQ(mmapDirectFlags, outputDesc->getFlags().output);
+
+    // After releasing the client, the output is closed. APM should reselect output for the policy
+    // mix.
+    mManager->releaseOutput(mPortId);
+    ASSERT_EQ(nullptr, mManager->getOutputs().valueFor(mOutput));
+    outputFlags = AUDIO_OUTPUT_FLAG_NONE;
+    mPortId = AUDIO_PORT_HANDLE_NONE;
+    ASSERT_EQ(NO_ERROR,
+              mManager->getOutputForAttr(&attr, &mOutput, AUDIO_SESSION_NONE, &mStream,
+                                         createAttributionSourceState(testUid), &audioConfig,
+                                         &outputFlags, &mSelectedDeviceId, &mPortId, {},
+                                         &mOutputType, &mIsSpatialized, &mIsBitPerfect));
+    ASSERT_EQ(usbDevicePort.id, mSelectedDeviceId);
+    outputDesc = mManager->getOutputs().valueFor(mOutput);
+    ASSERT_NE(nullptr, outputDesc);
+    ASSERT_NE(mmapDirectFlags, outputDesc->getFlags().output);
+
+    ASSERT_EQ(NO_ERROR, mManager->setDeviceConnectionState(
+            AUDIO_DEVICE_OUT_USB_DEVICE, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+            usbAddress.c_str(), "", AUDIO_FORMAT_DEFAULT));
 }
 
 TEST_F(AudioPolicyManagerTestMMapPlaybackRerouting,
@@ -1981,14 +2040,15 @@
     // Add render-only mix matching the test uid.
     const int testUid = 12345;
     // Per test_audio_policy_configuration.xml AUDIO_DEVICE_OUT_SPEAKER doesn't support mmap.
-    status_t ret = addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_RENDER,
-                                AUDIO_DEVICE_OUT_SPEAKER, /*mixAddress=*/"", audioConfig,
-                                {createUidCriterion(testUid)});
-    ASSERT_EQ(NO_ERROR, ret);
+    ASSERT_EQ(NO_ERROR,
+              addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_RENDER,
+                           AUDIO_DEVICE_OUT_SPEAKER, /*mixAddress=*/"", audioConfig,
+                           {createUidCriterion(testUid)}));
 
-    // Geting output for matching uid should fail for mmaped stream, because
+    // Getting output for matching uid should fail for mmaped stream, because
     // matched mix redirects to device which doesn't support mmap.
-    audio_output_flags_t outputFlags = AUDIO_OUTPUT_FLAG_MMAP_NOIRQ;
+    audio_output_flags_t outputFlags =
+            (audio_output_flags_t) (AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT);
     ASSERT_EQ(INVALID_OPERATION,
               mManager->getOutputForAttr(&attr, &mOutput, AUDIO_SESSION_NONE, &mStream,
                                          createAttributionSourceState(testUid), &audioConfig,
@@ -2322,6 +2382,7 @@
     static const std::string sCarBusNavigationOutput;
     static const std::string sCarRearZoneOneOutput;
     static const std::string sCarRearZoneTwoOutput;
+    static const std::string sCarBusMmapOutput;
 };
 
 const std::string AudioPolicyManagerCarTest::sCarConfig =
@@ -2335,6 +2396,8 @@
 
 const std::string AudioPolicyManagerCarTest::sCarRearZoneTwoOutput = "bus200_audio_zone_2";
 
+const std::string AudioPolicyManagerCarTest::sCarBusMmapOutput = "bus8_mmap_out";
+
 TEST_F(AudioPolicyManagerCarTest, InitSuccess) {
     // SetUp must finish with no assertions.
 }
@@ -2784,6 +2847,37 @@
     ASSERT_EQ(navDevicePort.id, selectedDeviceId);
 }
 
+TEST_F(AudioPolicyManagerCarTest, GetOutputForAttrForMMapWithPolicyMatched) {
+    status_t ret;
+    audio_config_t audioConfig = AUDIO_CONFIG_INITIALIZER;
+    audioConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+    audioConfig.format = AUDIO_FORMAT_PCM_16_BIT;
+    audioConfig.sample_rate = k48000SamplingRate;
+    std::vector<AudioMixMatchCriterion> mediaMatchCriteria = {
+            createUsageCriterion(AUDIO_USAGE_MEDIA, /*exclude=*/ false)};
+    ret = addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_RENDER,
+                       AUDIO_DEVICE_OUT_BUS, sCarBusMmapOutput, audioConfig, mediaMatchCriteria);
+    ASSERT_EQ(NO_ERROR, ret);
+    ASSERT_EQ(NO_ERROR, ret);
+    audio_port_v7 mmapDevicePort;
+    ASSERT_TRUE(findDevicePort(AUDIO_PORT_ROLE_SINK, AUDIO_DEVICE_OUT_BUS,
+                               sCarBusMmapOutput, &mmapDevicePort));
+    audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+    audio_io_handle_t output;
+    audio_port_handle_t portId;
+    const audio_attributes_t mediaAttribute = {
+            AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_MEDIA,
+            AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""};
+
+    getOutputForAttr(
+            &selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
+            k48000SamplingRate,
+            (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT),
+            &output, &portId, mediaAttribute);
+
+    ASSERT_EQ(mmapDevicePort.id, selectedDeviceId);
+}
+
 class AudioPolicyManagerTVTest : public AudioPolicyManagerTestWithConfigurationFile {
 protected:
     std::string getConfigFile() override { return sTvConfig; }
diff --git a/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml b/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
index 9e092c6..4efdf8a 100644
--- a/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
+++ b/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
@@ -56,7 +56,7 @@
                 </mixPort>
                 <mixPort name="hifi_output" role="source" flags="AUDIO_OUTPUT_FLAG_BIT_PERFECT"/>
                 <mixPort name="mmap_no_irq_out" role="source"
-                         flags="AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_MMAP_NOIRQ">
+                         flags="AUDIO_OUTPUT_FLAG_DIRECT AUDIO_OUTPUT_FLAG_MMAP_NOIRQ">
                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                              samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
                 </mixPort>
diff --git a/services/audiopolicy/tests/resources/test_car_ap_atmos_offload_configuration.xml b/services/audiopolicy/tests/resources/test_car_ap_atmos_offload_configuration.xml
index d131ed8..d40ebfc 100644
--- a/services/audiopolicy/tests/resources/test_car_ap_atmos_offload_configuration.xml
+++ b/services/audiopolicy/tests/resources/test_car_ap_atmos_offload_configuration.xml
@@ -30,6 +30,7 @@
                 <item>bus5_alarm_out</item>
                 <item>bus6_notification_out</item>
                 <item>bus7_system_sound_out</item>
+                <item>bus8_mmap_out</item>
                 <!-- names with _audio_zone_# are used for defined an emulator rear seat audio zone
                     where each number # is the zone id number -->
                 <item>bus100_audio_zone_1</item>
@@ -96,6 +97,11 @@
                              samplingRates="48000"
                              channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
                 </mixPort>
+                <mixPort name="mixport_bus8_mmap_out" role="source"
+                         flags="AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_MMAP_NOIRQ">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                </mixPort>
                 <mixPort name="mixport_bus100_audio_zone_1" role="source">
                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                              samplingRates="48000"
@@ -213,6 +219,16 @@
                                 stepValueMB="100"/>
                     </gains>
                 </devicePort>
+                <devicePort tagName="bus8_mmap_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+                    address="bus8_mmap_out">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                        samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                    <gains>
+                        <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+                            minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+                            stepValueMB="100"/>
+                    </gains>
+                </devicePort>
                 <devicePort tagName="bus100_audio_zone_1" role="sink" type="AUDIO_DEVICE_OUT_BUS"
                         address="bus100_audio_zone_1">
                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
@@ -295,6 +311,8 @@
                         sources="mixport_bus6_notification_out"/>
                 <route type="mix" sink="bus7_system_sound_out"
                         sources="mixport_bus7_system_sound_out"/>
+                <route type="mix" sink="bus8_mmap_out"
+                        sources="mixport_bus8_mmap_out"/>
                 <route type="mix" sink="bus100_audio_zone_1" sources="mixport_bus100_audio_zone_1"/>
                 <route type="mix" sink="bus200_audio_zone_2" sources="mixport_bus200_audio_zone_2"/>
                 <route type="mix" sink="primary input"
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index feb9c75..3f43af5 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -123,6 +123,10 @@
     android_atomic_write(level, &gLogLevel);
 }
 
+int32_t format_as(CameraService::StatusInternal s) {
+  return fmt::underlying(s);
+}
+
 // ----------------------------------------------------------------------------
 
 static const std::string sDumpPermission("android.permission.DUMP");
@@ -510,8 +514,8 @@
     }
 
     if (newStatus == StatusInternal::NOT_PRESENT) {
-        logDeviceRemoved(cameraId, fmt::sprintf("Device status changed from %d to %d", oldStatus,
-                newStatus));
+        logDeviceRemoved(cameraId, fmt::format("Device status changed from {} to {}",
+                oldStatus, newStatus));
 
         // Set the device status to NOT_PRESENT, clients will no longer be able to connect
         // to this device until the status changes
@@ -537,8 +541,8 @@
         removeStates(cameraId);
     } else {
         if (oldStatus == StatusInternal::NOT_PRESENT) {
-            logDeviceAdded(cameraId, fmt::sprintf("Device status changed from %d to %d", oldStatus,
-                    newStatus));
+            logDeviceAdded(cameraId, fmt::format("Device status changed from {} to {}",
+                    oldStatus, newStatus));
         }
         updateStatus(newStatus, cameraId);
     }
@@ -578,9 +582,9 @@
     if (updated) {
         std::string idCombo = id + " : " + physicalId;
         if (newStatus == StatusInternal::PRESENT) {
-            logDeviceAdded(idCombo, fmt::sprintf("Device status changed to %d", newStatus));
+            logDeviceAdded(idCombo, fmt::format("Device status changed to {}", newStatus));
         } else {
-            logDeviceRemoved(idCombo, fmt::sprintf("Device status changed to %d", newStatus));
+            logDeviceRemoved(idCombo, fmt::format("Device status changed to {}", newStatus));
         }
         // Avoid calling getSystemCameraKind() with mStatusListenerLock held (b/141756275)
         SystemCameraKind deviceKind = SystemCameraKind::PUBLIC;
@@ -1092,6 +1096,39 @@
     }
 }
 
+Status CameraService::injectSessionParams(
+            const std::string& cameraId,
+            const CameraMetadata& sessionParams) {
+   if (!checkCallingPermission(toString16(sCameraInjectExternalCameraPermission))) {
+        const int pid = CameraThreadState::getCallingPid();
+        const int uid = CameraThreadState::getCallingUid();
+        ALOGE("%s: Permission Denial: can't inject session params pid=%d, uid=%d",
+                __FUNCTION__, pid, uid);
+        return STATUS_ERROR(ERROR_PERMISSION_DENIED,
+                "Permission Denial: no permission to inject session params");
+    }
+
+    std::unique_ptr<AutoConditionLock> serviceLockWrapper =
+            AutoConditionLock::waitAndAcquire(mServiceLockWrapper);
+
+    auto clientDescriptor = mActiveClientManager.get(cameraId);
+    if (clientDescriptor == nullptr) {
+        ALOGI("%s: No active client for camera id %s", __FUNCTION__, cameraId.c_str());
+        return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
+                "No active client for camera id %s", cameraId.c_str());
+    }
+
+    sp<BasicClient> clientSp = clientDescriptor->getValue();
+    status_t res = clientSp->injectSessionParams(sessionParams);
+
+    if (res != OK) {
+        return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
+                "Error injecting session params into camera \"%s\": %s (%d)",
+                cameraId.c_str(), strerror(-res), res);
+    }
+    return Status::ok();
+}
+
 std::vector<std::string> CameraService::findOriginalIdsForRemappedCameraId(
     const std::string& inputCameraId, int clientUid) {
     std::string packageName = getPackageNameFromUid(clientUid);
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index cabac17..1487013 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -232,7 +232,11 @@
             const hardware::CameraExtensionSessionStats& stats, std::string* sessionKey /*out*/);
 
     virtual binder::Status remapCameraIds(const hardware::CameraIdRemapping&
-        cameraIdRemapping);
+            cameraIdRemapping);
+
+    virtual binder::Status injectSessionParams(
+            const std::string& cameraId,
+            const hardware::camera2::impl::CameraMetadataNative& sessionParams);
 
     virtual binder::Status createDefaultRequest(const std::string& cameraId, int templateId,
             /*out*/
@@ -412,6 +416,10 @@
         // Stop the injection camera and restore to internal camera session.
         virtual status_t stopInjection() = 0;
 
+        // Inject session parameters into an existing session.
+        virtual status_t injectSessionParams(
+                const hardware::camera2::impl::CameraMetadataNative& sessionParams) = 0;
+
     protected:
         BasicClient(const sp<CameraService>& cameraService,
                 const sp<IBinder>& remoteCallback,
@@ -685,6 +693,8 @@
         UNKNOWN = static_cast<int32_t>(hardware::ICameraServiceListener::STATUS_UNKNOWN)
     };
 
+    friend int32_t format_as(StatusInternal s);
+
     /**
      * Container class for the state of each logical camera device, including: ID, status, and
      * dependencies on other devices.  The mapping of camera ID -> state saved in mCameraStates
diff --git a/services/camera/libcameraservice/aidl/VndkVersionMetadataTags.h b/services/camera/libcameraservice/aidl/VndkVersionMetadataTags.h
index b193be2..e403b97 100644
--- a/services/camera/libcameraservice/aidl/VndkVersionMetadataTags.h
+++ b/services/camera/libcameraservice/aidl/VndkVersionMetadataTags.h
@@ -77,6 +77,7 @@
       {34, {
           ANDROID_CONTROL_AUTOFRAMING_AVAILABLE,
           ANDROID_CONTROL_AVAILABLE_SETTINGS_OVERRIDES,
+          ANDROID_CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE,
           ANDROID_FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL,
           ANDROID_FLASH_SINGLE_STRENGTH_MAX_LEVEL,
           ANDROID_FLASH_TORCH_STRENGTH_DEFAULT_LEVEL,
@@ -108,6 +109,7 @@
       {34, {
           ANDROID_CONTROL_AUTOFRAMING,
           ANDROID_CONTROL_AUTOFRAMING_STATE,
+          ANDROID_CONTROL_LOW_LIGHT_BOOST_STATE,
           ANDROID_CONTROL_SETTINGS_OVERRIDE,
           ANDROID_CONTROL_SETTINGS_OVERRIDING_FRAME_NUMBER,
           ANDROID_EXTENSION_CURRENT_TYPE,
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 095d425..3488629 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -777,6 +777,60 @@
     return res;
 }
 
+binder::Status CameraDeviceClient::getSessionCharacteristics(
+        const SessionConfiguration& sessionConfiguration,
+        /*out*/
+        hardware::camera2::impl::CameraMetadataNative* sessionCharacteristics) {
+    ATRACE_CALL();
+    binder::Status res;
+    status_t ret = OK;
+    if (!(res = checkPidStatus(__FUNCTION__)).isOk()) return res;
+
+    Mutex::Autolock icl(mBinderSerializationLock);
+
+    if (!mDevice.get()) {
+        return STATUS_ERROR(CameraService::ERROR_DISCONNECTED, "Camera device no longer alive");
+    }
+
+    auto operatingMode = sessionConfiguration.getOperatingMode();
+    res = SessionConfigurationUtils::checkOperatingMode(operatingMode, mDevice->info(),
+            mCameraIdStr);
+    if (!res.isOk()) {
+        return res;
+    }
+
+    camera3::metadataGetter getMetadata = [this](const std::string &id,
+            bool /*overrideForPerfClass*/) {
+          return mDevice->infoPhysical(id);};
+    ret = mProviderManager->getSessionCharacteristics(mCameraIdStr.c_str(),
+            sessionConfiguration, mOverrideForPerfClass, getMetadata,
+            sessionCharacteristics);
+
+    switch (ret) {
+        case OK:
+            // Expected, do nothing.
+            break;
+        case INVALID_OPERATION: {
+                std::string msg = fmt::sprintf(
+                        "Camera %s: Session characteristics query not supported!",
+                        mCameraIdStr.c_str());
+                ALOGD("%s: %s", __FUNCTION__, msg.c_str());
+                res = STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.c_str());
+            }
+
+            break;
+        default: {
+                std::string msg = fmt::sprintf( "Camera %s: Error: %s (%d)", mCameraIdStr.c_str(),
+                        strerror(-ret), ret);
+                ALOGE("%s: %s", __FUNCTION__, msg.c_str());
+                res = STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
+                        msg.c_str());
+            }
+    }
+
+    return res;
+}
+
 binder::Status CameraDeviceClient::deleteStream(int streamId) {
     ATRACE_CALL();
     ALOGV("%s (streamId = 0x%x)", __FUNCTION__, streamId);
@@ -1009,7 +1063,7 @@
         res = STATUS_ERROR_FMT(CameraService::ERROR_INVALID_OPERATION,
                 "Camera %s: Error creating output stream (%d x %d, fmt %x, dataSpace %x): %s (%d)",
                 mCameraIdStr.c_str(), streamInfo.width, streamInfo.height, streamInfo.format,
-                streamInfo.dataSpace, strerror(-err), err);
+                static_cast<int>(streamInfo.dataSpace), strerror(-err), err);
     } else {
         int i = 0;
         for (auto& binder : binders) {
@@ -1106,7 +1160,8 @@
     if (err != OK) {
         res = STATUS_ERROR_FMT(CameraService::ERROR_INVALID_OPERATION,
                 "Camera %s: Error creating output stream (%d x %d, fmt %x, dataSpace %x): %s (%d)",
-                mCameraIdStr.c_str(), width, height, format, dataSpace, strerror(-err), err);
+                mCameraIdStr.c_str(), width, height, format, static_cast<int>(dataSpace),
+                strerror(-err), err);
     } else {
         // Can not add streamId to mStreamMap here, as the surface is deferred. Add it to
         // a separate list to track. Once the deferred surface is set, this id will be
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index b2c9626..c2f7f56 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -109,6 +109,11 @@
             /*out*/
             bool* streamStatus) override;
 
+    virtual binder::Status getSessionCharacteristics(
+            const SessionConfiguration& sessionConfiguration,
+            /*out*/
+            hardware::camera2::impl::CameraMetadataNative* sessionCharacteristics) override;
+
     // Returns -EBUSY if device is not idle or in error state
     virtual binder::Status deleteStream(int streamId) override;
 
diff --git a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
index 4ed352d..dc9e0c1 100644
--- a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
@@ -383,5 +383,12 @@
     return OK;
 }
 
+status_t CameraOfflineSessionClient::injectSessionParams(
+        const hardware::camera2::impl::CameraMetadataNative& sessionParams) {
+    ALOGV("%s: This client doesn't support the injecting session parameters camera.",
+            __FUNCTION__);
+    (void)sessionParams;
+    return OK;
+}
 // ----------------------------------------------------------------------------
 }; // namespace android
diff --git a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
index 8aad4e9..804498f 100644
--- a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
+++ b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
@@ -120,6 +120,8 @@
     status_t injectCamera(const std::string& injectedCamId,
             sp<CameraProviderManager> manager) override;
     status_t stopInjection() override;
+    status_t injectSessionParams(
+        const hardware::camera2::impl::CameraMetadataNative& sessionParams) override;
 
 private:
     mutable Mutex mBinderSerializationLock;
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index c730b14..a126f61 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -509,6 +509,12 @@
     return mDevice->stopInjection();
 }
 
+template <typename TClientBase>
+status_t Camera2ClientBase<TClientBase>::injectSessionParams(
+    const CameraMetadata& sessionParams) {
+    return mDevice->injectSessionParams(sessionParams);
+}
+
 template class Camera2ClientBase<CameraService::Client>;
 template class Camera2ClientBase<CameraDeviceClientBase>;
 
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index bac4af8..2bb90d9 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -18,6 +18,7 @@
 #define ANDROID_SERVERS_CAMERA_CAMERA2CLIENT_BASE_H
 
 #include "common/CameraDeviceBase.h"
+#include "camera/CameraMetadata.h"
 #include "camera/CaptureResult.h"
 #include "utils/CameraServiceProxyWrapper.h"
 #include "CameraServiceWatchdog.h"
@@ -136,6 +137,8 @@
                                sp<CameraProviderManager> manager) override;
     status_t      stopInjection() override;
 
+    status_t      injectSessionParams(const CameraMetadata& sessionParams) override;
+
 protected:
 
     // The PID provided in the constructor call
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 983b2c1..cfc41c3 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -509,6 +509,10 @@
      */
     virtual status_t stopInjection() = 0;
 
+    // Inject session parameters into an existing client.
+    virtual status_t injectSessionParams(
+        const CameraMetadata& sessionParams) = 0;
+
 protected:
     bool mImageDumpMask = 0;
     std::vector<int64_t> mStreamUseCaseOverrides;
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index fbb5e1b..1ba3de4 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -442,6 +442,23 @@
     return OK;
 }
 
+status_t CameraProviderManager::getSessionCharacteristics(const std::string& id,
+        const SessionConfiguration &configuration, bool overrideForPerfClass,
+        metadataGetter getMetadata,
+        CameraMetadata* sessionCharacteristics /*out*/) const {
+    if (!flags::feature_combination_query()) {
+        return INVALID_OPERATION;
+    }
+    std::lock_guard<std::mutex> lock(mInterfaceMutex);
+    auto deviceInfo = findDeviceInfoLocked(id);
+    if (deviceInfo == nullptr) {
+        return NAME_NOT_FOUND;
+    }
+
+    return deviceInfo->getSessionCharacteristics(configuration,
+            overrideForPerfClass, getMetadata, sessionCharacteristics);
+}
+
 status_t CameraProviderManager::getCameraIdIPCTransport(const std::string &id,
         IPCTransport *providerTransport) const {
     std::lock_guard<std::mutex> lock(mInterfaceMutex);
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index 6142a71..53a2102 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -317,6 +317,14 @@
             bool *status /*out*/) const;
 
     /**
+     * Get session characteristics for a particular session.
+     */
+     status_t getSessionCharacteristics(const std::string& id,
+            const SessionConfiguration &configuration,
+            bool overrideForPerfClass, camera3::metadataGetter getMetadata,
+            CameraMetadata* sessionCharacteristics /*out*/) const;
+
+    /**
      * Return the highest supported device interface version for this ID
      */
     status_t getHighestSupportedVersion(const std::string &id,
@@ -632,6 +640,15 @@
                     bool * /*status*/) {
                 return INVALID_OPERATION;
             }
+
+            virtual status_t getSessionCharacteristics(
+                    const SessionConfiguration &/*configuration*/,
+                    bool /*overrideForPerfClass*/,
+                    camera3::metadataGetter /*getMetadata*/,
+                    CameraMetadata* /*sessionCharacteristics*/) {
+                return INVALID_OPERATION;
+            }
+
             virtual status_t filterSmallJpegSizes() = 0;
             virtual void notifyDeviceStateChange(int64_t /*newState*/) {}
             virtual status_t createDefaultRequest(
diff --git a/services/camera/libcameraservice/common/aidl/AidlProviderInfo.cpp b/services/camera/libcameraservice/common/aidl/AidlProviderInfo.cpp
index a64c9d7..d773af3 100644
--- a/services/camera/libcameraservice/common/aidl/AidlProviderInfo.cpp
+++ b/services/camera/libcameraservice/common/aidl/AidlProviderInfo.cpp
@@ -900,6 +900,54 @@
     return res;
 }
 
+status_t AidlProviderInfo::AidlDeviceInfo3::getSessionCharacteristics(
+        const SessionConfiguration &configuration, bool overrideForPerfClass,
+        camera3::metadataGetter getMetadata, CameraMetadata *sessionCharacteristics) {
+    camera::device::StreamConfiguration streamConfiguration;
+    bool earlyExit = false;
+    auto res = SessionConfigurationUtils::convertToHALStreamCombination(configuration,
+            mId, mCameraCharacteristics, mCompositeJpegRDisabled, getMetadata,
+            mPhysicalIds, streamConfiguration, overrideForPerfClass, mProviderTagid,
+            /*checkSessionParams*/true, &earlyExit);
+
+    if (!res.isOk()) {
+        return UNKNOWN_ERROR;
+    }
+
+    if (earlyExit) {
+        return BAD_VALUE;
+    }
+
+    const std::shared_ptr<camera::device::ICameraDevice> interface =
+            startDeviceInterface();
+
+    if (interface == nullptr) {
+        return DEAD_OBJECT;
+    }
+
+    aidl::android::hardware::camera::device::CameraMetadata chars;
+    ::ndk::ScopedAStatus ret =
+        interface->getSessionCharacteristics(streamConfiguration, &chars);
+    std::vector<uint8_t> &metadata = chars.metadata;
+
+    camera_metadata_t *buffer = reinterpret_cast<camera_metadata_t*>(metadata.data());
+    size_t expectedSize = metadata.size();
+    int resV = validate_camera_metadata_structure(buffer, &expectedSize);
+    if (resV == OK || resV == CAMERA_METADATA_VALIDATION_SHIFTED) {
+        set_camera_metadata_vendor_id(buffer, mProviderTagid);
+        *sessionCharacteristics = buffer;
+    } else {
+        ALOGE("%s: Malformed camera metadata received from HAL", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    if (!ret.isOk()) {
+        ALOGE("%s: Unexpected binder error: %s", __FUNCTION__, ret.getMessage());
+        return mapToStatusT(ret);
+    }
+    return OK;
+}
+
 status_t AidlProviderInfo::convertToAidlHALStreamCombinationAndCameraIdsLocked(
         const std::vector<CameraIdAndSessionConfiguration> &cameraIdsAndSessionConfigs,
         const std::set<std::string>& perfClassPrimaryCameraIds,
diff --git a/services/camera/libcameraservice/common/aidl/AidlProviderInfo.h b/services/camera/libcameraservice/common/aidl/AidlProviderInfo.h
index cc5101a..0bfa7d4 100644
--- a/services/camera/libcameraservice/common/aidl/AidlProviderInfo.h
+++ b/services/camera/libcameraservice/common/aidl/AidlProviderInfo.h
@@ -134,6 +134,11 @@
                     camera3::camera_request_template_t templateId,
                     camera_metadata_t** metadata) override;
 
+        virtual status_t getSessionCharacteristics(
+                const SessionConfiguration &/*configuration*/,
+                bool overrideForPerfClass, camera3::metadataGetter /*getMetadata*/,
+                CameraMetadata *sessionCharacteristics /*sessionCharacteristics*/);
+
         std::shared_ptr<aidl::android::hardware::camera::device::ICameraDevice>
                 startDeviceInterface();
     };
diff --git a/services/camera/libcameraservice/common/hidl/HidlProviderInfo.h b/services/camera/libcameraservice/common/hidl/HidlProviderInfo.h
index f53db7f..869bba0 100644
--- a/services/camera/libcameraservice/common/hidl/HidlProviderInfo.h
+++ b/services/camera/libcameraservice/common/hidl/HidlProviderInfo.h
@@ -107,6 +107,7 @@
                 const SessionConfiguration &/*configuration*/,
                 bool overrideForPerfClass, bool checkSessionParams,
                 bool *status/*status*/);
+
         sp<hardware::camera::device::V3_2::ICameraDevice> startDeviceInterface();
     };
 
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 0a5b0c4..28b2d78 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -80,7 +80,7 @@
 
 namespace flags = com::android::internal::camera::flags;
 namespace android {
-
+namespace flags = com::android::internal::camera::flags;
 Camera3Device::Camera3Device(std::shared_ptr<CameraServiceProxyWrapper>& cameraServiceProxyWrapper,
         const std::string &id, bool overrideForPerfClass, bool overrideToPortrait,
         bool legacyClient):
@@ -3590,12 +3590,16 @@
     for (size_t i = 0; i < mNextRequests.size(); i++) {
         auto& nextRequest = mNextRequests.editItemAt(i);
         sp<CaptureRequest> captureRequest = nextRequest.captureRequest;
+        captureRequest->mTestPatternChanged = overrideTestPattern(captureRequest);
         // Do not override rotate&crop for stream configurations that include
         // SurfaceViews(HW_COMPOSER) output, unless mOverrideToPortrait is set.
         // The display rotation there will be compensated by NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY
         captureRequest->mRotateAndCropChanged = (mComposerOutput && !mOverrideToPortrait) ? false :
             overrideAutoRotateAndCrop(captureRequest);
         captureRequest->mAutoframingChanged = overrideAutoframing(captureRequest);
+        if (flags::inject_session_params()) {
+            injectSessionParams(captureRequest, mInjectedSessionParams);
+        }
     }
 
     // 'mNextRequests' will at this point contain either a set of HFR batched requests
@@ -3619,7 +3623,10 @@
         if (res == OK) {
             sp<Camera3Device> parent = mParent.promote();
             if (parent != nullptr) {
-                mReconfigured |= parent->reconfigureCamera(mLatestSessionParams, mStatusId);
+                if (parent->reconfigureCamera(mLatestSessionParams, mStatusId)) {
+                    mForceNewRequestAfterReconfigure = true;
+                    mReconfigured = true;
+                }
             }
 
             if (mNextRequests[0].captureRequest->mInputStream != nullptr) {
@@ -3742,7 +3749,6 @@
         bool triggersMixedIn = (triggerCount > 0 || mPrevTriggers > 0);
         mPrevTriggers = triggerCount;
 
-        bool testPatternChanged = overrideTestPattern(captureRequest);
         bool settingsOverrideChanged = overrideSettingsOverride(captureRequest);
 
         // If the request is the same as last, or we had triggers now or last time or
@@ -3751,12 +3757,20 @@
                 (mPrevRequest != captureRequest || triggersMixedIn ||
                          captureRequest->mRotateAndCropChanged ||
                          captureRequest->mAutoframingChanged ||
-                         testPatternChanged || settingsOverrideChanged) &&
+                         captureRequest->mTestPatternChanged || settingsOverrideChanged ||
+                         (flags::inject_session_params() && mForceNewRequestAfterReconfigure)) &&
                 // Request settings are all the same within one batch, so only treat the first
                 // request in a batch as new
                 !(batchedRequest && i > 0);
+
         if (newRequest) {
             std::set<std::string> cameraIdsWithZoom;
+
+            if (flags::inject_session_params() && mForceNewRequestAfterReconfigure) {
+                // This only needs to happen once.
+                mForceNewRequestAfterReconfigure = false;
+            }
+
             /**
              * HAL workaround:
              * Insert a fake trigger ID if a trigger is set but no trigger ID is
@@ -4904,6 +4918,45 @@
     return false;
 }
 
+void Camera3Device::RequestThread::injectSessionParams(
+    const sp<CaptureRequest> &request,
+    const CameraMetadata& injectedSessionParams) {
+    CameraMetadata &requestMetadata = request->mSettingsList.begin()->metadata;
+    uint32_t tag_section;
+    camera_metadata_ro_entry entry;
+    for (auto tag : mSessionParamKeys) {
+        tag_section = tag >> 16;
+        if (tag_section < VENDOR_SECTION) {
+            // Only allow injection of vendor tags.
+            continue;
+        }
+        entry = injectedSessionParams.find(tag);
+        if (entry.count > 0) {
+            requestMetadata.update(entry);
+        }
+    }
+}
+
+status_t Camera3Device::RequestThread::setInjectedSessionParams(
+        const CameraMetadata& injectedSessionParams) {
+    ATRACE_CALL();
+    Mutex::Autolock l(mTriggerMutex);
+    mInjectedSessionParams = injectedSessionParams;
+    return OK;
+}
+
+status_t Camera3Device::injectSessionParams(const CameraMetadata& injectedSessionParams) {
+    ATRACE_CALL();
+    Mutex::Autolock il(mInterfaceLock);
+    Mutex::Autolock l(mLock);
+
+    if (mRequestThread == nullptr) {
+        return INVALID_OPERATION;
+    }
+
+    return mRequestThread->setInjectedSessionParams(injectedSessionParams);
+}
+
 bool Camera3Device::RequestThread::overrideTestPattern(
         const sp<CaptureRequest> &request) {
     ATRACE_CALL();
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index bde7e1b..e32a36f 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -338,6 +338,11 @@
      */
     status_t stopInjection();
 
+    /**
+     * Inject session params into the current client.
+     */
+    status_t injectSessionParams(const CameraMetadata& sessionParams);
+
   protected:
     status_t disconnectImpl();
     static status_t removeFwkOnlyRegionKeys(CameraMetadata *request);
@@ -385,7 +390,6 @@
     int                        mOperatingMode;
     // Current session wide parameters
     hardware::camera2::impl::CameraMetadataNative mSessionParams;
-
     // Constant to use for no set operating mode
     static const int           NO_MODE = -1;
 
@@ -651,6 +655,8 @@
         bool                                mAutoframingAuto;
         // Indicates that the auto framing value within 'mSettingsList' was modified
         bool                                mAutoframingChanged = false;
+        // Indicates that the camera test pattern setting is modified
+        bool                                mTestPatternChanged = false;
 
         // Whether this capture request has its zoom ratio set to 1.0x before
         // the framework overrides it for camera HAL consumption.
@@ -1005,6 +1011,12 @@
 
         status_t setHalInterface(sp<HalInterface> newHalInterface);
 
+        status_t setInjectedSessionParams(const CameraMetadata& sessionParams);
+
+        void injectSessionParams(
+            const sp<CaptureRequest> &request,
+            const CameraMetadata& injectedSessionParams);
+
       protected:
 
         virtual bool threadLoop();
@@ -1185,6 +1197,8 @@
 
         Vector<int32_t>    mSessionParamKeys;
         CameraMetadata     mLatestSessionParams;
+        CameraMetadata     mInjectedSessionParams;
+        bool               mForceNewRequestAfterReconfigure;
 
         std::map<int32_t, std::set<std::string>> mGroupIdPhysicalCameraMap;
 
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index c59138c..152687e 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -84,10 +84,10 @@
     status_t res = getEndpointUsage(&consumerUsage);
     if (res != OK) consumerUsage = 0;
 
-    lines << fmt::sprintf("      State: %d\n", mState);
+    lines << fmt::sprintf("      State: %d\n", static_cast<int>(mState));
     lines << fmt::sprintf("      Dims: %d x %d, format 0x%x, dataspace 0x%x\n",
             camera_stream::width, camera_stream::height,
-            camera_stream::format, camera_stream::data_space);
+            camera_stream::format, static_cast<int>(camera_stream::data_space));
     lines << fmt::sprintf("      Max size: %zu\n", mMaxSize);
     lines << fmt::sprintf("      Combined usage: 0x%" PRIx64 ", max HAL buffers: %d\n",
             mUsage | consumerUsage, camera_stream::max_buffers);
diff --git a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
index 467707e..a53d26d 100644
--- a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
+++ b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
@@ -309,6 +309,12 @@
             bool*) override {
         return ndk::ScopedAStatus::ok();
     }
+
+    ::ndk::ScopedAStatus getSessionCharacteristics(
+        const ::aidl::android::hardware::camera::device::StreamConfiguration&,
+        ::aidl::android::hardware::camera::device::CameraMetadata*) override {
+        return ndk::ScopedAStatus::ok();
+    }
 };
 
 /**
diff --git a/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp b/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
index 3be8e15..11ef9b7 100644
--- a/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
+++ b/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
@@ -623,7 +623,7 @@
     if (format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
         if (dataSpace != streamInfo.dataSpace) {
             std::string msg = fmt::sprintf("Camera %s:Surface dataSpace doesn't match: %d vs %d",
-                    logicalCameraId.c_str(), dataSpace, streamInfo.dataSpace);
+                    logicalCameraId.c_str(), static_cast<int>(dataSpace), static_cast<int>(streamInfo.dataSpace));
             ALOGE("%s: %s", __FUNCTION__, msg.c_str());
             return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.c_str());
         }
@@ -1133,7 +1133,7 @@
 }
 
 void filterParameters(const CameraMetadata& src, const CameraMetadata& deviceInfo,
-        int vendorTagId, CameraMetadata& dst) {
+        metadata_vendor_id_t vendorTagId, CameraMetadata& dst) {
     const CameraMetadata params(src);
     camera_metadata_ro_entry_t availableSessionKeys = deviceInfo.find(
             ANDROID_REQUEST_AVAILABLE_SESSION_KEYS);
diff --git a/services/camera/libcameraservice/utils/SessionConfigurationUtils.h b/services/camera/libcameraservice/utils/SessionConfigurationUtils.h
index 29e3eca..5b2ea5c 100644
--- a/services/camera/libcameraservice/utils/SessionConfigurationUtils.h
+++ b/services/camera/libcameraservice/utils/SessionConfigurationUtils.h
@@ -177,7 +177,7 @@
         aidl::android::hardware::camera::device::RequestTemplate* tempId /*out*/);
 
 void filterParameters(const CameraMetadata& src, const CameraMetadata& deviceInfo,
-        int vendorTagId, CameraMetadata& dst);
+        metadata_vendor_id_t vendorTagId, CameraMetadata& dst);
 
 constexpr int32_t MAX_SURFACES_PER_STREAM = 4;
 
diff --git a/services/camera/virtualcamera/Android.bp b/services/camera/virtualcamera/Android.bp
index c8fa84e..cb4e10f 100644
--- a/services/camera/virtualcamera/Android.bp
+++ b/services/camera/virtualcamera/Android.bp
@@ -54,6 +54,7 @@
         "util/EglProgram.cc",
         "util/EglSurfaceTexture.cc",
         "util/EglUtil.cc",
+        "util/Permissions.cc"
     ],
     defaults: [
         "libvirtualcamera_defaults",
diff --git a/services/camera/virtualcamera/VirtualCameraDevice.cc b/services/camera/virtualcamera/VirtualCameraDevice.cc
index ec72ee3..84f721b 100644
--- a/services/camera/virtualcamera/VirtualCameraDevice.cc
+++ b/services/camera/virtualcamera/VirtualCameraDevice.cc
@@ -119,9 +119,10 @@
     const std::vector<SupportedStreamConfiguration>& supportedInputConfig) {
   if (!std::all_of(supportedInputConfig.begin(), supportedInputConfig.end(),
                    [](const SupportedStreamConfiguration& config) {
-                     return config.pixelFormat == Format::YUV_420_888;
+                     return isFormatSupportedForInput(
+                         config.width, config.height, config.pixelFormat);
                    })) {
-    ALOGE("%s: input configuration contains unsupported pixel format", __func__);
+    ALOGE("%s: input configuration contains unsupported format", __func__);
     return std::nullopt;
   }
 
@@ -322,7 +323,7 @@
   ALOGV("%s", __func__);
 
   *_aidl_return = ndk::SharedRefBase::make<VirtualCameraSession>(
-      *this, in_callback, mVirtualCameraClientCallback);
+      sharedFromThis(), in_callback, mVirtualCameraClientCallback);
 
   return ndk::ScopedAStatus::ok();
 };
@@ -367,6 +368,13 @@
   return std::string(kDevicePathPrefix) + std::to_string(mCameraId);
 }
 
+std::shared_ptr<VirtualCameraDevice> VirtualCameraDevice::sharedFromThis() {
+  // SharedRefBase which BnCameraDevice inherits from breaks
+  // std::enable_shared_from_this. This is recommended replacement for
+  // shared_from_this() per documentation in binder_interface_utils.h.
+  return ref<VirtualCameraDevice>();
+}
+
 }  // namespace virtualcamera
 }  // namespace companion
 }  // namespace android
diff --git a/services/camera/virtualcamera/VirtualCameraDevice.h b/services/camera/virtualcamera/VirtualCameraDevice.h
index ee7a3a7..402de6c 100644
--- a/services/camera/virtualcamera/VirtualCameraDevice.h
+++ b/services/camera/virtualcamera/VirtualCameraDevice.h
@@ -98,6 +98,8 @@
   uint32_t getCameraId() const { return mCameraId; }
 
  private:
+  std::shared_ptr<VirtualCameraDevice> sharedFromThis();
+
   const uint32_t mCameraId;
   const std::shared_ptr<
       ::aidl::android::companion::virtualcamera::IVirtualCameraCallback>
diff --git a/services/camera/virtualcamera/VirtualCameraRenderThread.cc b/services/camera/virtualcamera/VirtualCameraRenderThread.cc
index 8621160..79c91ef 100644
--- a/services/camera/virtualcamera/VirtualCameraRenderThread.cc
+++ b/services/camera/virtualcamera/VirtualCameraRenderThread.cc
@@ -19,6 +19,7 @@
 
 #include <chrono>
 #include <cstddef>
+#include <cstdint>
 #include <future>
 #include <memory>
 #include <mutex>
@@ -38,6 +39,7 @@
 #include "android-base/thread_annotations.h"
 #include "android/binder_auto_utils.h"
 #include "android/hardware_buffer.h"
+#include "ui/GraphicBuffer.h"
 #include "util/EglFramebuffer.h"
 #include "util/JpegUtil.h"
 #include "util/MetadataBuilder.h"
@@ -109,6 +111,45 @@
   return msg;
 }
 
+std::shared_ptr<EglFrameBuffer> allocateTemporaryFramebuffer(
+    EGLDisplay eglDisplay, const uint width, const int height) {
+  const AHardwareBuffer_Desc desc{
+      .width = static_cast<uint32_t>(width),
+      .height = static_cast<uint32_t>(height),
+      .layers = 1,
+      .format = AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420,
+      .usage = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER |
+               AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+      .rfu0 = 0,
+      .rfu1 = 0};
+
+  AHardwareBuffer* hwBufferPtr;
+  int status = AHardwareBuffer_allocate(&desc, &hwBufferPtr);
+  if (status != NO_ERROR) {
+    ALOGE(
+        "%s: Failed to allocate hardware buffer for temporary framebuffer: %d",
+        __func__, status);
+    return nullptr;
+  }
+
+  return std::make_shared<EglFrameBuffer>(
+      eglDisplay,
+      std::shared_ptr<AHardwareBuffer>(hwBufferPtr, AHardwareBuffer_release));
+}
+
+bool isYuvFormat(const PixelFormat pixelFormat) {
+  switch (static_cast<android_pixel_format_t>(pixelFormat)) {
+    case HAL_PIXEL_FORMAT_YCBCR_422_I:
+    case HAL_PIXEL_FORMAT_YCBCR_422_SP:
+    case HAL_PIXEL_FORMAT_Y16:
+    case HAL_PIXEL_FORMAT_YV12:
+    case HAL_PIXEL_FORMAT_YCBCR_420_888:
+      return true;
+    default:
+      return false;
+  }
+}
+
 }  // namespace
 
 CaptureRequestBuffer::CaptureRequestBuffer(int streamId, int bufferId,
@@ -218,7 +259,10 @@
   ALOGV("Render thread starting");
 
   mEglDisplayContext = std::make_unique<EglDisplayContext>();
-  mEglTextureProgram = std::make_unique<EglTextureProgram>();
+  mEglTextureYuvProgram =
+      std::make_unique<EglTextureProgram>(EglTextureProgram::TextureFormat::YUV);
+  mEglTextureRgbProgram = std::make_unique<EglTextureProgram>(
+      EglTextureProgram::TextureFormat::RGBA);
   mEglSurfaceTexture = std::make_unique<EglSurfaceTexture>(mInputSurfaceWidth,
                                                            mInputSurfaceHeight);
   mInputSurfacePromise.set_value(mEglSurfaceTexture->getSurface());
@@ -371,6 +415,22 @@
     return cameraStatus(Status::INTERNAL_ERROR);
   }
 
+  // Let's create YUV framebuffer and render the surface into this.
+  // This will take care about rescaling as well as potential format conversion.
+  std::shared_ptr<EglFrameBuffer> framebuffer = allocateTemporaryFramebuffer(
+      mEglDisplayContext->getEglDisplay(), stream->width, stream->height);
+  if (framebuffer == nullptr) {
+    ALOGE("Failed to allocate temporary framebuffer for JPEG compression");
+    return cameraStatus(Status::INTERNAL_ERROR);
+  }
+
+  // Render into temporary framebuffer.
+  ndk::ScopedAStatus status = renderIntoEglFramebuffer(*framebuffer);
+  if (!status.isOk()) {
+    ALOGE("Failed to render input texture into temporary framebuffer");
+    return status;
+  }
+
   AHardwareBuffer_Planes planes_info;
 
   int32_t rawFence = fence != nullptr ? fence->get() : -1;
@@ -382,10 +442,21 @@
     return cameraStatus(Status::INTERNAL_ERROR);
   }
 
-  sp<GraphicBuffer> gBuffer = mEglSurfaceTexture->getCurrentBuffer();
+  std::shared_ptr<AHardwareBuffer> inHwBuffer = framebuffer->getHardwareBuffer();
+  GraphicBuffer* gBuffer = GraphicBuffer::fromAHardwareBuffer(inHwBuffer.get());
+
   bool compressionSuccess = true;
   if (gBuffer != nullptr) {
     android_ycbcr ycbcr;
+    if (gBuffer->getPixelFormat() != HAL_PIXEL_FORMAT_YCbCr_420_888) {
+      // This should never happen since we're allocating the temporary buffer
+      // with YUV420 layout above.
+      ALOGE("%s: Cannot compress non-YUV buffer (pixelFormat %d)", __func__,
+            gBuffer->getPixelFormat());
+      AHardwareBuffer_unlock(hwBuffer.get(), nullptr);
+      return cameraStatus(Status::INTERNAL_ERROR);
+    }
+
     status_t status =
         gBuffer->lockYCbCr(AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, &ycbcr);
     ALOGV("Locked buffers");
@@ -434,31 +505,7 @@
     return cameraStatus(Status::ILLEGAL_ARGUMENT);
   }
 
-  // Wait for fence to clear.
-  if (fence != nullptr && fence->isValid()) {
-    status_t ret = fence->wait(kAcquireFenceTimeout.count());
-    if (ret != 0) {
-      ALOGE(
-          "Timeout while waiting for the acquire fence for buffer %d"
-          " for streamId %d",
-          bufferId, streamId);
-      return cameraStatus(Status::INTERNAL_ERROR);
-    }
-  }
-
-  mEglDisplayContext->makeCurrent();
-  framebuffer->beforeDraw();
-
-  if (mEglSurfaceTexture->getCurrentBuffer() == nullptr) {
-    // If there's no current buffer, nothing was written to the surface and
-    // texture is not initialized yet. Let's render the framebuffer black
-    // instead of rendering the texture.
-    glClearColor(0.0f, 0.5f, 0.5f, 0.0f);
-    glClear(GL_COLOR_BUFFER_BIT);
-  } else {
-    mEglTextureProgram->draw(mEglSurfaceTexture->updateTexture());
-  }
-  framebuffer->afterDraw();
+  ndk::ScopedAStatus status = renderIntoEglFramebuffer(*framebuffer, fence);
 
   const std::chrono::nanoseconds after =
       std::chrono::duration_cast<std::chrono::nanoseconds>(
@@ -470,6 +517,43 @@
   return ndk::ScopedAStatus::ok();
 }
 
+ndk::ScopedAStatus VirtualCameraRenderThread::renderIntoEglFramebuffer(
+    EglFrameBuffer& framebuffer, sp<Fence> fence) {
+  ALOGV("%s", __func__);
+  // Wait for fence to clear.
+  if (fence != nullptr && fence->isValid()) {
+    status_t ret = fence->wait(kAcquireFenceTimeout.count());
+    if (ret != 0) {
+      ALOGE("Timeout while waiting for the acquire fence for buffer");
+      return cameraStatus(Status::INTERNAL_ERROR);
+    }
+  }
+
+  mEglDisplayContext->makeCurrent();
+  framebuffer.beforeDraw();
+
+  sp<GraphicBuffer> textureBuffer = mEglSurfaceTexture->getCurrentBuffer();
+  if (textureBuffer == nullptr) {
+    // If there's no current buffer, nothing was written to the surface and
+    // texture is not initialized yet. Let's render the framebuffer black
+    // instead of rendering the texture.
+    glClearColor(0.0f, 0.5f, 0.5f, 0.0f);
+    glClear(GL_COLOR_BUFFER_BIT);
+  } else {
+    const bool renderSuccess =
+        isYuvFormat(static_cast<PixelFormat>(textureBuffer->getPixelFormat()))
+            ? mEglTextureYuvProgram->draw(mEglSurfaceTexture->updateTexture())
+            : mEglTextureRgbProgram->draw(mEglSurfaceTexture->updateTexture());
+    if (!renderSuccess) {
+      ALOGE("%s: Failed to render texture", __func__);
+      return cameraStatus(Status::INTERNAL_ERROR);
+    }
+  }
+  framebuffer.afterDraw();
+
+  return ndk::ScopedAStatus::ok();
+}
+
 }  // namespace virtualcamera
 }  // namespace companion
 }  // namespace android
diff --git a/services/camera/virtualcamera/VirtualCameraRenderThread.h b/services/camera/virtualcamera/VirtualCameraRenderThread.h
index a4374e6..b3aaed8 100644
--- a/services/camera/virtualcamera/VirtualCameraRenderThread.h
+++ b/services/camera/virtualcamera/VirtualCameraRenderThread.h
@@ -24,7 +24,9 @@
 
 #include "VirtualCameraSessionContext.h"
 #include "aidl/android/hardware/camera/device/ICameraDeviceCallback.h"
+#include "android/binder_auto_utils.h"
 #include "util/EglDisplayContext.h"
+#include "util/EglFramebuffer.h"
 #include "util/EglProgram.h"
 #include "util/EglSurfaceTexture.h"
 
@@ -135,6 +137,13 @@
   ndk::ScopedAStatus renderIntoImageStreamBuffer(int streamId, int bufferId,
                                                  sp<Fence> fence = nullptr);
 
+  // Render current image into provided EglFramebuffer.
+  // If fence is specified, this function will block until the fence is cleared
+  // before writing to the buffer.
+  // Always called on the render thread.
+  ndk::ScopedAStatus renderIntoEglFramebuffer(EglFrameBuffer& framebuffer,
+                                              sp<Fence> fence = nullptr);
+
   // Camera callback
   const std::shared_ptr<
       ::aidl::android::hardware::camera::device::ICameraDeviceCallback>
@@ -156,7 +165,8 @@
 
   // EGL helpers - constructed and accessed only from rendering thread.
   std::unique_ptr<EglDisplayContext> mEglDisplayContext;
-  std::unique_ptr<EglTextureProgram> mEglTextureProgram;
+  std::unique_ptr<EglTextureProgram> mEglTextureYuvProgram;
+  std::unique_ptr<EglTextureProgram> mEglTextureRgbProgram;
   std::unique_ptr<EglSurfaceTexture> mEglSurfaceTexture;
 
   std::promise<sp<Surface>> mInputSurfacePromise;
diff --git a/services/camera/virtualcamera/VirtualCameraService.cc b/services/camera/virtualcamera/VirtualCameraService.cc
index 08bfff7..370a5a8 100644
--- a/services/camera/virtualcamera/VirtualCameraService.cc
+++ b/services/camera/virtualcamera/VirtualCameraService.cc
@@ -31,6 +31,7 @@
 #include "android/binder_auto_utils.h"
 #include "android/binder_libbinder.h"
 #include "binder/Status.h"
+#include "util/Permissions.h"
 #include "util/Util.h"
 
 using ::android::binder::Status;
@@ -54,6 +55,8 @@
  * enable_test_camera
  * disable_test_camera
 )";
+constexpr char kCreateVirtualDevicePermission[] =
+    "android.permission.CREATE_VIRTUAL_DEVICE";
 
 ndk::ScopedAStatus validateConfiguration(
     const VirtualCameraConfiguration& configuration) {
@@ -79,17 +82,26 @@
 }  // namespace
 
 VirtualCameraService::VirtualCameraService(
-    std::shared_ptr<VirtualCameraProvider> virtualCameraProvider)
-    : mVirtualCameraProvider(virtualCameraProvider) {
+    std::shared_ptr<VirtualCameraProvider> virtualCameraProvider,
+    const PermissionsProxy& permissionProxy)
+    : mVirtualCameraProvider(virtualCameraProvider),
+      mPermissionProxy(permissionProxy) {
 }
 
 ndk::ScopedAStatus VirtualCameraService::registerCamera(
     const ::ndk::SpAIBinder& token,
     const VirtualCameraConfiguration& configuration, bool* _aidl_return) {
+  if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
+    ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
+          getpid(), getuid(), kCreateVirtualDevicePermission);
+    return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
+  }
+
   if (_aidl_return == nullptr) {
     return ndk::ScopedAStatus::fromServiceSpecificError(
         Status::EX_ILLEGAL_ARGUMENT);
   }
+
   *_aidl_return = true;
 
   auto status = validateConfiguration(configuration);
@@ -127,6 +139,12 @@
 
 ndk::ScopedAStatus VirtualCameraService::unregisterCamera(
     const ::ndk::SpAIBinder& token) {
+  if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
+    ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
+          getpid(), getuid(), kCreateVirtualDevicePermission);
+    return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
+  }
+
   std::lock_guard lock(mLock);
 
   auto it = mTokenToCameraName.find(token);
@@ -145,6 +163,12 @@
 
 ndk::ScopedAStatus VirtualCameraService::getCameraId(
         const ::ndk::SpAIBinder& token, int32_t* _aidl_return) {
+  if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
+    ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
+          getpid(), getuid(), kCreateVirtualDevicePermission);
+    return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
+  }
+
   if (_aidl_return == nullptr) {
     return ndk::ScopedAStatus::fromServiceSpecificError(
             Status::EX_ILLEGAL_ARGUMENT);
diff --git a/services/camera/virtualcamera/VirtualCameraService.h b/services/camera/virtualcamera/VirtualCameraService.h
index b68d43a..d573986 100644
--- a/services/camera/virtualcamera/VirtualCameraService.h
+++ b/services/camera/virtualcamera/VirtualCameraService.h
@@ -24,6 +24,7 @@
 #include "VirtualCameraDevice.h"
 #include "VirtualCameraProvider.h"
 #include "aidl/android/companion/virtualcamera/BnVirtualCameraService.h"
+#include "util/Permissions.h"
 
 namespace android {
 namespace companion {
@@ -34,7 +35,8 @@
     : public aidl::android::companion::virtualcamera::BnVirtualCameraService {
  public:
   VirtualCameraService(
-      std::shared_ptr<VirtualCameraProvider> virtualCameraProvider);
+      std::shared_ptr<VirtualCameraProvider> virtualCameraProvider,
+      const PermissionsProxy& permissionProxy = PermissionsProxy::get());
 
   // Register camera corresponding to the binder token.
   ndk::ScopedAStatus registerCamera(
@@ -68,6 +70,8 @@
 
   std::shared_ptr<VirtualCameraProvider> mVirtualCameraProvider;
 
+  const PermissionsProxy& mPermissionProxy;
+
   std::mutex mLock;
   struct BinderTokenHash {
     std::size_t operator()(const ::ndk::SpAIBinder& key) const {
diff --git a/services/camera/virtualcamera/VirtualCameraSession.cc b/services/camera/virtualcamera/VirtualCameraSession.cc
index 9e15871..47780d8 100644
--- a/services/camera/virtualcamera/VirtualCameraSession.cc
+++ b/services/camera/virtualcamera/VirtualCameraSession.cc
@@ -153,7 +153,7 @@
 }  // namespace
 
 VirtualCameraSession::VirtualCameraSession(
-    VirtualCameraDevice& cameraDevice,
+    std::shared_ptr<VirtualCameraDevice> cameraDevice,
     std::shared_ptr<ICameraDeviceCallback> cameraDeviceCallback,
     std::shared_ptr<IVirtualCameraCallback> virtualCameraClientCallback)
     : mCameraDevice(cameraDevice),
@@ -201,6 +201,12 @@
     return cameraStatus(Status::ILLEGAL_ARGUMENT);
   }
 
+  std::shared_ptr<VirtualCameraDevice> virtualCamera = mCameraDevice.lock();
+  if (virtualCamera == nullptr) {
+    ALOGW("%s: configure called on already unregistered camera", __func__);
+    return cameraStatus(Status::CAMERA_DISCONNECTED);
+  }
+
   mSessionContext.removeStreamsNotInStreamConfiguration(
       in_requestedConfiguration);
 
@@ -213,7 +219,7 @@
   int inputWidth;
   int inputHeight;
 
-  if (!mCameraDevice.isStreamCombinationSupported(in_requestedConfiguration)) {
+  if (!virtualCamera->isStreamCombinationSupported(in_requestedConfiguration)) {
     ALOGE("%s: Requested stream configuration is not supported", __func__);
     return cameraStatus(Status::ILLEGAL_ARGUMENT);
   }
diff --git a/services/camera/virtualcamera/VirtualCameraSession.h b/services/camera/virtualcamera/VirtualCameraSession.h
index 50962e5..82a7a34 100644
--- a/services/camera/virtualcamera/VirtualCameraSession.h
+++ b/services/camera/virtualcamera/VirtualCameraSession.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_COMPANION_VIRTUALCAMERA_VIRTUALCAMERASESSION_H
 #define ANDROID_COMPANION_VIRTUALCAMERA_VIRTUALCAMERASESSION_H
 
+#include <atomic>
 #include <memory>
 #include <set>
 
@@ -46,7 +47,7 @@
   // When virtualCameraClientCallback is null, the input surface will be filled
   // with test pattern.
   VirtualCameraSession(
-      VirtualCameraDevice& mCameraDevice,
+      std::shared_ptr<VirtualCameraDevice> mCameraDevice,
       std::shared_ptr<
           ::aidl::android::hardware::camera::device::ICameraDeviceCallback>
           cameraDeviceCallback,
@@ -116,7 +117,7 @@
       const ::aidl::android::hardware::camera::device::CaptureRequest& request)
       EXCLUDES(mLock);
 
-  VirtualCameraDevice& mCameraDevice;
+  std::weak_ptr<VirtualCameraDevice> mCameraDevice;
 
   mutable std::mutex mLock;
 
diff --git a/services/camera/virtualcamera/aidl/android/companion/virtualcamera/Format.aidl b/services/camera/virtualcamera/aidl/android/companion/virtualcamera/Format.aidl
index d9b90a6..b155147 100644
--- a/services/camera/virtualcamera/aidl/android/companion/virtualcamera/Format.aidl
+++ b/services/camera/virtualcamera/aidl/android/companion/virtualcamera/Format.aidl
@@ -24,5 +24,6 @@
 @Backing(type="int")
 enum Format {
     UNKNOWN = 0,
+    RGBA_8888 = 1,
     YUV_420_888 = 0x23,
 }
diff --git a/services/camera/virtualcamera/main.cc b/services/camera/virtualcamera/main.cc
index b7e9c38..43b0219 100644
--- a/services/camera/virtualcamera/main.cc
+++ b/services/camera/virtualcamera/main.cc
@@ -49,16 +49,16 @@
 
   auto aidlBinder = defaultProvider->asBinder();
   AIBinder_forceDowngradeToLocalStability(aidlBinder.get());
-  binder_exception_t ret =
-      AServiceManager_addService(aidlBinder.get(), serviceName.c_str());
+  binder_exception_t ret = AServiceManager_registerLazyService(
+      aidlBinder.get(), serviceName.c_str());
   LOG_ALWAYS_FATAL_IF(
       ret != EX_NONE,
       "Error while registering virtual camera provider service: %d", ret);
 
   std::shared_ptr<VirtualCameraService> virtualCameraService =
       ndk::SharedRefBase::make<VirtualCameraService>(defaultProvider);
-  ret = AServiceManager_addService(virtualCameraService->asBinder().get(),
-                                   kVirtualCameraServiceName);
+  ret = AServiceManager_registerLazyService(
+      virtualCameraService->asBinder().get(), kVirtualCameraServiceName);
   LOG_ALWAYS_FATAL_IF(ret != EX_NONE,
                       "Error while registering virtual camera service: %d", ret);
 
diff --git a/services/camera/virtualcamera/tests/EglUtilTest.cc b/services/camera/virtualcamera/tests/EglUtilTest.cc
index d0b7218..589e312 100644
--- a/services/camera/virtualcamera/tests/EglUtilTest.cc
+++ b/services/camera/virtualcamera/tests/EglUtilTest.cc
@@ -61,12 +61,24 @@
   EXPECT_TRUE(eglTestPatternProgram.isInitialized());
 }
 
-TEST_F(EglTest, EglTextureProgramSuccessfulInit) {
+TEST_F(EglTest, EglTextureProgramYuvSuccessfulInit) {
   if (!isGlExtensionSupported(kGlExtYuvTarget)) {
       GTEST_SKIP() << "Skipping test because of missing required GL extension " << kGlExtYuvTarget;
   }
 
-  EglTextureProgram eglTextureProgram;
+  EglTextureProgram eglTextureProgram(EglTextureProgram::TextureFormat::YUV);
+
+  // Verify the shaders compiled and linked successfully.
+  EXPECT_TRUE(eglTextureProgram.isInitialized());
+}
+
+TEST_F(EglTest, EglTextureProgramRgbaSuccessfulInit) {
+  if (!isGlExtensionSupported(kGlExtYuvTarget)) {
+      GTEST_SKIP() << "Skipping test because of missing required GL extension "
+                   << kGlExtYuvTarget;
+  }
+
+  EglTextureProgram eglTextureProgram(EglTextureProgram::TextureFormat::RGBA);
 
   // Verify the shaders compiled and linked successfully.
   EXPECT_TRUE(eglTextureProgram.isInitialized());
diff --git a/services/camera/virtualcamera/tests/VirtualCameraServiceTest.cc b/services/camera/virtualcamera/tests/VirtualCameraServiceTest.cc
index f931cb4..38261fb 100644
--- a/services/camera/virtualcamera/tests/VirtualCameraServiceTest.cc
+++ b/services/camera/virtualcamera/tests/VirtualCameraServiceTest.cc
@@ -29,6 +29,7 @@
 #include "binder/Binder.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "util/Permissions.h"
 #include "utils/Errors.h"
 
 namespace android {
@@ -50,10 +51,13 @@
 using ::testing::IsEmpty;
 using ::testing::IsNull;
 using ::testing::Not;
+using ::testing::Return;
 using ::testing::SizeIs;
 
 constexpr int kVgaWidth = 640;
 constexpr int kVgaHeight = 480;
+constexpr char kCreateVirtualDevicePermissions[] =
+    "android.permission.CREATE_VIRTUAL_DEVICE";
 
 const VirtualCameraConfiguration kEmptyVirtualCameraConfiguration;
 
@@ -76,6 +80,12 @@
               (override));
 };
 
+class MockPermissionsProxy : public PermissionsProxy {
+ public:
+  MOCK_METHOD(bool, checkCallingPermission, (const std::string&),
+              (const override));
+};
+
 class VirtualCameraServiceTest : public ::testing::Test {
  public:
   void SetUp() override {
@@ -87,8 +97,11 @@
           return ndk::ScopedAStatus::ok();
         });
     mCameraProvider->setCallback(mMockCameraProviderCallback);
-    mCameraService =
-        ndk::SharedRefBase::make<VirtualCameraService>(mCameraProvider);
+    mCameraService = ndk::SharedRefBase::make<VirtualCameraService>(
+        mCameraProvider, mMockPermissionsProxy);
+
+    ON_CALL(mMockPermissionsProxy, checkCallingPermission)
+        .WillByDefault(Return(true));
 
     mDevNullFd = open("/dev/null", O_RDWR);
     ASSERT_THAT(mDevNullFd, Ge(0));
@@ -129,6 +142,7 @@
   std::shared_ptr<VirtualCameraProvider> mCameraProvider;
   std::shared_ptr<MockCameraProviderCallback> mMockCameraProviderCallback =
       ndk::SharedRefBase::make<MockCameraProviderCallback>();
+  MockPermissionsProxy mMockPermissionsProxy;
 
   sp<BBinder> mOwnerToken;
   ndk::SpAIBinder mNdkOwnerToken;
@@ -139,7 +153,7 @@
       createConfiguration(kVgaWidth, kVgaHeight, Format::YUV_420_888);
 };
 
-TEST_F(VirtualCameraServiceTest, RegisterCameraSucceeds) {
+TEST_F(VirtualCameraServiceTest, RegisterCameraWithYuvInputSucceeds) {
   sp<BBinder> token = sp<BBinder>::make();
   ndk::SpAIBinder ndkToken(AIBinder_fromPlatformBinder(token));
   bool aidlRet;
@@ -153,6 +167,20 @@
   EXPECT_THAT(getCameraIds(), SizeIs(1));
 }
 
+TEST_F(VirtualCameraServiceTest, RegisterCameraWithRgbaInputSucceeds) {
+  sp<BBinder> token = sp<BBinder>::make();
+  ndk::SpAIBinder ndkToken(AIBinder_fromPlatformBinder(token));
+  bool aidlRet;
+
+  VirtualCameraConfiguration config =
+      createConfiguration(kVgaWidth, kVgaHeight, Format::RGBA_8888);
+
+  ASSERT_TRUE(mCameraService->registerCamera(ndkToken, config, &aidlRet).isOk());
+
+  EXPECT_TRUE(aidlRet);
+  EXPECT_THAT(getCameraIds(), SizeIs(1));
+}
+
 TEST_F(VirtualCameraServiceTest, RegisterCameraTwiceSecondReturnsFalse) {
   createCamera();
   bool aidlRet;
@@ -242,6 +270,40 @@
   EXPECT_THAT(mCameraService->getCamera(mNdkOwnerToken), IsNull());
 }
 
+TEST_F(VirtualCameraServiceTest, RegisterCameraWithoutPermissionFails) {
+  bool aidlRet;
+  EXPECT_CALL(mMockPermissionsProxy,
+              checkCallingPermission(kCreateVirtualDevicePermissions))
+      .WillOnce(Return(false));
+
+  EXPECT_THAT(mCameraService
+                  ->registerCamera(mNdkOwnerToken, mVgaYUV420OnlyConfiguration,
+                                   &aidlRet)
+                  .getExceptionCode(),
+              Eq(EX_SECURITY));
+}
+
+TEST_F(VirtualCameraServiceTest, UnregisterCameraWithoutPermissionFails) {
+  EXPECT_CALL(mMockPermissionsProxy,
+              checkCallingPermission(kCreateVirtualDevicePermissions))
+      .WillOnce(Return(false));
+
+  EXPECT_THAT(
+      mCameraService->unregisterCamera(mNdkOwnerToken).getExceptionCode(),
+      Eq(EX_SECURITY));
+}
+
+TEST_F(VirtualCameraServiceTest, GetIdWithoutPermissionFails) {
+  int32_t aidlRet;
+  EXPECT_CALL(mMockPermissionsProxy,
+              checkCallingPermission(kCreateVirtualDevicePermissions))
+      .WillOnce(Return(false));
+
+  EXPECT_THAT(
+      mCameraService->getCameraId(mNdkOwnerToken, &aidlRet).getExceptionCode(),
+      Eq(EX_SECURITY));
+}
+
 TEST_F(VirtualCameraServiceTest, UnregisterCameraWithUnknownToken) {
   createCamera();
 
diff --git a/services/camera/virtualcamera/tests/VirtualCameraSessionTest.cc b/services/camera/virtualcamera/tests/VirtualCameraSessionTest.cc
index 9edfe77..30bd2b6 100644
--- a/services/camera/virtualcamera/tests/VirtualCameraSessionTest.cc
+++ b/services/camera/virtualcamera/tests/VirtualCameraSessionTest.cc
@@ -20,8 +20,8 @@
 #include "VirtualCameraDevice.h"
 #include "VirtualCameraSession.h"
 #include "aidl/android/companion/virtualcamera/BnVirtualCameraCallback.h"
-#include "aidl/android/companion/virtualcamera/IVirtualCameraCallback.h"
 #include "aidl/android/companion/virtualcamera/SupportedStreamConfiguration.h"
+#include "aidl/android/hardware/camera/common/Status.h"
 #include "aidl/android/hardware/camera/device/BnCameraDeviceCallback.h"
 #include "aidl/android/hardware/camera/device/StreamConfiguration.h"
 #include "aidl/android/hardware/graphics/common/PixelFormat.h"
@@ -44,6 +44,7 @@
 using ::aidl::android::companion::virtualcamera::BnVirtualCameraCallback;
 using ::aidl::android::companion::virtualcamera::Format;
 using ::aidl::android::companion::virtualcamera::SupportedStreamConfiguration;
+using ::aidl::android::hardware::camera::common::Status;
 using ::aidl::android::hardware::camera::device::BnCameraDeviceCallback;
 using ::aidl::android::hardware::camera::device::BufferRequest;
 using ::aidl::android::hardware::camera::device::BufferRequestStatus;
@@ -110,7 +111,7 @@
                                          .pixelFormat = Format::YUV_420_888}},
         mMockVirtualCameraClientCallback);
     mVirtualCameraSession = ndk::SharedRefBase::make<VirtualCameraSession>(
-        *mVirtualCameraDevice, mMockCameraDeviceCallback,
+        mVirtualCameraDevice, mMockCameraDeviceCallback,
         mMockVirtualCameraClientCallback);
 
     // Explicitly defining default actions below to prevent gmock from
@@ -223,6 +224,22 @@
   EXPECT_THAT(aidlReturn, Eq(requests.size()));
 }
 
+TEST_F(VirtualCameraSessionTest, configureAfterCameraRelease) {
+  StreamConfiguration streamConfiguration;
+  streamConfiguration.streams = {
+      createStream(kStreamId, kWidth, kHeight, PixelFormat::YCBCR_420_888)};
+  std::vector<HalStream> halStreams;
+
+  // Release virtual camera.
+  mVirtualCameraDevice.reset();
+
+  // Expect configuration attempt returns CAMERA_DISCONNECTED service specific code.
+  EXPECT_THAT(
+      mVirtualCameraSession->configureStreams(streamConfiguration, &halStreams)
+          .getServiceSpecificError(),
+      Eq(static_cast<int32_t>(Status::CAMERA_DISCONNECTED)));
+}
+
 }  // namespace
 }  // namespace virtualcamera
 }  // namespace companion
diff --git a/services/camera/virtualcamera/util/EglFramebuffer.cc b/services/camera/virtualcamera/util/EglFramebuffer.cc
index acf0122..57b94d3 100644
--- a/services/camera/virtualcamera/util/EglFramebuffer.cc
+++ b/services/camera/virtualcamera/util/EglFramebuffer.cc
@@ -112,6 +112,10 @@
   return mHeight;
 }
 
+std::shared_ptr<AHardwareBuffer> EglFrameBuffer::getHardwareBuffer() {
+  return mHardwareBuffer;
+}
+
 }  // namespace virtualcamera
 }  // namespace companion
 }  // namespace android
diff --git a/services/camera/virtualcamera/util/EglFramebuffer.h b/services/camera/virtualcamera/util/EglFramebuffer.h
index 35f85e2..b39df6d 100644
--- a/services/camera/virtualcamera/util/EglFramebuffer.h
+++ b/services/camera/virtualcamera/util/EglFramebuffer.h
@@ -50,6 +50,9 @@
   // Return height of framebuffer (in pixels).
   int getHeight() const;
 
+  // Return underlying hardware buffer.
+  std::shared_ptr<AHardwareBuffer> getHardwareBuffer();
+
  private:
   // Keeping shared_ptr to hardware buffer instance here prevents it from being
   // freed while tied to EGL framebufer / EGL texture.
diff --git a/services/camera/virtualcamera/util/EglProgram.cc b/services/camera/virtualcamera/util/EglProgram.cc
index c468d39..510fd33 100644
--- a/services/camera/virtualcamera/util/EglProgram.cc
+++ b/services/camera/virtualcamera/util/EglProgram.cc
@@ -76,17 +76,29 @@
     vTextureCoord = aTextureCoord;
   })";
 
-constexpr char kExternalTextureFragmentShader[] = R"(#version 300 es
+constexpr char kExternalYuvTextureFragmentShader[] = R"(#version 300 es
     #extension GL_OES_EGL_image_external_essl3 : require
     #extension GL_EXT_YUV_target : require
     precision mediump float;
     in vec2 vTextureCoord;
-    out vec4 fragColor;
+    layout (yuv) out vec4 fragColor;
     uniform __samplerExternal2DY2YEXT uTexture;
     void main() {
       fragColor = texture(uTexture, vTextureCoord);
     })";
 
+constexpr char kExternalRgbaTextureFragmentShader[] = R"(#version 300 es
+    #extension GL_OES_EGL_image_external : require
+    #extension GL_EXT_YUV_target : require
+    precision mediump float;
+    in vec2 vTextureCoord;
+    layout (yuv) out vec4 fragColor;
+    uniform samplerExternalOES uTexture;
+    void main() {
+      vec4 rgbaColor = texture(uTexture, vTextureCoord);
+      fragColor = vec4(rgb_2_yuv(rgbaColor.xyz, itu_601_full_range), 0.0);
+    })";
+
 constexpr int kCoordsPerVertex = 3;
 constexpr std::array<float, 12> kSquareCoords{-1.f, 1.0f, 0.0f,  // top left
                                               -1.f, -1.f, 0.0f,  // bottom left
@@ -237,7 +249,7 @@
   return true;
 }
 
-EglTextureProgram::EglTextureProgram() {
+EglTextureProgram::EglTextureProgram(const TextureFormat textureFormat) {
   if (!isGlExtensionSupported(kGlExtYuvTarget)) {
     ALOGE(
         "Cannot initialize external texture program due to missing "
@@ -245,7 +257,10 @@
     return;
   }
 
-  if (initialize(kExternalTextureVertexShader, kExternalTextureFragmentShader)) {
+  const char* fragmentShaderSrc = textureFormat == TextureFormat::YUV
+                                      ? kExternalYuvTextureFragmentShader
+                                      : kExternalRgbaTextureFragmentShader;
+  if (initialize(kExternalTextureVertexShader, fragmentShaderSrc)) {
     ALOGV("Successfully initialized EGL shaders for external texture program.");
   } else {
     ALOGE("External texture EGL shader program initialization failed.");
diff --git a/services/camera/virtualcamera/util/EglProgram.h b/services/camera/virtualcamera/util/EglProgram.h
index 8e394e7..1b5f2cd 100644
--- a/services/camera/virtualcamera/util/EglProgram.h
+++ b/services/camera/virtualcamera/util/EglProgram.h
@@ -55,7 +55,9 @@
 // TODO(b/301023410) Add support for translation / cropping.
 class EglTextureProgram : public EglProgram {
  public:
-  EglTextureProgram();
+  enum class TextureFormat { RGBA, YUV };
+
+  EglTextureProgram(TextureFormat textureFormat = TextureFormat::YUV);
 
   bool draw(GLuint textureId);
 };
diff --git a/services/camera/virtualcamera/util/Permissions.cc b/services/camera/virtualcamera/util/Permissions.cc
new file mode 100644
index 0000000..634bca3
--- /dev/null
+++ b/services/camera/virtualcamera/util/Permissions.cc
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "VirtualCameraPermissions"
+
+#include "Permissions.h"
+
+#include "binder/PermissionCache.h"
+#include "log/log.h"
+
+namespace android {
+namespace companion {
+namespace virtualcamera {
+namespace {
+
+class PermissionsProxyImpl : public PermissionsProxy {
+ public:
+  bool checkCallingPermission(const std::string& permission) const override;
+};
+
+bool PermissionsProxyImpl::checkCallingPermission(
+    const std::string& permission) const {
+  int32_t uid;
+  int32_t pid;
+  const bool hasPermission = PermissionCache::checkCallingPermission(
+      String16(permission.c_str()), &pid, &uid);
+
+  ALOGV("%s: Checking %s permission for pid %d uid %d: %s", __func__,
+        permission.c_str(), pid, uid, hasPermission ? "granted" : "denied");
+  return hasPermission;
+}
+}  // namespace
+
+const PermissionsProxy& PermissionsProxy::get() {
+  static PermissionsProxyImpl sPermissionProxyImpl;
+  return sPermissionProxyImpl;
+}
+
+}  // namespace virtualcamera
+}  // namespace companion
+}  // namespace android
diff --git a/services/camera/virtualcamera/util/Permissions.h b/services/camera/virtualcamera/util/Permissions.h
new file mode 100644
index 0000000..014989e
--- /dev/null
+++ b/services/camera/virtualcamera/util/Permissions.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_COMPANION_VIRTUALCAMERA_PERMISSIONS_H
+#define ANDROID_COMPANION_VIRTUALCAMERA_PERMISSIONS_H
+
+#include <string>
+
+#include "sys/types.h"
+
+namespace android {
+namespace companion {
+namespace virtualcamera {
+
+class PermissionsProxy {
+ public:
+  virtual ~PermissionsProxy() = default;
+
+  // Checks whether caller holds permission. Do not use with runtime permissions
+  // as the default implementation uses PermissionCache which doesn't reflect
+  // possible runtime changes of permissions.
+  //
+  // Returns true in case caller holds the permission, false otherwise or if
+  // there was any error while verifying the permission.
+  virtual bool checkCallingPermission(const std::string& permission) const = 0;
+
+  // Get instance of PermissionProxy.
+  static const PermissionsProxy& get();
+};
+
+}  // namespace virtualcamera
+}  // namespace companion
+}  // namespace android
+
+#endif  // ANDROID_COMPANION_VIRTUALCAMERA_PERMISSIONS_H
diff --git a/services/camera/virtualcamera/util/Util.cc b/services/camera/virtualcamera/util/Util.cc
index 11dc3a6..df771b1 100644
--- a/services/camera/virtualcamera/util/Util.cc
+++ b/services/camera/virtualcamera/util/Util.cc
@@ -18,20 +18,26 @@
 
 #include <unistd.h>
 
+#include <algorithm>
+#include <array>
+
 #include "jpeglib.h"
 
 namespace android {
 namespace companion {
 namespace virtualcamera {
 
+using ::aidl::android::companion::virtualcamera::Format;
+using ::aidl::android::hardware::common::NativeHandle;
+
 // Lower bound for maximal supported texture size is at least 2048x2048
 // but on most platforms will be more.
 // TODO(b/301023410) - Query actual max texture size.
 constexpr int kMaxTextureSize = 2048;
 constexpr int kLibJpegDctSize = DCTSIZE;
 
-using ::aidl::android::companion::virtualcamera::Format;
-using ::aidl::android::hardware::common::NativeHandle;
+constexpr std::array<Format, 2> kSupportedFormats{Format::YUV_420_888,
+                                                  Format::RGBA_8888};
 
 sp<Fence> importFence(const NativeHandle& aidlHandle) {
   if (aidlHandle.fds.size() != 1) {
@@ -41,11 +47,15 @@
   return sp<Fence>::make(::dup(aidlHandle.fds[0].get()));
 }
 
+bool isPixelFormatSupportedForInput(const Format format) {
+  return std::find(kSupportedFormats.begin(), kSupportedFormats.end(),
+                   format) != kSupportedFormats.end();
+}
+
 // Returns true if specified format is supported for virtual camera input.
 bool isFormatSupportedForInput(const int width, const int height,
                                const Format format) {
-  if (format != Format::YUV_420_888) {
-    // For now only YUV_420_888 is supported for input.
+  if (!isPixelFormatSupportedForInput(format)) {
     return false;
   }
 
diff --git a/services/camera/virtualcamera/util/Util.h b/services/camera/virtualcamera/util/Util.h
index b778321..a73c99b 100644
--- a/services/camera/virtualcamera/util/Util.h
+++ b/services/camera/virtualcamera/util/Util.h
@@ -43,6 +43,10 @@
 sp<Fence> importFence(
     const ::aidl::android::hardware::common::NativeHandle& handle);
 
+// Returns true if specified pixel format is supported for virtual camera input.
+bool isPixelFormatSupportedForInput(
+    ::aidl::android::companion::virtualcamera::Format format);
+
 // Returns true if specified format is supported for virtual camera input.
 bool isFormatSupportedForInput(
     int width, int height,
diff --git a/services/camera/virtualcamera/virtual_camera.hal.rc b/services/camera/virtualcamera/virtual_camera.hal.rc
index fd86965..a8bb61d 100644
--- a/services/camera/virtualcamera/virtual_camera.hal.rc
+++ b/services/camera/virtualcamera/virtual_camera.hal.rc
@@ -5,3 +5,7 @@
     capabilities SYS_NICE
     rlimit rtprio 10 10
     task_profiles CameraServiceCapacity CameraServicePerformance
+    interface aidl virtual_camera
+    interface aidl android.hardware.camera.provider.ICameraProvider/virtual/0
+    oneshot
+    disabled
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index 2ef6fe5..6b48075 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -76,7 +76,8 @@
 const static std::map<audio_format_t, audio_format_t> NEXT_FORMAT_TO_TRY = {
         {AUDIO_FORMAT_PCM_FLOAT,         AUDIO_FORMAT_PCM_32_BIT},
         {AUDIO_FORMAT_PCM_32_BIT,        AUDIO_FORMAT_PCM_24_BIT_PACKED},
-        {AUDIO_FORMAT_PCM_24_BIT_PACKED, AUDIO_FORMAT_PCM_16_BIT}
+        {AUDIO_FORMAT_PCM_24_BIT_PACKED, AUDIO_FORMAT_PCM_8_24_BIT},
+        {AUDIO_FORMAT_PCM_8_24_BIT,      AUDIO_FORMAT_PCM_16_BIT}
 };
 
 audio_format_t getNextFormatToTry(audio_format_t curFormat) {
@@ -421,10 +422,17 @@
     ALOGD("%s() called with dev %d, old = %d", __func__, deviceId, getDeviceId());
     if (getDeviceId() != deviceId) {
         if (getDeviceId() != AUDIO_PORT_HANDLE_NONE) {
+            // When there is a routing changed, mmap stream should be disconnected. Set `mConnected`
+            // as false here so that there won't be a new stream connect to this endpoint.
+            mConnected.store(false);
             const android::sp<AAudioServiceEndpointMMAP> holdEndpoint(this);
             std::thread asyncTask([holdEndpoint, deviceId]() {
                 ALOGD("onRoutingChanged() asyncTask launched");
-                holdEndpoint->disconnectRegisteredStreams();
+                // When routing changed, the stream is disconnected and cannot be used except for
+                // closing. In that case, it should be safe to release all registered streams.
+                // This can help release service side resource in case the client doesn't close
+                // the stream after receiving disconnect event.
+                holdEndpoint->releaseRegisteredStreams();
                 holdEndpoint->setDeviceId(deviceId);
             });
             asyncTask.detach();
diff --git a/services/oboeservice/AAudioServiceEndpointShared.cpp b/services/oboeservice/AAudioServiceEndpointShared.cpp
index 1dd0c3a..5e1e594 100644
--- a/services/oboeservice/AAudioServiceEndpointShared.cpp
+++ b/services/oboeservice/AAudioServiceEndpointShared.cpp
@@ -219,9 +219,13 @@
 
 void AAudioServiceEndpointShared::handleDisconnectRegisteredStreamsAsync() {
     android::sp<AAudioServiceEndpointShared> holdEndpoint(this);
+    // When there is a routing changed, mmap stream should be disconnected. Set `mConnected`
+    // as false here so that there won't be a new stream connect to this endpoint.
+    mConnected.store(false);
     std::thread asyncTask([holdEndpoint]() {
-        // We do not need the returned vector.
-        holdEndpoint->disconnectRegisteredStreams();
+        // When handling disconnection, the service side has disconnected. In that case,
+        // it should be safe to release all registered streams.
+        holdEndpoint->releaseRegisteredStreams();
     });
     asyncTask.detach();
 }
diff --git a/services/oboeservice/Android.bp b/services/oboeservice/Android.bp
index 3521979..9fe06b7 100644
--- a/services/oboeservice/Android.bp
+++ b/services/oboeservice/Android.bp
@@ -55,7 +55,7 @@
     "modernize-use-emplace",
     "modernize-use-equals-default",
     "modernize-use-equals-delete",
-    // "modernize-use-nodiscard", // Maybe add this in the future
+    "modernize-use-nodiscard",
     "modernize-use-noexcept",
     "modernize-use-nullptr",
     "modernize-use-override",