Merge "Thread: Add ThreadBase_ThreadLoop capability" into main
diff --git a/camera/Android.bp b/camera/Android.bp
index e5ae954..7de8a62 100644
--- a/camera/Android.bp
+++ b/camera/Android.bp
@@ -101,6 +101,7 @@
     ],
 
     shared_libs: [
+        "camera_platform_flags_c_lib",
         "libbase",
         "libcutils",
         "libutils",
diff --git a/camera/CameraSessionStats.cpp b/camera/CameraSessionStats.cpp
index 36bf24c..f0630dd 100644
--- a/camera/CameraSessionStats.cpp
+++ b/camera/CameraSessionStats.cpp
@@ -16,6 +16,7 @@
 
 // #define LOG_NDEBUG 0
 #define LOG_TAG "CameraSessionStats"
+
 #include <utils/Log.h>
 #include <utils/String16.h>
 
@@ -414,6 +415,12 @@
         return err;
     }
 
+    bool usedUltraWide = false;
+    if ((err = parcel->readBool(&usedUltraWide)) != OK) {
+        ALOGE("%s: Failed to read ultrawide usage from parcel", __FUNCTION__);
+        return err;
+    }
+
     int32_t sessionIdx;
     if ((err = parcel->readInt32(&sessionIdx)) != OK) {
         ALOGE("%s: Failed to read session index from parcel", __FUNCTION__);
@@ -443,6 +450,7 @@
     mStreamStats = std::move(streamStats);
     mUserTag = toStdString(userTag);
     mVideoStabilizationMode = videoStabilizationMode;
+    mUsedUltraWide = usedUltraWide;
     mSessionIndex = sessionIdx;
     mCameraExtensionSessionStats = extStats;
 
@@ -541,6 +549,10 @@
         ALOGE("%s: Failed to write video stabilization mode!", __FUNCTION__);
         return err;
     }
+    if ((err = parcel->writeBool(mUsedUltraWide)) != OK) {
+        ALOGE("%s: Failed to write ultrawide usage!", __FUNCTION__);
+        return err;
+    }
 
     if ((err = parcel->writeInt32(mSessionIndex)) != OK) {
         ALOGE("%s: Failed to write session index!", __FUNCTION__);
diff --git a/camera/camera_platform.aconfig b/camera/camera_platform.aconfig
index 6200489..3d12983 100644
--- a/camera/camera_platform.aconfig
+++ b/camera/camera_platform.aconfig
@@ -2,15 +2,15 @@
 
 flag {
      namespace: "camera_platform"
-     name: "initial_test_flag"
-     description: "Flag infrastructure test flag"
-     bug: "292631208"
+     name: "camera_hsum_permission"
+     description: "Camera access by headless system user"
+     bug: "273539631"
 }
 
 flag {
      namespace: "camera_platform"
-     name: "camera_hsum_permission"
-     description: "Camera access by headless system user"
-     bug: "273539631"
+     name: "log_ultrawide_usage"
+     description: "Enable measuring how much usage there is for ultrawide-angle cameras"
+     bug: "300515796"
 }
 
diff --git a/camera/include/camera/CameraSessionStats.h b/camera/include/camera/CameraSessionStats.h
index 70ca0b3..158ac2a 100644
--- a/camera/include/camera/CameraSessionStats.h
+++ b/camera/include/camera/CameraSessionStats.h
@@ -161,6 +161,7 @@
     std::vector<CameraStreamStats> mStreamStats;
     std::string mUserTag;
     int mVideoStabilizationMode;
+    bool mUsedUltraWide;
     int mSessionIndex;
 
     CameraExtensionSessionStats mCameraExtensionSessionStats;
diff --git a/camera/tests/fuzzer/Android.bp b/camera/tests/fuzzer/Android.bp
index bae8706..9aecba4 100644
--- a/camera/tests/fuzzer/Android.bp
+++ b/camera/tests/fuzzer/Android.bp
@@ -28,6 +28,7 @@
         "libcamera_client",
     ],
     shared_libs: [
+        "camera_platform_flags_c_lib",
         "libbase",
         "libcutils",
         "libutils",
diff --git a/media/codec2/components/base/Android.bp b/media/codec2/components/base/Android.bp
index 664647a..4b189b4 100644
--- a/media/codec2/components/base/Android.bp
+++ b/media/codec2/components/base/Android.bp
@@ -42,6 +42,10 @@
         "libnativewindow_headers",
     ],
 
+    static_libs: [
+        "libyuv_static", // for conversion routines
+    ],
+
     shared_libs: [
         "libcutils", // for properties
         "liblog", // for ALOG
diff --git a/media/codec2/components/base/SimpleC2Component.cpp b/media/codec2/components/base/SimpleC2Component.cpp
index 55a1164..06a21f6 100644
--- a/media/codec2/components/base/SimpleC2Component.cpp
+++ b/media/codec2/components/base/SimpleC2Component.cpp
@@ -21,8 +21,10 @@
 #include <android/hardware_buffer.h>
 #include <cutils/properties.h>
 #include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AUtils.h>
 
 #include <inttypes.h>
+#include <libyuv.h>
 
 #include <C2Config.h>
 #include <C2Debug.h>
@@ -32,6 +34,15 @@
 #include <SimpleC2Component.h>
 
 namespace android {
+
+// libyuv version required for I410ToAB30Matrix and I210ToAB30Matrix.
+#if LIBYUV_VERSION >= 1780
+#include <algorithm>
+#define HAVE_LIBYUV_I410_I210_TO_AB30 1
+#else
+#define HAVE_LIBYUV_I410_I210_TO_AB30 0
+#endif
+
 constexpr uint8_t kNeutralUVBitDepth8 = 128;
 constexpr uint16_t kNeutralUVBitDepth10 = 512;
 
@@ -506,6 +517,120 @@
     }
 }
 
+void convertPlanar16ToY410OrRGBA1010102(uint8_t* dst, const uint16_t* srcY, const uint16_t* srcU,
+                                        const uint16_t* srcV, size_t srcYStride, size_t srcUStride,
+                                        size_t srcVStride, size_t dstStride, size_t width,
+                                        size_t height,
+                                        std::shared_ptr<const C2ColorAspectsStruct> aspects,
+                                        CONV_FORMAT_T format) {
+    bool processed = false;
+#if HAVE_LIBYUV_I410_I210_TO_AB30
+    if (format == CONV_FORMAT_I444) {
+        libyuv::I410ToAB30Matrix(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, dst,
+                                 dstStride, &libyuv::kYuvV2020Constants, width, height);
+        processed = true;
+    } else if (format == CONV_FORMAT_I422) {
+        libyuv::I210ToAB30Matrix(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, dst,
+                                 dstStride, &libyuv::kYuvV2020Constants, width, height);
+        processed = true;
+    }
+#endif  // HAVE_LIBYUV_I410_I210_TO_AB30
+    if (!processed) {
+        convertYUV420Planar16ToY410OrRGBA1010102(
+                (uint32_t*)dst, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride,
+                dstStride / sizeof(uint32_t), width, height,
+                std::static_pointer_cast<const C2ColorAspectsStruct>(aspects));
+    }
+}
+
+void convertPlanar16ToP010(uint16_t* dstY, uint16_t* dstUV, const uint16_t* srcY,
+                           const uint16_t* srcU, const uint16_t* srcV, size_t srcYStride,
+                           size_t srcUStride, size_t srcVStride, size_t dstYStride,
+                           size_t dstUStride, size_t dstVStride, size_t width, size_t height,
+                           bool isMonochrome, CONV_FORMAT_T format, uint16_t* tmpFrameBuffer,
+                           size_t tmpFrameBufferSize) {
+#if LIBYUV_VERSION >= 1779
+    if ((format == CONV_FORMAT_I444) || (format == CONV_FORMAT_I422)) {
+        // TODO(https://crbug.com/libyuv/952): replace this block with libyuv::I410ToP010
+        // and libyuv::I210ToP010 when they are available. Note it may be safe to alias dstY
+        // in I010ToP010, but the libyuv API doesn't make any guarantees.
+        const size_t tmpSize = dstYStride * height + dstUStride * align(height, 2);
+        CHECK(tmpSize <= tmpFrameBufferSize);
+
+        uint16_t* const tmpY = tmpFrameBuffer;
+        uint16_t* const tmpU = tmpY + dstYStride * height;
+        uint16_t* const tmpV = tmpU + dstUStride * align(height, 2) / 2;
+        if (format == CONV_FORMAT_I444) {
+            libyuv::I410ToI010(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, tmpY,
+                               dstYStride, tmpU, dstUStride, tmpV, dstUStride, width, height);
+        } else {
+            libyuv::I210ToI010(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, tmpY,
+                               dstYStride, tmpU, dstUStride, tmpV, dstUStride, width, height);
+        }
+        libyuv::I010ToP010(tmpY, dstYStride, tmpU, dstUStride, tmpV, dstVStride, dstY, dstYStride,
+                           dstUV, dstUStride, width, height);
+    } else {
+        convertYUV420Planar16ToP010(dstY, dstUV, srcY, srcU, srcV, srcYStride, srcUStride,
+                                    srcVStride, dstYStride, dstUStride, width, height,
+                                    isMonochrome);
+    }
+#else   // LIBYUV_VERSION < 1779
+    convertYUV420Planar16ToP010(dstY, dstUV, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride,
+                                dstYStride, dstUStride, width, height, isMonochrome);
+#endif  // LIBYUV_VERSION >= 1779
+}
+
+void convertPlanar16ToYV12(uint8_t* dstY, uint8_t* dstU, uint8_t* dstV, const uint16_t* srcY,
+                           const uint16_t* srcU, const uint16_t* srcV, size_t srcYStride,
+                           size_t srcUStride, size_t srcVStride, size_t dstYStride,
+                           size_t dstUStride, size_t dstVStride, size_t width, size_t height,
+                           bool isMonochrome, CONV_FORMAT_T format, uint16_t* tmpFrameBuffer,
+                           size_t tmpFrameBufferSize) {
+#if LIBYUV_VERSION >= 1779
+    if (format == CONV_FORMAT_I444) {
+        // TODO(https://crbug.com/libyuv/950): replace this block with libyuv::I410ToI420
+        // when it's available.
+        const size_t tmpSize = dstYStride * height + dstUStride * align(height, 2);
+        CHECK(tmpSize <= tmpFrameBufferSize);
+
+        uint16_t* const tmpY = tmpFrameBuffer;
+        uint16_t* const tmpU = tmpY + dstYStride * height;
+        uint16_t* const tmpV = tmpU + dstUStride * align(height, 2) / 2;
+        libyuv::I410ToI010(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, tmpY, dstYStride,
+                           tmpU, dstUStride, tmpV, dstVStride, width, height);
+        libyuv::I010ToI420(tmpY, dstYStride, tmpU, dstUStride, tmpV, dstUStride, dstY, dstYStride,
+                           dstU, dstUStride, dstV, dstVStride, width, height);
+    } else if (format == CONV_FORMAT_I422) {
+        libyuv::I210ToI420(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, dstY, dstYStride,
+                           dstU, dstUStride, dstV, dstVStride, width, height);
+    } else {
+        convertYUV420Planar16ToYV12(dstY, dstU, dstV, srcY, srcU, srcV, srcYStride, srcUStride,
+                                    srcVStride, dstYStride, dstUStride, width, height,
+                                    isMonochrome);
+    }
+#else   // LIBYUV_VERSION < 1779
+    convertYUV420Planar16ToYV12(dstY, dstU, dstV, srcY, srcU, srcV, srcYStride, srcUStride,
+                                srcVStride, dstYStride, dstUStride, width, height, isMonochrome);
+#endif  // LIBYUV_VERSION >= 1779
+}
+
+void convertPlanar8ToYV12(uint8_t* dstY, uint8_t* dstU, uint8_t* dstV, const uint8_t* srcY,
+                          const uint8_t* srcU, const uint8_t* srcV, size_t srcYStride,
+                          size_t srcUStride, size_t srcVStride, size_t dstYStride,
+                          size_t dstUStride, size_t dstVStride, uint32_t width, uint32_t height,
+                          bool isMonochrome, CONV_FORMAT_T format) {
+    if (format == CONV_FORMAT_I444) {
+        libyuv::I444ToI420(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, dstY, dstYStride,
+                           dstU, dstUStride, dstV, dstVStride, width, height);
+    } else if (format == CONV_FORMAT_I422) {
+        libyuv::I422ToI420(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, dstY, dstYStride,
+                           dstU, dstUStride, dstV, dstVStride, width, height);
+    } else {
+        convertYUV420Planar8ToYV12(dstY, dstU, dstV, srcY, srcU, srcV, srcYStride, srcUStride,
+                                   srcVStride, dstYStride, dstUStride, dstVStride, width, height,
+                                   isMonochrome);
+    }
+}
 std::unique_ptr<C2Work> SimpleC2Component::WorkQueue::pop_front() {
     std::unique_ptr<C2Work> work = std::move(mQueue.front().work);
     mQueue.pop_front();
diff --git a/media/codec2/components/base/include/SimpleC2Component.h b/media/codec2/components/base/include/SimpleC2Component.h
index bc27474..b28c47e 100644
--- a/media/codec2/components/base/include/SimpleC2Component.h
+++ b/media/codec2/components/base/include/SimpleC2Component.h
@@ -31,6 +31,12 @@
 
 namespace android {
 
+typedef enum {
+    CONV_FORMAT_I420,
+    CONV_FORMAT_I422,
+    CONV_FORMAT_I444,
+} CONV_FORMAT_T;
+
 void convertYUV420Planar8ToYV12(uint8_t *dstY, uint8_t *dstU, uint8_t *dstV, const uint8_t *srcY,
                                 const uint8_t *srcU, const uint8_t *srcV, size_t srcYStride,
                                 size_t srcUStride, size_t srcVStride, size_t dstYStride,
@@ -66,6 +72,30 @@
                                         const uint32_t* srcRGBA, size_t srcRGBStride, size_t width,
                                         size_t height, C2Color::matrix_t colorMatrix,
                                         C2Color::range_t colorRange);
+void convertPlanar16ToY410OrRGBA1010102(uint8_t* dst, const uint16_t* srcY, const uint16_t* srcU,
+                                        const uint16_t* srcV, size_t srcYStride, size_t srcUStride,
+                                        size_t srcVStride, size_t dstStride, size_t width,
+                                        size_t height,
+                                        std::shared_ptr<const C2ColorAspectsStruct> aspects,
+                                        CONV_FORMAT_T format);
+
+void convertPlanar16ToP010(uint16_t* dstY, uint16_t* dstUV, const uint16_t* srcY,
+                           const uint16_t* srcU, const uint16_t* srcV, size_t srcYStride,
+                           size_t srcUStride, size_t srcVStride, size_t dstYStride,
+                           size_t dstUStride, size_t dstVStride, size_t width, size_t height,
+                           bool isMonochrome, CONV_FORMAT_T format, uint16_t* tmpFrameBuffer,
+                           size_t tmpFrameBufferSize);
+void convertPlanar16ToYV12(uint8_t* dstY, uint8_t* dstU, uint8_t* dstV, const uint16_t* srcY,
+                           const uint16_t* srcU, const uint16_t* srcV, size_t srcYStride,
+                           size_t srcUStride, size_t srcVStride, size_t dstYStride,
+                           size_t dstUStride, size_t dstVStride, size_t width, size_t height,
+                           bool isMonochrome, CONV_FORMAT_T format, uint16_t* tmpFrameBuffer,
+                           size_t tmpFrameBufferSize);
+void convertPlanar8ToYV12(uint8_t* dstY, uint8_t* dstU, uint8_t* dstV, const uint8_t* srcY,
+                          const uint8_t* srcU, const uint8_t* srcV, size_t srcYStride,
+                          size_t srcUStride, size_t srcVStride, size_t dstYStride,
+                          size_t dstUStride, size_t dstVStride, uint32_t width, uint32_t height,
+                          bool isMonochrome, CONV_FORMAT_T format);
 
 class SimpleC2Component
         : public C2Component, public std::enable_shared_from_this<SimpleC2Component> {
diff --git a/media/codec2/components/dav1d/Android.bp b/media/codec2/components/dav1d/Android.bp
index f7850ad..c9387dd 100644
--- a/media/codec2/components/dav1d/Android.bp
+++ b/media/codec2/components/dav1d/Android.bp
@@ -15,6 +15,7 @@
     defaults: [
         "libcodec2_soft-defaults",
         "libcodec2_soft_sanitize_all-defaults",
+        "libcodec2_soft_sanitize_cfi-defaults",
     ],
 
     cflags: [
@@ -22,9 +23,8 @@
         "-Wno-unused-variable",
     ],
 
-    srcs: ["C2SoftDav1dDec.cpp"],
+    srcs: ["C2SoftDav1dDec.cpp", "C2SoftDav1dDump.cpp"],
     static_libs: [
-        "libyuv_static",
         "libdav1d_8bit",
         "libdav1d_16bit",
     ],
diff --git a/media/codec2/components/dav1d/C2SoftDav1dDec.cpp b/media/codec2/components/dav1d/C2SoftDav1dDec.cpp
index b0cef41..3f96cb3 100644
--- a/media/codec2/components/dav1d/C2SoftDav1dDec.cpp
+++ b/media/codec2/components/dav1d/C2SoftDav1dDec.cpp
@@ -26,30 +26,13 @@
 #include <Codec2CommonUtils.h>
 #include <Codec2Mapper.h>
 #include <SimpleC2Interface.h>
-#include <libyuv.h>
 #include <log/log.h>
 #include <media/stagefright/foundation/AUtils.h>
 #include <media/stagefright/foundation/MediaDefs.h>
 #include "C2SoftDav1dDec.h"
 
-// libyuv version required for I410ToAB30Matrix and I210ToAB30Matrix.
-#if LIBYUV_VERSION >= 1780
-#include <algorithm>
-#define HAVE_LIBYUV_I410_I210_TO_AB30 1
-#else
-#define HAVE_LIBYUV_I410_I210_TO_AB30 0
-#endif
-
 namespace android {
 
-// Flag to enable dumping the bitsteram and the decoded pictures to files.
-static const bool ENABLE_DUMPING_FILES_DEFAULT = false;
-static const char ENABLE_DUMPING_FILES_PROPERTY[] = "debug.dav1d.enabledumping";
-
-// The number of frames to dump to a file
-static const int NUM_FRAMES_TO_DUMP_DEFAULT = INT_MAX;
-static const char NUM_FRAMES_TO_DUMP_PROPERTY[] = "debug.dav1d.numframestodump";
-
 // The number of threads used for the dav1d decoder.
 static const int NUM_THREADS_DAV1D_DEFAULT = 0;
 static const char NUM_THREADS_DAV1D_PROPERTY[] = "debug.dav1d.numthreads";
@@ -522,37 +505,8 @@
 }
 
 bool C2SoftDav1dDec::initDecoder() {
-    nsecs_t now = systemTime();
 #ifdef FILE_DUMP_ENABLE
-    snprintf(mInDataFileName, 256, "%s_%" PRId64 "d.%s", DUMP_FILE_PATH, now, INPUT_DATA_DUMP_EXT);
-    snprintf(mInSizeFileName, 256, "%s_%" PRId64 "d.%s", DUMP_FILE_PATH, now, INPUT_SIZE_DUMP_EXT);
-    snprintf(mDav1dOutYuvFileName, 256, "%s_%" PRId64 "dx.%s", DUMP_FILE_PATH, now,
-             OUTPUT_YUV_DUMP_EXT);
-
-    bool enableDumping = android::base::GetBoolProperty(ENABLE_DUMPING_FILES_PROPERTY,
-                                                        ENABLE_DUMPING_FILES_DEFAULT);
-
-    num_frames_to_dump =
-            android::base::GetIntProperty(NUM_FRAMES_TO_DUMP_PROPERTY, NUM_FRAMES_TO_DUMP_DEFAULT);
-
-    if (enableDumping) {
-        ALOGD("enableDumping = %d, num_frames_to_dump = %d", enableDumping, num_frames_to_dump);
-
-        mInDataFile = fopen(mInDataFileName, "wb");
-        if (mInDataFile == nullptr) {
-            ALOGD("Could not open file %s", mInDataFileName);
-        }
-
-        mInSizeFile = fopen(mInSizeFileName, "wb");
-        if (mInSizeFile == nullptr) {
-            ALOGD("Could not open file %s", mInSizeFileName);
-        }
-
-        mDav1dOutYuvFile = fopen(mDav1dOutYuvFileName, "wb");
-        if (mDav1dOutYuvFile == nullptr) {
-            ALOGD("Could not open file %s", mDav1dOutYuvFileName);
-        }
-    }
+    mC2SoftDav1dDump.initDumping();
 #endif
     mSignalledError = false;
     mSignalledOutputEos = false;
@@ -601,20 +555,7 @@
         mInputBufferIndex = 0;
     }
 #ifdef FILE_DUMP_ENABLE
-    if (mInDataFile != nullptr) {
-        fclose(mInDataFile);
-        mInDataFile = nullptr;
-    }
-
-    if (mInSizeFile != nullptr) {
-        fclose(mInSizeFile);
-        mInSizeFile = nullptr;
-    }
-
-    if (mDav1dOutYuvFile != nullptr) {
-        fclose(mDav1dOutYuvFile);
-        mDav1dOutYuvFile = nullptr;
-    }
+    mC2SoftDav1dDump.destroyDumping();
 #endif
 }
 
@@ -657,6 +598,10 @@
     }
 }
 
+static void freeCallback(const uint8_t */*data*/, void */*cookie*/) {
+    return;
+}
+
 void C2SoftDav1dDec::process(const std::unique_ptr<C2Work>& work,
                              const std::shared_ptr<C2BlockPool>& pool) {
     work->result = C2_OK;
@@ -709,65 +654,24 @@
                       seq.max_height, (long)in_frameIndex);
             }
 
-            // insert OBU TD if it is not present.
-            // TODO: b/286852962
-            uint8_t obu_type = (bitstream[0] >> 3) & 0xf;
             Dav1dData data;
 
-            uint8_t* ptr = (obu_type == DAV1D_OBU_TD) ? dav1d_data_create(&data, inSize)
-                                                      : dav1d_data_create(&data, inSize + 2);
-            if (ptr == nullptr) {
-                ALOGE("dav1d_data_create failed!");
+            res = dav1d_data_wrap(&data, bitstream, inSize, freeCallback, nullptr);
+            if (res != 0) {
+                ALOGE("Decoder wrap error %s!", strerror(DAV1D_ERR(res)));
                 i_ret = -1;
-
             } else {
                 data.m.timestamp = in_frameIndex;
+                // ALOGV("inSize=%ld, in_frameIndex=%ld, timestamp=%ld",
+                //       inSize, frameIndex, data.m.timestamp);
 
-                int new_Size;
-                if (obu_type != DAV1D_OBU_TD) {
-                    new_Size = (int)(inSize + 2);
-
-                    // OBU TD
-                    ptr[0] = 0x12;
-                    ptr[1] = 0;
-
-                    memcpy(ptr + 2, bitstream, inSize);
-                } else {
-                    new_Size = (int)(inSize);
-                    // TODO: b/277797541 - investigate how to wrap this pointer in Dav1dData to
-                    // avoid memcopy operations.
-                    memcpy(ptr, bitstream, new_Size);
-                }
-
-                // ALOGV("memcpy(ptr,bitstream,inSize=%ld,new_Size=%d,in_frameIndex=%ld,timestamp=%ld,"
-                //       "ptr[0,1,2,3,4]=%x,%x,%x,%x,%x)",
-                //       inSize, new_Size, frameIndex, data.m.timestamp, ptr[0], ptr[1], ptr[2],
-                //       ptr[3], ptr[4]);
 
                 // Dump the bitstream data (inputBuffer) if dumping is enabled.
 #ifdef FILE_DUMP_ENABLE
-                if (mInDataFile) {
-                    int ret = fwrite(ptr, 1, new_Size, mInDataFile);
-
-                    if (ret != new_Size) {
-                        ALOGE("Error in fwrite %s, requested %d, returned %d", mInDataFileName,
-                              new_Size, ret);
-                    }
-                }
-
-                // Dump the size per inputBuffer if dumping is enabled.
-                if (mInSizeFile) {
-                    int ret = fwrite(&new_Size, 1, 4, mInSizeFile);
-
-                    if (ret != 4) {
-                        ALOGE("Error in fwrite %s, requested %d, returned %d", mInSizeFileName, 4,
-                              ret);
-                    }
-                }
+                mC2SoftDav1dDump.dumpInput(ptr, new_Size);
 #endif
 
                 bool b_draining = false;
-                int res;
 
                 do {
                     res = dav1d_send_data(mDav1dCtx, &data);
@@ -1010,49 +914,6 @@
     return true;
 }
 
-#ifdef FILE_DUMP_ENABLE
-void C2SoftDav1dDec::writeDav1dOutYuvFile(const Dav1dPicture& p) {
-    if (mDav1dOutYuvFile != NULL) {
-        uint8_t* ptr;
-        const int hbd = p.p.bpc > 8;
-
-        ptr = (uint8_t*)p.data[0];
-        for (int y = 0; y < p.p.h; y++) {
-            int iSize = p.p.w << hbd;
-            int ret = fwrite(ptr, 1, iSize, mDav1dOutYuvFile);
-            if (ret != iSize) {
-                ALOGE("Error in fwrite %s, requested %d, returned %d", mDav1dOutYuvFileName, iSize,
-                      ret);
-                break;
-            }
-
-            ptr += p.stride[0];
-        }
-
-        if (p.p.layout != DAV1D_PIXEL_LAYOUT_I400) {
-            // u/v
-            const int ss_ver = p.p.layout == DAV1D_PIXEL_LAYOUT_I420;
-            const int ss_hor = p.p.layout != DAV1D_PIXEL_LAYOUT_I444;
-            const int cw = (p.p.w + ss_hor) >> ss_hor;
-            const int ch = (p.p.h + ss_ver) >> ss_ver;
-            for (int pl = 1; pl <= 2; pl++) {
-                ptr = (uint8_t*)p.data[pl];
-                for (int y = 0; y < ch; y++) {
-                    int iSize = cw << hbd;
-                    int ret = fwrite(ptr, 1, cw << hbd, mDav1dOutYuvFile);
-                    if (ret != iSize) {
-                        ALOGE("Error in fwrite %s, requested %d, returned %d", mDav1dOutYuvFileName,
-                              iSize, ret);
-                        break;
-                    }
-                    ptr += p.stride[1];
-                }
-            }
-        }
-    }
-}
-#endif
-
 bool C2SoftDav1dDec::outputBuffer(const std::shared_ptr<C2BlockPool>& pool,
                                   const std::unique_ptr<C2Work>& work) {
     if (!(work && pool)) return false;
@@ -1114,16 +975,6 @@
     // out_frameIndex that the decoded picture returns from dav1d.
     int64_t out_frameIndex = img.m.timestamp;
 
-#if LIBYUV_VERSION < 1779
-    if (!(img.p.layout != DAV1D_PIXEL_LAYOUT_I400 || img.p.layout != DAV1D_PIXEL_LAYOUT_I420)) {
-        ALOGE("image_format %d not supported", img.p.layout);
-        mSignalledError = true;
-        work->workletsProcessed = 1u;
-        work->result = C2_CORRUPTED;
-        return false;
-    }
-#endif
-
     const bool isMonochrome = img.p.layout == DAV1D_PIXEL_LAYOUT_I400;
 
     int bitdepth = img.p.bpc;
@@ -1141,17 +992,6 @@
             allowRGBA1010102 = true;
         }
         format = getHalPixelFormatForBitDepth10(allowRGBA1010102);
-#if !HAVE_LIBYUV_I410_I210_TO_AB30
-        if ((format == HAL_PIXEL_FORMAT_RGBA_1010102) &&
-            (is_img_ready ? img.p.layout == DAV1D_PIXEL_LAYOUT_I420
-                          : buffer->image_format != libgav1::kImageFormatYuv420)) {
-            ALOGE("Only YUV420 output is supported when targeting RGBA_1010102");
-            mSignalledError = true;
-            work->result = C2_OMITTED;
-            work->workletsProcessed = 1u;
-            return false;
-        }
-#endif
     }
 
     if (mHalPixelFormat != format) {
@@ -1206,6 +1046,19 @@
     size_t dstUStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc;
     size_t dstVStride = layout.planes[C2PlanarLayout::PLANE_V].rowInc;
 
+    CONV_FORMAT_T convFormat;
+    switch (img.p.layout) {
+        case DAV1D_PIXEL_LAYOUT_I444:
+            convFormat = CONV_FORMAT_I444;
+            break;
+        case DAV1D_PIXEL_LAYOUT_I422:
+            convFormat = CONV_FORMAT_I422;
+            break;
+        default:
+            convFormat = CONV_FORMAT_I420;
+            break;
+    }
+
     if (bitdepth == 10) {
         // TODO: b/277797541 - Investigate if we can ask DAV1D to output the required format during
         // decompression to avoid color conversion.
@@ -1217,146 +1070,67 @@
         size_t srcVStride = img.stride[1] / 2;
 
         if (format == HAL_PIXEL_FORMAT_RGBA_1010102) {
-            bool processed = false;
-#if HAVE_LIBYUV_I410_I210_TO_AB30
-            if (img.p.layout == DAV1D_PIXEL_LAYOUT_I444) {
-                libyuv::I410ToAB30Matrix(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, dstY,
-                                         dstYStride, &libyuv::kYuvV2020Constants, mWidth, mHeight);
-                processed = true;
-            } else if (img.p.layout == DAV1D_PIXEL_LAYOUT_I422) {
-                libyuv::I210ToAB30Matrix(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, dstY,
-                                         dstYStride, &libyuv::kYuvV2020Constants, mWidth, mHeight);
-                processed = true;
-            }
-#endif  // HAVE_LIBYUV_I410_I210_TO_AB30
-            if (!processed) {
-                if (isMonochrome) {
-                    const size_t tmpSize = mWidth;
-                    const bool needFill = tmpSize > mTmpFrameBufferSize;
-                    if (!allocTmpFrameBuffer(tmpSize)) {
-                        ALOGE("Error allocating temp conversion buffer (%zu bytes)", tmpSize);
-                        setError(work, C2_NO_MEMORY);
-                        return false;
-                    }
-                    srcU = srcV = mTmpFrameBuffer.get();
-                    srcUStride = srcVStride = 0;
-                    if (needFill) {
-                        std::fill_n(mTmpFrameBuffer.get(), tmpSize, 512);
-                    }
+            if (isMonochrome) {
+                const size_t tmpSize = mWidth;
+                const bool needFill = tmpSize > mTmpFrameBufferSize;
+                if (!allocTmpFrameBuffer(tmpSize)) {
+                    ALOGE("Error allocating temp conversion buffer (%zu bytes)", tmpSize);
+                    setError(work, C2_NO_MEMORY);
+                    return false;
                 }
-                convertYUV420Planar16ToY410OrRGBA1010102(
-                        (uint32_t*)dstY, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride,
-                        dstYStride / sizeof(uint32_t), mWidth, mHeight,
-                        std::static_pointer_cast<const C2ColorAspectsStruct>(codedColorAspects));
+                srcU = srcV = mTmpFrameBuffer.get();
+                srcUStride = srcVStride = 0;
+                if (needFill) {
+                    std::fill_n(mTmpFrameBuffer.get(), tmpSize, 512);
+                }
             }
+            convertPlanar16ToY410OrRGBA1010102(
+                    dstY, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride,
+                    dstYStride, mWidth, mHeight,
+                    std::static_pointer_cast<const C2ColorAspectsStruct>(codedColorAspects),
+                    convFormat);
         } else if (format == HAL_PIXEL_FORMAT_YCBCR_P010) {
             dstYStride /= 2;
             dstUStride /= 2;
             dstVStride /= 2;
-#if LIBYUV_VERSION >= 1779
+            size_t tmpSize = 0;
             if ((img.p.layout == DAV1D_PIXEL_LAYOUT_I444) ||
                 (img.p.layout == DAV1D_PIXEL_LAYOUT_I422)) {
-                // TODO(https://crbug.com/libyuv/952): replace this block with libyuv::I410ToP010
-                // and libyuv::I210ToP010 when they are available. Note it may be safe to alias dstY
-                // in I010ToP010, but the libyuv API doesn't make any guarantees.
-                const size_t tmpSize = dstYStride * mHeight + dstUStride * align(mHeight, 2);
+                tmpSize = dstYStride * mHeight + dstUStride * align(mHeight, 2);
                 if (!allocTmpFrameBuffer(tmpSize)) {
                     ALOGE("Error allocating temp conversion buffer (%zu bytes)", tmpSize);
                     setError(work, C2_NO_MEMORY);
                     return false;
                 }
-                uint16_t* const tmpY = mTmpFrameBuffer.get();
-                uint16_t* const tmpU = tmpY + dstYStride * mHeight;
-                uint16_t* const tmpV = tmpU + dstUStride * align(mHeight, 2) / 2;
-                if (img.p.layout == DAV1D_PIXEL_LAYOUT_I444) {
-                    libyuv::I410ToI010(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, tmpY,
-                                       dstYStride, tmpU, dstUStride, tmpV, dstUStride, mWidth,
-                                       mHeight);
-                } else {
-                    libyuv::I210ToI010(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, tmpY,
-                                       dstYStride, tmpU, dstUStride, tmpV, dstUStride, mWidth,
-                                       mHeight);
-                }
-                libyuv::I010ToP010(tmpY, dstYStride, tmpU, dstUStride, tmpV, dstVStride,
-                                   (uint16_t*)dstY, dstYStride, (uint16_t*)dstU, dstUStride, mWidth,
-                                   mHeight);
-            } else {
-                convertYUV420Planar16ToP010((uint16_t*)dstY, (uint16_t*)dstU, srcY, srcU, srcV,
-                                            srcYStride, srcUStride, srcVStride, dstYStride,
-                                            dstUStride, mWidth, mHeight, isMonochrome);
             }
-#else   // LIBYUV_VERSION < 1779
-            convertYUV420Planar16ToP010((uint16_t*)dstY, (uint16_t*)dstU, srcY, srcU, srcV,
-                                        srcYStride, srcUStride, srcVStride, dstYStride, dstUStride,
-                                        mWidth, mHeight, isMonochrome);
-#endif  // LIBYUV_VERSION >= 1779
+            convertPlanar16ToP010((uint16_t*)dstY, (uint16_t*)dstU, srcY, srcU, srcV, srcYStride,
+                                  srcUStride, srcVStride, dstYStride, dstUStride, dstVStride,
+                                  mWidth, mHeight, isMonochrome, convFormat, mTmpFrameBuffer.get(),
+                                  tmpSize);
         } else {
-#if LIBYUV_VERSION >= 1779
+            size_t tmpSize = 0;
             if (img.p.layout == DAV1D_PIXEL_LAYOUT_I444) {
-                // TODO(https://crbug.com/libyuv/950): replace this block with libyuv::I410ToI420
-                // when it's available.
-                const size_t tmpSize = dstYStride * mHeight + dstUStride * align(mHeight, 2);
+                tmpSize = dstYStride * mHeight + dstUStride * align(mHeight, 2);
                 if (!allocTmpFrameBuffer(tmpSize)) {
                     ALOGE("Error allocating temp conversion buffer (%zu bytes)", tmpSize);
                     setError(work, C2_NO_MEMORY);
                     return false;
                 }
-                uint16_t* const tmpY = mTmpFrameBuffer.get();
-                uint16_t* const tmpU = tmpY + dstYStride * mHeight;
-                uint16_t* const tmpV = tmpU + dstUStride * align(mHeight, 2) / 2;
-                libyuv::I410ToI010(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, tmpY,
-                                   dstYStride, tmpU, dstUStride, tmpV, dstVStride, mWidth, mHeight);
-                libyuv::I010ToI420(tmpY, dstYStride, tmpU, dstUStride, tmpV, dstUStride, dstY,
-                                   dstYStride, dstU, dstUStride, dstV, dstVStride, mWidth, mHeight);
-            } else if (img.p.layout == DAV1D_PIXEL_LAYOUT_I422) {
-                libyuv::I210ToI420(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, dstY,
-                                   dstYStride, dstU, dstUStride, dstV, dstVStride, mWidth, mHeight);
-            } else {
-                convertYUV420Planar16ToYV12(dstY, dstU, dstV, srcY, srcU, srcV, srcYStride,
-                                            srcUStride, srcVStride, dstYStride, dstUStride, mWidth,
-                                            mHeight, isMonochrome);
             }
-#else   // LIBYUV_VERSION < 1779
-            convertYUV420Planar16ToYV12(dstY, dstU, dstV, srcY, srcU, srcV, srcYStride, srcUStride,
-                                        srcVStride, dstYStride, dstUStride, mWidth, mHeight,
-                                        isMonochrome);
-#endif  // LIBYUV_VERSION >= 1779
+            convertPlanar16ToYV12(dstY, dstU, dstV, srcY, srcU, srcV, srcYStride, srcUStride,
+                                  srcVStride, dstYStride, dstUStride, dstVStride, mWidth, mHeight,
+                                  isMonochrome, convFormat, mTmpFrameBuffer.get(), tmpSize);
         }
 
-        // Dump the output buffer if dumping is enabled (debug only).
-#ifdef FILE_DUMP_ENABLE
-        FILE* fp_out = mDav1dOutYuvFile;
-
         // if(mOutputBufferIndex % 100 == 0)
         ALOGV("output a 10bit picture %dx%d from dav1d "
               "(mInputBufferIndex=%d,mOutputBufferIndex=%d,format=%d).",
               mWidth, mHeight, mInputBufferIndex, mOutputBufferIndex, format);
 
-        if (fp_out && mOutputBufferIndex <= num_frames_to_dump) {
-            for (int i = 0; i < mHeight; i++) {
-                int ret = fwrite((uint8_t*)srcY + i * srcYStride * 2, 1, mWidth * 2, fp_out);
-                if (ret != mWidth * 2) {
-                    ALOGE("Error in fwrite, requested %d, returned %d", mWidth * 2, ret);
-                    break;
-                }
-            }
-
-            for (int i = 0; i < mHeight / 2; i++) {
-                int ret = fwrite((uint8_t*)srcU + i * srcUStride * 2, 1, mWidth, fp_out);
-                if (ret != mWidth) {
-                    ALOGE("Error in fwrite, requested %d, returned %d", mWidth, ret);
-                    break;
-                }
-            }
-
-            for (int i = 0; i < mHeight / 2; i++) {
-                int ret = fwrite((uint8_t*)srcV + i * srcVStride * 2, 1, mWidth, fp_out);
-                if (ret != mWidth) {
-                    ALOGE("Error in fwrite, requested %d, returned %d", mWidth, ret);
-                    break;
-                }
-            }
-        }
+        // Dump the output buffer if dumping is enabled (debug only).
+#ifdef FILE_DUMP_ENABLE
+        mC2SoftDav1dDump.dumpOutput<uint16_t>(srcY, srcU, srcV, srcYStride, srcUStride, srcVStride,
+                                              mWidth, mHeight);
 #endif
     } else {
         const uint8_t* srcY = (const uint8_t*)img.data[0];
@@ -1367,51 +1141,19 @@
         size_t srcUStride = img.stride[1];
         size_t srcVStride = img.stride[1];
 
-        // Dump the output buffer is dumping is enabled (debug only)
-#ifdef FILE_DUMP_ENABLE
-        FILE* fp_out = mDav1dOutYuvFile;
         // if(mOutputBufferIndex % 100 == 0)
         ALOGV("output a 8bit picture %dx%d from dav1d "
               "(mInputBufferIndex=%d,mOutputBufferIndex=%d,format=%d).",
               mWidth, mHeight, mInputBufferIndex, mOutputBufferIndex, format);
 
-        if (fp_out && mOutputBufferIndex <= num_frames_to_dump) {
-            for (int i = 0; i < mHeight; i++) {
-                int ret = fwrite((uint8_t*)srcY + i * srcYStride, 1, mWidth, fp_out);
-                if (ret != mWidth) {
-                    ALOGE("Error in fwrite, requested %d, returned %d", mWidth, ret);
-                    break;
-                }
-            }
-
-            for (int i = 0; i < mHeight / 2; i++) {
-                int ret = fwrite((uint8_t*)srcU + i * srcUStride, 1, mWidth / 2, fp_out);
-                if (ret != mWidth / 2) {
-                    ALOGE("Error in fwrite, requested %d, returned %d", mWidth / 2, ret);
-                    break;
-                }
-            }
-
-            for (int i = 0; i < mHeight / 2; i++) {
-                int ret = fwrite((uint8_t*)srcV + i * srcVStride, 1, mWidth / 2, fp_out);
-                if (ret != mWidth / 2) {
-                    ALOGE("Error in fwrite, requested %d, returned %d", mWidth / 2, ret);
-                    break;
-                }
-            }
-        }
+        // Dump the output buffer is dumping is enabled (debug only)
+#ifdef FILE_DUMP_ENABLE
+        mC2SoftDav1dDump.dumpOutput<uint8_t>(srcY, srcU, srcV, srcYStride, srcUStride, srcVStride,
+                                             mWidth, mHeight);
 #endif
-        if (img.p.layout == DAV1D_PIXEL_LAYOUT_I444) {
-            libyuv::I444ToI420(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, dstY,
-                               dstYStride, dstU, dstUStride, dstV, dstVStride, mWidth, mHeight);
-        } else if (img.p.layout == DAV1D_PIXEL_LAYOUT_I422) {
-            libyuv::I422ToI420(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, dstY,
-                               dstYStride, dstU, dstUStride, dstV, dstVStride, mWidth, mHeight);
-        } else {
-            convertYUV420Planar8ToYV12(dstY, dstU, dstV, srcY, srcU, srcV, srcYStride, srcUStride,
-                                       srcVStride, dstYStride, dstUStride, dstVStride, mWidth,
-                                       mHeight, isMonochrome);
-        }
+        convertPlanar8ToYV12(dstY, dstU, dstV, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride,
+                             dstYStride, dstUStride, dstVStride, mWidth, mHeight, isMonochrome,
+                             convFormat);
     }
 
     dav1d_picture_unref(&img);
diff --git a/media/codec2/components/dav1d/C2SoftDav1dDec.h b/media/codec2/components/dav1d/C2SoftDav1dDec.h
index 5201456..e3d2a93 100644
--- a/media/codec2/components/dav1d/C2SoftDav1dDec.h
+++ b/media/codec2/components/dav1d/C2SoftDav1dDec.h
@@ -28,12 +28,9 @@
 
 #include <dav1d/dav1d.h>
 #include <deque>
+#include <C2SoftDav1dDump.h>
 
 //#define FILE_DUMP_ENABLE 1
-#define DUMP_FILE_PATH "/data/local/tmp/dump"
-#define INPUT_DATA_DUMP_EXT "av1"
-#define INPUT_SIZE_DUMP_EXT "size"
-#define OUTPUT_YUV_DUMP_EXT "yuv"
 
 namespace android {
 
@@ -122,17 +119,7 @@
     void flushDav1d();
 
 #ifdef FILE_DUMP_ENABLE
-    char mInDataFileName[256];
-    char mInSizeFileName[256];
-    char mDav1dOutYuvFileName[256];
-
-    FILE* mInDataFile = nullptr;
-    FILE* mInSizeFile = nullptr;
-    FILE* mDav1dOutYuvFile = nullptr;
-
-    void writeDav1dOutYuvFile(const Dav1dPicture& p);
-
-    int num_frames_to_dump = 0;
+    C2SoftDav1dDump mC2SoftDav1dDump;
 #endif
 
     C2_DO_NOT_COPY(C2SoftDav1dDec);
diff --git a/media/codec2/components/dav1d/C2SoftDav1dDump.cpp b/media/codec2/components/dav1d/C2SoftDav1dDump.cpp
new file mode 100644
index 0000000..ec8d6cd
--- /dev/null
+++ b/media/codec2/components/dav1d/C2SoftDav1dDump.cpp
@@ -0,0 +1,191 @@
+/*
+ * 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 "C2SoftDav1dDump"
+#include "C2SoftDav1dDump.h"
+
+namespace android {
+
+// Flag to enable dumping the bitsteram and the decoded pictures to files.
+static const bool ENABLE_DUMPING_FILES_DEFAULT = true;
+static const char ENABLE_DUMPING_FILES_PROPERTY[] = "debug.dav1d.enabledumping";
+
+// The number of frames to dump to a file
+static const int NUM_FRAMES_TO_DUMP_DEFAULT = INT_MAX;
+static const char NUM_FRAMES_TO_DUMP_PROPERTY[] = "debug.dav1d.numframestodump";
+
+// start dumping from this frame
+static const int STARTING_FRAME_TO_DUMP_DEFAULT = 0;
+static const char STARTING_FRAME_TO_DUMP_PROPERTY[] = "debug.dav1d.startingframetodump";
+
+void C2SoftDav1dDump::initDumping() {
+    nsecs_t now = systemTime();
+    snprintf(mInDataFileName, kFileNameLength, "%s_%" PRId64 "d.%s", DUMP_FILE_PATH, now,
+             INPUT_DATA_DUMP_EXT);
+    snprintf(mInSizeFileName, kFileNameLength, "%s_%" PRId64 "d.%s", DUMP_FILE_PATH, now,
+             INPUT_SIZE_DUMP_EXT);
+    snprintf(mDav1dOutYuvFileName, kFileNameLength, "%s_%" PRId64 "dx.%s", DUMP_FILE_PATH, now,
+             OUTPUT_YUV_DUMP_EXT);
+
+    mFramesToDump =
+            android::base::GetIntProperty(NUM_FRAMES_TO_DUMP_PROPERTY, NUM_FRAMES_TO_DUMP_DEFAULT);
+    mFirstFrameToDump = android::base::GetIntProperty(STARTING_FRAME_TO_DUMP_PROPERTY,
+                                                      STARTING_FRAME_TO_DUMP_DEFAULT);
+    bool enableDumping = android::base::GetBoolProperty(ENABLE_DUMPING_FILES_PROPERTY,
+                                                        ENABLE_DUMPING_FILES_DEFAULT);
+    ALOGD("enableDumping = %d, mFramesToDump = %d", enableDumping, mFramesToDump);
+
+    if (enableDumping) {
+        mInDataFile = fopen(mInDataFileName, "wb");
+        if (mInDataFile == nullptr) {
+            ALOGD("Could not open file %s", mInDataFileName);
+        }
+
+        mInSizeFile = fopen(mInSizeFileName, "wb");
+        if (mInSizeFile == nullptr) {
+            ALOGD("Could not open file %s", mInSizeFileName);
+        }
+
+        mDav1dOutYuvFile = fopen(mDav1dOutYuvFileName, "wb");
+        if (mDav1dOutYuvFile == nullptr) {
+            ALOGD("Could not open file %s", mDav1dOutYuvFileName);
+        }
+    }
+}
+
+void C2SoftDav1dDump::destroyDumping() {
+    if (mInDataFile != nullptr) {
+        fclose(mInDataFile);
+        mInDataFile = nullptr;
+    }
+
+    if (mInSizeFile != nullptr) {
+        fclose(mInSizeFile);
+        mInSizeFile = nullptr;
+    }
+
+    if (mDav1dOutYuvFile != nullptr) {
+        fclose(mDav1dOutYuvFile);
+        mDav1dOutYuvFile = nullptr;
+    }
+}
+
+void C2SoftDav1dDump::dumpInput(uint8_t* ptr, int size) {
+    if (mInDataFile) {
+        int ret = fwrite(ptr, 1, size, mInDataFile);
+
+        if (ret != size) {
+            ALOGE("Error in fwrite %s, requested %d, returned %d", mInDataFileName, size, ret);
+        }
+    }
+
+    // Dump the size per inputBuffer if dumping is enabled.
+    if (mInSizeFile) {
+        int ret = fwrite(&size, 1, 4, mInSizeFile);
+
+        if (ret != 4) {
+            ALOGE("Error in fwrite %s, requested %d, returned %d", mInSizeFileName, 4, ret);
+        }
+    }
+}
+
+template <typename T>
+void C2SoftDav1dDump::dumpOutput(const T* srcY, const T* srcU, const T* srcV, size_t srcYStride,
+                                 size_t srcUStride, size_t srcVStride, int width, int height) {
+    mOutputCount++;
+    FILE* fp_out = mDav1dOutYuvFile;
+    int typeSize = sizeof(T);
+    if (fp_out && mOutputCount >= mFirstFrameToDump &&
+        mOutputCount <= (mFirstFrameToDump + mFramesToDump - 1)) {
+        for (int i = 0; i < height; i++) {
+            int ret =
+                    fwrite((uint8_t*)srcY + i * srcYStride * typeSize, 1, width * typeSize, fp_out);
+            if (ret != width * typeSize) {
+                ALOGE("Error in fwrite, requested %d, returned %d", width * typeSize, ret);
+                break;
+            }
+        }
+
+        for (int i = 0; i < height / 2; i++) {
+            int ret = fwrite((uint8_t*)srcU + i * srcUStride * typeSize, 1, width * typeSize / 2,
+                             fp_out);
+            if (ret != width * typeSize / 2) {
+                ALOGE("Error in fwrite, requested %d, returned %d", width * typeSize / 2, ret);
+                break;
+            }
+        }
+
+        for (int i = 0; i < height / 2; i++) {
+            int ret = fwrite((uint8_t*)srcV + i * srcVStride * typeSize, 1, width * typeSize / 2,
+                             fp_out);
+            if (ret != width * typeSize / 2) {
+                ALOGE("Error in fwrite, requested %d, returned %d", width * typeSize / 2, ret);
+                break;
+            }
+        }
+    }
+}
+
+void C2SoftDav1dDump::writeDav1dOutYuvFile(const Dav1dPicture& p) {
+    if (mDav1dOutYuvFile != NULL) {
+        uint8_t* ptr;
+        const int hbd = p.p.bpc > 8;
+
+        ptr = (uint8_t*)p.data[0];
+        for (int y = 0; y < p.p.h; y++) {
+            int iSize = p.p.w << hbd;
+            int ret = fwrite(ptr, 1, iSize, mDav1dOutYuvFile);
+            if (ret != iSize) {
+                ALOGE("Error in fwrite %s, requested %d, returned %d", mDav1dOutYuvFileName, iSize,
+                      ret);
+                break;
+            }
+
+            ptr += p.stride[0];
+        }
+
+        if (p.p.layout != DAV1D_PIXEL_LAYOUT_I400) {
+            // u/v
+            const int ss_ver = p.p.layout == DAV1D_PIXEL_LAYOUT_I420;
+            const int ss_hor = p.p.layout != DAV1D_PIXEL_LAYOUT_I444;
+            const int cw = (p.p.w + ss_hor) >> ss_hor;
+            const int ch = (p.p.h + ss_ver) >> ss_ver;
+            for (int pl = 1; pl <= 2; pl++) {
+                ptr = (uint8_t*)p.data[pl];
+                for (int y = 0; y < ch; y++) {
+                    int iSize = cw << hbd;
+                    int ret = fwrite(ptr, 1, cw << hbd, mDav1dOutYuvFile);
+                    if (ret != iSize) {
+                        ALOGE("Error in fwrite %s, requested %d, returned %d", mDav1dOutYuvFileName,
+                              iSize, ret);
+                        break;
+                    }
+                    ptr += p.stride[1];
+                }
+            }
+        }
+    }
+}
+
+template void C2SoftDav1dDump::dumpOutput<uint8_t>(const uint8_t* srcY, const uint8_t* srcU,
+                                                   const uint8_t* srcV, size_t srcYStride,
+                                                   size_t srcUStride, size_t srcVStride, int width,
+                                                   int height);
+template void C2SoftDav1dDump::dumpOutput<uint16_t>(const uint16_t* srcY, const uint16_t* srcU,
+                                                    const uint16_t* srcV, size_t srcYStride,
+                                                    size_t srcUStride, size_t srcVStride, int width,
+                                                    int height);
+}  // namespace android
\ No newline at end of file
diff --git a/media/codec2/components/dav1d/C2SoftDav1dDump.h b/media/codec2/components/dav1d/C2SoftDav1dDump.h
new file mode 100644
index 0000000..ea7a48a
--- /dev/null
+++ b/media/codec2/components/dav1d/C2SoftDav1dDump.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+#include <android-base/properties.h>
+#include <Codec2CommonUtils.h>
+#include <Codec2Mapper.h>
+#include <dav1d/dav1d.h>
+
+#define DUMP_FILE_PATH "/data/local/tmp/dump"
+#define INPUT_DATA_DUMP_EXT "av1"
+#define INPUT_SIZE_DUMP_EXT "size"
+#define OUTPUT_YUV_DUMP_EXT "yuv"
+
+namespace android {
+constexpr size_t kFileNameLength = 256;
+
+class C2SoftDav1dDump {
+  public:
+    void initDumping();
+    void destroyDumping();
+    void dumpInput(uint8_t* ptr, int new_size);
+    template <typename T>
+    void dumpOutput(const T* srcY, const T* srcU, const T* srcV, size_t srcYStride,
+                    size_t srcUStride, size_t srcVStride, int width, int height);
+    void writeDav1dOutYuvFile(const Dav1dPicture& p);
+
+  private:
+    int mFramesToDump = 0;
+    int mFirstFrameToDump = 0;
+    int mOutputCount = 0;
+
+    char mInDataFileName[kFileNameLength];
+    char mInSizeFileName[kFileNameLength];
+    char mDav1dOutYuvFileName[kFileNameLength];
+
+    FILE* mInDataFile = nullptr;
+    FILE* mInSizeFile = nullptr;
+    FILE* mDav1dOutYuvFile = nullptr;
+};
+}  // namespace android
diff --git a/media/codec2/hal/client/client.cpp b/media/codec2/hal/client/client.cpp
index b680931..e7dd7c2 100644
--- a/media/codec2/hal/client/client.cpp
+++ b/media/codec2/hal/client/client.cpp
@@ -1954,7 +1954,8 @@
         std::shared_ptr<Codec2Client::Configurable>* configurable) {
     if (mAidlBase) {
         c2_aidl::IComponent::BlockPool aidlBlockPool;
-        ::ndk::ScopedAStatus transStatus = mAidlBase->createBlockPool(id, &aidlBlockPool);
+        ::ndk::ScopedAStatus transStatus = mAidlBase->createBlockPool(static_cast<int32_t>(id),
+                                                                      &aidlBlockPool);
         c2_status_t status = GetC2Status(transStatus, "createBlockPool");
         if (status != C2_OK) {
             return status;
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 2bf11ba..490f957 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -2175,8 +2175,15 @@
 
     if (notifyClient && !buffer && !flags) {
         if (mTunneled && drop && outputFormat) {
-            ALOGV("[%s] onWorkDone: Keep tunneled, drop frame with format change (%lld)",
-                  mName, work->input.ordinal.frameIndex.peekull());
+            if (mOutputFormat != outputFormat) {
+                ALOGV("[%s] onWorkDone: Keep tunneled, drop frame with format change (%lld)",
+                      mName, work->input.ordinal.frameIndex.peekull());
+                mOutputFormat = outputFormat;
+            } else {
+                ALOGV("[%s] onWorkDone: Not reporting output buffer without format change (%lld)",
+                      mName, work->input.ordinal.frameIndex.peekull());
+                notifyClient = false;
+            }
         } else {
             ALOGV("[%s] onWorkDone: Not reporting output buffer (%lld)",
                   mName, work->input.ordinal.frameIndex.peekull());
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h
index 2bdb7bc..6800107 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.h
+++ b/media/codec2/sfplugin/CCodecBufferChannel.h
@@ -303,6 +303,7 @@
     std::shared_ptr<C2BlockPool> mInputAllocator;
     QueueSync mQueueSync;
     std::vector<std::unique_ptr<C2Param>> mParamsToBeSet;
+    sp<AMessage> mOutputFormat;
 
     struct Input {
         Input();
diff --git a/media/libaudiohal/impl/EffectConversionHelperAidl.cpp b/media/libaudiohal/impl/EffectConversionHelperAidl.cpp
index aecbba7..196b432 100644
--- a/media/libaudiohal/impl/EffectConversionHelperAidl.cpp
+++ b/media/libaudiohal/impl/EffectConversionHelperAidl.cpp
@@ -255,37 +255,37 @@
 status_t EffectConversionHelperAidl::handleReset(uint32_t cmdSize __unused,
                                                  const void* pCmdData __unused, uint32_t* replySize,
                                                  void* pReplyData) {
-    if (!replySize || !pReplyData) {
+    if (!replySize || *replySize != sizeof(int) || !pReplyData) {
         ALOGE("%s parameter invalid, replySize %s pReplyData %p", __func__,
               numericPointerToString(replySize).c_str(), pReplyData);
         return BAD_VALUE;
     }
 
-    return statusTFromBinderStatus(mEffect->command(CommandId::RESET));
+    return *(int *)pReplyData = statusTFromBinderStatus(mEffect->command(CommandId::RESET));
 }
 
 status_t EffectConversionHelperAidl::handleEnable(uint32_t cmdSize __unused,
                                                   const void* pCmdData __unused,
                                                   uint32_t* replySize, void* pReplyData) {
-    if (!replySize || !pReplyData) {
+    if (!replySize || *replySize != sizeof(int) || !pReplyData) {
         ALOGE("%s parameter invalid, replySize %s pReplyData %p", __func__,
               numericPointerToString(replySize).c_str(), pReplyData);
         return BAD_VALUE;
     }
 
-    return statusTFromBinderStatus(mEffect->command(CommandId::START));
+    return *(int *)pReplyData = statusTFromBinderStatus(mEffect->command(CommandId::START));
 }
 
 status_t EffectConversionHelperAidl::handleDisable(uint32_t cmdSize __unused,
                                                    const void* pCmdData __unused,
                                                    uint32_t* replySize, void* pReplyData) {
-    if (!replySize || !pReplyData) {
+    if (!replySize || *replySize != sizeof(int) || !pReplyData) {
         ALOGE("%s parameter invalid, replySize %s pReplyData %p", __func__,
               numericPointerToString(replySize).c_str(), pReplyData);
         return BAD_VALUE;
     }
 
-    return statusTFromBinderStatus(mEffect->command(CommandId::STOP));
+    return *(int *)pReplyData = statusTFromBinderStatus(mEffect->command(CommandId::STOP));
 }
 
 status_t EffectConversionHelperAidl::handleSetAudioSource(uint32_t cmdSize, const void* pCmdData,
@@ -477,7 +477,7 @@
                   efGroup);
             status = (status == OK) ? BAD_VALUE : status;
         }
-    } else if (isBypassingOrOffload()) {
+    } else if (isBypassingOrTunnel()) {
         // for effect with bypass (no processing) or offloadIndication flag, it's okay to not have
         // statusQ
         return OK;
@@ -487,8 +487,8 @@
     return status;
 }
 
-bool EffectConversionHelperAidl::isBypassingOrOffload() const {
-    return isBypassing() || isOffload();
+bool EffectConversionHelperAidl::isBypassingOrTunnel() const {
+    return isBypassing() || isTunnel();
 }
 
 bool EffectConversionHelperAidl::isBypassing() const {
@@ -497,10 +497,10 @@
             (mIsProxyEffect && std::static_pointer_cast<EffectProxy>(mEffect)->isBypassing()));
 }
 
-bool EffectConversionHelperAidl::isOffload() const {
+bool EffectConversionHelperAidl::isTunnel() const {
     return mEffect &&
-           (mDesc.common.flags.offloadIndication ||
-            (mIsProxyEffect && std::static_pointer_cast<EffectProxy>(mEffect)->isOffload()));
+           (mDesc.common.flags.hwAcceleratorMode == Flags::HardwareAccelerator::TUNNEL ||
+            (mIsProxyEffect && std::static_pointer_cast<EffectProxy>(mEffect)->isTunnel()));
 }
 
 Descriptor EffectConversionHelperAidl::getDescriptor() const {
diff --git a/media/libaudiohal/impl/EffectConversionHelperAidl.h b/media/libaudiohal/impl/EffectConversionHelperAidl.h
index b2ef155..5db334c 100644
--- a/media/libaudiohal/impl/EffectConversionHelperAidl.h
+++ b/media/libaudiohal/impl/EffectConversionHelperAidl.h
@@ -43,8 +43,8 @@
     std::shared_ptr<android::hardware::EventFlag> getEventFlagGroup() { return mEfGroup; }
 
     bool isBypassing() const;
-    bool isOffload() const;
-    bool isBypassingOrOffload() const;
+    bool isTunnel() const;
+    bool isBypassingOrTunnel() const;
 
     ::aidl::android::hardware::audio::effect::Descriptor getDescriptor() const;
 
diff --git a/media/libaudiohal/impl/EffectProxy.cpp b/media/libaudiohal/impl/EffectProxy.cpp
index 4d7db4c..1099b6d 100644
--- a/media/libaudiohal/impl/EffectProxy.cpp
+++ b/media/libaudiohal/impl/EffectProxy.cpp
@@ -278,8 +278,9 @@
     return mSubEffects[mActiveSubIdx].descriptor.common.flags.bypass;
 }
 
-bool EffectProxy::isOffload() const {
-    return mSubEffects[mActiveSubIdx].descriptor.common.flags.offloadIndication;
+bool EffectProxy::isTunnel() const {
+    return mSubEffects[mActiveSubIdx].descriptor.common.flags.hwAcceleratorMode ==
+           Flags::HardwareAccelerator::TUNNEL;
 }
 
 binder_status_t EffectProxy::dump(int fd, const char** args, uint32_t numArgs) {
diff --git a/media/libaudiohal/impl/EffectProxy.h b/media/libaudiohal/impl/EffectProxy.h
index e14eb8a..0d62642 100644
--- a/media/libaudiohal/impl/EffectProxy.h
+++ b/media/libaudiohal/impl/EffectProxy.h
@@ -98,7 +98,7 @@
     }
 
     bool isBypassing() const;
-    bool isOffload() const;
+    bool isTunnel() const;
 
     // call dump for all sub-effects
     binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
diff --git a/media/libstagefright/VideoRenderQualityTracker.cpp b/media/libstagefright/VideoRenderQualityTracker.cpp
index e920bd1..90d5405 100644
--- a/media/libstagefright/VideoRenderQualityTracker.cpp
+++ b/media/libstagefright/VideoRenderQualityTracker.cpp
@@ -15,7 +15,11 @@
  */
 
 #define LOG_TAG "VideoRenderQualityTracker"
+#define ATRACE_TAG ATRACE_TAG_VIDEO
+
 #include <utils/Log.h>
+#include <utils/Trace.h>
+#include <utils/Mutex.h>
 
 #include <media/stagefright/VideoRenderQualityTracker.h>
 
@@ -25,8 +29,10 @@
 #include <stdio.h>
 #include <sys/time.h>
 
+#include <android-base/macros.h>
 #include <android-base/parsebool.h>
 #include <android-base/parseint.h>
+#include <android-base/properties.h>
 
 namespace android {
 
@@ -38,6 +44,7 @@
 
 typedef VideoRenderQualityTracker::Configuration::GetServerConfigurableFlagFn
         GetServerConfigurableFlagFn;
+typedef VideoRenderQualityTracker::TraceTriggerFn TraceTriggerFn;
 
 static void getServerConfigurableFlag(GetServerConfigurableFlagFn getServerConfigurableFlagFn,
                                       char const *flagNameSuffix, bool *value) {
@@ -149,6 +156,10 @@
     getFlag(judderEventMax, "judder_event_max");
     getFlag(judderEventDetailsMax, "judder_event_details_max");
     getFlag(judderEventDistanceToleranceMs, "judder_event_distance_tolerance_ms");
+    getFlag(traceTriggerEnabled, "trace_trigger_enabled");
+    getFlag(traceTriggerThrottleMs, "trace_trigger_throttle_ms");
+    getFlag(traceMinFreezeDurationMs, "trace_minimum_freeze_duration_ms");
+    getFlag(traceMaxFreezeDurationMs, "trace_maximum_freeze_duration_ms");
 #undef getFlag
     return c;
 }
@@ -186,15 +197,25 @@
     judderEventMax = 0; // enabled only when debugging
     judderEventDetailsMax = 20;
     judderEventDistanceToleranceMs = 5000; // lump judder occurrences together when 5s or less
+
+    // Perfetto trigger configuration.
+    traceTriggerEnabled = android::base::GetProperty(
+        "ro.build.type", "user") != "user"; // Enabled for non-user builds for debugging.
+    traceTriggerThrottleMs = 5 * 60 * 1000; // 5 mins.
+    traceMinFreezeDurationMs = 400;
+    traceMaxFreezeDurationMs = 1500;
 }
 
-VideoRenderQualityTracker::VideoRenderQualityTracker() : mConfiguration(Configuration()) {
+VideoRenderQualityTracker::VideoRenderQualityTracker()
+    : mConfiguration(Configuration()), mTraceTriggerFn(triggerTrace) {
     configureHistograms(mMetrics, mConfiguration);
     clear();
 }
 
-VideoRenderQualityTracker::VideoRenderQualityTracker(const Configuration &configuration) :
-        mConfiguration(configuration) {
+VideoRenderQualityTracker::VideoRenderQualityTracker(const Configuration &configuration,
+                                                     const TraceTriggerFn traceTriggerFn)
+    : mConfiguration(configuration),
+      mTraceTriggerFn(traceTriggerFn == nullptr ? triggerTrace : traceTriggerFn) {
     configureHistograms(mMetrics, mConfiguration);
     clear();
 }
@@ -231,6 +252,11 @@
 
     resetIfDiscontinuity(contentTimeUs, -1);
 
+    if (mTraceFrameSkippedToken == -1) {
+       mTraceFrameSkippedToken = contentTimeUs;
+       ATRACE_ASYNC_BEGIN("Video frame(s) skipped", mTraceFrameSkippedToken);
+    }
+
     // Frames skipped at the end of playback shouldn't be counted as skipped frames, since the
     // app could be terminating the playback. The pending count will be added to the metrics if and
     // when the next frame is rendered.
@@ -261,11 +287,25 @@
         return;
     }
 
+    if (mTraceFrameSkippedToken != -1) {
+        ATRACE_ASYNC_END("Video frame(s) skipped", mTraceFrameSkippedToken);
+        mTraceFrameSkippedToken = -1;
+    }
+
     int64_t actualRenderTimeUs = actualRenderTimeNs / 1000;
 
     if (mLastRenderTimeUs != -1) {
-        mRenderDurationMs += (actualRenderTimeUs - mLastRenderTimeUs) / 1000;
+        int64_t frameRenderDurationMs = (actualRenderTimeUs - mLastRenderTimeUs) / 1000;
+        mRenderDurationMs += frameRenderDurationMs;
+        if (mConfiguration.traceTriggerEnabled
+            // Threshold for visible video freeze.
+            && frameRenderDurationMs >= mConfiguration.traceMinFreezeDurationMs
+            // Threshold for removing long render durations which could be video pause.
+            && frameRenderDurationMs < mConfiguration.traceMaxFreezeDurationMs) {
+            triggerTraceWithThrottle(mTraceTriggerFn, mConfiguration, actualRenderTimeUs);
+        }
     }
+
     // Now that a frame has been rendered, the previously skipped frames can be processed as skipped
     // frames since the app is not skipping them to terminate playback.
     for (int64_t contentTimeUs : mPendingSkippedFrameContentTimeUsList) {
@@ -738,4 +778,42 @@
     return false;
 }
 
+void VideoRenderQualityTracker::triggerTraceWithThrottle(const TraceTriggerFn traceTriggerFn,
+                                                         const Configuration &c,
+                                                         const int64_t triggerTimeUs) {
+    static int64_t lastTriggerUs = -1;
+    static Mutex updateLastTriggerLock;
+
+    Mutex::Autolock autoLock(updateLastTriggerLock);
+    if (lastTriggerUs != -1) {
+        int32_t sinceLastTriggerMs = int32_t((triggerTimeUs - lastTriggerUs) / 1000);
+        // Throttle the trace trigger calls to reduce continuous PID fork calls in a short time
+        // to impact device performance, and reduce spamming trace reports.
+        if (sinceLastTriggerMs < c.traceTriggerThrottleMs) {
+            ALOGI("Not triggering trace - not enough time since last trigger");
+            return;
+        }
+    }
+    lastTriggerUs = triggerTimeUs;
+    (*traceTriggerFn)();
+}
+
+void VideoRenderQualityTracker::triggerTrace() {
+    // Trigger perfetto to stop always-on-tracing (AOT) to collect trace into a file for video
+    // freeze event, the collected trace categories are configured by AOT.
+    const char* args[] = {"/system/bin/trigger_perfetto", "com.android.codec-video-freeze", NULL};
+    pid_t pid = fork();
+    if (pid < 0) {
+        ALOGI("Failed to fork for triggering trace");
+        return;
+    }
+    if (pid == 0) {
+        // child process.
+        execvp(args[0], const_cast<char**>(args));
+        ALOGW("Failed to trigger trace %s", args[1]);
+        _exit(1);
+    }
+    ALOGI("Triggered trace %s", args[1]);
+}
+
 } // namespace android
diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
index a4b3e2f..e091cb8 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
@@ -286,9 +286,11 @@
             return;
         }
 
-        // decoder deals in ms, OMX in us.
-        outHeader->nTimeStamp = mPvToOmxTimeMap.valueFor(timestamp);
-        mPvToOmxTimeMap.removeItem(timestamp);
+        if (mPvToOmxTimeMap.indexOfKey(timestamp) >= 0) {
+            // decoder deals in ms, OMX in us.
+            outHeader->nTimeStamp = mPvToOmxTimeMap.valueFor(timestamp);
+            mPvToOmxTimeMap.removeItem(timestamp);
+        }
 
         inHeader->nOffset += bufferSize;
         inHeader->nFilledLen = 0;
diff --git a/media/libstagefright/codecs/on2/enc/SoftVP8Encoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVP8Encoder.cpp
index 04737a9..9198b7c 100644
--- a/media/libstagefright/codecs/on2/enc/SoftVP8Encoder.cpp
+++ b/media/libstagefright/codecs/on2/enc/SoftVP8Encoder.cpp
@@ -120,6 +120,11 @@
 
 OMX_ERRORTYPE SoftVP8Encoder::internalGetVp8Params(
         OMX_VIDEO_PARAM_VP8TYPE* vp8Params) {
+    if (!isValidOMXParam(vp8Params)) {
+        android_errorWriteLog(0x534e4554, "273936274");
+        return OMX_ErrorBadParameter;
+    }
+
     if (vp8Params->nPortIndex != kOutputPortIndex) {
         return OMX_ErrorUnsupportedIndex;
     }
@@ -133,6 +138,11 @@
 
 OMX_ERRORTYPE SoftVP8Encoder::internalSetVp8Params(
         const OMX_VIDEO_PARAM_VP8TYPE* vp8Params) {
+    if (!isValidOMXParam(vp8Params)) {
+        android_errorWriteLog(0x534e4554, "273937171");
+        return OMX_ErrorBadParameter;
+    }
+
     if (vp8Params->nPortIndex != kOutputPortIndex) {
         return OMX_ErrorUnsupportedIndex;
     }
diff --git a/media/libstagefright/codecs/on2/enc/SoftVP9Encoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVP9Encoder.cpp
index 1ea1c85..f8495c2 100644
--- a/media/libstagefright/codecs/on2/enc/SoftVP9Encoder.cpp
+++ b/media/libstagefright/codecs/on2/enc/SoftVP9Encoder.cpp
@@ -119,6 +119,11 @@
 
 OMX_ERRORTYPE SoftVP9Encoder::internalGetVp9Params(
         OMX_VIDEO_PARAM_VP9TYPE *vp9Params) {
+    if (!isValidOMXParam(vp9Params)) {
+        android_errorWriteLog(0x534e4554, "273936553");
+        return OMX_ErrorBadParameter;
+    }
+
     if (vp9Params->nPortIndex != kOutputPortIndex) {
         return OMX_ErrorUnsupportedIndex;
     }
@@ -133,6 +138,11 @@
 
 OMX_ERRORTYPE SoftVP9Encoder::internalSetVp9Params(
         const OMX_VIDEO_PARAM_VP9TYPE *vp9Params) {
+    if (!isValidOMXParam(vp9Params)) {
+        android_errorWriteLog(0x534e4554, "273937136");
+        return OMX_ErrorBadParameter;
+    }
+
     if (vp9Params->nPortIndex != kOutputPortIndex) {
         return OMX_ErrorUnsupportedIndex;
     }
diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp
index e9b4341..cbedb72 100644
--- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp
+++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp
@@ -485,6 +485,11 @@
 
 OMX_ERRORTYPE SoftVPXEncoder::internalGetAndroidVpxParams(
         OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE *vpxAndroidParams) {
+    if (!isValidOMXParam(vpxAndroidParams)) {
+        android_errorWriteLog(0x534e4554, "273936601");
+        return OMX_ErrorBadParameter;
+    }
+
     if (vpxAndroidParams->nPortIndex != kOutputPortIndex) {
         return OMX_ErrorUnsupportedIndex;
     }
@@ -501,6 +506,10 @@
 
 OMX_ERRORTYPE SoftVPXEncoder::internalSetAndroidVpxParams(
         const OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE *vpxAndroidParams) {
+    if (!isValidOMXParam(vpxAndroidParams)) {
+        android_errorWriteLog(0x534e4554, "273937551");
+        return OMX_ErrorBadParameter;
+    }
     if (vpxAndroidParams->nPortIndex != kOutputPortIndex) {
         return OMX_ErrorUnsupportedIndex;
     }
diff --git a/media/libstagefright/include/media/stagefright/VideoRenderQualityTracker.h b/media/libstagefright/include/media/stagefright/VideoRenderQualityTracker.h
index a656e6e..cf53f27 100644
--- a/media/libstagefright/include/media/stagefright/VideoRenderQualityTracker.h
+++ b/media/libstagefright/include/media/stagefright/VideoRenderQualityTracker.h
@@ -200,6 +200,21 @@
         // The maximum distance in time between two judder occurrences such that both will be
         // lumped into the same judder event.
         int32_t judderEventDistanceToleranceMs;
+        //
+        // Whether or not Perfetto trace trigger is enabled.
+        bool traceTriggerEnabled;
+        //
+        // The throttle time for Perfetto trace trigger to avoid triggering multiple traces for
+        // the same event in a short time.
+        int32_t traceTriggerThrottleMs;
+        //
+        // The minimum frame render duration to recognize video freeze event to collect trace.
+        int32_t traceMinFreezeDurationMs;
+        //
+        // The maximum frame render duration to recognize video freeze event. A frame render
+        // duration that is larger than the max duration would not trigger trace collection for
+        // video freeze because it's highly possible a video pause.
+        int32_t traceMaxFreezeDurationMs;
     };
 
     struct FreezeEvent {
@@ -256,8 +271,11 @@
         Details details;
     };
 
+    typedef void (*TraceTriggerFn)();
+
     VideoRenderQualityTracker();
-    VideoRenderQualityTracker(const Configuration &configuration);
+    VideoRenderQualityTracker(const Configuration &configuration,
+                              const TraceTriggerFn traceTriggerFn = nullptr);
 
     // Called when a tunnel mode frame has been queued.
     void onTunnelFrameQueued(int64_t contentTimeUs);
@@ -376,6 +394,14 @@
                                         JudderEvent &e, const VideoRenderQualityMetrics & m,
                                         const Configuration &c, JudderEvent *judderEventOut);
 
+    // Trigger trace collection for video freeze.
+    static void triggerTrace();
+
+    // Trigger collection of a Perfetto Always-On-Tracing (AOT) trace file for video freeze,
+    // triggerTimeUs is used as a throttle to avoid triggering multiple traces in a short time.
+    static void triggerTraceWithThrottle(TraceTriggerFn traceTriggerFn,
+                                         const Configuration &c, const int64_t triggerTimeUs);
+
     // Check to see if a discontinuity has occurred by examining the content time and the
     // app-desired render time. If so, reset some internal state.
     bool resetIfDiscontinuity(int64_t contentTimeUs, int64_t desiredRenderTimeUs);
@@ -394,6 +420,9 @@
     // Configurable elements of the metrics algorithms.
     const Configuration mConfiguration;
 
+    // The function for triggering trace collection for video freeze.
+    const TraceTriggerFn mTraceTriggerFn;
+
     // Metrics are updated every time a frame event occurs - skipped, dropped, rendered.
     VideoRenderQualityMetrics mMetrics;
 
@@ -445,6 +474,9 @@
     // Frame durations derived from timestamps captured by the display subsystem, indicating the
     // wall clock atime at which the frame is actually rendered.
     FrameDurationUs mActualFrameDurationUs;
+
+    // Token of async atrace for video frame dropped/skipped by the app.
+    int64_t mTraceFrameSkippedToken= -1;
 };
 
 }  // namespace android
diff --git a/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp b/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp
index 7823922..3b70636 100644
--- a/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp
+++ b/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp
@@ -36,10 +36,11 @@
 class Helper {
 public:
     Helper(double contentFrameDurationMs, const Configuration &configuration) :
-            mVideoRenderQualityTracker(configuration) {
+            mVideoRenderQualityTracker(configuration, testTraceTrigger) {
         mContentFrameDurationUs = int64_t(contentFrameDurationMs * 1000);
         mMediaTimeUs = 0;
         mClockTimeNs = 0;
+        sTraceTriggeredCount = 0;
     }
 
     void changeContentFrameDuration(double contentFrameDurationMs) {
@@ -100,6 +101,10 @@
         return e;
     }
 
+    int getTraceTriggeredCount() {
+        return sTraceTriggeredCount;
+    }
+
 private:
     VideoRenderQualityTracker mVideoRenderQualityTracker;
     int64_t mContentFrameDurationUs;
@@ -107,8 +112,16 @@
     int64_t mClockTimeNs;
     VideoRenderQualityTracker::FreezeEvent mFreezeEvent;
     VideoRenderQualityTracker::JudderEvent mJudderEvent;
+
+    static int sTraceTriggeredCount;
+
+    static void testTraceTrigger() {
+        sTraceTriggeredCount++;
+    };
 };
 
+int Helper::sTraceTriggeredCount = 0;
+
 class VideoRenderQualityTrackerTest : public ::testing::Test {
 public:
     VideoRenderQualityTrackerTest() {}
@@ -139,6 +152,10 @@
     EXPECT_EQ(c.judderEventMax, d.judderEventMax);
     EXPECT_EQ(c.judderEventDetailsMax, d.judderEventDetailsMax);
     EXPECT_EQ(c.judderEventDistanceToleranceMs, d.judderEventDistanceToleranceMs);
+    EXPECT_EQ(c.traceTriggerEnabled, d.traceTriggerEnabled);
+    EXPECT_EQ(c.traceTriggerThrottleMs, d.traceTriggerThrottleMs);
+    EXPECT_EQ(c.traceMinFreezeDurationMs, d.traceMinFreezeDurationMs);
+    EXPECT_EQ(c.traceMaxFreezeDurationMs, d.traceMaxFreezeDurationMs);
 }
 
 TEST_F(VideoRenderQualityTrackerTest, getFromServerConfigurableFlags_withEmpty) {
@@ -166,6 +183,10 @@
     EXPECT_EQ(c.judderEventMax, d.judderEventMax);
     EXPECT_EQ(c.judderEventDetailsMax, d.judderEventDetailsMax);
     EXPECT_EQ(c.judderEventDistanceToleranceMs, d.judderEventDistanceToleranceMs);
+    EXPECT_EQ(c.traceTriggerEnabled, d.traceTriggerEnabled);
+    EXPECT_EQ(c.traceTriggerThrottleMs, d.traceTriggerThrottleMs);
+    EXPECT_EQ(c.traceMinFreezeDurationMs, d.traceMinFreezeDurationMs);
+    EXPECT_EQ(c.traceMaxFreezeDurationMs, d.traceMaxFreezeDurationMs);
 }
 
 TEST_F(VideoRenderQualityTrackerTest, getFromServerConfigurableFlags_withInvalid) {
@@ -193,6 +214,10 @@
     EXPECT_EQ(c.judderEventMax, d.judderEventMax);
     EXPECT_EQ(c.judderEventDetailsMax, d.judderEventDetailsMax);
     EXPECT_EQ(c.judderEventDistanceToleranceMs, d.judderEventDistanceToleranceMs);
+    EXPECT_EQ(c.traceTriggerEnabled, d.traceTriggerEnabled);
+    EXPECT_EQ(c.traceTriggerThrottleMs, d.traceTriggerThrottleMs);
+    EXPECT_EQ(c.traceMinFreezeDurationMs, d.traceMinFreezeDurationMs);
+    EXPECT_EQ(c.traceMaxFreezeDurationMs, d.traceMaxFreezeDurationMs);
 }
 
 TEST_F(VideoRenderQualityTrackerTest, getFromServerConfigurableFlags_withAlmostValid) {
@@ -232,6 +257,14 @@
                 return "10*10";
             } else if (flag == "render_metrics_judder_event_distance_tolerance_ms") {
                 return "140-a";
+            } else if (flag == "render_metrics_trace_trigger_enabled") {
+                return "fals";
+            } else if (flag == "render_metrics_trace_trigger_throttle_ms") {
+                return "12345678901234";
+            } else if (flag == "render_metrics_trace_minimum_freeze_duration_ms") {
+                return "10b0";
+            } else if (flag == "render_metrics_trace_maximum_freeze_duration_ms") {
+                return "100a";
             }
             return "";
         }
@@ -255,6 +288,10 @@
     EXPECT_EQ(c.judderEventMax, d.judderEventMax);
     EXPECT_EQ(c.judderEventDetailsMax, d.judderEventDetailsMax);
     EXPECT_EQ(c.judderEventDistanceToleranceMs, d.judderEventDistanceToleranceMs);
+    EXPECT_EQ(c.traceTriggerEnabled, d.traceTriggerEnabled);
+    EXPECT_EQ(c.traceTriggerThrottleMs, d.traceTriggerThrottleMs);
+    EXPECT_EQ(c.traceMinFreezeDurationMs, d.traceMinFreezeDurationMs);
+    EXPECT_EQ(c.traceMaxFreezeDurationMs, d.traceMaxFreezeDurationMs);
 }
 
 TEST_F(VideoRenderQualityTrackerTest, getFromServerConfigurableFlags_withValid) {
@@ -294,6 +331,14 @@
                 return "10000";
             } else if (flag == "render_metrics_judder_event_distance_tolerance_ms") {
                 return "11000";
+            } else if (flag == "render_metrics_trace_trigger_enabled") {
+                return "true";
+            } else if (flag == "render_metrics_trace_trigger_throttle_ms") {
+                return "50000";
+            } else if (flag == "render_metrics_trace_minimum_freeze_duration_ms") {
+                return "1000";
+            } else if (flag == "render_metrics_trace_maximum_freeze_duration_ms") {
+                return "5000";
             }
             return "";
         }
@@ -353,6 +398,11 @@
     EXPECT_NE(c.judderEventDetailsMax, d.judderEventDetailsMax);
     EXPECT_EQ(c.judderEventDistanceToleranceMs, 11000);
     EXPECT_NE(c.judderEventDistanceToleranceMs, d.judderEventDistanceToleranceMs);
+
+    EXPECT_EQ(c.traceTriggerEnabled, true);
+    EXPECT_EQ(c.traceTriggerThrottleMs, 50000);
+    EXPECT_EQ(c.traceMinFreezeDurationMs, 1000);
+    EXPECT_EQ(c.traceMaxFreezeDurationMs, 5000);
 }
 
 TEST_F(VideoRenderQualityTrackerTest, countsReleasedFrames) {
@@ -1024,4 +1074,75 @@
     EXPECT_EQ(h.getMetrics().judderScore, 10 + 300 + 2000);
 }
 
+TEST_F(VideoRenderQualityTrackerTest,
+       freezesForTraceDuration_withThrottle_throttlesTraceTrigger) {
+    Configuration c;
+    c.enabled = true;
+    c.traceTriggerEnabled = true; // The trigger is enabled, so traces should be triggered.
+    // The value of traceTriggerThrottleMs must be larger than traceMinFreezeDurationMs. Otherwise,
+    // the throttle does work.
+    c.traceTriggerThrottleMs = 200;
+    c.traceMinFreezeDurationMs = 40;
+    int32_t freeze = c.traceMinFreezeDurationMs;
+
+    Helper h(20, c);
+    // Freeze triggers separated by 80ms which is less than the threshold.
+    h.render({
+        freeze, // Freeze duration does not check trace trigger.
+        20,     // Trace triggered.
+        20,     // Throttle time:  20/200ms
+        20,     // Throttle time:  40/200ms
+        freeze, // Throttle time:  80/200ms
+        20,     // Throttle time: 100/200ms (Trace not triggered)
+    });
+    EXPECT_EQ(h.getTraceTriggeredCount(), 1);
+    // Next freeze trigger is separated by 200ms which breaks the throttle threshold.
+    h.render({
+        20,     // Throttle time: 120/200ms
+        20,     // Throttle time: 140/200ms
+        20,     // Throttle time: 160/200ms
+        freeze, // Throttle time: 200/200ms
+        20,     // Trace triggered.
+    });
+    EXPECT_EQ(h.getTraceTriggeredCount(), 2);
+    // Next freeze trigger is separated by 80ms which is less than the threshold.
+    h.render({
+        20,     // Throttle time:  20/200ms
+        20,     // Throttle time:  40/200ms
+        freeze, // Throttle time:  80/200ms
+        20,     // Throttle time: 100/200ms (Trace not triggered)
+    });
+    EXPECT_EQ(h.getTraceTriggeredCount(), 2);
+}
+
+TEST_F(VideoRenderQualityTrackerTest, freezeForTraceDuration_triggersTrace) {
+    Configuration c;
+    c.enabled = true;
+    c.traceTriggerEnabled = true; // The trigger is enabled, so traces should be triggered.
+    c.traceTriggerThrottleMs = 0; // Disable throttle in the test case.
+    int32_t freeze1 = c.traceMinFreezeDurationMs;
+    int32_t freeze2 = c.traceMaxFreezeDurationMs - 1;
+    int32_t couldBeAPause = c.traceMaxFreezeDurationMs + 1;
+
+    Helper h(20, c);
+    h.render({freeze1, 20, freeze2, 20, couldBeAPause, 20});
+
+    EXPECT_EQ(h.getTraceTriggeredCount(), 2);
+}
+
+TEST_F(VideoRenderQualityTrackerTest,
+       freezeForTraceDuration_withTraceDisabled_doesNotTriggerTrace) {
+    Configuration c;
+    c.enabled = true;
+    c.traceTriggerEnabled = false; // The trigger is disabled, so no traces should be triggered.
+    c.traceTriggerThrottleMs = 0; // Disable throttle in the test case.
+    int32_t freeze1 = c.traceMinFreezeDurationMs;
+    int32_t freeze2 = c.traceMaxFreezeDurationMs - 1;
+    int32_t couldBeAPause = c.traceMaxFreezeDurationMs + 1;
+
+    Helper h(20, c);
+    h.render({freeze1, 20, freeze2, 20, couldBeAPause, 20});
+
+    EXPECT_EQ(h.getTraceTriggeredCount(), 0);
+}
 } // android
diff --git a/media/ndk/include/media/NdkImageReader.h b/media/ndk/include/media/NdkImageReader.h
index 48a0a82..b722b74 100644
--- a/media/ndk/include/media/NdkImageReader.h
+++ b/media/ndk/include/media/NdkImageReader.h
@@ -534,6 +534,11 @@
  * Get the native_handle_t corresponding to the ANativeWindow owned by the
  * AImageReader provided.
  *
+ * This is deprecated in API level 35 and will return AMEDIA_ERROR_UNKNOWN.
+ * The native_handle_t is no longer used with AIDL interfaces and
+ * ANativeWindow is used directly instead.
+ * Use AImageRead_getWindow to get the ANativeWindow and use that object.
+ *
  * @param reader The image reader of interest.
  * @param handle The output native_handle_t. This native handle is owned by
  *               this image reader.
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 93db608..589050c 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -830,7 +830,10 @@
     if (mStatus != NO_ERROR || mEffectInterface == 0) {
         return;
     }
-    mEffectInterface->command(EFFECT_CMD_RESET, 0, NULL, 0, NULL);
+
+    int reply = 0;
+    uint32_t replySize = sizeof(reply);
+    mEffectInterface->command(EFFECT_CMD_RESET, 0, NULL, &replySize, &reply);
 }
 
 status_t EffectModule::configure()
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index 89f0a6e..5f238fb 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -412,7 +412,23 @@
                 mAfPatchPanelCallback->updateOutDevicesForRecordThreads_l(devices);
             }
 
+            // For endpoint patches, we do not need to re-evaluate the device effect state
+            // if the same HAL patch is reused (see calls to mAfPatchPanelCallback below)
+            if (endpointPatch) {
+                for (auto& p : mPatches) {
+                    // end point patches are skipped so we do not compare against this patch
+                    if (!p.second.mIsEndpointPatch && patchesHaveSameRoute(
+                            newPatch.mAudioPatch, p.second.mAudioPatch)) {
+                        ALOGV("%s() Sw Bridge endpoint reusing halHandle=%d", __func__,
+                              p.second.mHalHandle);
+                        halHandle = p.second.mHalHandle;
+                        reuseExistingHalPatch = true;
+                        break;
+                    }
+                }
+            }
             mAfPatchPanelCallback->mutex().unlock();
+
             status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
             mAfPatchPanelCallback->mutex().lock();
             if (status == NO_ERROR) {
@@ -442,11 +458,18 @@
         *handle = static_cast<audio_patch_handle_t>(
                 mAfPatchPanelCallback->nextUniqueId(AUDIO_UNIQUE_ID_USE_PATCH));
         newPatch.mHalHandle = halHandle;
-        if (reuseExistingHalPatch) {
-            mAfPatchPanelCallback->getPatchCommandThread()->updateAudioPatch(
-                    oldhandle, *handle, newPatch);
-        } else {
-            mAfPatchPanelCallback->getPatchCommandThread()->createAudioPatch(*handle, newPatch);
+        // Skip device effect:
+        //  -for sw bridge as effect are likely held by endpoint patches
+        //  -for endpoint reusing a HalPatch handle
+        if (!(newPatch.isSoftware()
+                || (endpointPatch && reuseExistingHalPatch))) {
+            if (reuseExistingHalPatch) {
+                mAfPatchPanelCallback->getPatchCommandThread()->updateAudioPatch(
+                        oldhandle, *handle, newPatch);
+            } else {
+                 mAfPatchPanelCallback->getPatchCommandThread()->createAudioPatch(
+                        *handle, newPatch);
+            }
         }
         if (insertedModule != AUDIO_MODULE_HANDLE_NONE) {
             addSoftwarePatchToInsertedModules_l(insertedModule, *handle, &newPatch.mAudioPatch);
@@ -734,12 +757,14 @@
  {
     ALOGV("%s handle %d", __func__, handle);
     status_t status = NO_ERROR;
+    bool doReleasePatch = true;
 
     auto iter = mPatches.find(handle);
     if (iter == mPatches.end()) {
         return BAD_VALUE;
     }
     Patch &removedPatch = iter->second;
+    const bool isSwBridge = removedPatch.isSoftware();
     const struct audio_patch &patch = removedPatch.mAudioPatch;
 
     const struct audio_port_config &src = patch.sources[0];
@@ -791,15 +816,31 @@
                     break;
                 }
             }
-            mAfPatchPanelCallback->mutex().unlock();
-            status = thread->sendReleaseAudioPatchConfigEvent(removedPatch.mHalHandle);
-            mAfPatchPanelCallback->mutex().lock();
+            // Check whether the removed patch Hal Handle is used in another non-Endpoint patch.
+            // Since this is a non-Endpoint patch, the removed patch is not considered (it is
+            // removed later from mPatches).
+            if (removedPatch.mIsEndpointPatch) {
+                for (auto& p: mPatches) {
+                    if (!p.second.mIsEndpointPatch
+                            && p.second.mHalHandle == removedPatch.mHalHandle) {
+                        ALOGV("%s() Sw Bridge endpoint used existing halHandle=%d, do not release",
+                              __func__,  p.second.mHalHandle);
+                        doReleasePatch = false;
+                        break;
+                    }
+                }
+            }
+            if (doReleasePatch) {
+                mAfPatchPanelCallback->mutex().unlock();
+                status = thread->sendReleaseAudioPatchConfigEvent(removedPatch.mHalHandle);
+                mAfPatchPanelCallback->mutex().lock();
+            }
         } break;
         default:
             status = BAD_VALUE;
     }
 
-    erasePatch(handle);
+    erasePatch(handle, /* reuseExistingHalPatch= */ !doReleasePatch || isSwBridge);
     return status;
 }
 
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index a72598e..99042af 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -658,7 +658,7 @@
 
 status_t AudioPolicyManager::updateCallRouting(bool fromCache, uint32_t delayMs, uint32_t *waitMs)
 {
-    if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
+    if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL) {
         DeviceVector rxDevices = selectBestRxSinkDevicesForCall(fromCache);
         return updateCallRoutingInternal(rxDevices, delayMs, waitMs);
     }
@@ -671,14 +671,21 @@
     bool createTxPatch = false;
     bool createRxPatch = false;
     uint32_t muteWaitMs = 0;
-    if(!hasPrimaryOutput() ||
+    if (hasPrimaryOutput() &&
             mPrimaryOutput->devices().onlyContainsDevicesWithType(AUDIO_DEVICE_OUT_STUB)) {
         return INVALID_OPERATION;
     }
-    ALOG_ASSERT(!rxDevices.isEmpty(), "%s() no selected output device", __func__);
 
     audio_attributes_t attr = { .source = AUDIO_SOURCE_VOICE_COMMUNICATION };
     auto txSourceDevice = mEngine->getInputDeviceForAttributes(attr);
+
+    disconnectTelephonyAudioSource(mCallRxSourceClient);
+    disconnectTelephonyAudioSource(mCallTxSourceClient);
+
+    if (rxDevices.isEmpty()) {
+        ALOGW("%s() no selected output device", __func__);
+        return INVALID_OPERATION;
+    }
     if (txSourceDevice == nullptr) {
         ALOGE("%s() selected input device not available", __func__);
         return INVALID_OPERATION;
@@ -687,9 +694,6 @@
     ALOGV("%s device rxDevice %s txDevice %s", __func__,
           rxDevices.itemAt(0)->toString().c_str(), txSourceDevice->toString().c_str());
 
-    disconnectTelephonyAudioSource(mCallRxSourceClient);
-    disconnectTelephonyAudioSource(mCallTxSourceClient);
-
     auto telephonyRxModule =
         mHwModules.getModuleForDeviceType(AUDIO_DEVICE_IN_TELEPHONY_RX, AUDIO_FORMAT_DEFAULT);
     auto telephonyTxModule =
@@ -729,6 +733,10 @@
     // Use legacy routing method for voice calls via setOutputDevice() on primary output.
     // Otherwise, create two audio patches for TX and RX path.
     if (!createRxPatch) {
+        if (!hasPrimaryOutput()) {
+            ALOGW("%s() no primary output available", __func__);
+            return INVALID_OPERATION;
+        }
         muteWaitMs = setOutputDevices(__func__, mPrimaryOutput, rxDevices, true, delayMs);
     } else { // create RX path audio patch
         connectTelephonyRxAudioSource();
@@ -875,20 +883,20 @@
         }
     }
 
-    if (hasPrimaryOutput()) {
-        if (state == AUDIO_MODE_IN_CALL) {
-            (void)updateCallRouting(false /*fromCache*/, delayMs);
-        } else {
+    if (state == AUDIO_MODE_IN_CALL) {
+        (void)updateCallRouting(false /*fromCache*/, delayMs);
+    } else {
+        if (oldState == AUDIO_MODE_IN_CALL) {
+            disconnectTelephonyAudioSource(mCallRxSourceClient);
+            disconnectTelephonyAudioSource(mCallTxSourceClient);
+        }
+        if (hasPrimaryOutput()) {
             DeviceVector rxDevices = getNewOutputDevices(mPrimaryOutput, false /*fromCache*/);
             // force routing command to audio hardware when ending call
             // even if no device change is needed
             if (isStateInCall(oldState) && rxDevices.isEmpty()) {
                 rxDevices = mPrimaryOutput->devices();
             }
-            if (oldState == AUDIO_MODE_IN_CALL) {
-                disconnectTelephonyAudioSource(mCallRxSourceClient);
-                disconnectTelephonyAudioSource(mCallTxSourceClient);
-            }
             setOutputDevices(__func__, mPrimaryOutput, rxDevices, force, 0);
         }
     }
@@ -5250,14 +5258,9 @@
                 return NO_ERROR;
             }
             patchHandle = outputDesc->getPatchHandle();
-            // When a Sw bridge is released, the mixer used by this bridge will release its
-            // patch at AudioFlinger side. Hence, the mixer audio patch must be recreated
-            // Reuse patch handle to force audio flinger removing initial mixer patch removal
-            // updating hal patch handle (prevent leaks).
             // While using a HwBridge, force reconsidering device only if not reusing an existing
             // output and no more activity on output (will force to close).
-            bool force = sourceDesc->useSwBridge() ||
-                    (sourceDesc->canCloseOutput() && !outputDesc->isActive());
+            const bool force = sourceDesc->canCloseOutput() && !outputDesc->isActive();
             // APM pattern is to have always outputs opened / patch realized for reachable devices.
             // Update device may result to NONE (empty), coupled with force, it releases the patch.
             // Reconsider device only for cases:
@@ -6280,6 +6283,7 @@
             if (mPrimaryOutput == nullptr &&
                     outProfile->getFlags() & AUDIO_OUTPUT_FLAG_PRIMARY) {
                 mPrimaryOutput = outputDesc;
+                mPrimaryModuleHandle = mPrimaryOutput->getModuleHandle();
             }
             if ((outProfile->getFlags() & AUDIO_OUTPUT_FLAG_DIRECT) != 0) {
                 outputDesc->close();
@@ -7114,7 +7118,9 @@
 DeviceVector AudioPolicyManager::getNewOutputDevices(const sp<SwAudioOutputDescriptor>& outputDesc,
                                                      bool fromCache)
 {
-    DeviceVector devices;
+    if (outputDesc == nullptr) {
+        return DeviceVector{};
+    }
 
     ssize_t index = mAudioPatches.indexOfKey(outputDesc->getPatchHandle());
     if (index >= 0) {
@@ -7148,6 +7154,7 @@
         return DeviceVector(device);
     }
 
+    DeviceVector devices;
     for (const auto &productStrategy : mEngine->getOrderedProductStrategies()) {
         StreamTypeVector streams = mEngine->getStreamTypesForProductStrategy(productStrategy);
         auto attr = mEngine->getAllAttributesForProductStrategy(productStrategy).front();
@@ -8442,6 +8449,7 @@
     if (mPrimaryOutput == nullptr && profile->getFlags() & AUDIO_OUTPUT_FLAG_PRIMARY) {
         ALOGV("%s(): re-assigning mPrimaryOutput", __func__);
         mPrimaryOutput = desc;
+        mPrimaryModuleHandle = mPrimaryOutput->getModuleHandle();
     }
     return desc;
 }
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 91fe1cc..6365962 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -820,10 +820,10 @@
 
         bool isPrimaryModule(const sp<HwModule> &module) const
         {
-            if (module == 0 || !hasPrimaryOutput()) {
+            if (module == nullptr || mPrimaryModuleHandle == AUDIO_MODULE_HANDLE_NONE) {
                 return false;
             }
-            return module->getHandle() == mPrimaryOutput->getModuleHandle();
+            return module->getHandle() == mPrimaryModuleHandle;
         }
         DeviceVector availablePrimaryOutputDevices() const
         {
@@ -935,6 +935,8 @@
         EngineInstance mEngine;                         // Audio Policy Engine instance
         AudioPolicyClientInterface *mpClientInterface;  // audio policy client interface
         sp<SwAudioOutputDescriptor> mPrimaryOutput;     // primary output descriptor
+        // mPrimaryModuleHandle is cached mPrimaryOutput->getModuleHandle();
+        audio_module_handle_t mPrimaryModuleHandle = AUDIO_MODULE_HANDLE_NONE;
         // list of descriptors for outputs currently opened
 
         sp<SwAudioOutputDescriptor> mSpatializerOutput;
diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp
index ea467e7..abc3ecf 100644
--- a/services/camera/libcameraservice/Android.bp
+++ b/services/camera/libcameraservice/Android.bp
@@ -84,8 +84,8 @@
         "android.hardware.common-V2-ndk",
         "android.hardware.common.fmq-V1-ndk",
         "android.hardware.graphics.common-V4-ndk",
+        "camera_platform_flags_c_lib",
         "media_permission-aidl-cpp",
-        "server_configurable_flags",
     ],
 
     static_libs: [
@@ -106,7 +106,6 @@
         "android.hardware.camera.provider@2.6",
         "android.hardware.camera.provider@2.7",
         "android.hardware.camera.provider-V2-ndk",
-        "camera_platform_flags_c_lib",
         "libaidlcommonsupport",
         "libbinderthreadstateutils",
         "libcameraservice_device_independent",
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 45ce4c8..6819136 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -367,7 +367,7 @@
         static bool isValidAudioRestriction(int32_t mode);
 
         // Override rotate-and-crop AUTO behavior
-        virtual status_t setRotateAndCropOverride(uint8_t rotateAndCrop) = 0;
+        virtual status_t setRotateAndCropOverride(uint8_t rotateAndCrop, bool fromHal = false) = 0;
 
         // Override autoframing AUTO behaviour
         virtual status_t setAutoframingOverride(uint8_t autoframingValue) = 0;
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index b217476..caa6424 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -2342,7 +2342,7 @@
     return mDevice->setCameraServiceWatchdog(enabled);
 }
 
-status_t Camera2Client::setRotateAndCropOverride(uint8_t rotateAndCrop) {
+status_t Camera2Client::setRotateAndCropOverride(uint8_t rotateAndCrop, bool fromHal) {
     if (rotateAndCrop > ANDROID_SCALER_ROTATE_AND_CROP_AUTO) return BAD_VALUE;
 
     {
@@ -2356,7 +2356,7 @@
     }
 
     return mDevice->setRotateAndCropAutoBehavior(
-        static_cast<camera_metadata_enum_android_scaler_rotate_and_crop_t>(rotateAndCrop));
+        static_cast<camera_metadata_enum_android_scaler_rotate_and_crop_t>(rotateAndCrop), fromHal);
 }
 
 status_t Camera2Client::setAutoframingOverride(uint8_t autoframingValue) {
diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index 9ec1eb5..2cb7af0 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -81,7 +81,7 @@
     virtual status_t        setVideoTarget(const sp<IGraphicBufferProducer>& bufferProducer);
     virtual status_t        setAudioRestriction(int mode);
     virtual int32_t         getGlobalAudioRestriction();
-    virtual status_t        setRotateAndCropOverride(uint8_t rotateAndCrop);
+    virtual status_t        setRotateAndCropOverride(uint8_t rotateAndCrop, bool fromHal = false);
     virtual status_t        setAutoframingOverride(uint8_t autoframingMode);
 
     virtual bool            supportsCameraMute();
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 939f969..2e6eb06 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -18,6 +18,7 @@
 #define ATRACE_TAG ATRACE_TAG_CAMERA
 //#define LOG_NDEBUG 0
 
+#include <com_android_internal_camera_flags.h>
 #include <cutils/properties.h>
 #include <utils/CameraThreadState.h>
 #include <utils/Log.h>
@@ -55,6 +56,8 @@
 using namespace camera3;
 using camera3::camera_stream_rotation_t::CAMERA_STREAM_ROTATION_0;
 
+namespace flags = com::android::internal::camera::flags;
+
 CameraDeviceClientBase::CameraDeviceClientBase(
         const sp<CameraService>& cameraService,
         const sp<hardware::camera2::ICameraDeviceCallbacks>& remoteCallback,
@@ -539,6 +542,13 @@
         if (entry.count == 1) {
             mVideoStabilizationMode = entry.data.u8[0];
         }
+        if (flags::log_ultrawide_usage()) {
+            entry = physicalSettingsList.begin()->metadata.find(
+                    ANDROID_CONTROL_ZOOM_RATIO);
+            if (entry.count == 1 && entry.data.f[0] < 1.0f ) {
+                mUsedUltraWide = true;
+            }
+        }
     }
     mRequestIdCounter++;
 
@@ -1762,11 +1772,11 @@
     return mDevice->setCameraServiceWatchdog(enabled);
 }
 
-status_t CameraDeviceClient::setRotateAndCropOverride(uint8_t rotateAndCrop) {
+status_t CameraDeviceClient::setRotateAndCropOverride(uint8_t rotateAndCrop, bool fromHal) {
     if (rotateAndCrop > ANDROID_SCALER_ROTATE_AND_CROP_AUTO) return BAD_VALUE;
 
     return mDevice->setRotateAndCropAutoBehavior(
-        static_cast<camera_metadata_enum_android_scaler_rotate_and_crop_t>(rotateAndCrop));
+        static_cast<camera_metadata_enum_android_scaler_rotate_and_crop_t>(rotateAndCrop), fromHal);
 }
 
 status_t CameraDeviceClient::setAutoframingOverride(uint8_t autoframingValue) {
@@ -2051,7 +2061,7 @@
         }
     }
     Camera2ClientBase::notifyIdleWithUserTag(requestCount, resultErrorCount, deviceError,
-            fullStreamStats, mUserTag, mVideoStabilizationMode);
+            fullStreamStats, mUserTag, mVideoStabilizationMode, mUsedUltraWide);
 }
 
 void CameraDeviceClient::notifyShutter(const CaptureResultExtras& resultExtras,
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 86a94e2..c2870aa 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -198,7 +198,8 @@
     virtual status_t      initialize(sp<CameraProviderManager> manager,
             const std::string& monitorTags) override;
 
-    virtual status_t      setRotateAndCropOverride(uint8_t rotateAndCrop) override;
+    virtual status_t      setRotateAndCropOverride(uint8_t rotateAndCrop,
+            bool fromHal = false) override;
 
     virtual status_t      setAutoframingOverride(uint8_t autoframingValue) override;
 
@@ -369,6 +370,8 @@
     std::string mUserTag;
     // The last set video stabilization mode
     int mVideoStabilizationMode = -1;
+    // Whether a zoom_ratio < 1.0 has been used during this session
+    bool mUsedUltraWide = false;
 
     // This only exists in case of camera ID Remapping.
     const std::string mOriginalCameraId;
diff --git a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
index 99bdb0e..4ed352d 100644
--- a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
@@ -80,7 +80,8 @@
     return OK;
 }
 
-status_t CameraOfflineSessionClient::setRotateAndCropOverride(uint8_t /*rotateAndCrop*/) {
+status_t CameraOfflineSessionClient::setRotateAndCropOverride(uint8_t /*rotateAndCrop*/,
+        bool /*fromHal*/) {
     // Since we're not submitting more capture requests, changes to rotateAndCrop override
     // make no difference.
     return OK;
diff --git a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
index 70bad03..8aad4e9 100644
--- a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
+++ b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
@@ -80,7 +80,7 @@
     status_t initialize(sp<CameraProviderManager> /*manager*/,
             const std::string& /*monitorTags*/) override;
 
-    status_t setRotateAndCropOverride(uint8_t rotateAndCrop) override;
+    status_t setRotateAndCropOverride(uint8_t rotateAndCrop, bool fromHal = false) override;
 
     status_t setAutoframingOverride(uint8_t autoframingValue) override;
 
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index a611cc6..5c88cfa 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -370,7 +370,8 @@
             rotateAndCropMode = ANDROID_SCALER_ROTATE_AND_CROP_90;
         }
 
-        static_cast<TClientBase *>(this)->setRotateAndCropOverride(rotateAndCropMode);
+        static_cast<TClientBase *>(this)->setRotateAndCropOverride(rotateAndCropMode,
+                                                                   /*fromHal*/ true);
     }
 }
 
@@ -395,7 +396,7 @@
 void Camera2ClientBase<TClientBase>::notifyIdleWithUserTag(
         int64_t requestCount, int64_t resultErrorCount, bool deviceError,
         const std::vector<hardware::CameraStreamStats>& streamStats,
-        const std::string& userTag, int videoStabilizationMode) {
+        const std::string& userTag, int videoStabilizationMode, bool usedUltraWide) {
     if (mDeviceActive) {
         status_t res = TClientBase::finishCameraStreamingOps();
         if (res != OK) {
@@ -404,7 +405,7 @@
         }
         mCameraServiceProxyWrapper->logIdle(TClientBase::mCameraIdStr,
                 requestCount, resultErrorCount, deviceError, userTag, videoStabilizationMode,
-                streamStats);
+                usedUltraWide, streamStats);
     }
     mDeviceActive = false;
 
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index 88d65dc..226881a 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -97,7 +97,8 @@
     void                  notifyIdleWithUserTag(int64_t requestCount, int64_t resultErrorCount,
                                      bool deviceError,
                                      const std::vector<hardware::CameraStreamStats>& streamStats,
-                                     const std::string& userTag, int videoStabilizationMode);
+                                     const std::string& userTag, int videoStabilizationMode,
+                                     bool usedUltraWide);
 
     int                   getCameraId() const;
     const sp<CameraDeviceBase>&
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 017da0f..01199af 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -444,7 +444,8 @@
      * and defaults to NONE.
      */
     virtual status_t setRotateAndCropAutoBehavior(
-            camera_metadata_enum_android_scaler_rotate_and_crop_t rotateAndCropValue) = 0;
+            camera_metadata_enum_android_scaler_rotate_and_crop_t rotateAndCropValue,
+            bool fromHal = false) = 0;
 
     /**
      * Set the current behavior for the AUTOFRAMING control when in AUTO.
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index e2181ec..6765c1d 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -70,6 +70,7 @@
 #include "utils/TraceHFR.h"
 
 #include <algorithm>
+#include <optional>
 #include <tuple>
 
 using namespace android::camera3;
@@ -5394,9 +5395,13 @@
 }
 
 status_t Camera3Device::setRotateAndCropAutoBehavior(
-    camera_metadata_enum_android_scaler_rotate_and_crop_t rotateAndCropValue) {
+    camera_metadata_enum_android_scaler_rotate_and_crop_t rotateAndCropValue, bool fromHal) {
     ATRACE_CALL();
-    Mutex::Autolock il(mInterfaceLock);
+    // We shouldn't hold mInterfaceLock when called as an effect of a HAL
+    // callback since this can lead to a deadlock : b/299348355.
+    // mLock still protects state.
+    std::optional<Mutex::Autolock> maybeMutex =
+        fromHal ? std::nullopt : std::optional<Mutex::Autolock>(mInterfaceLock);
     Mutex::Autolock l(mLock);
     if (mRequestThread == nullptr) {
         return INVALID_OPERATION;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index a890c2b..d812c01 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -275,7 +275,7 @@
      * and defaults to NONE.
      */
     status_t setRotateAndCropAutoBehavior(
-            camera_metadata_enum_android_scaler_rotate_and_crop_t rotateAndCropValue);
+            camera_metadata_enum_android_scaler_rotate_and_crop_t rotateAndCropValue, bool fromHal);
 
     /**
      * Set the current behavior for the AUTOFRAMING control when in AUTO.
diff --git a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
index d07bf6d..d227606 100644
--- a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
+++ b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
@@ -94,7 +94,7 @@
 void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onIdle(
         sp<hardware::ICameraServiceProxy>& proxyBinder,
         int64_t requestCount, int64_t resultErrorCount, bool deviceError,
-        const std::string& userTag, int32_t videoStabilizationMode,
+        const std::string& userTag, int32_t videoStabilizationMode, bool usedUltraWide,
         const std::vector<hardware::CameraStreamStats>& streamStats) {
     Mutex::Autolock l(mLock);
 
@@ -104,6 +104,7 @@
     mSessionStats.mDeviceError = deviceError;
     mSessionStats.mUserTag = userTag;
     mSessionStats.mVideoStabilizationMode = videoStabilizationMode;
+    mSessionStats.mUsedUltraWide = usedUltraWide;
     mSessionStats.mStreamStats = streamStats;
 
     updateProxyDeviceState(proxyBinder);
@@ -278,7 +279,7 @@
 
 void CameraServiceProxyWrapper::logIdle(const std::string& id,
         int64_t requestCount, int64_t resultErrorCount, bool deviceError,
-        const std::string& userTag, int32_t videoStabilizationMode,
+        const std::string& userTag, int32_t videoStabilizationMode, bool usedUltraWide,
         const std::vector<hardware::CameraStreamStats>& streamStats) {
     std::shared_ptr<CameraSessionStatsWrapper> sessionStats;
     {
@@ -304,7 +305,7 @@
 
     sp<hardware::ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
     sessionStats->onIdle(proxyBinder, requestCount, resultErrorCount, deviceError, userTag,
-            videoStabilizationMode, streamStats);
+            videoStabilizationMode, usedUltraWide, streamStats);
 }
 
 void CameraServiceProxyWrapper::logOpen(const std::string& id, int facing,
diff --git a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
index 1afe5b3..4e21e58 100644
--- a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
+++ b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
@@ -61,7 +61,7 @@
         void onActive(sp<hardware::ICameraServiceProxy>& proxyBinder, float maxPreviewFps);
         void onIdle(sp<hardware::ICameraServiceProxy>& proxyBinder,
                 int64_t requestCount, int64_t resultErrorCount, bool deviceError,
-                const std::string& userTag, int32_t videoStabilizationMode,
+                const std::string& userTag, int32_t videoStabilizationMode, bool usedUltraWide,
                 const std::vector<hardware::CameraStreamStats>& streamStats);
 
         std::string updateExtensionSessionStats(
@@ -110,7 +110,7 @@
     // Session state becomes idle
     void logIdle(const std::string& id,
             int64_t requestCount, int64_t resultErrorCount, bool deviceError,
-            const std::string& userTag, int32_t videoStabilizationMode,
+            const std::string& userTag, int32_t videoStabilizationMode, bool usedUltraWide,
             const std::vector<hardware::CameraStreamStats>& streamStats);
 
     // Ping camera service proxy for user update