Merge "Audioflinger: adjust wait time for duplicating thread" into main
diff --git a/Android.bp b/Android.bp
index 0c7ed6e..c716a06 100644
--- a/Android.bp
+++ b/Android.bp
@@ -135,6 +135,7 @@
 
 aidl_interface {
     name: "av-audio-types-aidl",
+    unstable: true,
     host_supported: true,
     vendor_available: true,
     double_loadable: true,
@@ -154,28 +155,4 @@
             sdk_version: "module_current",
         },
     },
-    versions_with_info: [
-        {
-            version: "1",
-            imports: ["android.hardware.audio.core-V2"],
-        },
-    ],
-    frozen: false,
-
-}
-
-latest_av_audio_types_aidl = "av-audio-types-aidl-V2"
-
-cc_defaults {
-    name: "latest_av_audio_types_aidl_ndk_shared",
-    shared_libs: [
-        latest_av_audio_types_aidl + "-ndk",
-    ],
-}
-
-cc_defaults {
-    name: "latest_av_audio_types_aidl_ndk_static",
-    static_libs: [
-        latest_av_audio_types_aidl + "-ndk",
-    ],
 }
diff --git a/aidl_api/av-audio-types-aidl/1/.hash b/aidl_api/av-audio-types-aidl/1/.hash
deleted file mode 100644
index 0002682..0000000
--- a/aidl_api/av-audio-types-aidl/1/.hash
+++ /dev/null
@@ -1 +0,0 @@
-ef1bc5ed9db445fbfc116cdec6e6ad081458ee40
diff --git a/aidl_api/av-audio-types-aidl/1/android/media/audio/IHalAdapterVendorExtension.aidl b/aidl_api/av-audio-types-aidl/1/android/media/audio/IHalAdapterVendorExtension.aidl
deleted file mode 100644
index a9aa2c1..0000000
--- a/aidl_api/av-audio-types-aidl/1/android/media/audio/IHalAdapterVendorExtension.aidl
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.
- */
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a snapshot of an AIDL file. Do not edit it manually. There are
-// two cases:
-// 1). this is a frozen version file - do not edit this in any case.
-// 2). this is a 'current' file. If you make a backwards compatible change to
-//     the interface (from the latest frozen version), the build system will
-//     prompt you to update this file with `m <name>-update-api`.
-//
-// You must not make a backward incompatible change to any AIDL file built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.media.audio;
-/* @hide */
-interface IHalAdapterVendorExtension {
-  @utf8InCpp String[] parseVendorParameterIds(android.media.audio.IHalAdapterVendorExtension.ParameterScope scope, in @utf8InCpp String rawKeys);
-  void parseVendorParameters(android.media.audio.IHalAdapterVendorExtension.ParameterScope scope, in @utf8InCpp String rawKeysAndValues, out android.hardware.audio.core.VendorParameter[] syncParameters, out android.hardware.audio.core.VendorParameter[] asyncParameters);
-  android.hardware.audio.core.VendorParameter[] parseBluetoothA2dpReconfigureOffload(in @utf8InCpp String rawValue);
-  android.hardware.audio.core.VendorParameter[] parseBluetoothLeReconfigureOffload(in @utf8InCpp String rawValue);
-  @utf8InCpp String processVendorParameters(android.media.audio.IHalAdapterVendorExtension.ParameterScope scope, in android.hardware.audio.core.VendorParameter[] parameters);
-  enum ParameterScope {
-    MODULE = 0,
-    STREAM = 1,
-  }
-}
diff --git a/aidl_api/av-audio-types-aidl/current/android/media/audio/IHalAdapterVendorExtension.aidl b/aidl_api/av-audio-types-aidl/current/android/media/audio/IHalAdapterVendorExtension.aidl
deleted file mode 100644
index a9aa2c1..0000000
--- a/aidl_api/av-audio-types-aidl/current/android/media/audio/IHalAdapterVendorExtension.aidl
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.
- */
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a snapshot of an AIDL file. Do not edit it manually. There are
-// two cases:
-// 1). this is a frozen version file - do not edit this in any case.
-// 2). this is a 'current' file. If you make a backwards compatible change to
-//     the interface (from the latest frozen version), the build system will
-//     prompt you to update this file with `m <name>-update-api`.
-//
-// You must not make a backward incompatible change to any AIDL file built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.media.audio;
-/* @hide */
-interface IHalAdapterVendorExtension {
-  @utf8InCpp String[] parseVendorParameterIds(android.media.audio.IHalAdapterVendorExtension.ParameterScope scope, in @utf8InCpp String rawKeys);
-  void parseVendorParameters(android.media.audio.IHalAdapterVendorExtension.ParameterScope scope, in @utf8InCpp String rawKeysAndValues, out android.hardware.audio.core.VendorParameter[] syncParameters, out android.hardware.audio.core.VendorParameter[] asyncParameters);
-  android.hardware.audio.core.VendorParameter[] parseBluetoothA2dpReconfigureOffload(in @utf8InCpp String rawValue);
-  android.hardware.audio.core.VendorParameter[] parseBluetoothLeReconfigureOffload(in @utf8InCpp String rawValue);
-  @utf8InCpp String processVendorParameters(android.media.audio.IHalAdapterVendorExtension.ParameterScope scope, in android.hardware.audio.core.VendorParameter[] parameters);
-  enum ParameterScope {
-    MODULE = 0,
-    STREAM = 1,
-  }
-}
diff --git a/apex/Android.bp b/apex/Android.bp
index b0d7c02..356bf03 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -173,6 +173,7 @@
         "mediaswcodec",
     ],
     native_shared_libs: [
+        "libapexcodecs",
         "libcodec2_hidl@1.0",
         "libcodec2_hidl@1.1",
         "libcodec2_hidl@1.2",
diff --git a/apex/ld.config.txt b/apex/ld.config.txt
index 4dc5fb1..c24d51f 100644
--- a/apex/ld.config.txt
+++ b/apex/ld.config.txt
@@ -33,7 +33,7 @@
 # TODO: replace the following when apex has a way to auto-generate this list
 # namespace.default.link.platform.shared_libs  = %LLNDK_LIBRARIES%
 # namespace.default.link.platform.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-namespace.default.link.platform.shared_libs = libEGL.so:libGLESv1_CM.so:libGLESv2.so:libGLESv3.so:libandroid_net.so:libc.so:libcgrouprc.so:libclang_rt.asan-aarch64-android.so:libclang_rt.asan-arm-android.so:libclang_rt.hwasan-aarch64-android.so:libclang_rt.asan-i686-android.so:libclang_rt.asan-x86_64-android.so:libdl.so:libft2.so:liblog.so:libm.so:libmediandk.so:libnativewindow.so:libneuralnetworks.so:libsync.so:libvndksupport.so:libdl_android.so:libvulkan.so:libbinder_ndk.so
+namespace.default.link.platform.shared_libs = libEGL.so:libGLESv1_CM.so:libGLESv2.so:libGLESv3.so:libandroid_net.so:libc.so:libclang_rt.asan-aarch64-android.so:libclang_rt.asan-arm-android.so:libclang_rt.hwasan-aarch64-android.so:libclang_rt.asan-i686-android.so:libclang_rt.asan-x86_64-android.so:libdl.so:libft2.so:liblog.so:libm.so:libmediandk.so:libnativewindow.so:libneuralnetworks.so:libsync.so:libvndksupport.so:libdl_android.so:libvulkan.so:libbinder_ndk.so
 
 ###############################################################################
 # "platform" namespace
@@ -138,7 +138,7 @@
 # TODO: replace the following when apex has a way to auto-generate this list
 # namespace.sphal.link.platform.shared_libs  = %LLNDK_LIBRARIES%
 # namespace.sphal.link.platform.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-namespace.sphal.link.platform.shared_libs = libEGL.so:libGLESv1_CM.so:libGLESv2.so:libGLESv3.so:libandroid_net.so:libc.so:libcgrouprc.so:libclang_rt.asan-aarch64-android.so:libclang_rt.asan-arm-android.so:libclang_rt.hwasan-aarch64-android.so:libclang_rt.asan-i686-android.so:libclang_rt.asan-x86_64-android.so:libdl.so:libft2.so:liblog.so:libm.so:libmediandk.so:libnativewindow.so:libneuralnetworks.so:libsync.so:libvndksupport.so:libvulkan.so:libbinder_ndk.so
+namespace.sphal.link.platform.shared_libs = libEGL.so:libGLESv1_CM.so:libGLESv2.so:libGLESv3.so:libandroid_net.so:libc.so:libclang_rt.asan-aarch64-android.so:libclang_rt.asan-arm-android.so:libclang_rt.hwasan-aarch64-android.so:libclang_rt.asan-i686-android.so:libclang_rt.asan-x86_64-android.so:libdl.so:libft2.so:liblog.so:libm.so:libmediandk.so:libnativewindow.so:libneuralnetworks.so:libsync.so:libvndksupport.so:libvulkan.so:libbinder_ndk.so
 
 # Add a link for libz.so which is llndk on devices where VNDK is not enforced.
 namespace.sphal.link.platform.shared_libs += libz.so
diff --git a/cmds/stagefright/AudioPlayer.cpp b/cmds/stagefright/AudioPlayer.cpp
index 6cddf47..54885ef 100644
--- a/cmds/stagefright/AudioPlayer.cpp
+++ b/cmds/stagefright/AudioPlayer.cpp
@@ -101,6 +101,10 @@
 
     CHECK(mFirstBuffer == NULL);
 
+    if (!mAudioPlayerWrapper) {
+        mAudioPlayerWrapper = sp<MediaPlayerBase::WeakWrapper<AudioPlayer>>::make(this);
+    }
+
     MediaSource::ReadOptions options;
     if (mSeeking) {
         options.setSeekTo(mSeekTimeUs);
@@ -203,7 +207,7 @@
                 mSampleRate, numChannels, channelMask, audioFormat,
                 DEFAULT_AUDIOSINK_BUFFERCOUNT,
                 &AudioPlayer::AudioSinkCallback,
-                this,
+                mAudioPlayerWrapper,
                 (audio_output_flags_t)flags,
                 useOffload() ? &offloadInfo : NULL);
 
@@ -430,10 +434,11 @@
 
 // static
 size_t AudioPlayer::AudioSinkCallback(
-        MediaPlayerBase::AudioSink * /* audioSink */,
-        void *buffer, size_t size, void *cookie,
+        const sp<MediaPlayerBase::AudioSink>& /* audioSink */,
+        void *buffer, size_t size, const wp<RefBase>& cookie,
         MediaPlayerBase::AudioSink::cb_event_t event) {
-    AudioPlayer *me = (AudioPlayer *)cookie;
+    const auto me = MediaPlayerBase::WeakWrapper<AudioPlayer>::promoteFromRefBase(cookie);
+    if (!me) return 0;
 
     switch(event) {
     case MediaPlayerBase::AudioSink::CB_EVENT_FILL_BUFFER:
diff --git a/cmds/stagefright/AudioPlayer.h b/cmds/stagefright/AudioPlayer.h
index 608f54b..877ac13 100644
--- a/cmds/stagefright/AudioPlayer.h
+++ b/cmds/stagefright/AudioPlayer.h
@@ -29,7 +29,7 @@
 
 struct AwesomePlayer;
 
-class AudioPlayer : AudioTrack::IAudioTrackCallback {
+class AudioPlayer : public AudioTrack::IAudioTrackCallback {
 public:
     enum {
         REACHED_EOS,
@@ -97,14 +97,15 @@
     MediaBufferBase *mFirstBuffer;
 
     sp<MediaPlayerBase::AudioSink> mAudioSink;
+    sp<MediaPlayerBase::WeakWrapper<AudioPlayer>> mAudioPlayerWrapper;
 
     bool mPlaying;
     int64_t mStartPosUs;
     const uint32_t mCreateFlags;
 
     static size_t AudioSinkCallback(
-            MediaPlayerBase::AudioSink *audioSink,
-            void *data, size_t size, void *me,
+            const sp<MediaPlayerBase::AudioSink>& audioSink,
+            void *data, size_t size, const wp<RefBase>& me,
             MediaPlayerBase::AudioSink::cb_event_t event);
 
     size_t fillBuffer(void *data, size_t size);
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index 754f066..eaa5bd5 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -288,10 +288,10 @@
 }
 
 DrmStatus DrmHal::getSupportedSchemes(std::vector<uint8_t>& schemes) const {
-    status_t statusResult;
-    statusResult = mDrmHalAidl->getSupportedSchemes(schemes);
-    if (statusResult == OK) return statusResult;
-    return mDrmHalHidl->getSupportedSchemes(schemes);
+    const DrmStatus statusResultAidl = mDrmHalAidl->getSupportedSchemes(schemes);
+    const DrmStatus statusResultHidl = mDrmHalHidl->getSupportedSchemes(schemes);
+    if (statusResultHidl == OK || statusResultAidl == OK) return OK;
+    return statusResultAidl;
 }
 
 }  // namespace android
diff --git a/drm/libmediadrm/DrmHalHidl.cpp b/drm/libmediadrm/DrmHalHidl.cpp
index c8c6e8e..33ea5ea 100644
--- a/drm/libmediadrm/DrmHalHidl.cpp
+++ b/drm/libmediadrm/DrmHalHidl.cpp
@@ -339,7 +339,7 @@
             DrmUtils::LOG2BI("makeDrmFactories: using default passthrough drm instance");
             factories.push_back(passthrough);
         } else {
-            DrmUtils::LOG2BE("Failed to find passthrough drm factories");
+            DrmUtils::LOG2BW("Failed to find passthrough drm factories");
         }
     }
     return factories;
diff --git a/media/OWNERS b/media/OWNERS
index b926075..5e32047 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -1,6 +1,7 @@
 # Bug component: 1344
 elaurent@google.com
 essick@google.com
+atneya@google.com
 hunga@google.com
 jiabin@google.com
 jmtrivi@google.com
diff --git a/media/aconfig/Android.bp b/media/aconfig/Android.bp
index 16beb28..1e5eafb 100644
--- a/media/aconfig/Android.bp
+++ b/media/aconfig/Android.bp
@@ -50,3 +50,22 @@
     ],
     aconfig_declarations: "aconfig_codec_fwk_flags",
 }
+
+aconfig_declarations {
+    name: "aconfig_media_swcodec_flags",
+    package: "android.media.swcodec.flags",
+    container: "com.android.media.swcodec",
+    srcs: ["swcodec_flags.aconfig"],
+}
+
+cc_aconfig_library {
+    name: "android.media.swcodec.flags-aconfig-cc",
+    aconfig_declarations: "aconfig_media_swcodec_flags",
+    min_sdk_version: "apex_inherit",
+    vendor_available: true,
+    double_loadable: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media.swcodec",
+    ],
+}
diff --git a/media/aconfig/codec_fwk.aconfig b/media/aconfig/codec_fwk.aconfig
index c061cf8..af82982 100644
--- a/media/aconfig/codec_fwk.aconfig
+++ b/media/aconfig/codec_fwk.aconfig
@@ -7,6 +7,7 @@
 
 flag {
   name: "aidl_hal_input_surface"
+  is_exported: true
   namespace: "codec_fwk"
   description: "Feature flags for enabling AIDL HAL InputSurface handling"
   bug: "201479783"
@@ -21,6 +22,20 @@
 }
 
 flag {
+  name: "codec_availability"
+  namespace: "codec_fwk"
+  description: "Feature flag for codec availability HAL API support"
+  bug: "363282971"
+}
+
+flag {
+  name: "codec_availability_support"
+  namespace: "codec_fwk"
+  description: "Feature flag for codec availability HAL API implementation"
+  bug: "363282971"
+}
+
+flag {
   name: "codec_buffer_state_cleanup"
   namespace: "codec_fwk"
   description: "Bugfix flag for more buffer state cleanup in MediaCodec"
@@ -111,6 +126,21 @@
 }
 
 flag {
+  name: "num_input_slots"
+  namespace: "codec_fwk"
+  description: "Feature flag for exposing number of input slots"
+  bug: "159891571"
+}
+
+flag {
+  name: "p210_format_support"
+  is_exported: true
+  namespace: "codec_fwk"
+  description: "Feature flag for Android support for P210 YCbCr format"
+  bug: "368395888"
+}
+
+flag {
   name: "region_of_interest"
   is_exported: true
   namespace: "codec_fwk"
@@ -173,6 +203,13 @@
 }
 
 flag {
+  name: "subsession_metrics"
+  namespace: "codec_fwk"
+  description: "Feature flag for subsession codec metrics"
+  bug: "363382811"
+}
+
+flag {
   name: "teamfood"
   namespace: "codec_fwk"
   description: "Feature flag to track teamfood population"
diff --git a/media/aconfig/swcodec_flags.aconfig b/media/aconfig/swcodec_flags.aconfig
new file mode 100644
index 0000000..9dd1fdd
--- /dev/null
+++ b/media/aconfig/swcodec_flags.aconfig
@@ -0,0 +1,23 @@
+# Media SW Codec Flags.
+#
+# !!! Please add flags in alphabetical order. !!!
+package: "android.media.swcodec.flags"
+container: "com.android.media.swcodec"
+
+flag {
+  name: "apv_software_codec"
+  is_exported: true
+  is_fixed_read_only: true
+  namespace: "codec_fwk"
+  description: "Feature flag for APV Software C2 codec"
+  bug: "376770121"
+}
+
+flag {
+  name: "mpeg2_keep_threads_active"
+  is_exported: true
+  is_fixed_read_only: true
+  namespace: "codec_fwk"
+  description: "Enable keep_threads_active in mpeg2 decoder"
+  bug: "343793479"
+}
diff --git a/media/audio/aconfig/Android.bp b/media/audio/aconfig/Android.bp
index a5aeff2..5f4a6a1 100644
--- a/media/audio/aconfig/Android.bp
+++ b/media/audio/aconfig/Android.bp
@@ -116,7 +116,11 @@
     package: "android.media.audio",
     container: "system",
     srcs: ["audio_framework.aconfig"],
-    visibility: ["//frameworks/base/api"],
+    visibility: [
+        "//frameworks/base/api",
+        "//frameworks/base/core/res",
+    ],
+    exportable: true,
 }
 
 aconfig_declarations {
@@ -149,6 +153,20 @@
 }
 
 java_aconfig_library {
+    name: "android.media.audio-aconfig-exported-java",
+    aconfig_declarations: "android.media.audio-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+    min_sdk_version: "Tiramisu",
+    mode: "exported",
+    apex_available: [
+        "com.android.btservices",
+    ],
+    visibility: [
+        "//packages/modules/Bluetooth:__subpackages__",
+    ],
+}
+
+java_aconfig_library {
     name: "android.media.audiopolicy-aconfig-java",
     aconfig_declarations: "android.media.audiopolicy-aconfig",
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
diff --git a/media/audio/aconfig/audio_framework.aconfig b/media/audio/aconfig/audio_framework.aconfig
index ea5f26d..b155bac 100644
--- a/media/audio/aconfig/audio_framework.aconfig
+++ b/media/audio/aconfig/audio_framework.aconfig
@@ -23,6 +23,14 @@
 }
 
 flag {
+    name: "deprecate_stream_bt_sco"
+    namespace: "media_audio"
+    description: "Deprecate STREAM_BLUETOOTH_SCO"
+    is_exported: true
+    bug: "376756660"
+}
+
+flag {
     name: "feature_spatial_audio_headtracking_low_latency"
     is_exported: true
     namespace: "media_audio"
diff --git a/media/audioaidlconversion/AidlConversionCppNdk.cpp b/media/audioaidlconversion/AidlConversionCppNdk.cpp
index 90996a3..0682f65 100644
--- a/media/audioaidlconversion/AidlConversionCppNdk.cpp
+++ b/media/audioaidlconversion/AidlConversionCppNdk.cpp
@@ -76,8 +76,6 @@
 using media::audio::common::AudioOffloadInfo;
 using media::audio::common::AudioOutputFlags;
 using media::audio::common::AudioPlaybackRate;
-using media::audio::common::AudioPolicyForcedConfig;
-using media::audio::common::AudioPolicyForceUse;
 using media::audio::common::AudioPort;
 using media::audio::common::AudioPortConfig;
 using media::audio::common::AudioPortDeviceExt;
@@ -2315,6 +2313,15 @@
     audio_port_config_device_ext legacy{};
     RETURN_IF_ERROR(aidl2legacy_AudioDevice_audio_device(
                     aidl.device, &legacy.type, legacy.address));
+    const bool isInput = false;  // speaker_layout_channel_mask only represents output.
+    if (aidl.speakerLayout.has_value()) {
+        legacy.speaker_layout_channel_mask =
+                VALUE_OR_RETURN(aidl2legacy_AudioChannelLayout_audio_channel_mask_t(
+                        aidl.speakerLayout.value(), isInput));
+    } else {
+        // Default to none when the field is null in the AIDL.
+        legacy.speaker_layout_channel_mask = AUDIO_CHANNEL_NONE;
+    }
     return legacy;
 }
 
@@ -2323,6 +2330,14 @@
     AudioPortDeviceExt aidl;
     aidl.device = VALUE_OR_RETURN(
             legacy2aidl_audio_device_AudioDevice(legacy.type, legacy.address));
+    const bool isInput = false;  // speaker_layout_channel_mask only represents output.
+    // The AIDL speakerLayout is nullable and if set, can only be a layoutMask.
+    if (audio_channel_mask_is_valid(legacy.speaker_layout_channel_mask) &&
+        audio_channel_mask_get_representation(legacy.speaker_layout_channel_mask) ==
+                AUDIO_CHANNEL_REPRESENTATION_POSITION) {
+        aidl.speakerLayout = VALUE_OR_RETURN(legacy2aidl_audio_channel_mask_t_AudioChannelLayout(
+                legacy.speaker_layout_channel_mask, isInput));
+    }
     return aidl;
 }
 
@@ -3305,138 +3320,6 @@
     return OK;
 }
 
-ConversionResult<audio_policy_force_use_t>
-aidl2legacy_AudioPolicyForceUse_audio_policy_force_use_t(AudioPolicyForceUse aidl) {
-    switch (aidl) {
-        case AudioPolicyForceUse::COMMUNICATION:
-            return AUDIO_POLICY_FORCE_FOR_COMMUNICATION;
-        case AudioPolicyForceUse::MEDIA:
-            return AUDIO_POLICY_FORCE_FOR_MEDIA;
-        case AudioPolicyForceUse::RECORD:
-            return AUDIO_POLICY_FORCE_FOR_RECORD;
-        case AudioPolicyForceUse::DOCK:
-            return AUDIO_POLICY_FORCE_FOR_DOCK;
-        case AudioPolicyForceUse::SYSTEM:
-            return AUDIO_POLICY_FORCE_FOR_SYSTEM;
-        case AudioPolicyForceUse::HDMI_SYSTEM_AUDIO:
-            return AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO;
-        case AudioPolicyForceUse::ENCODED_SURROUND:
-            return AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND;
-        case AudioPolicyForceUse::VIBRATE_RINGING:
-            return AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING;
-    }
-    return unexpected(BAD_VALUE);
-}
-
-ConversionResult<AudioPolicyForceUse>
-legacy2aidl_audio_policy_force_use_t_AudioPolicyForceUse(audio_policy_force_use_t legacy) {
-    switch (legacy) {
-        case AUDIO_POLICY_FORCE_FOR_COMMUNICATION:
-            return AudioPolicyForceUse::COMMUNICATION;
-        case AUDIO_POLICY_FORCE_FOR_MEDIA:
-            return AudioPolicyForceUse::MEDIA;
-        case AUDIO_POLICY_FORCE_FOR_RECORD:
-            return AudioPolicyForceUse::RECORD;
-        case AUDIO_POLICY_FORCE_FOR_DOCK:
-            return AudioPolicyForceUse::DOCK;
-        case AUDIO_POLICY_FORCE_FOR_SYSTEM:
-            return AudioPolicyForceUse::SYSTEM;
-        case AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO:
-            return AudioPolicyForceUse::HDMI_SYSTEM_AUDIO;
-        case AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND:
-            return AudioPolicyForceUse::ENCODED_SURROUND;
-        case AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING:
-            return AudioPolicyForceUse::VIBRATE_RINGING;
-        case AUDIO_POLICY_FORCE_USE_CNT:
-            break;
-    }
-    return unexpected(BAD_VALUE);
-}
-
-ConversionResult<audio_policy_forced_cfg_t>
-aidl2legacy_AudioPolicyForcedConfig_audio_policy_forced_cfg_t(AudioPolicyForcedConfig aidl) {
-    switch (aidl) {
-        case AudioPolicyForcedConfig::NONE:
-            return AUDIO_POLICY_FORCE_NONE;
-        case AudioPolicyForcedConfig::SPEAKER:
-            return AUDIO_POLICY_FORCE_SPEAKER;
-        case AudioPolicyForcedConfig::HEADPHONES:
-            return AUDIO_POLICY_FORCE_HEADPHONES;
-        case AudioPolicyForcedConfig::BT_SCO:
-            return AUDIO_POLICY_FORCE_BT_SCO;
-        case AudioPolicyForcedConfig::BT_A2DP:
-            return AUDIO_POLICY_FORCE_BT_A2DP;
-        case AudioPolicyForcedConfig::WIRED_ACCESSORY:
-            return AUDIO_POLICY_FORCE_WIRED_ACCESSORY;
-        case AudioPolicyForcedConfig::BT_CAR_DOCK:
-            return AUDIO_POLICY_FORCE_BT_CAR_DOCK;
-        case AudioPolicyForcedConfig::BT_DESK_DOCK:
-            return AUDIO_POLICY_FORCE_BT_DESK_DOCK;
-        case AudioPolicyForcedConfig::ANALOG_DOCK:
-            return AUDIO_POLICY_FORCE_ANALOG_DOCK;
-        case AudioPolicyForcedConfig::DIGITAL_DOCK:
-            return AUDIO_POLICY_FORCE_DIGITAL_DOCK;
-        case AudioPolicyForcedConfig::NO_BT_A2DP:
-            return AUDIO_POLICY_FORCE_NO_BT_A2DP;
-        case AudioPolicyForcedConfig::SYSTEM_ENFORCED:
-            return AUDIO_POLICY_FORCE_SYSTEM_ENFORCED;
-        case AudioPolicyForcedConfig::HDMI_SYSTEM_AUDIO_ENFORCED:
-            return AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED;
-        case AudioPolicyForcedConfig::ENCODED_SURROUND_NEVER:
-            return AUDIO_POLICY_FORCE_ENCODED_SURROUND_NEVER;
-        case AudioPolicyForcedConfig::ENCODED_SURROUND_ALWAYS:
-            return AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS;
-        case AudioPolicyForcedConfig::ENCODED_SURROUND_MANUAL:
-            return AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL;
-        case AudioPolicyForcedConfig::BT_BLE:
-            return AUDIO_POLICY_FORCE_BT_BLE;
-    }
-    return unexpected(BAD_VALUE);
-}
-
-ConversionResult<AudioPolicyForcedConfig>
-legacy2aidl_audio_policy_forced_cfg_t_AudioPolicyForcedConfig(audio_policy_forced_cfg_t legacy) {
-    switch (legacy) {
-        case AUDIO_POLICY_FORCE_NONE:
-            return AudioPolicyForcedConfig::NONE;
-        case AUDIO_POLICY_FORCE_SPEAKER:
-            return AudioPolicyForcedConfig::SPEAKER;
-        case AUDIO_POLICY_FORCE_HEADPHONES:
-            return AudioPolicyForcedConfig::HEADPHONES;
-        case AUDIO_POLICY_FORCE_BT_SCO:
-            return AudioPolicyForcedConfig::BT_SCO;
-        case AUDIO_POLICY_FORCE_BT_A2DP:
-            return AudioPolicyForcedConfig::BT_A2DP;
-        case AUDIO_POLICY_FORCE_WIRED_ACCESSORY:
-            return AudioPolicyForcedConfig::WIRED_ACCESSORY;
-        case AUDIO_POLICY_FORCE_BT_CAR_DOCK:
-            return AudioPolicyForcedConfig::BT_CAR_DOCK;
-        case AUDIO_POLICY_FORCE_BT_DESK_DOCK:
-            return AudioPolicyForcedConfig::BT_DESK_DOCK;
-        case AUDIO_POLICY_FORCE_ANALOG_DOCK:
-            return AudioPolicyForcedConfig::ANALOG_DOCK;
-        case AUDIO_POLICY_FORCE_DIGITAL_DOCK:
-            return AudioPolicyForcedConfig::DIGITAL_DOCK;
-        case AUDIO_POLICY_FORCE_NO_BT_A2DP:
-            return AudioPolicyForcedConfig::NO_BT_A2DP;
-        case AUDIO_POLICY_FORCE_SYSTEM_ENFORCED:
-            return AudioPolicyForcedConfig::SYSTEM_ENFORCED;
-        case AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED:
-            return AudioPolicyForcedConfig::HDMI_SYSTEM_AUDIO_ENFORCED;
-        case AUDIO_POLICY_FORCE_ENCODED_SURROUND_NEVER:
-            return AudioPolicyForcedConfig::ENCODED_SURROUND_NEVER;
-        case AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS:
-            return AudioPolicyForcedConfig::ENCODED_SURROUND_ALWAYS;
-        case AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL:
-            return AudioPolicyForcedConfig::ENCODED_SURROUND_MANUAL;
-        case AUDIO_POLICY_FORCE_BT_BLE:
-            return AudioPolicyForcedConfig::BT_BLE;
-        case AUDIO_POLICY_FORCE_CFG_CNT:
-            break;
-    }
-    return unexpected(BAD_VALUE);
-}
-
 }  // namespace android
 
 #undef GET_DEVICE_DESC_CONNECTION
diff --git a/media/audioaidlconversion/include/media/AidlConversionCppNdk-impl.h b/media/audioaidlconversion/include/media/AidlConversionCppNdk-impl.h
index 9dfb7e7..7268464 100644
--- a/media/audioaidlconversion/include/media/AidlConversionCppNdk-impl.h
+++ b/media/audioaidlconversion/include/media/AidlConversionCppNdk-impl.h
@@ -58,8 +58,6 @@
 #include PREFIX(android/media/audio/common/AudioMode.h)
 #include PREFIX(android/media/audio/common/AudioOffloadInfo.h)
 #include PREFIX(android/media/audio/common/AudioOutputFlags.h)
-#include PREFIX(android/media/audio/common/AudioPolicyForceUse.h)
-#include PREFIX(android/media/audio/common/AudioPolicyForcedConfig.h)
 #include PREFIX(android/media/audio/common/AudioPort.h)
 #include PREFIX(android/media/audio/common/AudioPortConfig.h)
 #include PREFIX(android/media/audio/common/AudioPortExt.h)
@@ -78,7 +76,6 @@
 
 #include <system/audio.h>
 #include <system/audio_effect.h>
-#include <system/audio_policy.h>
 
 #if defined(BACKEND_NDK_IMPL)
 namespace aidl {
@@ -457,18 +454,6 @@
         media::audio::common::MicrophoneInfo* aidlInfo,
         media::audio::common::MicrophoneDynamicInfo* aidlDynamic);
 
-ConversionResult<audio_policy_forced_cfg_t>
-aidl2legacy_AudioPolicyForcedConfig_audio_policy_forced_cfg_t(
-        media::audio::common::AudioPolicyForcedConfig aidl);
-ConversionResult<media::audio::common::AudioPolicyForcedConfig>
-legacy2aidl_audio_policy_forced_cfg_t_AudioPolicyForcedConfig(audio_policy_forced_cfg_t legacy);
-
-ConversionResult<audio_policy_force_use_t>
-aidl2legacy_AudioPolicyForceUse_audio_policy_force_use_t(
-        media::audio::common::AudioPolicyForceUse aidl);
-ConversionResult<media::audio::common::AudioPolicyForceUse>
-legacy2aidl_audio_policy_force_use_t_AudioPolicyForceUse(audio_policy_force_use_t legacy);
-
 }  // namespace android
 
 #if defined(BACKEND_NDK_IMPL)
diff --git a/media/codec2/components/apv/Android.bp b/media/codec2/components/apv/Android.bp
new file mode 100644
index 0000000..f565978
--- /dev/null
+++ b/media/codec2/components/apv/Android.bp
@@ -0,0 +1,58 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_av_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_library {
+    enabled: true,
+    name: "libcodec2_soft_apvenc",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_signed-defaults",
+        "libcodec2_soft_sanitize_cfi-defaults",
+    ],
+
+    static_libs: [
+        "libopenapv",
+        "android.media.swcodec.flags-aconfig-cc",
+    ],
+
+    srcs: ["C2SoftApvEnc.cpp"],
+
+    cflags: [
+        "-DOAPV_STATIC_DEFINE",
+        "-Wno-unused-variable",
+        "-Wno-unused-parameter",
+        "-Wno-unused-function",
+        "-Wno-reorder-ctor",
+    ],
+}
+
+cc_library {
+    enabled: true,
+    name: "libcodec2_soft_apvdec",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_signed-defaults",
+        "libcodec2_soft_sanitize_cfi-defaults",
+    ],
+
+    static_libs: [
+        "libopenapv",
+        "android.media.swcodec.flags-aconfig-cc",
+    ],
+
+    srcs: ["C2SoftApvDec.cpp"],
+
+    cflags: [
+        "-DOAPV_STATIC_DEFINE",
+        "-Wno-unused-variable",
+        "-Wno-unused-parameter",
+        "-Wno-unused-function",
+        "-Wno-reorder-ctor",
+    ],
+}
diff --git a/media/codec2/components/apv/C2SoftApvCommon.h b/media/codec2/components/apv/C2SoftApvCommon.h
new file mode 100644
index 0000000..9325f28
--- /dev/null
+++ b/media/codec2/components/apv/C2SoftApvCommon.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_C2_SOFT_APV_COMMON_H__
+#define ANDROID_C2_SOFT_APV_COMMON_H__
+
+typedef enum {
+    PIX_CHROMA_NA = 0xFFFFFFFF,
+    PIX_YUV_420P = 0x1,
+    PIX_YUV_422P = 0x2,
+    PIX_420_UV_INTL = 0x3,
+    PIX_YUV_422IBE = 0x4,
+    PIX_YUV_422ILE = 0x5,
+    PIX_YUV_444P = 0x6,
+    PIX_YUV_411P = 0x7,
+    PIX_GRAY = 0x8,
+    PIX_RGB_565 = 0x9,
+    PIX_RGB_24 = 0xa,
+    PIX_YUV_420SP_UV = 0xb,
+    PIX_YUV_420SP_VU = 0xc,
+    PIX_YUV_422SP_UV = 0xd,
+    PIX_YUV_422SP_VU = 0xe
+} PIX_COLOR_FORMAT_T;
+
+#define CLIP_VAL(n, min, max) (((n) > (max)) ? (max) : (((n) < (min)) ? (min) : (n)))
+#define ALIGN_VAL(val, align) ((((val) + (align) - 1) / (align)) * (align))
+
+static int atomic_inc(volatile int* pcnt) {
+    int ret;
+    ret = *pcnt;
+    ret++;
+    *pcnt = ret;
+    return ret;
+}
+
+static int atomic_dec(volatile int* pcnt) {
+    int ret;
+    ret = *pcnt;
+    ret--;
+    *pcnt = ret;
+    return ret;
+}
+
+/* Function to allocate memory for picture buffer:
+   This function might need to modify according to O/S or CPU platform
+*/
+static void* picbuf_alloc(int size) {
+    return malloc(size);
+}
+
+/* Function to free memory allocated for picture buffer:
+   This function might need to modify according to O/S or CPU platform
+*/
+static void picbuf_free(void* p) {
+    if (p) {
+        free(p);
+    }
+}
+
+static int imgb_addref(oapv_imgb_t* imgb) {
+    return atomic_inc(&imgb->refcnt);
+}
+
+static int imgb_getref(oapv_imgb_t* imgb) {
+    return imgb->refcnt;
+}
+
+static int imgb_release(oapv_imgb_t* imgb) {
+    int refcnt, i;
+    refcnt = atomic_dec(&imgb->refcnt);
+    if (refcnt == 0) {
+        for (i = 0; i < OAPV_MAX_CC; i++) {
+            if (imgb->baddr[i]) picbuf_free(imgb->baddr[i]);
+        }
+        free(imgb);
+    }
+    return refcnt;
+}
+
+static oapv_imgb_t* imgb_create(int w, int h, int cs) {
+    int i, bd;
+    oapv_imgb_t* imgb;
+
+    imgb = (oapv_imgb_t*)malloc(sizeof(oapv_imgb_t));
+    if (imgb == NULL) goto ERR;
+    memset(imgb, 0, sizeof(oapv_imgb_t));
+
+    bd = OAPV_CS_GET_BYTE_DEPTH(cs); /* byte unit */
+
+    imgb->w[0] = w;
+    imgb->h[0] = h;
+    switch (OAPV_CS_GET_FORMAT(cs)) {
+        case OAPV_CF_YCBCR400:
+            imgb->w[1] = imgb->w[2] = w;
+            imgb->h[1] = imgb->h[2] = h;
+            imgb->np = 1;
+            break;
+        case OAPV_CF_YCBCR420:
+            imgb->w[1] = imgb->w[2] = (w + 1) >> 1;
+            imgb->h[1] = imgb->h[2] = (h + 1) >> 1;
+            imgb->np = 3;
+            break;
+        case OAPV_CF_YCBCR422:
+            imgb->w[1] = imgb->w[2] = (w + 1) >> 1;
+            imgb->h[1] = imgb->h[2] = h;
+            imgb->np = 3;
+            break;
+        case OAPV_CF_YCBCR444:
+            imgb->w[1] = imgb->w[2] = w;
+            imgb->h[1] = imgb->h[2] = h;
+            imgb->np = 3;
+            break;
+        case OAPV_CF_YCBCR4444:
+            imgb->w[1] = imgb->w[2] = imgb->w[3] = w;
+            imgb->h[1] = imgb->h[2] = imgb->h[3] = h;
+            imgb->np = 4;
+            break;
+        case OAPV_CF_PLANAR2:
+            imgb->w[1] = w;
+            imgb->h[1] = h;
+            imgb->np = 2;
+            break;
+        default:
+            goto ERR;
+    }
+
+    for (i = 0; i < imgb->np; i++) {
+        // width and height need to be aligned to macroblock size
+        imgb->aw[i] = ALIGN_VAL(imgb->w[i], OAPV_MB_W);
+        imgb->s[i] = imgb->aw[i] * bd;
+        imgb->ah[i] = ALIGN_VAL(imgb->h[i], OAPV_MB_H);
+        imgb->e[i] = imgb->ah[i];
+
+        imgb->bsize[i] = imgb->s[i] * imgb->e[i];
+        imgb->a[i] = imgb->baddr[i] = picbuf_alloc(imgb->bsize[i]);
+        memset(imgb->a[i], 0, imgb->bsize[i]);
+    }
+    imgb->cs = cs;
+    imgb->addref = imgb_addref;
+    imgb->getref = imgb_getref;
+    imgb->release = imgb_release;
+
+    imgb->addref(imgb); /* increase reference count */
+    return imgb;
+
+ERR:
+    if (imgb) {
+        for (int i = 0; i < OAPV_MAX_CC; i++) {
+            if (imgb->a[i]) picbuf_free(imgb->a[i]);
+        }
+        free(imgb);
+    }
+    return NULL;
+}
+
+#endif  // ANDROID_C2_SOFT_APV_COMMON_H__
\ No newline at end of file
diff --git a/media/codec2/components/apv/C2SoftApvDec.cpp b/media/codec2/components/apv/C2SoftApvDec.cpp
new file mode 100644
index 0000000..77305ce
--- /dev/null
+++ b/media/codec2/components/apv/C2SoftApvDec.cpp
@@ -0,0 +1,1349 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "C2SoftApvDec"
+#include <log/log.h>
+
+#include <android_media_swcodec_flags.h>
+
+#include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+#include <Codec2BufferUtils.h>
+#include <Codec2CommonUtils.h>
+#include <Codec2Mapper.h>
+#include <SimpleC2Interface.h>
+#include "C2SoftApvDec.h"
+
+#include <cutils/properties.h>
+
+const char* MEDIA_MIMETYPE_VIDEO_APV = "video/apv";
+
+#define MAX_NUM_FRMS (1)  // supports only 1-frame output
+#define FRM_IDX (0)       // supports only 1-frame output
+// check generic frame or not
+#define IS_NON_AUX_FRM(frm)                              \
+    (((frm)->pbu_type == OAPV_PBU_TYPE_PRIMARY_FRAME) || \
+     ((frm)->pbu_type == OAPV_PBU_TYPE_NON_PRIMARY_FRAME))
+// check auxiliary frame or not
+#define IS_AUX_FRM(frm) (!(IS_NON_AUX_FRM(frm)))
+#define OUTPUT_CSP_NATIVE (0)
+#define OUTPUT_CSP_P210 (1)
+
+namespace android {
+namespace {
+constexpr char COMPONENT_NAME[] = "c2.android.apv.decoder";
+constexpr uint32_t kDefaultOutputDelay = 8;
+constexpr uint32_t kMaxOutputDelay = 16;
+constexpr size_t kMinInputBufferSize = 2 * 1024 * 1024;
+}  // namespace
+
+class C2SoftApvDec::IntfImpl : public SimpleInterface<void>::BaseParams {
+  public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper>& helper)
+        : SimpleInterface<void>::BaseParams(helper, COMPONENT_NAME, C2Component::KIND_DECODER,
+                                            C2Component::DOMAIN_VIDEO, MEDIA_MIMETYPE_VIDEO_APV) {
+        noPrivateBuffers();  // TODO: account for our buffers here.
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+
+        addParameter(DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                             .withConstValue(new C2ComponentAttributesSetting(
+                                     C2Component::ATTRIB_IS_TEMPORAL))
+                             .build());
+
+        addParameter(DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
+                             .withDefault(new C2StreamPictureSizeInfo::output(0u, 320, 240))
+                             .withFields({
+                                     C2F(mSize, width).inRange(2, 4096),
+                                     C2F(mSize, height).inRange(2, 4096),
+                             })
+                             .withSetter(SizeSetter)
+                             .build());
+
+        addParameter(
+                DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+                        .withDefault(new C2StreamProfileLevelInfo::input(
+                                0u, C2Config::PROFILE_APV_422_10))
+                        .withFields(
+                                {C2F(mProfileLevel, profile).oneOf({C2Config::PROFILE_APV_422_10}),
+                                 C2F(mProfileLevel, level)
+                                         .oneOf({
+                                                C2Config::LEVEL_APV_1_BAND_0,
+                                                C2Config::LEVEL_APV_1_1_BAND_0,
+                                                C2Config::LEVEL_APV_2_BAND_0,
+                                                C2Config::LEVEL_APV_2_1_BAND_0,
+                                                C2Config::LEVEL_APV_3_BAND_0,
+                                                C2Config::LEVEL_APV_3_1_BAND_0,
+                                                C2Config::LEVEL_APV_4_BAND_0,
+                                                C2Config::LEVEL_APV_4_1_BAND_0,
+                                                C2Config::LEVEL_APV_5_BAND_0,
+                                                C2Config::LEVEL_APV_5_1_BAND_0,
+                                                C2Config::LEVEL_APV_6_BAND_0,
+                                                C2Config::LEVEL_APV_6_1_BAND_0,
+                                                C2Config::LEVEL_APV_7_BAND_0,
+                                                C2Config::LEVEL_APV_7_1_BAND_0,
+                                                C2Config::LEVEL_APV_1_BAND_1,
+                                                C2Config::LEVEL_APV_1_1_BAND_1,
+                                                C2Config::LEVEL_APV_2_BAND_1,
+                                                C2Config::LEVEL_APV_2_1_BAND_1,
+                                                C2Config::LEVEL_APV_3_BAND_1,
+                                                C2Config::LEVEL_APV_3_1_BAND_1,
+                                                C2Config::LEVEL_APV_4_BAND_1,
+                                                C2Config::LEVEL_APV_4_1_BAND_1,
+                                                C2Config::LEVEL_APV_5_BAND_1,
+                                                C2Config::LEVEL_APV_5_1_BAND_1,
+                                                C2Config::LEVEL_APV_6_BAND_1,
+                                                C2Config::LEVEL_APV_6_1_BAND_1,
+                                                C2Config::LEVEL_APV_7_BAND_1,
+                                                C2Config::LEVEL_APV_7_1_BAND_1,
+                                                C2Config::LEVEL_APV_1_BAND_2,
+                                                C2Config::LEVEL_APV_1_1_BAND_2,
+                                                C2Config::LEVEL_APV_2_BAND_2,
+                                                C2Config::LEVEL_APV_2_1_BAND_2,
+                                                C2Config::LEVEL_APV_3_BAND_2,
+                                                C2Config::LEVEL_APV_3_1_BAND_2,
+                                                C2Config::LEVEL_APV_4_BAND_2,
+                                                C2Config::LEVEL_APV_4_1_BAND_2,
+                                                C2Config::LEVEL_APV_5_BAND_2,
+                                                C2Config::LEVEL_APV_5_1_BAND_2,
+                                                C2Config::LEVEL_APV_6_BAND_2,
+                                                C2Config::LEVEL_APV_6_1_BAND_2,
+                                                C2Config::LEVEL_APV_7_BAND_2,
+                                                C2Config::LEVEL_APV_7_1_BAND_2,
+                                                C2Config::LEVEL_APV_1_BAND_3,
+                                                C2Config::LEVEL_APV_1_1_BAND_3,
+                                                C2Config::LEVEL_APV_2_BAND_3,
+                                                C2Config::LEVEL_APV_2_1_BAND_3,
+                                                C2Config::LEVEL_APV_3_BAND_3,
+                                                C2Config::LEVEL_APV_3_1_BAND_3,
+                                                C2Config::LEVEL_APV_4_BAND_3,
+                                                C2Config::LEVEL_APV_4_1_BAND_3,
+                                                C2Config::LEVEL_APV_5_BAND_3,
+                                                C2Config::LEVEL_APV_5_1_BAND_3,
+                                                C2Config::LEVEL_APV_6_BAND_3,
+                                                C2Config::LEVEL_APV_6_1_BAND_3,
+                                                C2Config::LEVEL_APV_7_BAND_3,
+                                                C2Config::LEVEL_APV_7_1_BAND_3,
+                                                 })})
+                        .withSetter(ProfileLevelSetter, mSize)
+                        .build());
+
+        mHdr10PlusInfoInput = C2StreamHdr10PlusInfo::input::AllocShared(0);
+        addParameter(DefineParam(mHdr10PlusInfoInput, C2_PARAMKEY_INPUT_HDR10_PLUS_INFO)
+                             .withDefault(mHdr10PlusInfoInput)
+                             .withFields({
+                                     C2F(mHdr10PlusInfoInput, m.value).any(),
+                             })
+                             .withSetter(Hdr10PlusInfoInputSetter)
+                             .build());
+
+        mHdr10PlusInfoOutput = C2StreamHdr10PlusInfo::output::AllocShared(0);
+        addParameter(DefineParam(mHdr10PlusInfoOutput, C2_PARAMKEY_OUTPUT_HDR10_PLUS_INFO)
+                             .withDefault(mHdr10PlusInfoOutput)
+                             .withFields({
+                                     C2F(mHdr10PlusInfoOutput, m.value).any(),
+                             })
+                             .withSetter(Hdr10PlusInfoOutputSetter)
+                             .build());
+
+        // default static info
+        C2HdrStaticMetadataStruct defaultStaticInfo{};
+        helper->addStructDescriptors<C2MasteringDisplayColorVolumeStruct, C2ColorXyStruct>();
+        addParameter(
+                DefineParam(mHdrStaticInfo, C2_PARAMKEY_HDR_STATIC_INFO)
+                        .withDefault(new C2StreamHdrStaticInfo::output(0u, defaultStaticInfo))
+                        .withFields({C2F(mHdrStaticInfo, mastering.red.x).inRange(0, 1),
+                                     C2F(mHdrStaticInfo, mastering.red.y).inRange(0, 1),
+                                     C2F(mHdrStaticInfo, mastering.green.x).inRange(0, 1),
+                                     C2F(mHdrStaticInfo, mastering.green.y).inRange(0, 1),
+                                     C2F(mHdrStaticInfo, mastering.blue.x).inRange(0, 1),
+                                     C2F(mHdrStaticInfo, mastering.blue.y).inRange(0, 1),
+                                     C2F(mHdrStaticInfo, mastering.white.x).inRange(0, 1),
+                                     C2F(mHdrStaticInfo, mastering.white.x).inRange(0, 1),
+                                     C2F(mHdrStaticInfo, mastering.maxLuminance).inRange(0, 65535),
+                                     C2F(mHdrStaticInfo, mastering.minLuminance).inRange(0, 6.5535),
+                                     C2F(mHdrStaticInfo, maxCll).inRange(0, 0XFFFF),
+                                     C2F(mHdrStaticInfo, maxFall).inRange(0, 0XFFFF)})
+                        .withSetter(HdrStaticInfoSetter)
+                        .build());
+
+        addParameter(DefineParam(mMaxSize, C2_PARAMKEY_MAX_PICTURE_SIZE)
+                             .withDefault(new C2StreamMaxPictureSizeTuning::output(0u, 320, 240))
+                             .withFields({
+                                     C2F(mSize, width).inRange(2, 4096, 2),
+                                     C2F(mSize, height).inRange(2, 4096, 2),
+                             })
+                             .withSetter(MaxPictureSizeSetter, mSize)
+                             .build());
+
+        addParameter(
+                DefineParam(mMaxInputSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+                        .withDefault(new C2StreamMaxBufferSizeInfo::input(0u, kMinInputBufferSize))
+                        .withFields({
+                                C2F(mMaxInputSize, value).any(),
+                        })
+                        .calculatedAs(MaxInputSizeSetter, mMaxSize)
+                        .build());
+
+        C2ChromaOffsetStruct locations[1] = {C2ChromaOffsetStruct::ITU_YUV_420_0()};
+        std::shared_ptr<C2StreamColorInfo::output> defaultColorInfo =
+                C2StreamColorInfo::output::AllocShared(1u, 0u, 8u /* bitDepth */, C2Color::YUV_420);
+        memcpy(defaultColorInfo->m.locations, locations, sizeof(locations));
+
+        defaultColorInfo = C2StreamColorInfo::output::AllocShared(
+                {C2ChromaOffsetStruct::ITU_YUV_420_0()}, 0u, 8u /* bitDepth */, C2Color::YUV_420);
+        helper->addStructDescriptors<C2ChromaOffsetStruct>();
+        addParameter(DefineParam(mColorInfo, C2_PARAMKEY_CODED_COLOR_INFO)
+                             .withConstValue(defaultColorInfo)
+                             .build());
+
+        addParameter(DefineParam(mDefaultColorAspects, C2_PARAMKEY_DEFAULT_COLOR_ASPECTS)
+                             .withDefault(new C2StreamColorAspectsTuning::output(
+                                     0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED,
+                                     C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+                             .withFields({C2F(mDefaultColorAspects, range)
+                                                  .inRange(C2Color::RANGE_UNSPECIFIED,
+                                                           C2Color::RANGE_OTHER),
+                                          C2F(mDefaultColorAspects, primaries)
+                                                  .inRange(C2Color::PRIMARIES_UNSPECIFIED,
+                                                           C2Color::PRIMARIES_OTHER),
+                                          C2F(mDefaultColorAspects, transfer)
+                                                  .inRange(C2Color::TRANSFER_UNSPECIFIED,
+                                                           C2Color::TRANSFER_OTHER),
+                                          C2F(mDefaultColorAspects, matrix)
+                                                  .inRange(C2Color::MATRIX_UNSPECIFIED,
+                                                           C2Color::MATRIX_OTHER)})
+                             .withSetter(DefaultColorAspectsSetter)
+                             .build());
+
+        addParameter(DefineParam(mCodedColorAspects, C2_PARAMKEY_VUI_COLOR_ASPECTS)
+                             .withDefault(new C2StreamColorAspectsInfo::input(
+                                     0u, C2Color::RANGE_LIMITED, C2Color::PRIMARIES_UNSPECIFIED,
+                                     C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+                             .withFields({C2F(mCodedColorAspects, range)
+                                                  .inRange(C2Color::RANGE_UNSPECIFIED,
+                                                           C2Color::RANGE_OTHER),
+                                          C2F(mCodedColorAspects, primaries)
+                                                  .inRange(C2Color::PRIMARIES_UNSPECIFIED,
+                                                           C2Color::PRIMARIES_OTHER),
+                                          C2F(mCodedColorAspects, transfer)
+                                                  .inRange(C2Color::TRANSFER_UNSPECIFIED,
+                                                           C2Color::TRANSFER_OTHER),
+                                          C2F(mCodedColorAspects, matrix)
+                                                  .inRange(C2Color::MATRIX_UNSPECIFIED,
+                                                           C2Color::MATRIX_OTHER)})
+                             .withSetter(CodedColorAspectsSetter)
+                             .build());
+
+        addParameter(
+                DefineParam(mColorAspects, C2_PARAMKEY_COLOR_ASPECTS)
+                        .withDefault(new C2StreamColorAspectsInfo::output(
+                                0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED,
+                                C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+                        .withFields(
+                                {C2F(mColorAspects, range)
+                                         .inRange(C2Color::RANGE_UNSPECIFIED, C2Color::RANGE_OTHER),
+                                 C2F(mColorAspects, primaries)
+                                         .inRange(C2Color::PRIMARIES_UNSPECIFIED,
+                                                  C2Color::PRIMARIES_OTHER),
+                                 C2F(mColorAspects, transfer)
+                                         .inRange(C2Color::TRANSFER_UNSPECIFIED,
+                                                  C2Color::TRANSFER_OTHER),
+                                 C2F(mColorAspects, matrix)
+                                         .inRange(C2Color::MATRIX_UNSPECIFIED,
+                                                  C2Color::MATRIX_OTHER)})
+                        .withSetter(ColorAspectsSetter, mDefaultColorAspects, mCodedColorAspects)
+                        .build());
+
+        // TODO: support more formats?
+        std::vector<uint32_t> pixelFormats = {HAL_PIXEL_FORMAT_YCBCR_420_888};
+        if (isHalPixelFormatSupported((AHardwareBuffer_Format)HAL_PIXEL_FORMAT_YCBCR_P010)) {
+            pixelFormats.push_back(HAL_PIXEL_FORMAT_YCBCR_P010);
+        }
+        if (isHalPixelFormatSupported((AHardwareBuffer_Format)AHARDWAREBUFFER_FORMAT_YCbCr_P210)) {
+            pixelFormats.push_back(AHARDWAREBUFFER_FORMAT_YCbCr_P210);
+        }
+
+        // If color format surface isn't added to supported formats, there is no way to know
+        // when the color-format is configured to surface. This is necessary to be able to
+        // choose 10-bit format while decoding 10-bit clips in surface mode.
+        pixelFormats.push_back(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
+        addParameter(DefineParam(mPixelFormat, C2_PARAMKEY_PIXEL_FORMAT)
+                             .withDefault(new C2StreamPixelFormatInfo::output(
+                                     0u, HAL_PIXEL_FORMAT_YCBCR_420_888))
+                             .withFields({C2F(mPixelFormat, value).oneOf(pixelFormats)})
+                             .withSetter((Setter<decltype(*mPixelFormat)>::StrictValueWithNoDeps))
+                             .build());
+    }
+
+    static C2R SizeSetter(bool mayBlock, const C2P<C2StreamPictureSizeInfo::output>& oldMe,
+                          C2P<C2StreamPictureSizeInfo::output>& me) {
+        (void)mayBlock;
+        ALOGV("%s - %d x %d", __FUNCTION__, me.v.width, me.v.height);
+        C2R res = C2R::Ok();
+        if (!me.F(me.v.width).supportsAtAll(me.v.width)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width)));
+            me.set().width = oldMe.v.width;
+        }
+        if (!me.F(me.v.height).supportsAtAll(me.v.height)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height)));
+            me.set().height = oldMe.v.height;
+        }
+        return res;
+    }
+
+    static C2R MaxPictureSizeSetter(bool mayBlock, C2P<C2StreamMaxPictureSizeTuning::output>& me,
+                                    const C2P<C2StreamPictureSizeInfo::output>& size) {
+        (void)mayBlock;
+        ALOGV("%s - %d x %d", __FUNCTION__, me.v.width, me.v.height);
+        // TODO: get max width/height from the size's field helpers vs.
+        // hardcoding
+        me.set().width = c2_min(c2_max(me.v.width, size.v.width), 4096u);
+        me.set().height = c2_min(c2_max(me.v.height, size.v.height), 4096u);
+        return C2R::Ok();
+    }
+
+    static C2R MaxInputSizeSetter(bool mayBlock, C2P<C2StreamMaxBufferSizeInfo::input>& me,
+                                  const C2P<C2StreamMaxPictureSizeTuning::output>& maxSize) {
+        (void)mayBlock;
+        ALOGV("%s", __FUNCTION__);
+        // assume compression ratio of 2, but enforce a floor
+        me.set().value =
+                c2_max((((maxSize.v.width + 63) / 64) * ((maxSize.v.height + 63) / 64) * 3072),
+                       kMinInputBufferSize);
+        return C2R::Ok();
+    }
+
+    static C2R DefaultColorAspectsSetter(bool mayBlock,
+                                         C2P<C2StreamColorAspectsTuning::output>& me) {
+        (void)mayBlock;
+        ALOGV("%s - range: %u, primary: %u, transfer: %u, matrix: %u", __FUNCTION__, me.v.range,
+              me.v.primaries, me.v.transfer, me.v.matrix);
+        if (me.v.range > C2Color::RANGE_OTHER) {
+            me.set().range = C2Color::RANGE_OTHER;
+        }
+        if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
+            me.set().primaries = C2Color::PRIMARIES_OTHER;
+        }
+        if (me.v.transfer > C2Color::TRANSFER_OTHER) {
+            me.set().transfer = C2Color::TRANSFER_OTHER;
+        }
+        if (me.v.matrix > C2Color::MATRIX_OTHER) {
+            me.set().matrix = C2Color::MATRIX_OTHER;
+        }
+        return C2R::Ok();
+    }
+
+    static C2R CodedColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::input>& me) {
+        (void)mayBlock;
+        ALOGV("%s - range: %u, primaries: %u, transfer: %u, matrix: %u", __func__, me.v.range,
+              me.v.primaries, me.v.transfer, me.v.matrix);
+        if (me.v.range > C2Color::RANGE_OTHER) {
+            me.set().range = C2Color::RANGE_OTHER;
+        }
+        if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
+            me.set().primaries = C2Color::PRIMARIES_OTHER;
+        }
+        if (me.v.transfer > C2Color::TRANSFER_OTHER) {
+            me.set().transfer = C2Color::TRANSFER_OTHER;
+        }
+        if (me.v.matrix > C2Color::MATRIX_OTHER) {
+            me.set().matrix = C2Color::MATRIX_OTHER;
+        }
+        return C2R::Ok();
+    }
+
+    static C2R ColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::output>& me,
+                                  const C2P<C2StreamColorAspectsTuning::output>& def,
+                                  const C2P<C2StreamColorAspectsInfo::input>& coded) {
+        (void)mayBlock;
+        ALOGV("%s", __FUNCTION__);
+        // take default values for all unspecified fields, and coded values for specified ones
+        ALOGV("%s - coded range: %u, primaries: %u, transfer: %u, matrix: %u",
+             __func__, coded.v.range, coded.v.primaries, coded.v.transfer, coded.v.matrix);
+        me.set().range = coded.v.range == RANGE_UNSPECIFIED ? def.v.range : coded.v.range;
+        me.set().primaries =
+                coded.v.primaries == PRIMARIES_UNSPECIFIED ? def.v.primaries : coded.v.primaries;
+        me.set().transfer =
+                coded.v.transfer == TRANSFER_UNSPECIFIED ? def.v.transfer : coded.v.transfer;
+        me.set().matrix = coded.v.matrix == MATRIX_UNSPECIFIED ? def.v.matrix : coded.v.matrix;
+        ALOGV("%s - me.v.range = %u, me.v.primaries = %u, me.v.transfer = %u, me.v.matrix = %u",
+              __func__, me.v.range, me.v.primaries, me.v.transfer, me.v.matrix);
+        return C2R::Ok();
+    }
+
+    static C2R ProfileLevelSetter(bool mayBlock, C2P<C2StreamProfileLevelInfo::input>& me,
+                                  const C2P<C2StreamPictureSizeInfo::output>& size) {
+        (void)mayBlock;
+        ALOGV("%s", __FUNCTION__);
+        (void)size;
+        (void)me;  // TODO: validate
+        return C2R::Ok();
+    }
+
+    std::shared_ptr<C2StreamColorAspectsTuning::output> getDefaultColorAspects_l() {
+        ALOGV("%s - mDefaultColorAspects: %u", __FUNCTION__, mDefaultColorAspects->primaries);
+        return mDefaultColorAspects;
+    }
+
+    std::shared_ptr<C2StreamColorAspectsInfo::output> getColorAspects_l() {
+        ALOGV("%s - mColorAspects: %u", __FUNCTION__, mColorAspects->primaries);
+        return mColorAspects;
+    }
+
+    static C2R Hdr10PlusInfoInputSetter(bool mayBlock, C2P<C2StreamHdr10PlusInfo::input>& me) {
+        (void)mayBlock;
+        ALOGV("%s", __FUNCTION__);
+        (void)me;  // TODO: validate
+        return C2R::Ok();
+    }
+
+    static C2R Hdr10PlusInfoOutputSetter(bool mayBlock, C2P<C2StreamHdr10PlusInfo::output>& me) {
+        (void)mayBlock;
+        ALOGV("%s", __FUNCTION__);
+        (void)me;  // TODO: validate
+        return C2R::Ok();
+    }
+
+    // unsafe getters
+    std::shared_ptr<C2StreamPixelFormatInfo::output> getPixelFormat_l() const {
+        return mPixelFormat;
+    }
+
+    static C2R HdrStaticInfoSetter(bool mayBlock, C2P<C2StreamHdrStaticInfo::output>& me) {
+        (void)mayBlock;
+        ALOGV("%s", __FUNCTION__);
+        if (me.v.mastering.red.x > 1) {
+            me.set().mastering.red.x = 1;
+        }
+        if (me.v.mastering.red.y > 1) {
+            me.set().mastering.red.y = 1;
+        }
+        if (me.v.mastering.green.x > 1) {
+            me.set().mastering.green.x = 1;
+        }
+        if (me.v.mastering.green.y > 1) {
+            me.set().mastering.green.y = 1;
+        }
+        if (me.v.mastering.blue.x > 1) {
+            me.set().mastering.blue.x = 1;
+        }
+        if (me.v.mastering.blue.y > 1) {
+            me.set().mastering.blue.y = 1;
+        }
+        if (me.v.mastering.white.x > 1) {
+            me.set().mastering.white.x = 1;
+        }
+        if (me.v.mastering.white.y > 1) {
+            me.set().mastering.white.y = 1;
+        }
+        if (me.v.mastering.maxLuminance > 65535.0) {
+            me.set().mastering.maxLuminance = 65535.0;
+        }
+        if (me.v.mastering.minLuminance > 6.5535) {
+            me.set().mastering.minLuminance = 6.5535;
+        }
+        if (me.v.maxCll > 65535.0) {
+            me.set().maxCll = 65535.0;
+        }
+        if (me.v.maxFall > 65535.0) {
+            me.set().maxFall = 65535.0;
+        }
+        return C2R::Ok();
+    }
+
+  private:
+    std::shared_ptr<C2StreamProfileLevelInfo::input> mProfileLevel;
+    std::shared_ptr<C2StreamPictureSizeInfo::output> mSize;
+    std::shared_ptr<C2StreamMaxPictureSizeTuning::output> mMaxSize;
+    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mMaxInputSize;
+    std::shared_ptr<C2StreamColorInfo::output> mColorInfo;
+    std::shared_ptr<C2StreamPixelFormatInfo::output> mPixelFormat;
+    std::shared_ptr<C2StreamColorAspectsTuning::output> mDefaultColorAspects;
+    std::shared_ptr<C2StreamColorAspectsInfo::input> mCodedColorAspects;
+    std::shared_ptr<C2StreamColorAspectsInfo::output> mColorAspects;
+    std::shared_ptr<C2StreamHdr10PlusInfo::input> mHdr10PlusInfoInput;
+    std::shared_ptr<C2StreamHdr10PlusInfo::output> mHdr10PlusInfoOutput;
+    std::shared_ptr<C2StreamHdrStaticInfo::output> mHdrStaticInfo;
+};
+
+static void ivd_aligned_free(void* ctxt, void* mem) {
+    (void)ctxt;
+    free(mem);
+}
+
+C2SoftApvDec::C2SoftApvDec(const char* name, c2_node_id_t id,
+                           const std::shared_ptr<IntfImpl>& intfImpl)
+    : SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl),
+      mDecHandle(nullptr),
+      mOutBufferFlush(nullptr),
+      mIvColorformat(IV_YUV_420P),
+      mOutputDelay(kDefaultOutputDelay),
+      mHeaderDecoded(false),
+      mOutIndex(0u),
+      mHalPixelFormat(HAL_PIXEL_FORMAT_YV12),
+      mWidth(320),
+      mHeight(240),
+      mSignalledOutputEos(false),
+      mSignalledError(false) {
+    oapvdHandle = NULL;
+    oapvmHandle = NULL;
+    outputCsp = OUTPUT_CSP_NATIVE;
+}
+
+C2SoftApvDec::~C2SoftApvDec() {
+    onRelease();
+}
+
+c2_status_t C2SoftApvDec::onInit() {
+    ALOGV("%s", __FUNCTION__);
+    status_t err = initDecoder();
+    return err == OK ? C2_OK : C2_CORRUPTED;
+}
+
+c2_status_t C2SoftApvDec::onStop() {
+    ALOGV("%s", __FUNCTION__);
+    if (OK != resetDecoder()) return C2_CORRUPTED;
+    resetPlugin();
+    return C2_OK;
+}
+
+void C2SoftApvDec::onReset() {
+    ALOGV("%s", __FUNCTION__);
+    (void)onStop();
+}
+
+status_t C2SoftApvDec::deleteDecoder() {
+    ALOGV("%s", __FUNCTION__);
+    if (oapvdHandle) {
+        oapvd_delete(oapvdHandle);
+        oapvdHandle = NULL;
+    }
+    if (oapvmHandle) {
+        oapvm_delete(oapvmHandle);
+        oapvmHandle = NULL;
+    }
+    for (int i = 0; i < ofrms.num_frms; i++) {
+        if (ofrms.frm[i].imgb != NULL) {
+            ofrms.frm[i].imgb->release(ofrms.frm[i].imgb);
+            ofrms.frm[i].imgb = NULL;
+        }
+    }
+    return OK;
+}
+
+void C2SoftApvDec::onRelease() {
+    ALOGV("%s", __FUNCTION__);
+    (void)deleteDecoder();
+    if (mOutBufferFlush) {
+        ivd_aligned_free(nullptr, mOutBufferFlush);
+        mOutBufferFlush = nullptr;
+    }
+    if (mOutBlock) {
+        mOutBlock.reset();
+    }
+}
+
+c2_status_t C2SoftApvDec::onFlush_sm() {
+    ALOGV("%s", __FUNCTION__);
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+    return C2_OK;
+}
+
+status_t C2SoftApvDec::createDecoder() {
+    ALOGV("%s", __FUNCTION__);
+    return OK;
+}
+
+status_t C2SoftApvDec::initDecoder() {
+    int ret;
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+
+    mHalPixelFormat = HAL_PIXEL_FORMAT_YV12;
+    {
+        IntfImpl::Lock lock = mIntf->lock();
+        mPixelFormatInfo = mIntf->getPixelFormat_l();
+        ALOGW("Hal pixel format = %d", mPixelFormatInfo->value);
+    }
+    memset(&cdesc, 0, sizeof(oapvd_cdesc_t));
+
+    cdesc.threads = 1;  // default
+    oapvdHandle = oapvd_create(&cdesc, &ret);
+    if (oapvdHandle == NULL) {
+        ALOGE("ERROR: cannot create APV decoder (err=%d)\n", ret);
+        return C2_NO_INIT;
+    }
+
+    memset(&ofrms, 0, sizeof(oapv_frms_t));
+
+    oapvmHandle = oapvm_create(&ret);
+    if (OAPV_FAILED(ret)) {
+        ALOGE("oapvm create failed");
+        oapvd_delete(oapvdHandle);
+        oapvdHandle = NULL;
+        return C2_NO_INIT;
+    }
+
+    ALOGV("oapvd init done");
+    return OK;
+}
+
+status_t C2SoftApvDec::setFlushMode() {
+    ALOGV("%s", __FUNCTION__);
+    return OK;
+}
+
+status_t C2SoftApvDec::resetDecoder() {
+    ALOGV("%s", __FUNCTION__);
+    return OK;
+}
+
+void C2SoftApvDec::resetPlugin() {
+    ALOGV("%s", __FUNCTION__);
+    mSignalledOutputEos = false;
+    if (mOutBlock) {
+        mOutBlock.reset();
+    }
+}
+
+void fillEmptyWork(const std::unique_ptr<C2Work>& work) {
+    uint32_t flags = 0;
+    if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+        flags |= C2FrameData::FLAG_END_OF_STREAM;
+        ALOGV("signalling eos");
+    }
+    work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->workletsProcessed = 1u;
+}
+
+void C2SoftApvDec::finishWork(uint64_t index, const std::unique_ptr<C2Work>& work,
+                              const std::shared_ptr<C2GraphicBlock>& block) {
+    std::shared_ptr<C2Buffer> buffer = createGraphicBuffer(block, C2Rect(mWidth, mHeight));
+
+    {
+        IntfImpl::Lock lock = mIntf->lock();
+        buffer->setInfo(mIntf->getColorAspects_l());
+    }
+
+    class FillWork {
+      public:
+        FillWork(uint32_t flags, C2WorkOrdinalStruct ordinal,
+                 const std::shared_ptr<C2Buffer>& buffer)
+            : mFlags(flags), mOrdinal(ordinal), mBuffer(buffer) {}
+        ~FillWork() = default;
+
+        void operator()(const std::unique_ptr<C2Work>& work) {
+            work->worklets.front()->output.flags = (C2FrameData::flags_t)mFlags;
+            work->worklets.front()->output.buffers.clear();
+            work->worklets.front()->output.ordinal = mOrdinal;
+            work->workletsProcessed = 1u;
+            work->result = C2_OK;
+            if (mBuffer) {
+                work->worklets.front()->output.buffers.push_back(mBuffer);
+            }
+            ALOGV("timestamp = %lld, index = %lld, w/%s buffer", mOrdinal.timestamp.peekll(),
+                  mOrdinal.frameIndex.peekll(), mBuffer ? "" : "o");
+        }
+
+      private:
+        const uint32_t mFlags;
+        const C2WorkOrdinalStruct mOrdinal;
+        const std::shared_ptr<C2Buffer> mBuffer;
+    };
+
+    auto fillWork = [buffer](const std::unique_ptr<C2Work>& work) {
+        work->worklets.front()->output.flags = (C2FrameData::flags_t)0;
+        work->worklets.front()->output.buffers.clear();
+        work->worklets.front()->output.buffers.push_back(buffer);
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        work->workletsProcessed = 1u;
+    };
+
+    if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
+        bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+        // TODO: Check if cloneAndSend can be avoided by tracking number of frames remaining
+        if (eos) {
+            if (buffer) {
+                mOutIndex = index;
+                C2WorkOrdinalStruct outOrdinal = work->input.ordinal;
+                cloneAndSend(mOutIndex, work,
+                             FillWork(C2FrameData::FLAG_INCOMPLETE, outOrdinal, buffer));
+                buffer.reset();
+            }
+        } else {
+            fillWork(work);
+        }
+    } else {
+        finish(index, fillWork);
+    }
+}
+
+static void copyBufferFromYUV420ToYV12(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) {
+    for (size_t i = 0; i < height; ++i) {
+        memcpy(dstY, srcY, width);
+        srcY += srcYStride;
+        dstY += dstYStride;
+    }
+
+    for (size_t i = 0; i < height / 2; ++i) {
+        memcpy(dstU, srcU, width / 2);
+        memcpy(dstV, srcV, width / 2);
+        dstU += dstUStride;
+        srcU += srcUStride;
+        dstV += dstVStride;
+        srcV += srcVStride;
+    }
+}
+
+static void copyBufferP210(uint16_t *dstY, uint16_t *dstUV, const uint16_t *srcY,
+            const uint16_t *srcUV, size_t srcYStride, size_t srcUVStride, size_t dstYStride,
+            size_t dstUVStride, size_t width, size_t height) {
+    for (size_t y = 0; y < height; ++y) {
+        memcpy(dstY, srcY, width * sizeof(uint16_t));
+        srcY += srcYStride;
+        dstY += dstYStride;
+    }
+
+    for (size_t y = 0; y < height; ++y) {
+        memcpy(dstUV, srcUV, width * sizeof(uint16_t));
+        srcUV += srcUVStride;
+        dstUV += dstUVStride;
+    }
+}
+
+static void copyBufferFromYUV422ToYV12(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) {
+    for (size_t i = 0; i < height; ++i) {
+        memcpy(dstY, srcY, width);
+        srcY += srcYStride;
+        dstY += dstYStride;
+    }
+
+    for (size_t i = 0; i < height / 2; ++i) {
+        memcpy(dstU, srcU, width / 2);
+        memcpy(dstV, srcV, width / 2);
+        dstU += dstUStride;
+        srcU += srcUStride * 2;
+        dstV += dstVStride;
+        srcV += srcVStride * 2;
+    }
+}
+
+static void copyBufferFromYUV42010bitToP010(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 dstUVStride, size_t width,
+                                            size_t height) {
+    for (size_t y = 0; y < height; ++y) {
+        for (size_t x = 0; x < width; ++x) {
+            dstY[x] = srcY[x] << 6;
+        }
+        srcY += srcYStride;
+        dstY += dstYStride;
+    }
+
+    for (size_t y = 0; y < height / 2; ++y) {
+        for (size_t x = 0; x < width / 2; ++x) {
+            dstUV[2 * x] = srcU[x] << 6;
+            dstUV[2 * x + 1] = srcV[x] << 6;
+        }
+        srcU += srcUStride;
+        srcV += srcVStride;
+        dstUV += dstUVStride;
+    }
+}
+
+static void copyBufferFromYUV42210bitToP010(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 dstUVStride, size_t width,
+                                            size_t height) {
+    for (size_t y = 0; y < height; ++y) {
+        for (size_t x = 0; x < width; ++x) {
+            dstY[x] = srcY[x] << 6;
+        }
+        srcY += srcYStride;
+        dstY += dstYStride;
+    }
+
+    for (size_t y = 0; y < height / 2; ++y) {
+        for (size_t x = 0; x < width / 2; ++x) {
+            dstUV[2 * x] = srcU[x] << 6;
+            dstUV[2 * x + 1] = srcV[x] << 6;
+        }
+        srcU += srcUStride * 2;
+        srcV += srcVStride * 2;
+        dstUV += dstUVStride;
+    }
+}
+
+static void copyBufferFromP210ToP010(uint16_t* dstY, uint16_t* dstUV, const uint16_t* srcY,
+                                     const uint16_t* srcUV, size_t srcYStride, size_t srcUVStride,
+                                     size_t dstYStride, size_t dstUVStride, size_t width,
+                                     size_t height) {
+    for (size_t y = 0; y < height; ++y) {
+        memcpy(dstY, srcY, width * sizeof(uint16_t));
+        srcY += srcYStride;
+        dstY += dstYStride;
+    }
+
+    for (size_t y = 0; y < height / 2; ++y) {
+        memcpy(dstUV, srcUV, width * 2);
+        srcUV += srcUVStride * 2;
+        dstUV += dstUVStride;
+    }
+}
+
+static void copyBufferFromYUV42010bitToYV12(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, uint32_t width,
+                                            uint32_t height) {
+    for (size_t i = 0; i < height; ++i) {
+        for (size_t j = 0; j < width; ++j) {
+            dstY[i * dstYStride + j] = (srcY[i * srcYStride + j] >> 2) & 0xFF;
+        }
+    }
+
+    for (size_t i = 0; i < height / 2; ++i) {
+        for (size_t j = 0; j < width / 2; ++j) {
+            dstU[i * dstUStride + j] = (srcU[i * srcUStride + j] >> 2) & 0xFF;
+        }
+    }
+
+    for (size_t i = 0; i < height / 2; ++i) {
+        for (size_t j = 0; j < width / 2; ++j) {
+            dstV[i * dstVStride + j] = (srcV[i * srcVStride + j] >> 2) & 0xFF;
+        }
+    }
+}
+
+static void copyBufferFromYUV42210bitToYV12(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, uint32_t width,
+                                            uint32_t height) {
+    for (size_t i = 0; i < height; ++i) {
+        for (size_t j = 0; j < width; ++j) {
+            dstY[i * dstYStride + j] = (srcY[i * srcYStride + j] >> 2) & 0xFF;
+        }
+    }
+
+    for (size_t i = 0; i < height / 2; ++i) {
+        for (size_t j = 0; j < width / 2; ++j) {
+            dstU[i * dstUStride + j] = (srcU[i * srcUStride * 2 + j] >> 2) & 0xFF;
+        }
+    }
+
+    for (size_t i = 0; i < height / 2; ++i) {
+        for (size_t j = 0; j < width / 2; ++j) {
+            dstV[i * dstVStride + j] = (srcV[i * srcVStride * 2 + j] >> 2) & 0xFF;
+        }
+    }
+}
+
+static void copyBufferFromP210ToYV12(uint8_t* dstY, uint8_t* dstU, uint8_t* dstV,
+                                     const uint16_t* srcY, const uint16_t* srcUV, size_t srcYStride,
+                                     size_t srcUVStride, size_t dstYStride, size_t dstUStride,
+                                     size_t dstVStride, size_t width, size_t height) {
+    for (size_t i = 0; i < height; ++i) {
+        for (size_t j = 0; j < width; ++j) {
+            dstY[i * dstYStride + j] = (srcY[i * srcYStride + j] >> 8) & 0xFF;
+        }
+    }
+
+    for (size_t i = 0; i < height / 2; ++i) {
+        for (size_t j = 0; j < width / 2; ++j) {
+            dstV[i * dstVStride + j] = (srcUV[i * srcUVStride * 2 + j * 2] >> 8) & 0xFF;
+            dstU[i * dstUStride + j] = (srcUV[i * srcUVStride * 2 + j * 2 + 1] >> 8) & 0xFF;
+        }
+    }
+}
+
+void C2SoftApvDec::process(const std::unique_ptr<C2Work>& work,
+                           const std::shared_ptr<C2BlockPool>& pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 0u;
+    work->worklets.front()->output.configUpdate.clear();
+    work->worklets.front()->output.flags = work->input.flags;
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    int ret = 0;
+    size_t inOffset = 0u;
+    size_t inSize = 0u;
+    C2ReadView rView = mDummyReadView;
+    if (!work->input.buffers.empty()) {
+        rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+        inSize = rView.capacity();
+        if (inSize && rView.error()) {
+            ALOGE("read view map failed %d", rView.error());
+            work->result = C2_CORRUPTED;
+            return;
+        }
+    }
+
+    bool codecConfig = ((work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) != 0);
+    bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+
+    ALOGV("in buffer attr. size %zu timestamp %llu frameindex %d, flags %x", inSize,
+          work->input.ordinal.timestamp.peekull(), (int)work->input.ordinal.frameIndex.peeku(),
+          work->input.flags);
+
+    if (codecConfig) {
+        fillEmptyWork(work);
+        return;
+    }
+
+    if (inSize > 0) {
+        uint8_t* bitstream = const_cast<uint8_t*>(rView.data() + inOffset);
+        oapv_au_info_t aui;
+        oapv_bitb_t bitb;
+        bitb.addr = bitstream + 4;  // skip au
+        bitb.ssize = inSize - 4;
+
+        if (OAPV_FAILED(oapvd_info(bitb.addr, bitb.ssize, &aui))) {
+            ALOGE("cannot get information from bitstream");
+            return;
+        }
+
+        /* create decoding frame buffers */
+        ofrms.num_frms = aui.num_frms;
+        if (ofrms.num_frms <= 0) {
+            ALOGE("Parse error - no output frame(%d)", ofrms.num_frms);
+            fillEmptyWork(work);
+            return;
+        }
+        for (int i = 0; i < ofrms.num_frms; i++) {
+            oapv_frm_info_t* finfo = &aui.frm_info[FRM_IDX];
+            oapv_frm_t* frm = &ofrms.frm[i];
+
+            if (mWidth != finfo->w || mHeight != finfo->w) {
+                mWidth = finfo->w;
+                mHeight = finfo->h;
+            }
+
+            if (frm->imgb != NULL && (frm->imgb->w[0] != finfo->w || frm->imgb->h[0] != finfo->h)) {
+                frm->imgb->release(frm->imgb);
+                frm->imgb = NULL;
+            }
+
+            if (frm->imgb == NULL) {
+                if (outputCsp == OUTPUT_CSP_P210) {
+                    frm->imgb = imgb_create(finfo->w, finfo->h, OAPV_CS_P210);
+                } else {
+                    frm->imgb = imgb_create(finfo->w, finfo->h, finfo->cs);
+                }
+                if (frm->imgb == NULL) {
+                    ALOGE("cannot allocate image buffer (w:%d, h:%d, cs:%d)", finfo->w, finfo->h,
+                          finfo->cs);
+                    fillEmptyWork(work);
+                    return;
+                }
+            }
+        }
+
+        oapvd_stat_t stat;
+        ret = oapvd_decode(oapvdHandle, &bitb, &ofrms, oapvmHandle, &stat);
+        if (bitb.ssize != stat.read) {
+            ALOGW("decode done, input size: %d, processed size: %d", bitb.ssize, stat.read);
+        }
+
+        if (OAPV_FAILED(ret)) {
+            ALOGE("failed to decode bitstream\n");
+            fillEmptyWork(work);
+            return;
+        }
+
+        status_t err = outputBuffer(pool, work);
+        if (err == NOT_ENOUGH_DATA) {
+            if (inSize > 0) {
+                ALOGV("Maybe non-display frame at %lld.", work->input.ordinal.frameIndex.peekll());
+                // send the work back with empty buffer.
+                inSize = 0;
+            }
+        } else if (err != OK) {
+            ALOGD("Error while getting the output frame out");
+            // work->result would be already filled; do fillEmptyWork() below to
+            // send the work back.
+            inSize = 0;
+        }
+    }
+
+    if (eos) {
+        drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
+        mSignalledOutputEos = true;
+    } else if (!inSize) {
+        fillEmptyWork(work);
+    }
+}
+
+void C2SoftApvDec::getVuiParams(VuiColorAspects* buffer) {
+    VuiColorAspects vuiColorAspects;
+    vuiColorAspects.primaries = buffer->primaries;
+    vuiColorAspects.transfer = buffer->transfer;
+    vuiColorAspects.coeffs = buffer->coeffs;
+    vuiColorAspects.fullRange = buffer->fullRange;
+
+    // convert vui aspects to C2 values if changed
+    if (!(vuiColorAspects == mBitstreamColorAspects)) {
+        mBitstreamColorAspects = vuiColorAspects;
+        ColorAspects sfAspects;
+        C2StreamColorAspectsInfo::input codedAspects = { 0u };
+        ColorUtils::convertIsoColorAspectsToCodecAspects(
+                vuiColorAspects.primaries, vuiColorAspects.transfer, vuiColorAspects.coeffs,
+                vuiColorAspects.fullRange, sfAspects);
+        if (!C2Mapper::map(sfAspects.mPrimaries, &codedAspects.primaries)) {
+            codedAspects.primaries = C2Color::PRIMARIES_UNSPECIFIED;
+        }
+        if (!C2Mapper::map(sfAspects.mRange, &codedAspects.range)) {
+            codedAspects.range = C2Color::RANGE_UNSPECIFIED;
+        }
+        if (!C2Mapper::map(sfAspects.mMatrixCoeffs, &codedAspects.matrix)) {
+            codedAspects.matrix = C2Color::MATRIX_UNSPECIFIED;
+        }
+        if (!C2Mapper::map(sfAspects.mTransfer, &codedAspects.transfer)) {
+            codedAspects.transfer = C2Color::TRANSFER_UNSPECIFIED;
+        }
+        ALOGV("colorAspects: primaries:%d, transfer:%d, coeffs:%d, fullRange:%d",
+                codedAspects.primaries, codedAspects.transfer, codedAspects.matrix,
+                codedAspects.range);
+        std::vector<std::unique_ptr<C2SettingResult>> failures;
+        mIntf->config({&codedAspects}, C2_MAY_BLOCK, &failures);
+    }
+}
+
+status_t C2SoftApvDec::outputBuffer(const std::shared_ptr<C2BlockPool>& pool,
+                                    const std::unique_ptr<C2Work>& work) {
+    if (!(work && pool)) return BAD_VALUE;
+
+    oapv_imgb_t* imgbOutput = nullptr;
+    std::shared_ptr<C2GraphicBlock> block;
+
+    if (ofrms.num_frms > 0) {
+        for(int i = 0; i < ofrms.num_frms; i++) {
+            oapv_frm_t* frm = &ofrms.frm[0];
+            if(frm->pbu_type == OAPV_PBU_TYPE_PRIMARY_FRAME) {
+                imgbOutput = frm->imgb;
+                break;
+            }
+        }
+        if(imgbOutput == nullptr) {
+            ALOGW("No OAPV primary frame");
+            return false;
+        }
+    } else {
+        ALOGW("No output frames");
+        return false;
+    }
+    bool isMonochrome = OAPV_CS_GET_FORMAT(imgbOutput->cs) == OAPV_CS_YCBCR400;
+
+    // TODO: use bitstream color aspect after vui parsing
+    VuiColorAspects colorAspect;
+    colorAspect.primaries = 2;
+    colorAspect.transfer = 2;
+    colorAspect.coeffs = 2;
+    colorAspect.fullRange = 1;
+    getVuiParams(&colorAspect);
+
+    uint32_t format = HAL_PIXEL_FORMAT_YV12;
+    std::shared_ptr<C2StreamColorAspectsInfo::output> codedColorAspects;
+    if (OAPV_CS_GET_BIT_DEPTH(imgbOutput->cs) == 10 &&
+        mPixelFormatInfo->value != HAL_PIXEL_FORMAT_YCBCR_420_888) {
+        IntfImpl::Lock lock = mIntf->lock();
+        codedColorAspects = mIntf->getColorAspects_l();
+
+        bool allowRGBA1010102 = false;
+        if (codedColorAspects->primaries == C2Color::PRIMARIES_BT2020 &&
+            codedColorAspects->matrix == C2Color::MATRIX_BT2020 &&
+            codedColorAspects->transfer == C2Color::TRANSFER_ST2084) {
+            allowRGBA1010102 = true;
+        }
+        format = getHalPixelFormatForBitDepth10(allowRGBA1010102);
+    }
+
+    if (mHalPixelFormat != format) {
+        C2StreamPixelFormatInfo::output pixelFormat(0u, format);
+        std::vector<std::unique_ptr<C2SettingResult>> failures;
+        c2_status_t err = mIntf->config({&pixelFormat}, C2_MAY_BLOCK, &failures);
+        if (err == C2_OK) {
+            work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(pixelFormat));
+        } else {
+            ALOGE("Config update pixelFormat failed");
+            mSignalledError = true;
+            work->workletsProcessed = 1u;
+            work->result = C2_CORRUPTED;
+            return UNKNOWN_ERROR;
+        }
+        mHalPixelFormat = format;
+    }
+    ALOGV("mHalPixelFormat: %u, format: %d", mHalPixelFormat, format);
+
+    C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
+
+    // check. align height to 2 times does not work.
+    c2_status_t err =
+            pool->fetchGraphicBlock(align(mWidth, 16), align(mHeight, 16), format, usage, &block);
+
+    if (err != C2_OK) {
+        ALOGE("fetchGraphicBlock for Output failed with status %d", err);
+        work->result = err;
+        return false;
+    }
+
+    C2GraphicView wView = block->map().get();
+    if (wView.error()) {
+        ALOGE("graphic view map failed %d", wView.error());
+        work->result = C2_CORRUPTED;
+        return false;
+    }
+
+    ALOGV("provided (%dx%d) required (%dx%d)", block->width(), block->height(), mWidth, mHeight);
+
+    uint8_t* dstY = const_cast<uint8_t*>(wView.data()[C2PlanarLayout::PLANE_Y]);
+    uint8_t* dstU = const_cast<uint8_t*>(wView.data()[C2PlanarLayout::PLANE_U]);
+    uint8_t* dstV = const_cast<uint8_t*>(wView.data()[C2PlanarLayout::PLANE_V]);
+
+    C2PlanarLayout layout = wView.layout();
+    size_t dstYStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
+    size_t dstUStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc;
+    size_t dstVStride = layout.planes[C2PlanarLayout::PLANE_V].rowInc;
+
+    if(format == AHARDWAREBUFFER_FORMAT_YCbCr_P210) {
+        if(OAPV_CS_GET_BIT_DEPTH(imgbOutput->cs) == 10) {
+            const uint16_t *srcY = (const uint16_t *)imgbOutput->a[0];
+            const uint16_t *srcU = (const uint16_t *)imgbOutput->a[1];
+            const uint16_t *srcV = (const uint16_t *)imgbOutput->a[2];
+            size_t srcYStride = imgbOutput->s[0] / 2;
+            size_t srcUStride = imgbOutput->s[1] / 2;
+            size_t srcVStride = imgbOutput->s[2] / 2;
+            dstYStride /= 2;
+            dstUStride /= 2;
+            dstVStride /= 2;
+            ALOGV("OAPV_CS_P210 buffer");
+            copyBufferP210((uint16_t *)dstY, (uint16_t *)dstU, srcY, srcU,
+                            srcYStride, srcUStride, dstYStride, dstUStride, mWidth, mHeight);
+        } else {
+            ALOGE("Not supported convder from bd:%d, format: %d(%s), to format: %d(%s)",
+                OAPV_CS_GET_BIT_DEPTH(imgbOutput->cs),
+                OAPV_CS_GET_FORMAT(imgbOutput->cs),
+                OAPV_CS_GET_FORMAT(imgbOutput->cs) == OAPV_CF_YCBCR420 ?
+                    "YUV420" : (OAPV_CS_GET_FORMAT(imgbOutput->cs) == OAPV_CF_YCBCR422 ?
+                                 "YUV422" : "UNKNOWN"),
+                format,
+                format == HAL_PIXEL_FORMAT_YCBCR_P010 ?
+                    "P010" : (format == HAL_PIXEL_FORMAT_YCBCR_420_888 ?
+                         "YUV420" : (format == HAL_PIXEL_FORMAT_YV12 ? "YV12" : "UNKNOWN"))
+                );
+        }
+    } else if(format == HAL_PIXEL_FORMAT_YCBCR_P010) {
+        if (OAPV_CS_GET_BIT_DEPTH(imgbOutput->cs) == 10) {
+            const uint16_t* srcY = (const uint16_t*)imgbOutput->a[0];
+            const uint16_t* srcU = (const uint16_t*)imgbOutput->a[1];
+            const uint16_t* srcV = (const uint16_t*)imgbOutput->a[2];
+            size_t srcYStride = imgbOutput->s[0] / 2;
+            size_t srcUStride = imgbOutput->s[1] / 2;
+            size_t srcVStride = imgbOutput->s[2] / 2;
+            dstYStride /= 2;
+            dstUStride /= 2;
+            dstVStride /= 2;
+            if (OAPV_CS_GET_FORMAT(imgbOutput->cs) == OAPV_CF_YCBCR420) {
+                ALOGV("OAPV_CS_YUV420 10bit to P010");
+                copyBufferFromYUV42010bitToP010((uint16_t*)dstY, (uint16_t*)dstU, srcY, srcU, srcV,
+                                                srcYStride, srcUStride, srcVStride, dstYStride,
+                                                dstUStride, mWidth, mHeight);
+            } else if (OAPV_CS_GET_FORMAT(imgbOutput->cs) == OAPV_CF_YCBCR422) {
+                ALOGV("OAPV_CS_YUV422 10bit to P010");
+                copyBufferFromYUV42210bitToP010((uint16_t*)dstY, (uint16_t*)dstU, srcY, srcU, srcV,
+                                                srcYStride, srcUStride, srcVStride, dstYStride,
+                                                dstUStride, mWidth, mHeight);
+            } else if (OAPV_CS_GET_FORMAT(imgbOutput->cs) == OAPV_CF_PLANAR2) {
+                ALOGV("OAPV_CS_P210 to P010");
+                copyBufferFromP210ToP010((uint16_t*)dstY, (uint16_t*)dstU, srcY, srcU, srcYStride,
+                                         srcUStride, dstYStride, dstUStride, mWidth, mHeight);
+            } else {
+                ALOGE("Not supported convert format : %d", OAPV_CS_GET_FORMAT(imgbOutput->cs));
+            }
+        } else {
+            ALOGE("Not supported convder from bd:%d, format: %d(%s), to format: %d(%s)",
+                  OAPV_CS_GET_BIT_DEPTH(imgbOutput->cs), OAPV_CS_GET_FORMAT(imgbOutput->cs),
+                  OAPV_CS_GET_FORMAT(imgbOutput->cs) == OAPV_CF_YCBCR420
+                          ? "YUV420"
+                          : (OAPV_CS_GET_FORMAT(imgbOutput->cs) == OAPV_CF_YCBCR422 ? "YUV422"
+                                                                                    : "UNKNOWN"),
+                  format,
+                  format == HAL_PIXEL_FORMAT_YCBCR_P010
+                          ? "P010"
+                          : (format == HAL_PIXEL_FORMAT_YCBCR_420_888
+                                     ? "YUV420"
+                                     : (format == HAL_PIXEL_FORMAT_YV12 ? "YV12" : "UNKNOWN")));
+        }
+    } else {  // HAL_PIXEL_FORMAT_YV12
+        if (OAPV_CS_GET_BIT_DEPTH(imgbOutput->cs) == 10) {
+            const uint16_t* srcY = (const uint16_t*)imgbOutput->a[0];
+            const uint16_t* srcV = (const uint16_t*)imgbOutput->a[1];
+            const uint16_t* srcU = (const uint16_t*)imgbOutput->a[2];
+            size_t srcYStride = imgbOutput->s[0] / 2;
+            size_t srcVStride = imgbOutput->s[1] / 2;
+            size_t srcUStride = imgbOutput->s[2] / 2;
+            if (OAPV_CS_GET_FORMAT(imgbOutput->cs) == OAPV_CF_YCBCR420) {
+                ALOGV("OAPV_CS_YUV420 10bit to YV12");
+                copyBufferFromYUV42010bitToYV12(dstY, dstU, dstV, srcY, srcU, srcV, srcYStride,
+                                                srcUStride, srcVStride, dstYStride, dstUStride,
+                                                dstVStride, mWidth, mHeight);
+            } else if (OAPV_CS_GET_FORMAT(imgbOutput->cs) == OAPV_CF_YCBCR422) {
+                ALOGV("OAPV_CS_YUV422 10bit to YV12");
+                copyBufferFromYUV42210bitToYV12(dstY, dstU, dstV, srcY, srcU, srcV, srcYStride,
+                                                srcUStride, srcVStride, dstYStride, dstUStride,
+                                                dstVStride, mWidth, mHeight);
+            } else if (OAPV_CS_GET_FORMAT(imgbOutput->cs) == OAPV_CF_PLANAR2) {
+                ALOGV("OAPV_CS_P210 to YV12");
+                copyBufferFromP210ToYV12(dstY, dstU, dstV, srcY, srcV, srcYStride, srcVStride,
+                                         dstYStride, dstUStride, dstVStride, mWidth, mHeight);
+            } else {
+                ALOGE("Not supported convert format : %d", OAPV_CS_GET_FORMAT(imgbOutput->cs));
+            }
+        } else if (OAPV_CS_GET_BIT_DEPTH(imgbOutput->cs) == 8) {
+            const uint8_t* srcY = (const uint8_t*)imgbOutput->a[0];
+            const uint8_t* srcV = (const uint8_t*)imgbOutput->a[1];
+            const uint8_t* srcU = (const uint8_t*)imgbOutput->a[2];
+            size_t srcYStride = imgbOutput->s[0];
+            size_t srcVStride = imgbOutput->s[1];
+            size_t srcUStride = imgbOutput->s[2];
+            if (OAPV_CS_GET_FORMAT(imgbOutput->cs) == OAPV_CF_YCBCR420) {
+                ALOGV("OAPV_CS_YUV420 to YV12");
+                copyBufferFromYUV420ToYV12(dstY, dstU, dstV, srcY, srcU, srcV, srcYStride,
+                                           srcUStride, srcVStride, dstYStride, dstUStride,
+                                           dstVStride, mWidth, mHeight);
+            } else if (OAPV_CS_GET_FORMAT(imgbOutput->cs) == OAPV_CF_YCBCR422) {
+                ALOGV("OAPV_CS_YUV422 to YV12");
+                copyBufferFromYUV422ToYV12(dstY, dstU, dstV, srcY, srcU, srcV, srcYStride,
+                                           srcUStride, srcVStride, dstYStride, dstUStride,
+                                           dstVStride, mWidth, mHeight);
+            } else {
+                ALOGE("Not supported convert format : %d", OAPV_CS_GET_FORMAT(imgbOutput->cs));
+            }
+        } else {
+            ALOGE("Not supported convert from bd:%d, format: %d(%s), to format: %d(%s)",
+                  OAPV_CS_GET_BIT_DEPTH(imgbOutput->cs), OAPV_CS_GET_FORMAT(imgbOutput->cs),
+                  OAPV_CS_GET_FORMAT(imgbOutput->cs) == OAPV_CF_YCBCR420
+                          ? "YUV420"
+                          : (OAPV_CS_GET_FORMAT(imgbOutput->cs) == OAPV_CF_YCBCR422 ? "YUV422"
+                                                                                    : "UNKNOWN"),
+                  format,
+                  format == HAL_PIXEL_FORMAT_YCBCR_P010
+                          ? "P010"
+                          : (format == HAL_PIXEL_FORMAT_YCBCR_420_888
+                                     ? "YUV420"
+                                     : (format == HAL_PIXEL_FORMAT_YV12 ? "YV12" : "UNKNOWN")));
+        }
+    }
+
+    finishWork(work->input.ordinal.frameIndex.peekll(), work, std::move(block));
+    return OK;
+}
+
+c2_status_t C2SoftApvDec::drainInternal(uint32_t drainMode,
+                                        const std::shared_ptr<C2BlockPool>& pool,
+                                        const std::unique_ptr<C2Work>& work) {
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    if (drainMode == DRAIN_COMPONENT_WITH_EOS && work && work->workletsProcessed == 0u) {
+        fillEmptyWork(work);
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftApvDec::drain(uint32_t drainMode, const std::shared_ptr<C2BlockPool>& pool) {
+    return drainInternal(drainMode, pool, nullptr);
+}
+
+class C2SoftApvDecFactory : public C2ComponentFactory {
+  public:
+    C2SoftApvDecFactory()
+        : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+                  GetCodec2PlatformComponentStore()->getParamReflector())) {}
+
+    virtual c2_status_t createComponent(c2_node_id_t id,
+                                        std::shared_ptr<C2Component>* const component,
+                                        std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+                new C2SoftApvDec(COMPONENT_NAME, id,
+                                 std::make_shared<C2SoftApvDec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id, std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+                new SimpleInterface<C2SoftApvDec::IntfImpl>(
+                        COMPONENT_NAME, id, std::make_shared<C2SoftApvDec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftApvDecFactory() override = default;
+
+  private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+__attribute__((cfi_canonical_jump_table)) extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    if (!android::media::swcodec::flags::apv_software_codec()) {
+        ALOGV("APV SW Codec is not enabled");
+        return nullptr;
+    }
+    return new ::android::C2SoftApvDecFactory();
+}
+
+__attribute__((cfi_canonical_jump_table)) extern "C" void DestroyCodec2Factory(
+        ::C2ComponentFactory* factory) {
+    delete factory;
+}
diff --git a/media/codec2/components/apv/C2SoftApvDec.h b/media/codec2/components/apv/C2SoftApvDec.h
new file mode 100644
index 0000000..05afdb2
--- /dev/null
+++ b/media/codec2/components/apv/C2SoftApvDec.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_C2_SOFT_APV_DEC_H_
+#define ANDROID_C2_SOFT_APV_DEC_H_
+
+#include <media/stagefright/foundation/ColorUtils.h>
+
+#include <SimpleC2Component.h>
+#include <inttypes.h>
+#include <atomic>
+
+#include "oapv.h"
+#include <C2SoftApvCommon.h>
+
+typedef unsigned int UWORD32;
+
+typedef enum {
+    IV_CHROMA_NA = 0xFFFFFFFF,
+    IV_YUV_420P = 0x1,
+    IV_YUV_422P = 0x2,
+    IV_420_UV_INTL = 0x3,
+    IV_YUV_422IBE = 0x4,
+    IV_YUV_422ILE = 0x5,
+    IV_YUV_444P = 0x6,
+    IV_YUV_411P = 0x7,
+    IV_GRAY = 0x8,
+    IV_RGB_565 = 0x9,
+    IV_RGB_24 = 0xa,
+    IV_YUV_420SP_UV = 0xb,
+    IV_YUV_420SP_VU = 0xc,
+    IV_YUV_422SP_UV = 0xd,
+    IV_YUV_422SP_VU = 0xe
+
+} IV_COLOR_FORMAT_T;
+
+typedef struct {
+    /**
+     * u4_size of the structure
+     */
+    UWORD32 u4_size;
+
+    /**
+     * Pointer to the API function pointer table of the codec
+     */
+    void* pv_fxns;
+
+    /**
+     * Pointer to the handle of the codec
+     */
+    void* pv_codec_handle;
+} iv_obj_t;
+
+namespace android {
+
+struct C2SoftApvDec final : public SimpleC2Component {
+    class IntfImpl;
+
+    C2SoftApvDec(const char* name, c2_node_id_t id, const std::shared_ptr<IntfImpl>& intfImpl);
+    virtual ~C2SoftApvDec();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(const std::unique_ptr<C2Work>& work,
+                 const std::shared_ptr<C2BlockPool>& pool) override;
+    c2_status_t drain(uint32_t drainMode, const std::shared_ptr<C2BlockPool>& pool) override;
+
+  private:
+    status_t createDecoder();
+    status_t initDecoder();
+    bool isConfigured() const;
+    void drainDecoder();
+    status_t setFlushMode();
+    status_t resetDecoder();
+    void resetPlugin();
+    status_t deleteDecoder();
+    void finishWork(uint64_t index, const std::unique_ptr<C2Work>& work,
+                    const std::shared_ptr<C2GraphicBlock>& block);
+    void drainRingBuffer(const std::unique_ptr<C2Work>& work,
+                         const std::shared_ptr<C2BlockPool>& pool, bool eos);
+    c2_status_t drainInternal(uint32_t drainMode, const std::shared_ptr<C2BlockPool>& pool,
+                              const std::unique_ptr<C2Work>& work);
+
+    status_t outputBuffer(const std::shared_ptr<C2BlockPool>& pool,
+                          const std::unique_ptr<C2Work>& work);
+
+    std::shared_ptr<IntfImpl> mIntf;
+    iv_obj_t* mDecHandle;
+    uint8_t* mOutBufferFlush;
+    IV_COLOR_FORMAT_T mIvColorformat;
+    uint32_t mOutputDelay;
+    bool mHeaderDecoded;
+    std::atomic_uint64_t mOutIndex;
+    std::shared_ptr<C2GraphicBlock> mOutBlock;
+
+    std::shared_ptr<C2StreamPixelFormatInfo::output> mPixelFormatInfo;
+
+    std::shared_ptr<C2StreamPictureSizeInfo::input> mSize;
+    uint32_t mHalPixelFormat;
+    uint32_t mWidth;
+    uint32_t mHeight;
+    bool mSignalledOutputEos;
+    bool mSignalledError;
+    // Color aspects. These are ISO values and are meant to detect changes in aspects to avoid
+    // converting them to C2 values for each frame
+    struct VuiColorAspects {
+        uint8_t primaries;
+        uint8_t transfer;
+        uint8_t coeffs;
+        uint8_t fullRange;
+
+        // default color aspects
+        VuiColorAspects()
+            : primaries(C2Color::PRIMARIES_UNSPECIFIED),
+            transfer(C2Color::TRANSFER_UNSPECIFIED),
+            coeffs(C2Color::MATRIX_UNSPECIFIED),
+            fullRange(C2Color::RANGE_UNSPECIFIED) { }
+
+        bool operator==(const VuiColorAspects &o) {
+            return primaries == o.primaries && transfer == o.transfer && coeffs == o.coeffs
+                && fullRange == o.fullRange;
+        }
+    } mBitstreamColorAspects;
+
+    oapvd_t oapvdHandle;
+    oapvm_t oapvmHandle;
+    oapvd_cdesc_t cdesc;
+    oapv_frms_t ofrms;
+
+    int outputCsp;
+
+    void getVuiParams(VuiColorAspects* buffer);
+
+    C2_DO_NOT_COPY(C2SoftApvDec);
+};
+
+}  // namespace android
+
+#endif
diff --git a/media/codec2/components/apv/C2SoftApvEnc.cpp b/media/codec2/components/apv/C2SoftApvEnc.cpp
new file mode 100644
index 0000000..9036df1
--- /dev/null
+++ b/media/codec2/components/apv/C2SoftApvEnc.cpp
@@ -0,0 +1,1461 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "C2SoftApvEnc"
+#include <log/log.h>
+
+#include <android_media_swcodec_flags.h>
+
+#include <media/hardware/VideoAPI.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/foundation/AUtils.h>
+
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+#include <Codec2BufferUtils.h>
+#include <Codec2CommonUtils.h>
+#include <Codec2Mapper.h>
+#include <SimpleC2Interface.h>
+#include <media/stagefright/foundation/ABitReader.h>
+#include <util/C2InterfaceHelper.h>
+#include <cmath>
+#include "C2SoftApvEnc.h"
+
+namespace android {
+
+namespace {
+
+constexpr char COMPONENT_NAME[] = "c2.android.apv.encoder";
+constexpr uint32_t kMinOutBufferSize = 524288;
+constexpr uint32_t kMaxBitstreamBufSize = 16 * 1024 * 1024;
+constexpr int32_t kApvQpMin = 0;
+constexpr int32_t kApvQpMax = 51;
+constexpr int32_t kApvDefaultQP = 32;
+
+#define PROFILE_APV_DEFAULT 0
+#define LEVEL_APV_DEFAULT 0
+#define MAX_NUM_FRMS (1)  // supports only 1-frame input
+
+}  // namespace
+
+class C2SoftApvEnc::IntfImpl : public SimpleInterface<void>::BaseParams {
+  public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper>& helper)
+        : SimpleInterface<void>::BaseParams(helper, COMPONENT_NAME, C2Component::KIND_ENCODER,
+                                            C2Component::DOMAIN_VIDEO, MEDIA_MIMETYPE_VIDEO_APV) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noTimeStretch();
+        setDerivedInstance(this);
+
+        addParameter(DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                             .withConstValue(new C2ComponentAttributesSetting(
+                                     C2Component::ATTRIB_IS_TEMPORAL))
+                             .build());
+
+        addParameter(DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE)
+                             .withConstValue(new C2StreamUsageTuning::input(
+                                     0u, (uint64_t)C2MemoryUsage::CPU_READ))
+                             .build());
+
+        // matches size limits in codec library
+        addParameter(DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
+                             .withDefault(new C2StreamPictureSizeInfo::input(0u, 320, 240))
+                             .withFields({
+                                     C2F(mSize, width).inRange(2, 4096, 2),
+                                     C2F(mSize, height).inRange(2, 4096, 2),
+                             })
+                             .withSetter(SizeSetter)
+                             .build());
+
+        // matches limits in codec library
+        addParameter(DefineParam(mBitrateMode, C2_PARAMKEY_BITRATE_MODE)
+                             .withDefault(new C2StreamBitrateModeTuning::output(
+                                     0u, C2Config::BITRATE_VARIABLE))
+                             .withFields({C2F(mBitrateMode, value)
+                                                  .oneOf({C2Config::BITRATE_CONST,
+                                                          C2Config::BITRATE_VARIABLE,
+                                                          C2Config::BITRATE_IGNORE})})
+                             .withSetter(Setter<decltype(*mBitrateMode)>::StrictValueWithNoDeps)
+                             .build());
+
+        addParameter(DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
+                             .withDefault(new C2StreamBitrateInfo::output(0u, 512000))
+                             .withFields({C2F(mBitrate, value).inRange(512000, 240000000)})
+                             .withSetter(BitrateSetter)
+                             .build());
+
+        addParameter(DefineParam(mFrameRate, C2_PARAMKEY_FRAME_RATE)
+                             .withDefault(new C2StreamFrameRateInfo::output(0u, 15.))
+                             .withFields({C2F(mFrameRate, value).greaterThan(0.)})
+                             .withSetter(Setter<decltype(*mFrameRate)>::StrictValueWithNoDeps)
+                             .build());
+
+        addParameter(DefineParam(mQuality, C2_PARAMKEY_QUALITY)
+                             .withDefault(new C2StreamQualityTuning::output(0u, 40))
+                             .withFields({C2F(mQuality, value).inRange(0, 100)})
+                             .withSetter(Setter<decltype(*mQuality)>::NonStrictValueWithNoDeps)
+                             .build());
+
+        addParameter(
+                DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+                        .withDefault(new C2StreamProfileLevelInfo::output(
+                                0u, C2Config::PROFILE_APV_422_10, LEVEL_APV_1_BAND_0))
+                        .withFields({
+                                C2F(mProfileLevel, profile).oneOf({C2Config::PROFILE_APV_422_10}),
+                                C2F(mProfileLevel, level)
+                                        .oneOf({
+                                                C2Config::LEVEL_APV_1_BAND_0,
+                                                C2Config::LEVEL_APV_1_1_BAND_0,
+                                                C2Config::LEVEL_APV_2_BAND_0,
+                                                C2Config::LEVEL_APV_2_1_BAND_0,
+                                                C2Config::LEVEL_APV_3_BAND_0,
+                                                C2Config::LEVEL_APV_3_1_BAND_0,
+                                                C2Config::LEVEL_APV_4_BAND_0,
+                                                C2Config::LEVEL_APV_4_1_BAND_0,
+                                                C2Config::LEVEL_APV_5_BAND_0,
+                                                C2Config::LEVEL_APV_5_1_BAND_0,
+                                                C2Config::LEVEL_APV_6_BAND_0,
+                                                C2Config::LEVEL_APV_6_1_BAND_0,
+                                                C2Config::LEVEL_APV_7_BAND_0,
+                                                C2Config::LEVEL_APV_7_1_BAND_0,
+                                                C2Config::LEVEL_APV_1_BAND_1,
+                                                C2Config::LEVEL_APV_1_1_BAND_1,
+                                                C2Config::LEVEL_APV_2_BAND_1,
+                                                C2Config::LEVEL_APV_2_1_BAND_1,
+                                                C2Config::LEVEL_APV_3_BAND_1,
+                                                C2Config::LEVEL_APV_3_1_BAND_1,
+                                                C2Config::LEVEL_APV_4_BAND_1,
+                                                C2Config::LEVEL_APV_4_1_BAND_1,
+                                                C2Config::LEVEL_APV_5_BAND_1,
+                                                C2Config::LEVEL_APV_5_1_BAND_1,
+                                                C2Config::LEVEL_APV_6_BAND_1,
+                                                C2Config::LEVEL_APV_6_1_BAND_1,
+                                                C2Config::LEVEL_APV_7_BAND_1,
+                                                C2Config::LEVEL_APV_7_1_BAND_1,
+                                                C2Config::LEVEL_APV_1_BAND_2,
+                                                C2Config::LEVEL_APV_1_1_BAND_2,
+                                                C2Config::LEVEL_APV_2_BAND_2,
+                                                C2Config::LEVEL_APV_2_1_BAND_2,
+                                                C2Config::LEVEL_APV_3_BAND_2,
+                                                C2Config::LEVEL_APV_3_1_BAND_2,
+                                                C2Config::LEVEL_APV_4_BAND_2,
+                                                C2Config::LEVEL_APV_4_1_BAND_2,
+                                                C2Config::LEVEL_APV_5_BAND_2,
+                                                C2Config::LEVEL_APV_5_1_BAND_2,
+                                                C2Config::LEVEL_APV_6_BAND_2,
+                                                C2Config::LEVEL_APV_6_1_BAND_2,
+                                                C2Config::LEVEL_APV_7_BAND_2,
+                                                C2Config::LEVEL_APV_7_1_BAND_2,
+                                                C2Config::LEVEL_APV_1_BAND_3,
+                                                C2Config::LEVEL_APV_1_1_BAND_3,
+                                                C2Config::LEVEL_APV_2_BAND_3,
+                                                C2Config::LEVEL_APV_2_1_BAND_3,
+                                                C2Config::LEVEL_APV_3_BAND_3,
+                                                C2Config::LEVEL_APV_3_1_BAND_3,
+                                                C2Config::LEVEL_APV_4_BAND_3,
+                                                C2Config::LEVEL_APV_4_1_BAND_3,
+                                                C2Config::LEVEL_APV_5_BAND_3,
+                                                C2Config::LEVEL_APV_5_1_BAND_3,
+                                                C2Config::LEVEL_APV_6_BAND_3,
+                                                C2Config::LEVEL_APV_6_1_BAND_3,
+                                                C2Config::LEVEL_APV_7_BAND_3,
+                                                C2Config::LEVEL_APV_7_1_BAND_3,
+                                        }),
+                        })
+                        .withSetter(ProfileLevelSetter, mSize, mFrameRate, mBitrate)
+                        .build());
+
+        addParameter(DefineParam(mColorAspects, C2_PARAMKEY_COLOR_ASPECTS)
+                             .withDefault(new C2StreamColorAspectsInfo::input(
+                                     0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED,
+                                     C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+                             .withFields({C2F(mColorAspects, range)
+                                                  .inRange(C2Color::RANGE_UNSPECIFIED,
+                                                           C2Color::RANGE_OTHER),
+                                          C2F(mColorAspects, primaries)
+                                                  .inRange(C2Color::PRIMARIES_UNSPECIFIED,
+                                                           C2Color::PRIMARIES_OTHER),
+                                          C2F(mColorAspects, transfer)
+                                                  .inRange(C2Color::TRANSFER_UNSPECIFIED,
+                                                           C2Color::TRANSFER_OTHER),
+                                          C2F(mColorAspects, matrix)
+                                                  .inRange(C2Color::MATRIX_UNSPECIFIED,
+                                                           C2Color::MATRIX_OTHER)})
+                             .withSetter(ColorAspectsSetter)
+                             .build());
+
+        addParameter(DefineParam(mCodedColorAspects, C2_PARAMKEY_VUI_COLOR_ASPECTS)
+                             .withDefault(new C2StreamColorAspectsInfo::output(
+                                     0u, C2Color::RANGE_LIMITED, C2Color::PRIMARIES_UNSPECIFIED,
+                                     C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+                             .withFields({C2F(mCodedColorAspects, range)
+                                                  .inRange(C2Color::RANGE_UNSPECIFIED,
+                                                           C2Color::RANGE_OTHER),
+                                          C2F(mCodedColorAspects, primaries)
+                                                  .inRange(C2Color::PRIMARIES_UNSPECIFIED,
+                                                           C2Color::PRIMARIES_OTHER),
+                                          C2F(mCodedColorAspects, transfer)
+                                                  .inRange(C2Color::TRANSFER_UNSPECIFIED,
+                                                           C2Color::TRANSFER_OTHER),
+                                          C2F(mCodedColorAspects, matrix)
+                                                  .inRange(C2Color::MATRIX_UNSPECIFIED,
+                                                           C2Color::MATRIX_OTHER)})
+                             .withSetter(CodedColorAspectsSetter, mColorAspects)
+                             .build());
+        std::vector<uint32_t> pixelFormats = {
+            HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
+            HAL_PIXEL_FORMAT_YCBCR_420_888,
+        };
+        if (isHalPixelFormatSupported((AHardwareBuffer_Format)HAL_PIXEL_FORMAT_YCBCR_P010)) {
+            pixelFormats.push_back(HAL_PIXEL_FORMAT_YCBCR_P010);
+        }
+        if (isHalPixelFormatSupported((AHardwareBuffer_Format)AHARDWAREBUFFER_FORMAT_YCbCr_P210)) {
+            pixelFormats.push_back(AHARDWAREBUFFER_FORMAT_YCbCr_P210);
+        }
+        addParameter(DefineParam(mPixelFormat, C2_PARAMKEY_PIXEL_FORMAT)
+                             .withDefault(new C2StreamPixelFormatInfo::input(
+                                     0u, HAL_PIXEL_FORMAT_YCBCR_420_888))
+                             .withFields({C2F(mPixelFormat, value).oneOf({pixelFormats})})
+                             .withSetter((Setter<decltype(*mPixelFormat)>::StrictValueWithNoDeps))
+                             .build());
+    }
+
+    static C2R BitrateSetter(bool mayBlock, C2P<C2StreamBitrateInfo::output>& me) {
+        (void)mayBlock;
+        C2R res = C2R::Ok();
+        if (me.v.value < 1000000) {
+            me.set().value = 1000000;
+        }
+        return res;
+    }
+
+    static C2R SizeSetter(bool mayBlock, const C2P<C2StreamPictureSizeInfo::input>& oldMe,
+                          C2P<C2StreamPictureSizeInfo::input>& me) {
+        (void)mayBlock;
+        C2R res = C2R::Ok();
+        if (!me.F(me.v.width).supportsAtAll(me.v.width)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width)));
+            me.set().width = oldMe.v.width;
+        }
+        if (!me.F(me.v.height).supportsAtAll(me.v.height)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height)));
+            me.set().height = oldMe.v.height;
+        }
+        return res;
+    }
+
+    static C2R ProfileLevelSetter(bool mayBlock, C2P<C2StreamProfileLevelInfo::output>& me,
+                                  const C2P<C2StreamPictureSizeInfo::input>& size,
+                                  const C2P<C2StreamFrameRateInfo::output>& frameRate,
+                                  const C2P<C2StreamBitrateInfo::output>& bitrate) {
+        (void)mayBlock;
+        if (!me.F(me.v.profile).supportsAtAll(me.v.profile)) {
+            me.set().profile = C2Config::PROFILE_APV_422_10;
+        }
+        if (!me.F(me.v.level).supportsAtAll(me.v.level)) {
+            me.set().level = LEVEL_APV_1_BAND_0;
+        }
+
+        int32_t bandIdc = me.v.level <= LEVEL_APV_7_1_BAND_0 ? 0 :
+                          me.v.level <= LEVEL_APV_7_1_BAND_1 ? 1 :
+                          me.v.level <= LEVEL_APV_7_1_BAND_2 ? 2 : 3;
+
+        me.set().level = decisionApvLevel(size.v.width, size.v.height, frameRate.v.value,
+                                            (uint64_t)bitrate.v.value, bandIdc);
+        return C2R::Ok();
+    }
+
+    static C2R ColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::input>& me) {
+        (void)mayBlock;
+        if (me.v.range > C2Color::RANGE_OTHER) {
+            me.set().range = C2Color::RANGE_OTHER;
+        }
+        if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
+            me.set().primaries = C2Color::PRIMARIES_OTHER;
+        }
+        if (me.v.transfer > C2Color::TRANSFER_OTHER) {
+            me.set().transfer = C2Color::TRANSFER_OTHER;
+        }
+        if (me.v.matrix > C2Color::MATRIX_OTHER) {
+            me.set().matrix = C2Color::MATRIX_OTHER;
+        }
+        return C2R::Ok();
+    }
+
+    static C2R CodedColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::output>& me,
+                                       const C2P<C2StreamColorAspectsInfo::input>& coded) {
+        (void)mayBlock;
+        me.set().range = coded.v.range;
+        me.set().primaries = coded.v.primaries;
+        me.set().transfer = coded.v.transfer;
+        me.set().matrix = coded.v.matrix;
+        return C2R::Ok();
+    }
+
+    static C2Config::level_t decisionApvLevel(int32_t width, int32_t height, int32_t fps,
+                                                    uint64_t bitrate, int32_t band) {
+        C2Config::level_t level = C2Config::LEVEL_APV_1_BAND_0;
+        struct LevelLimits {
+            C2Config::level_t level;
+            uint64_t samplesPerSec;
+            uint64_t kbpsOfBand;
+        };
+
+        constexpr LevelLimits kLimitsBand0[] = {
+                {LEVEL_APV_1_BAND_0, 3'041'280, 7'000},
+                {LEVEL_APV_1_1_BAND_0, 6'082'560, 14'000},
+                {LEVEL_APV_2_BAND_0, 15'667'200, 36'000},
+                {LEVEL_APV_2_1_BAND_0, 31'334'400, 71'000},
+                {LEVEL_APV_3_BAND_0, 66'846'720, 101'000},
+                {LEVEL_APV_3_1_BAND_0, 133'693'440, 201'000},
+                {LEVEL_APV_4_BAND_0, 265'420'800, 401'000},
+                {LEVEL_APV_4_1_BAND_0, 530'841'600, 780'000},
+                {LEVEL_APV_5_BAND_0, 1'061'683'200, 1'560'000},
+                {LEVEL_APV_5_1_BAND_0, 2'123'366'400, 3'324'000},
+                {LEVEL_APV_6_BAND_0, 4'777'574'400, 6'648'000},
+                {LEVEL_APV_6_1_BAND_0, 8'493'465'600, 13'296'000},
+                {LEVEL_APV_7_BAND_0, 16'986'931'200, 26'592'000},
+                {LEVEL_APV_7_1_BAND_0, 33'973'862'400, 53'184'000},
+        };
+
+        constexpr LevelLimits kLimitsBand1[] = {
+                {LEVEL_APV_1_BAND_1, 3'041'280, 11'000},
+                {LEVEL_APV_1_1_BAND_1, 6'082'560, 21'000},
+                {LEVEL_APV_2_BAND_1, 15'667'200, 53'000},
+                {LEVEL_APV_2_1_BAND_1, 31'334'400, 106'00},
+                {LEVEL_APV_3_BAND_1, 66'846'720, 151'000},
+                {LEVEL_APV_3_1_BAND_1, 133'693'440, 301'000},
+                {LEVEL_APV_4_BAND_1, 265'420'800, 602'000},
+                {LEVEL_APV_4_1_BAND_1, 530'841'600, 1'170'000},
+                {LEVEL_APV_5_BAND_1, 1'061'683'200, 2'340'000},
+                {LEVEL_APV_5_1_BAND_1, 2'123'366'400, 4'986'000},
+                {LEVEL_APV_6_BAND_1, 4'777'574'400, 9'972'000},
+                {LEVEL_APV_6_1_BAND_1, 8'493'465'600, 19'944'000},
+                {LEVEL_APV_7_BAND_1, 16'986'931'200, 39'888'000},
+                {LEVEL_APV_7_1_BAND_1, 33'973'862'400, 79'776'000},
+        };
+
+        constexpr LevelLimits kLimitsBand2[] = {
+                {LEVEL_APV_1_BAND_2, 3'041'280, 14'000},
+                {LEVEL_APV_1_1_BAND_2, 6'082'560, 28'000},
+                {LEVEL_APV_2_BAND_2, 15'667'200, 71'000},
+                {LEVEL_APV_2_1_BAND_2, 31'334'400, 141'000},
+                {LEVEL_APV_3_BAND_2, 66'846'720, 201'000},
+                {LEVEL_APV_3_1_BAND_2, 133'693'440, 401'000},
+                {LEVEL_APV_4_BAND_2, 265'420'800, 780'000},
+                {LEVEL_APV_4_1_BAND_2, 530'841'600, 1'560'000},
+                {LEVEL_APV_5_BAND_2, 1'061'683'200, 3'324'000},
+                {LEVEL_APV_5_1_BAND_2, 2'123'366'400, 6'648'000},
+                {LEVEL_APV_6_BAND_2, 4'777'574'400, 13'296'000},
+                {LEVEL_APV_6_1_BAND_2, 8'493'465'600, 26'592'000},
+                {LEVEL_APV_7_BAND_2, 16'986'931'200, 53'184'000},
+                {LEVEL_APV_7_1_BAND_2, 33'973'862'400, 106'368'000},
+        };
+
+        constexpr LevelLimits kLimitsBand3[] = {
+                {LEVEL_APV_1_BAND_3, 3'041'280, 21'000},
+                {LEVEL_APV_1_1_BAND_3, 6'082'560, 42'000},
+                {LEVEL_APV_2_BAND_3, 15'667'200, 106'000},
+                {LEVEL_APV_2_1_BAND_3, 31'334'400, 212'000},
+                {LEVEL_APV_3_BAND_3, 66'846'720, 301'000},
+                {LEVEL_APV_3_1_BAND_3, 133'693'440, 602'000},
+                {LEVEL_APV_4_BAND_3, 265'420'800, 1'170'000},
+                {LEVEL_APV_4_1_BAND_3, 530'841'600, 2'340'000},
+                {LEVEL_APV_5_BAND_3, 1'061'683'200, 4'986'000},
+                {LEVEL_APV_5_1_BAND_3, 2'123'366'400, 9'972'000},
+                {LEVEL_APV_6_BAND_3, 4'777'574'400, 19'944'000},
+                {LEVEL_APV_6_1_BAND_3, 8'493'465'600, 39'888'000},
+                {LEVEL_APV_7_BAND_3, 16'986'931'200, 79'776'000},
+                {LEVEL_APV_7_1_BAND_3, 33'973'862'400, 159'552'000},
+        };
+
+        uint64_t samplesPerSec = width * height * fps;
+        if (band == 0) {
+            for (const LevelLimits& limit : kLimitsBand0) {
+                if (samplesPerSec <= limit.samplesPerSec && bitrate <= limit.kbpsOfBand * 1000) {
+                    level = limit.level;
+                    break;
+                }
+            }
+        } else if (band == 1) {
+            for (const LevelLimits& limit : kLimitsBand1) {
+                if (samplesPerSec <= limit.samplesPerSec && bitrate <= limit.kbpsOfBand * 1000) {
+                    level = limit.level;
+                    break;
+                }
+            }
+        } else if (band == 2) {
+            for (const LevelLimits& limit : kLimitsBand2) {
+                if (samplesPerSec <= limit.samplesPerSec && bitrate <= limit.kbpsOfBand * 1000) {
+                    level = limit.level;
+                    break;
+                }
+            }
+        } else if (band == 3) {
+            for (const LevelLimits& limit : kLimitsBand3) {
+                if (samplesPerSec <= limit.samplesPerSec && bitrate <= limit.kbpsOfBand * 1000) {
+                    level = limit.level;
+                    break;
+                }
+            }
+        } else {
+            ALOGE("Invalid band_idc on calculte level");
+        }
+
+        return level;
+    }
+
+    uint32_t getProfile_l() const {
+        int32_t profile = PROFILE_UNUSED;
+
+        switch (mProfileLevel->profile) {
+            case C2Config::PROFILE_APV_422_10:
+                profile = 33;
+                break;
+            case C2Config::PROFILE_APV_422_12:
+                profile = 44;
+                break;
+            case C2Config::PROFILE_APV_444_10:
+                profile = 55;
+                break;
+            case C2Config::PROFILE_APV_444_12:
+                profile = 66;
+                break;
+            case C2Config::PROFILE_APV_4444_10:
+                profile = 77;
+                break;
+            case C2Config::PROFILE_APV_4444_12:
+                profile = 88;
+                break;
+            case C2Config::PROFILE_APV_400_10:
+                profile = 99;
+                break;
+            default:
+                ALOGW("Unrecognized profile: %x", mProfileLevel->profile);
+        }
+        return profile;
+    }
+
+    uint32_t getLevel_l() const {
+        int32_t level = LEVEL_UNUSED;
+
+        // TODO: Add Band settings
+        switch (mProfileLevel->level) {
+            case C2Config::LEVEL_APV_1_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_1_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_1_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_1_BAND_3:
+                level = 10;
+                break;
+            case C2Config::LEVEL_APV_1_1_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_1_1_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_1_1_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_1_1_BAND_3:
+                level = 11;
+                break;
+            case C2Config::LEVEL_APV_2_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_2_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_2_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_2_BAND_3:
+                level = 20;
+                break;
+            case C2Config::LEVEL_APV_2_1_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_2_1_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_2_1_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_2_1_BAND_3:
+                level = 21;
+                break;
+            case C2Config::LEVEL_APV_3_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_3_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_3_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_3_BAND_3:
+                level = 30;
+                break;
+            case C2Config::LEVEL_APV_3_1_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_3_1_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_3_1_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_3_1_BAND_3:
+                level = 31;
+                break;
+            case C2Config::LEVEL_APV_4_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_4_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_4_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_4_BAND_3:
+                level = 40;
+                break;
+            case C2Config::LEVEL_APV_4_1_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_4_1_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_4_1_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_4_1_BAND_3:
+                level = 41;
+                break;
+            case C2Config::LEVEL_APV_5_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_5_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_5_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_5_BAND_3:
+                level = 50;
+                break;
+            case C2Config::LEVEL_APV_5_1_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_5_1_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_5_1_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_5_1_BAND_3:
+                level = 51;
+                break;
+            case C2Config::LEVEL_APV_6_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_6_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_6_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_6_BAND_3:
+                level = 60;
+                break;
+            case C2Config::LEVEL_APV_6_1_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_6_1_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_6_1_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_6_1_BAND_3:
+                level = 61;
+                break;
+            case C2Config::LEVEL_APV_7_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_7_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_7_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_7_BAND_3:
+                level = 70;
+                break;
+            case C2Config::LEVEL_APV_7_1_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_7_1_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_7_1_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_7_1_BAND_3:
+                level = 71;
+                break;
+            default:
+                ALOGW("Unrecognized level: %x", mProfileLevel->level);
+        }
+        // Convert to APV level_idc according to APV spec
+        return level * 3;
+    }
+
+    uint32_t getBandIdc_l() const {
+        uint32_t bandIdc = 0;
+
+        switch (mProfileLevel->level) {
+            case C2Config::LEVEL_APV_1_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_1_1_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_2_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_2_1_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_3_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_3_1_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_4_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_4_1_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_5_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_5_1_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_6_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_6_1_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_7_BAND_0:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_7_1_BAND_0:
+                bandIdc = 0;
+                break;
+            case C2Config::LEVEL_APV_1_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_1_1_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_2_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_2_1_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_3_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_3_1_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_4_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_4_1_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_5_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_5_1_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_6_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_6_1_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_7_BAND_1:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_7_1_BAND_1:
+                bandIdc = 1;
+                break;
+            case C2Config::LEVEL_APV_1_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_1_1_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_2_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_2_1_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_3_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_3_1_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_4_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_4_1_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_5_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_5_1_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_6_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_6_1_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_7_BAND_2:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_7_1_BAND_2:
+                bandIdc = 2;
+                break;
+            case C2Config::LEVEL_APV_1_BAND_3:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_1_1_BAND_3:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_2_BAND_3:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_2_1_BAND_3:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_3_BAND_3:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_3_1_BAND_3:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_4_BAND_3:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_4_1_BAND_3:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_5_BAND_3:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_5_1_BAND_3:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_6_BAND_3:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_6_1_BAND_3:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_7_BAND_3:
+                [[fallthrough]];
+            case C2Config::LEVEL_APV_7_1_BAND_3:
+                bandIdc = 3;
+                break;
+            default:
+                ALOGW("Unrecognized bandIdc through level: %x", mProfileLevel->level);
+        }
+        return bandIdc;
+    }
+
+    int32_t getBitrateMode_l() const {
+        int32_t bitrateMode = C2Config::BITRATE_CONST;
+
+        switch (mBitrateMode->value) {
+            case C2Config::BITRATE_CONST:
+                bitrateMode = OAPV_RC_CQP;
+                break;
+            case C2Config::BITRATE_VARIABLE:
+                bitrateMode = OAPV_RC_ABR;
+                break;
+            case C2Config::BITRATE_IGNORE:
+                bitrateMode = 0;
+                break;
+            default:
+                ALOGE("Unrecognized bitrate mode: %x", mBitrateMode->value);
+        }
+        return bitrateMode;
+    }
+
+    std::shared_ptr<C2StreamPictureSizeInfo::input> getSize_l() const { return mSize; }
+    std::shared_ptr<C2StreamFrameRateInfo::output> getFrameRate_l() const { return mFrameRate; }
+    std::shared_ptr<C2StreamBitrateInfo::output> getBitrate_l() const { return mBitrate; }
+    std::shared_ptr<C2StreamQualityTuning::output> getQuality_l() const { return mQuality; }
+    std::shared_ptr<C2StreamColorAspectsInfo::input> getColorAspects_l() const {
+        return mColorAspects;
+    }
+    std::shared_ptr<C2StreamColorAspectsInfo::output> getCodedColorAspects_l() const {
+        return mCodedColorAspects;
+    }
+    std::shared_ptr<C2StreamPictureQuantizationTuning::output> getPictureQuantization_l() const {
+        return mPictureQuantization;
+    }
+    std::shared_ptr<C2StreamProfileLevelInfo::output> getProfileLevel_l() const {
+        return mProfileLevel;
+    }
+    std::shared_ptr<C2StreamPixelFormatInfo::input> getPixelFormat_l() const {
+        return mPixelFormat;
+    }
+
+  private:
+    std::shared_ptr<C2StreamProfileLevelInfo::output> mProfileLevel;
+    std::shared_ptr<C2StreamUsageTuning::input> mUsage;
+    std::shared_ptr<C2StreamPictureSizeInfo::input> mSize;
+    std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
+    std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
+    std::shared_ptr<C2StreamBitrateModeTuning::output> mBitrateMode;
+    std::shared_ptr<C2StreamQualityTuning::output> mQuality;
+    std::shared_ptr<C2StreamColorAspectsInfo::input> mColorAspects;
+    std::shared_ptr<C2StreamColorAspectsInfo::output> mCodedColorAspects;
+    std::shared_ptr<C2StreamPictureQuantizationTuning::output> mPictureQuantization;
+    std::shared_ptr<C2StreamColorInfo::input> mColorFormat;
+    std::shared_ptr<C2StreamPixelFormatInfo::input> mPixelFormat;
+};
+
+C2SoftApvEnc::C2SoftApvEnc(const char* name, c2_node_id_t id,
+                           const std::shared_ptr<IntfImpl>& intfImpl)
+    : SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl),
+      mColorFormat(OAPV_CF_PLANAR2),
+      mStarted(false),
+      mSignalledEos(false),
+      mSignalledError(false),
+      mOutBlock(nullptr) {
+    reset();
+}
+
+C2SoftApvEnc::~C2SoftApvEnc() {
+    onRelease();
+}
+
+c2_status_t C2SoftApvEnc::onInit() {
+    return C2_OK;
+}
+
+c2_status_t C2SoftApvEnc::onStop() {
+    return C2_OK;
+}
+
+void C2SoftApvEnc::onReset() {
+    releaseEncoder();
+    reset();
+}
+
+void C2SoftApvEnc::onRelease() {
+    releaseEncoder();
+}
+
+c2_status_t C2SoftApvEnc::onFlush_sm() {
+    return C2_OK;
+}
+
+static void fillEmptyWork(const std::unique_ptr<C2Work>& work) {
+    uint32_t flags = 0;
+    if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+        flags |= C2FrameData::FLAG_END_OF_STREAM;
+        ALOGV("Signalling EOS");
+    }
+    work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->workletsProcessed = 1u;
+}
+
+int32_t C2SoftApvEnc::getQpFromQuality(int32_t quality) {
+    int32_t qp = ((kApvQpMin - kApvQpMax) * quality / 100) + kApvQpMax;
+    qp = std::min(qp, (int)kApvQpMax);
+    qp = std::max(qp, (int)kApvQpMin);
+    return qp;
+}
+
+c2_status_t C2SoftApvEnc::reset() {
+    ALOGV("reset");
+    mInitEncoder = false;
+    mStarted = false;
+    mSignalledEos = false;
+    mSignalledError = false;
+    mBitDepth = 10;
+    mMaxFrames = MAX_NUM_FRMS;
+    mReceivedFrames = 0;
+    mReceivedFirstFrame = false;
+    mColorFormat = OAPV_CF_PLANAR2;
+    memset(&mInputFrames, 0, sizeof(mInputFrames));
+    memset(&mReconFrames, 0, sizeof(mReconFrames));
+    return C2_OK;
+}
+
+c2_status_t C2SoftApvEnc::releaseEncoder() {
+    for (int32_t i = 0; i < MAX_NUM_FRMS; i++) {
+        if (mInputFrames.frm[i].imgb != nullptr) {
+            imgb_release(mInputFrames.frm[i].imgb);
+            mInputFrames.frm[i].imgb = nullptr;
+        }
+    }
+
+    if (mBitstreamBuf) {
+        std::free(mBitstreamBuf);
+        mBitstreamBuf = nullptr;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftApvEnc::drain(uint32_t drainMode, const std::shared_ptr<C2BlockPool>& pool) {
+    return drainInternal(drainMode, pool, nullptr);
+}
+
+void C2SoftApvEnc::showEncoderParams(oapve_cdesc_t* cdsc) {
+    std::string title = "APV encoder params:";
+    ALOGD("%s width = %d, height = %d", title.c_str(), cdsc->param[0].w, cdsc->param[0].h);
+    ALOGD("%s FrameRate = %f", title.c_str(),
+          (double)cdsc->param[0].fps_num / cdsc->param[0].fps_den);
+    ALOGD("%s BitRate = %d Kbps", title.c_str(), cdsc->param[0].bitrate);
+    ALOGD("%s QP = %d", title.c_str(), cdsc->param[0].qp);
+    ALOGD("%s profile_idc = %d, level_idc = %d, band_idc = %d", title.c_str(),
+          cdsc->param[0].profile_idc, cdsc->param[0].level_idc / 3, cdsc->param[0].band_idc);
+    ALOGD("%s Bitrate Mode: %d", title.c_str(), cdsc->param[0].rc_type);
+    ALOGD("%s mColorAspects primaries: %d, transfer: %d, matrix: %d, range: %d", title.c_str(),
+          mColorAspects->primaries, mColorAspects->transfer, mColorAspects->matrix,
+          mColorAspects->range);
+    ALOGD("%s mCodedColorAspects primaries: %d, transfer: %d, matrix: %d, range: %d", title.c_str(),
+          mCodedColorAspects->primaries, mCodedColorAspects->transfer, mCodedColorAspects->matrix,
+          mCodedColorAspects->range);
+    ALOGD("%s Input color format: %s", title.c_str(),
+          mColorFormat == OAPV_CF_YCBCR422 ? "YUV422P10LE" : "P210");
+    ALOGD("%s max_num_frms: %d", title.c_str(), cdsc->max_num_frms);
+}
+
+c2_status_t C2SoftApvEnc::initEncoder() {
+    if (mInitEncoder) {
+        return C2_OK;
+    }
+    ALOGV("initEncoder");
+
+    mSize = mIntf->getSize_l();
+    mFrameRate = mIntf->getFrameRate_l();
+    mBitrate = mIntf->getBitrate_l();
+    mQuality = mIntf->getQuality_l();
+    mColorAspects = mIntf->getColorAspects_l();
+    mCodedColorAspects = mIntf->getCodedColorAspects_l();
+    mProfileLevel = mIntf->getProfileLevel_l();
+    mPixelFormat = mIntf->getPixelFormat_l();
+
+    mCodecDesc = std::make_unique<oapve_cdesc_t>();
+    if (mCodecDesc == nullptr) {
+        ALOGE("Allocate ctx failed");
+        return C2_NO_INIT;
+    }
+    mCodecDesc->max_bs_buf_size = kMaxBitstreamBufSize;
+    mCodecDesc->max_num_frms = MAX_NUM_FRMS;
+    // TODO: Bound parameters to CPU count
+    mCodecDesc->threads = 4;
+
+    int32_t ret = C2_OK;
+    /* set params */
+    for (int32_t i = 0; i < mMaxFrames; i++) {
+        oapve_param_t* param = &mCodecDesc->param[i];
+        ret = oapve_param_default(param);
+        if (OAPV_FAILED(ret)) {
+            ALOGE("cannot set default parameter");
+            return C2_NO_INIT;
+        }
+        setParams(*param);
+    }
+
+    showEncoderParams(mCodecDesc.get());
+
+    /* create encoder */
+    mEncoderId = oapve_create(mCodecDesc.get(), NULL);
+    if (mEncoderId == NULL) {
+        ALOGE("cannot create APV encoder");
+        return C2_CORRUPTED;
+    }
+
+    /* create metadata */
+    mMetaId = oapvm_create(&ret);
+    if (mMetaId == NULL) {
+        ALOGE("cannot create APV encoder");
+        return C2_NO_MEMORY;
+    }
+
+    /* create image buffers */
+    for (int32_t i = 0; i < mMaxFrames; i++) {
+        if (mBitDepth == 10) {
+            mInputFrames.frm[i].imgb = imgb_create(mCodecDesc->param[0].w, mCodecDesc->param[0].h,
+                                                  OAPV_CS_SET(mColorFormat, mBitDepth, 0));
+            mReconFrames.frm[i].imgb = nullptr;
+        } else {
+            mInputFrames.frm[i].imgb = imgb_create(mCodecDesc->param[0].w, mCodecDesc->param[0].h,
+                                                  OAPV_CS_SET(mColorFormat, 10, 0));
+            mReconFrames.frm[i].imgb = nullptr;
+        }
+    }
+
+    /* allocate bitstream buffer */
+    mBitstreamBuf = new unsigned char[kMaxBitstreamBufSize];
+    if (mBitstreamBuf == nullptr) {
+        ALOGE("cannot allocate bitstream buffer, size= %d", kMaxBitstreamBufSize);
+        return C2_NO_MEMORY;
+    }
+
+    mStarted = true;
+    mInitEncoder = true;
+    return C2_OK;
+}
+
+void C2SoftApvEnc::setParams(oapve_param_t& param) {
+    param.w = mSize->width;
+    param.h = mSize->height;
+    param.fps_num = (int)(mFrameRate->value * 100);
+    param.fps_den = 100;
+    param.bitrate = (int)(mBitrate->value / 1000);
+    param.rc_type = mIntf->getBitrateMode_l();
+
+    int ApvQP = kApvDefaultQP;
+    if (param.rc_type == OAPV_RC_CQP) {
+        ApvQP = getQpFromQuality(mQuality->value);
+        ALOGI("Bitrate mode is CQ, so QP value is derived from Quality. Quality is %d, QP is %d",
+              mQuality->value, ApvQP);
+    }
+    param.qp = ApvQP;
+    param.band_idc = mIntf->getBandIdc_l();
+    param.profile_idc = mIntf->getProfile_l();
+    param.level_idc = mIntf->getLevel_l();
+}
+
+c2_status_t C2SoftApvEnc::setEncodeArgs(oapv_frms_t* inputFrames, const C2GraphicView* const input,
+                                        uint64_t workIndex) {
+    if (input->width() < mSize->width || input->height() < mSize->height) {
+        /* Expect width height to be configured */
+        ALOGW("unexpected Capacity Aspect %d(%d) x %d(%d)", input->width(), mSize->width,
+              input->height(), mSize->height);
+        return C2_BAD_VALUE;
+    }
+    const C2PlanarLayout& layout = input->layout();
+    uint8_t* yPlane = const_cast<uint8_t*>(input->data()[C2PlanarLayout::PLANE_Y]);
+    uint8_t* uPlane = const_cast<uint8_t*>(input->data()[C2PlanarLayout::PLANE_U]);
+    uint8_t* vPlane = const_cast<uint8_t*>(input->data()[C2PlanarLayout::PLANE_V]);
+    int32_t yStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
+    int32_t uStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc;
+    int32_t vStride = layout.planes[C2PlanarLayout::PLANE_V].rowInc;
+
+    uint32_t width = mSize->width;
+    uint32_t height = mSize->height;
+
+    /* width and height must be even */
+    if (width & 1u || height & 1u) {
+        ALOGW("height(%u) and width(%u) must both be even", height, width);
+        return C2_BAD_VALUE;
+    }
+
+    /* Set num frames */
+    inputFrames->num_frms = MAX_NUM_FRMS;
+    inputFrames->frm[mReceivedFrames].group_id = 1;
+    inputFrames->frm[mReceivedFrames].pbu_type = OAPV_PBU_TYPE_PRIMARY_FRAME;
+
+    switch (layout.type) {
+        case C2PlanarLayout::TYPE_RGB:
+            ALOGE("Not supported RGB color format");
+            return C2_BAD_VALUE;
+        case C2PlanarLayout::TYPE_RGBA: {
+            [[fallthrough]];
+        }
+        case C2PlanarLayout::TYPE_YUVA: {
+            ALOGV("Convert from ABGR2101010 to P210");
+            uint16_t *dstY, *dstU, *dstV;
+            dstY = (uint16_t*)inputFrames->frm[0].imgb->a[0];
+            dstU = (uint16_t*)inputFrames->frm[0].imgb->a[1];
+            dstV = (uint16_t*)inputFrames->frm[0].imgb->a[2];
+            convertRGBA1010102ToYUV420Planar16(dstY, dstU, dstV, (uint32_t*)(input->data()[0]),
+                                                layout.planes[layout.PLANE_Y].rowInc / 4, width,
+                                                height, mColorAspects->matrix,
+                                                mColorAspects->range);
+            break;
+        }
+        case C2PlanarLayout::TYPE_YUV: {
+            if (IsP010(*input)) {
+                if (mColorFormat == OAPV_CF_YCBCR422) {
+                    ColorConvertP010ToYUV422P10le(input, inputFrames->frm[0].imgb);
+                } else if (mColorFormat == OAPV_CF_PLANAR2) {
+                    uint16_t *srcY  = (uint16_t*)(input->data()[0]);
+                    uint16_t *srcUV = (uint16_t*)(input->data()[1]);
+                    uint16_t *dstY  = (uint16_t*)inputFrames->frm[0].imgb->a[0];
+                    uint16_t *dstUV = (uint16_t*)inputFrames->frm[0].imgb->a[1];
+                    convertP010ToP210(dstY, dstUV, srcY, srcUV,
+                                      input->width(), input->width(), input->width(),
+                                      input->height());
+                } else {
+                    ALOGE("Not supported color format. %d", mColorFormat);
+                    return C2_BAD_VALUE;
+                }
+            } else if (IsNV12(*input)) {
+                uint8_t  *srcY  = (uint8_t*)input->data()[0];
+                uint8_t  *srcUV = (uint8_t*)input->data()[1];
+                uint16_t *dstY  = (uint16_t*)inputFrames->frm[0].imgb->a[0];
+                uint16_t *dstUV = (uint16_t*)inputFrames->frm[0].imgb->a[1];
+                convertSemiPlanar8ToP210(dstY, dstUV, srcY, srcUV,
+                                         input->width(), input->width(), input->width(),
+                                         input->width(), input->width(), input->height(),
+                                         CONV_FORMAT_I420);
+            } else if (IsI420(*input)) {
+                uint8_t  *srcY  = (uint8_t*)input->data()[0];
+                uint8_t  *srcU  = (uint8_t*)input->data()[1];
+                uint8_t  *srcV  = (uint8_t*)input->data()[2];
+                uint16_t *dstY  = (uint16_t*)inputFrames->frm[0].imgb->a[0];
+                uint16_t *dstUV = (uint16_t*)inputFrames->frm[0].imgb->a[1];
+                convertPlanar8ToP210(dstY, dstUV, srcY, srcU, srcV,
+                                        layout.planes[C2PlanarLayout::PLANE_Y].rowInc,
+                                        layout.planes[C2PlanarLayout::PLANE_U].rowInc,
+                                        layout.planes[C2PlanarLayout::PLANE_V].rowInc,
+                                        input->width(), input->width(),
+                                        input->width(), input->height(),
+                                        CONV_FORMAT_I420);
+
+            } else {
+                ALOGE("Not supported color format. %d", mColorFormat);
+                return C2_BAD_VALUE;
+            }
+            break;
+        }
+
+        default:
+            ALOGE("Unrecognized plane type: %d", layout.type);
+            return C2_BAD_VALUE;
+    }
+
+    return C2_OK;
+}
+
+void C2SoftApvEnc::ColorConvertP010ToYUV422P10le(const C2GraphicView* const input,
+                                                 oapv_imgb_t* imgb) {
+    uint32_t width = input->width();
+    uint32_t height = input->height();
+
+    uint8_t* yPlane = (uint8_t*)input->data()[0];
+    auto* uvPlane = (uint8_t*)input->data()[1];
+    uint32_t stride[3];
+    stride[0] = width * 2;
+    stride[1] = stride[2] = width;
+
+    uint8_t *dst, *src;
+    uint16_t tmp;
+    for (int32_t y = 0; y < height; ++y) {
+        src = yPlane + y * stride[0];
+        dst = (uint8_t*)imgb->a[0] + y * stride[0];
+        for (int32_t x = 0; x < stride[0]; x += 2) {
+            tmp = (src[x + 1] << 2) | (src[x] >> 6);
+            dst[x] = tmp & 0xFF;
+            dst[x + 1] = tmp >> 8;
+        }
+    }
+
+    uint8_t *dst_u, *dst_v;
+    for (int32_t y = 0; y < height / 2; ++y) {
+        src = uvPlane + y * stride[1] * 2;
+        dst_u = (uint8_t*)imgb->a[1] + (y * 2) * stride[1];
+        dst_v = (uint8_t*)imgb->a[2] + (y * 2) * stride[2];
+        for (int32_t x = 0; x < stride[1] * 2; x += 4) {
+            tmp = (src[x + 1] << 2) | (src[x] >> 6);  // cb
+            dst_u[x / 2] = tmp & 0xFF;
+            dst_u[x / 2 + 1] = tmp >> 8;
+            dst_u[x / 2 + stride[1]] = dst_u[x / 2];
+            dst_u[x / 2 + stride[1] + 1] = dst_u[x / 2 + 1];
+
+            tmp = (src[x + 3] << 2) | (src[x + 2] >> 6);  // cr
+            dst_v[x / 2] = tmp & 0xFF;
+            dst_v[x / 2 + 1] = tmp >> 8;
+            dst_v[x / 2 + stride[2]] = dst_v[x / 2];
+            dst_v[x / 2 + stride[2] + 1] = dst_v[x / 2 + 1];
+        }
+    }
+}
+
+void C2SoftApvEnc::finishWork(uint64_t workIndex, const std::unique_ptr<C2Work>& work,
+                              const std::shared_ptr<C2BlockPool>& pool, oapv_bitb_t* bitb,
+                              oapve_stat_t* stat) {
+    std::shared_ptr<C2LinearBlock> block;
+    C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
+    c2_status_t status = pool->fetchLinearBlock(stat->write, usage, &block);
+    if (C2_OK != status) {
+        ALOGE("fetchLinearBlock for Output failed with status 0x%x", status);
+        mSignalledError = true;
+        work->result = status;
+        work->workletsProcessed = 1u;
+        return;
+    }
+
+    C2WriteView wView = block->map().get();
+    if (C2_OK != wView.error()) {
+        ALOGE("write view map failed with status 0x%x", wView.error());
+        mSignalledError = true;
+        work->result = wView.error();
+        work->workletsProcessed = 1u;
+        return;
+    }
+    if ((!mReceivedFirstFrame)) {
+        createCsdData(work, bitb, stat->write);
+        mReceivedFirstFrame = true;
+    }
+
+    memcpy(wView.data(), bitb->addr, stat->write);
+    std::shared_ptr<C2Buffer> buffer = createLinearBuffer(block, 0, stat->write);
+
+    /* All frames are SYNC FRAME */
+    buffer->setInfo(std::make_shared<C2StreamPictureTypeMaskInfo::output>(0u /* stream id */,
+                                                                          C2Config::SYNC_FRAME));
+
+    auto fillWork = [buffer](const std::unique_ptr<C2Work>& work) {
+        work->worklets.front()->output.flags = (C2FrameData::flags_t)0;
+        work->worklets.front()->output.buffers.clear();
+        work->worklets.front()->output.buffers.push_back(buffer);
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        work->workletsProcessed = 1u;
+    };
+    if (work && c2_cntr64_t(workIndex) == work->input.ordinal.frameIndex) {
+        fillWork(work);
+        if (mSignalledEos) {
+            work->worklets.front()->output.flags = C2FrameData::FLAG_END_OF_STREAM;
+        }
+    } else {
+        finish(workIndex, fillWork);
+    }
+}
+
+void C2SoftApvEnc::createCsdData(const std::unique_ptr<C2Work>& work,
+                                 oapv_bitb_t* bitb,
+                                 uint32_t encodedSize) {
+    if (encodedSize < 31) {
+        ALOGE("the first frame size is too small, so no csd data will be created.");
+        return;
+    }
+    ABitReader reader((uint8_t*)bitb->addr, encodedSize);
+
+    uint8_t number_of_configuration_entry = 0;
+    uint8_t pbu_type = 0;
+    uint8_t number_of_frame_info = 0;
+    bool color_description_present_flag = false;
+    bool capture_time_distance_ignored = false;
+    uint8_t profile_idc = 0;
+    uint8_t level_idc = 0;
+    uint8_t band_idc = 0;
+    uint32_t frame_width_minus1 = 0;
+    uint32_t frame_height_minus1 = 0;
+    uint8_t chroma_format_idc = 0;
+    uint8_t bit_depth_minus8 = 0;
+    uint8_t capture_time_distance = 0;
+    uint8_t color_primaries = 0;
+    uint8_t transfer_characteristics = 0;
+    uint8_t matrix_coefficients = 0;
+
+    /* pbu_header() */
+    reader.skipBits(32);           // pbu_size
+    reader.skipBits(32);           // currReadSize
+    pbu_type = reader.getBits(8);  // pbu_type
+    reader.skipBits(16);           // group_id
+    reader.skipBits(8);            // reserved_zero_8bits
+
+    /* frame info() */
+    profile_idc = reader.getBits(8);            // profile_idc
+    level_idc = reader.getBits(8);              // level_idc
+    band_idc = reader.getBits(3);               // band_idc
+    reader.skipBits(5);                         // reserved_zero_5bits
+    frame_width_minus1 = reader.getBits(32);    // width
+    frame_height_minus1 = reader.getBits(32);   // height
+    chroma_format_idc = reader.getBits(4);      // chroma_format_idc
+    bit_depth_minus8 = reader.getBits(4);       // bit_depth
+    capture_time_distance = reader.getBits(8);  // capture_time_distance
+    reader.skipBits(8);                         // reserved_zero_8bits
+
+    /* frame header() */
+    reader.skipBits(8);  // reserved_zero_8bit
+    color_description_present_flag = reader.getBits(1);  // color_description_present_flag
+    if (color_description_present_flag) {
+        color_primaries = reader.getBits(8);           // color_primaries
+        transfer_characteristics = reader.getBits(8);  // transfer_characteristics
+        matrix_coefficients = reader.getBits(8);       // matrix_coefficients
+    }
+
+    number_of_configuration_entry = 1;  // The real-time encoding on the device is assumed to be 1.
+    number_of_frame_info = 1;  // The real-time encoding on the device is assumed to be 1.
+
+    std::vector<uint8_t> csdData;
+    csdData.push_back((uint8_t)0x1);
+    csdData.push_back(number_of_configuration_entry);
+
+    for (uint8_t i = 0; i < number_of_configuration_entry; i++) {
+        csdData.push_back(pbu_type);
+        csdData.push_back(number_of_frame_info);
+        for (uint8_t j = 0; j < number_of_frame_info; j++) {
+            csdData.push_back((uint8_t)((color_description_present_flag << 1) |
+                                      capture_time_distance_ignored));
+            csdData.push_back(profile_idc);
+            csdData.push_back(level_idc);
+            csdData.push_back(band_idc);
+            csdData.push_back((uint8_t)((frame_width_minus1 >> 24) & 0xff));
+            csdData.push_back((uint8_t)((frame_width_minus1 >> 16) & 0xff));
+            csdData.push_back((uint8_t)((frame_width_minus1 >> 8) & 0xff));
+            csdData.push_back((uint8_t)(frame_width_minus1 & 0xff));
+            csdData.push_back((uint8_t)((frame_height_minus1 >> 24) & 0xff));
+            csdData.push_back((uint8_t)((frame_height_minus1 >> 16) & 0xff));
+            csdData.push_back((uint8_t)((frame_height_minus1 >> 8) & 0xff));
+            csdData.push_back((uint8_t)(frame_height_minus1 & 0xff));
+            csdData.push_back((uint8_t)(((chroma_format_idc << 4) & 0xf0) |
+                                      (bit_depth_minus8 & 0xf)));
+            csdData.push_back((uint8_t)(capture_time_distance));
+            if (color_description_present_flag) {
+                csdData.push_back(color_primaries);
+                csdData.push_back(transfer_characteristics);
+                csdData.push_back(matrix_coefficients);
+            }
+        }
+    }
+
+    std::unique_ptr<C2StreamInitDataInfo::output> csd =
+        C2StreamInitDataInfo::output::AllocUnique(csdData.size(), 0u);
+    if (!csd) {
+        ALOGE("CSD allocation failed");
+        mSignalledError = true;
+        work->result = C2_NO_MEMORY;
+        work->workletsProcessed = 1u;
+        return;
+    }
+
+    memcpy(csd->m.value, csdData.data(), csdData.size());
+    work->worklets.front()->output.configUpdate.push_back(std::move(csd));
+}
+
+c2_status_t C2SoftApvEnc::drainInternal(uint32_t drainMode,
+                                        const std::shared_ptr<C2BlockPool>& pool,
+                                        const std::unique_ptr<C2Work>& work) {
+    fillEmptyWork(work);
+    return C2_OK;
+}
+
+void C2SoftApvEnc::process(const std::unique_ptr<C2Work>& work,
+                           const std::shared_ptr<C2BlockPool>& pool) {
+    c2_status_t error;
+    work->result = C2_OK;
+    work->workletsProcessed = 0u;
+    work->worklets.front()->output.flags = work->input.flags;
+
+    nsecs_t timeDelay = 0;
+    uint64_t workIndex = work->input.ordinal.frameIndex.peekull();
+
+    mSignalledEos = false;
+    mOutBlock = nullptr;
+
+    if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+        ALOGV("Got FLAG_END_OF_STREAM");
+        mSignalledEos = true;
+    }
+
+    /* Initialize encoder if not already initialized */
+    if (initEncoder() != C2_OK) {
+        ALOGE("Failed to initialize encoder");
+        mSignalledError = true;
+        work->result = C2_CORRUPTED;
+        work->workletsProcessed = 1u;
+        ALOGE("[%s] Failed to make Codec context", __func__);
+        return;
+    }
+    if (mSignalledError) {
+        ALOGE("[%s] Received signalled error", __func__);
+        return;
+    }
+
+    if (mSignalledEos) {
+        drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
+        return;
+    }
+
+    std::shared_ptr<C2GraphicView> view;
+    std::shared_ptr<C2Buffer> inputBuffer = nullptr;
+    if (!work->input.buffers.empty()) {
+        inputBuffer = work->input.buffers[0];
+        view = std::make_shared<C2GraphicView>(
+                inputBuffer->data().graphicBlocks().front().map().get());
+        if (view->error() != C2_OK) {
+            ALOGE("graphic view map err = %d", view->error());
+            work->workletsProcessed = 1u;
+            return;
+        }
+    } else {
+        ALOGV("Empty input Buffer");
+        uint32_t flags = 0;
+        if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+            flags |= C2FrameData::FLAG_END_OF_STREAM;
+        }
+        work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+        work->worklets.front()->output.buffers.clear();
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        work->workletsProcessed = 1u;
+        return;
+    }
+
+    if (!inputBuffer) {
+        fillEmptyWork(work);
+        return;
+    }
+
+    oapve_stat_t stat;
+    auto outBufferSize =
+            mCodecDesc->param[mReceivedFrames].w * mCodecDesc->param[mReceivedFrames].h * 4;
+    if (!mOutBlock) {
+        C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
+        c2_status_t err = pool->fetchLinearBlock(outBufferSize, usage, &mOutBlock);
+        if (err != C2_OK) {
+            work->result = err;
+            work->workletsProcessed = 1u;
+            ALOGE("fetchLinearBlock has failed. err = %d", err);
+            return;
+        }
+    }
+
+    C2WriteView wView = mOutBlock->map().get();
+    if (wView.error() != C2_OK) {
+        work->result = wView.error();
+        work->workletsProcessed = 1u;
+        return;
+    }
+
+    error = setEncodeArgs(&mInputFrames, view.get(), workIndex);
+    if (error != C2_OK) {
+        ALOGE("setEncodeArgs has failed. err = %d", error);
+        mSignalledError = true;
+        work->result = error;
+        work->workletsProcessed = 1u;
+        return;
+    }
+
+    if (++mReceivedFrames < mMaxFrames) {
+        return;
+    }
+    mReceivedFrames = 0;
+
+    std::shared_ptr<oapv_bitb_t> bits = std::make_shared<oapv_bitb_t>();
+    std::memset(mBitstreamBuf, 0, kMaxBitstreamBufSize);
+    bits->addr = mBitstreamBuf;
+    bits->bsize = kMaxBitstreamBufSize;
+    bits->err = C2_OK;
+
+    if (mInputFrames.frm[0].imgb) {
+        int32_t status =
+                oapve_encode(mEncoderId, &mInputFrames, mMetaId, bits.get(), &stat, &mReconFrames);
+        if (status != C2_OK) {
+            ALOGE("oapve_encode has failed. err = %d", status);
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            work->workletsProcessed = 1u;
+            return;
+        }
+    } else if (!mSignalledEos) {
+        fillEmptyWork(work);
+    }
+    finishWork(workIndex, work, pool, bits.get(), &stat);
+}
+
+class C2SoftApvEncFactory : public C2ComponentFactory {
+  public:
+    C2SoftApvEncFactory()
+        : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+                  GetCodec2PlatformComponentStore()->getParamReflector())) {}
+
+    virtual c2_status_t createComponent(c2_node_id_t id,
+                                        std::shared_ptr<C2Component>* const component,
+                                        std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+                new C2SoftApvEnc(COMPONENT_NAME, id,
+                                 std::make_shared<C2SoftApvEnc::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    c2_status_t createInterface(c2_node_id_t id,
+                                std::shared_ptr<C2ComponentInterface>* const interface,
+                                std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+                new SimpleInterface<C2SoftApvEnc::IntfImpl>(
+                        COMPONENT_NAME, id, std::make_shared<C2SoftApvEnc::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    ~C2SoftApvEncFactory() override = default;
+
+  private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+__attribute__((cfi_canonical_jump_table)) extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    if (!android::media::swcodec::flags::apv_software_codec()) {
+        ALOGV("APV SW Codec is not enabled");
+        return nullptr;
+    }
+    return new ::android::C2SoftApvEncFactory();
+}
+
+__attribute__((cfi_canonical_jump_table)) extern "C" void DestroyCodec2Factory(
+        ::C2ComponentFactory* factory) {
+    delete factory;
+}
diff --git a/media/codec2/components/apv/C2SoftApvEnc.h b/media/codec2/components/apv/C2SoftApvEnc.h
new file mode 100644
index 0000000..f281052
--- /dev/null
+++ b/media/codec2/components/apv/C2SoftApvEnc.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_C2_SOFT_APV_ENC_H_
+#define ANDROID_C2_SOFT_APV_ENC_H_
+
+#include <SimpleC2Component.h>
+#include <utils/Vector.h>
+#include <map>
+#include "oapv.h"
+
+#include <C2SoftApvCommon.h>
+
+namespace android {
+
+#define CODEC_MAX_CORES 4
+
+#define APV_QP_MIN 1
+#define APV_QP_MAX 51
+
+struct C2SoftApvEnc final : public SimpleC2Component {
+    class IntfImpl;
+
+    C2SoftApvEnc(const char* name, c2_node_id_t id, const std::shared_ptr<IntfImpl>& intfImpl);
+    virtual ~C2SoftApvEnc();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(const std::unique_ptr<C2Work>& work,
+                 const std::shared_ptr<C2BlockPool>& pool) override;
+    c2_status_t drain(uint32_t drainMode, const std::shared_ptr<C2BlockPool>& pool) override;
+
+  private:
+    c2_status_t reset();
+    c2_status_t initEncoder();
+    c2_status_t releaseEncoder();
+    c2_status_t setEncodeArgs(oapv_frms_t* imgb_inp, const C2GraphicView* const input,
+                              uint64_t workIndex);
+    void finishWork(uint64_t workIndex, const std::unique_ptr<C2Work>& work,
+                    const std::shared_ptr<C2BlockPool>& pool, oapv_bitb_t* bitb,
+                    oapve_stat_t* stat);
+    c2_status_t drainInternal(uint32_t drainMode, const std::shared_ptr<C2BlockPool>& pool,
+                              const std::unique_ptr<C2Work>& work);
+    void setParams(oapve_param_t& param);
+    int32_t getQpFromQuality(int quality);
+
+    void showEncoderParams(oapve_cdesc_t* cdsc);
+
+    void ColorConvertP010ToYUV422P10le(const C2GraphicView* const input, oapv_imgb_t* imgb);
+
+    void createCsdData(const std::unique_ptr<C2Work>& work, oapv_bitb_t* bitb,
+                       uint32_t encodedSize);
+
+    std::shared_ptr<IntfImpl> mIntf;
+    int32_t mBitDepth;
+    int32_t mColorFormat;
+
+    bool mStarted;
+    bool mSignalledEos;
+    bool mSignalledError;
+
+    std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
+    std::shared_ptr<C2StreamPictureSizeInfo::input> mSize;
+    std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
+    std::shared_ptr<C2StreamProfileLevelInfo::output> mProfileLevel;
+    std::shared_ptr<C2StreamColorAspectsInfo::input> mColorAspects;
+    std::shared_ptr<C2StreamColorAspectsInfo::output> mCodedColorAspects;
+    std::shared_ptr<C2StreamPictureQuantizationTuning::output> mPictureQuantization;
+    std::shared_ptr<C2StreamQualityTuning::output> mQuality;
+    std::shared_ptr<C2StreamBitrateModeTuning::output> mBitrateMode;
+    std::shared_ptr<C2LinearBlock> mOutBlock;
+    std::shared_ptr<C2StreamComplexityTuning::output> mComplexity;
+    std::shared_ptr<C2StreamPixelFormatInfo::input> mPixelFormat;
+
+    std::map<const void*, std::shared_ptr<C2Buffer>> mBuffers;
+    MemoryBlockPool mConversionBuffers;
+    std::map<const void*, MemoryBlock> mConversionBuffersInUse;
+
+    bool mInitEncoder;
+    int32_t mMaxFrames;
+    int32_t mReceivedFrames;
+    std::unique_ptr<oapve_cdesc_t> mCodecDesc;
+    oapv_frms_t mInputFrames;
+    oapv_frms_t mReconFrames;
+    oapve_t mEncoderId;
+    oapvm_t mMetaId;
+    uint8_t* mBitstreamBuf = nullptr;
+    bool mReceivedFirstFrame = false;
+    C2_DO_NOT_COPY(C2SoftApvEnc);
+};
+}  // namespace android
+
+#endif  // ANDROID_C2_SOFT_APV_ENC_H_
diff --git a/media/codec2/components/base/SimpleC2Component.cpp b/media/codec2/components/base/SimpleC2Component.cpp
index aec6523..ea67bf4 100644
--- a/media/codec2/components/base/SimpleC2Component.cpp
+++ b/media/codec2/components/base/SimpleC2Component.cpp
@@ -463,6 +463,19 @@
     }
 }
 
+void convertP010ToP210(uint16_t *dstY, uint16_t *dstUV, const uint16_t *srcY, const uint16_t *srcUV,
+                       size_t srcUVStride, size_t dstUVStride, size_t width, size_t height) {
+    std::memcpy(dstY, srcY, width * height * sizeof(uint16_t));
+
+    int32_t offsetTop, offsetBot;
+    for (size_t y = 0; y < (height + 1) / 2; ++y) {
+        offsetTop = (y * 2) * dstUVStride;
+        offsetBot = (y * 2 + 1) * dstUVStride;
+        std::memcpy(dstUV + offsetTop, srcUV + (y * srcUVStride), srcUVStride * sizeof(uint16_t));
+        std::memcpy(dstUV + offsetBot, srcUV + (y * srcUVStride), srcUVStride * sizeof(uint16_t));
+    }
+}
+
 static const int16_t bt709Matrix_10bit[2][3][3] = {
     { { 218, 732, 74 }, { -117, -395, 512 }, { 512, -465, -47 } }, /* RANGE_FULL */
     { { 186, 627, 63 }, { -103, -345, 448 }, { 448, -407, -41 } }, /* RANGE_LIMITED */
@@ -517,6 +530,45 @@
     }
 }
 
+void convertRGBA1010102ToP210(uint16_t* dstY, uint16_t* dstUV, const uint32_t* srcRGBA,
+                              size_t srcRGBStride, size_t width, size_t height,
+                              C2Color::matrix_t colorMatrix, C2Color::range_t colorRange) {
+    uint16_t r, g, b;
+    int32_t i32Y, i32U, i32V;
+    uint16_t zeroLvl =  colorRange == C2Color::RANGE_FULL ? 0 : 64;
+    uint16_t maxLvlLuma =  colorRange == C2Color::RANGE_FULL ? 1023 : 940;
+    uint16_t maxLvlChroma =  colorRange == C2Color::RANGE_FULL ? 1023 : 960;
+    // set default range as limited
+    if (colorRange != C2Color::RANGE_FULL) {
+        colorRange = C2Color::RANGE_LIMITED;
+    }
+    const int16_t(*weights)[3] = (colorMatrix == C2Color::MATRIX_BT709)
+                                         ? bt709Matrix_10bit[colorRange - 1]
+                                         : bt2020Matrix_10bit[colorRange - 1];
+
+    for (size_t y = 0; y < height; ++y) {
+        for (size_t x = 0; x < width; ++x) {
+            b = (srcRGBA[x]  >> 20) & 0x3FF;
+            g = (srcRGBA[x]  >> 10) & 0x3FF;
+            r = srcRGBA[x] & 0x3FF;
+
+            i32Y = ((r * weights[0][0] + g * weights[0][1] + b * weights[0][2] + 512) >> 10) +
+                   zeroLvl;
+            dstY[x] = (CLIP3(zeroLvl, i32Y, maxLvlLuma) << 6) & 0xFFC0;
+            if (x % 2 == 0) {
+                i32U = ((r * weights[1][0] + g * weights[1][1] + b * weights[1][2] + 512) >> 10) +
+                       512;
+                i32V = ((r * weights[2][0] + g * weights[2][1] + b * weights[2][2] + 512) >> 10) +
+                       512;
+                dstUV[x] = (CLIP3(zeroLvl, i32U, maxLvlChroma) << 6) & 0xFFC0;
+                dstUV[x + 1] = (CLIP3(zeroLvl, i32V, maxLvlChroma) << 6) & 0xFFC0;
+            }
+        }
+        srcRGBA += srcRGBStride;
+        dstY += width;
+    }
+}
+
 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,
@@ -631,6 +683,69 @@
                                    isMonochrome);
     }
 }
+
+void convertSemiPlanar8ToP210(uint16_t *dstY, uint16_t *dstUV,
+                              const uint8_t *srcY, const uint8_t *srcUV,
+                              size_t srcYStride, size_t srcUVStride,
+                              size_t dstYStride, size_t dstUVStride,
+                              uint32_t width, uint32_t height,
+                              CONV_FORMAT_T format) {
+  if (format != CONV_FORMAT_I420) {
+    ALOGE("No support for semi-planar8 to P210. format is %d", format);
+    return;
+  }
+
+  for (int32_t y = 0; y < height; ++y) {
+    for (int32_t x = 0; x < width; ++x) {
+      dstY[x] = ((uint16_t)((double)srcY[x] * 1023 / 255 + 0.5) << 6) & 0xFFC0;
+    }
+    dstY += dstYStride;
+    srcY += srcYStride;
+  }
+
+  for (int32_t y = 0; y < height / 2; ++y) {
+    for (int32_t x = 0; x < width; ++x) {
+      dstUV[x] = dstUV[dstUVStride + x] =
+          ((uint16_t)((double)srcUV[x] * 1023 / 255 + 0.5) << 6) & 0xFFC0;
+    }
+    srcUV += srcUVStride;
+    dstUV += dstUVStride << 1;
+  }
+}
+
+void convertPlanar8ToP210(uint16_t *dstY, uint16_t *dstUV,
+                              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 dstUVStride,
+                              uint32_t width, uint32_t height,
+                              CONV_FORMAT_T format) {
+  if (format != CONV_FORMAT_I420) {
+    ALOGE("No support for planar8 to P210. format is %d", format);
+    return;
+  }
+
+  for (int32_t y = 0; y < height; ++y) {
+    for (int32_t x = 0; x < width; ++x) {
+      dstY[x] = ((uint16_t)((double)srcY[x] * 1023 / 255 + 0.5) << 6) & 0xFFC0;
+    }
+    dstY += dstYStride;
+    srcY += srcYStride;
+  }
+
+  for (int32_t y = 0; y < height / 2; ++y) {
+    for (int32_t x = 0; x < width / 2; ++x) {
+      dstUV[x<<1] = dstUV[(x<<1) + dstUVStride] =
+                ((uint16_t)((double)srcU[x] * 1023 / 255 + 0.5) << 6) & 0xFFC0;
+      dstUV[(x<<1) + 1] = dstUV[(x<<1) + dstUVStride + 1] =
+                ((uint16_t)((double)srcV[x] * 1023 / 255 + 0.5) << 6) & 0xFFC0;
+    }
+    dstUV += dstUVStride << 1;
+    srcU += srcUStride;
+    srcV += srcVStride;
+  }
+}
+
+
 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 b28c47e..5d2e8cd 100644
--- a/media/codec2/components/base/include/SimpleC2Component.h
+++ b/media/codec2/components/base/include/SimpleC2Component.h
@@ -68,10 +68,19 @@
                                  size_t dstUStride, size_t dstVStride, size_t width,
                                  size_t height, bool isMonochrome = false);
 
+void convertP010ToP210(uint16_t *dstY, uint16_t *dstUV, const uint16_t *srcY,
+                       const uint16_t *srcUV, size_t srcUVStride, size_t dstUVStride,
+                       size_t width, size_t height);
+
 void convertRGBA1010102ToYUV420Planar16(uint16_t* dstY, uint16_t* dstU, uint16_t* dstV,
                                         const uint32_t* srcRGBA, size_t srcRGBStride, size_t width,
                                         size_t height, C2Color::matrix_t colorMatrix,
                                         C2Color::range_t colorRange);
+
+void convertRGBA1010102ToP210(uint16_t* dstY, uint16_t* dstUV, 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,
@@ -96,6 +105,18 @@
                           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);
+void convertSemiPlanar8ToP210(uint16_t *dstY, uint16_t *dstUV,
+                              const uint8_t *srcY, const uint8_t *srcUV,
+                              size_t srcYStride, size_t srcUVStride,
+                              size_t dstYStride, size_t dstUVStride,
+                              uint32_t width, uint32_t height,
+                              CONV_FORMAT_T format);
+void convertPlanar8ToP210(uint16_t *dstY, uint16_t *dstUV,
+                              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 dstUVStride,
+                              uint32_t width, uint32_t height,
+                              CONV_FORMAT_T format);
 
 class SimpleC2Component
         : public C2Component, public std::enable_shared_from_this<SimpleC2Component> {
diff --git a/media/codec2/components/mpeg2/Android.bp b/media/codec2/components/mpeg2/Android.bp
index e644ee3..ed711ee 100644
--- a/media/codec2/components/mpeg2/Android.bp
+++ b/media/codec2/components/mpeg2/Android.bp
@@ -14,11 +14,10 @@
         "libcodec2_soft_sanitize_signed-defaults",
     ],
 
-    cflags: [
-        "-DKEEP_THREADS_ACTIVE=0",
-    ],
-
     srcs: ["C2SoftMpeg2Dec.cpp"],
 
-    static_libs: ["libmpeg2dec"],
+    static_libs: [
+        "libmpeg2dec",
+        "android.media.swcodec.flags-aconfig-cc",
+    ],
 }
diff --git a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
index 52920c2..64e4bf0 100644
--- a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
+++ b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
@@ -16,11 +16,10 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "C2SoftMpeg2Dec"
-#ifndef KEEP_THREADS_ACTIVE
-#define KEEP_THREADS_ACTIVE 0
-#endif
 #include <log/log.h>
 
+#include <android_media_swcodec_flags.h>
+
 #include <media/stagefright/foundation/MediaDefs.h>
 
 #include <C2Debug.h>
@@ -320,14 +319,7 @@
         c2_node_id_t id,
         const std::shared_ptr<IntfImpl> &intfImpl)
     : SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
-        mIntf(intfImpl),
-        mDecHandle(nullptr),
-        mMemRecords(nullptr),
-        mOutBufferDrain(nullptr),
-        mIvColorformat(IV_YUV_420P),
-        mWidth(320),
-        mHeight(240),
-        mOutIndex(0u) {
+        mIntf(intfImpl) {
     // If input dump is enabled, then open create an empty file
     GENERATE_FILE_NAMES();
     CREATE_DUMP_FILE(mInFile);
@@ -436,7 +428,7 @@
 
     s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_size = sizeof(ivdext_fill_mem_rec_ip_t);
     s_fill_mem_ip.u4_share_disp_buf = 0;
-    s_fill_mem_ip.u4_keep_threads_active = KEEP_THREADS_ACTIVE;
+    s_fill_mem_ip.u4_keep_threads_active = mKeepThreadsActive;
     s_fill_mem_ip.e_output_format = mIvColorformat;
     s_fill_mem_ip.u4_deinterlace = 1;
     s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.e_cmd = IV_CMD_FILL_NUM_MEM_REC;
@@ -478,7 +470,7 @@
     s_init_ip.s_ivd_init_ip_t.u4_frm_max_ht = mHeight;
     s_init_ip.u4_share_disp_buf = 0;
     s_init_ip.u4_deinterlace = 1;
-    s_init_ip.u4_keep_threads_active = KEEP_THREADS_ACTIVE;
+    s_init_ip.u4_keep_threads_active = mKeepThreadsActive;
     s_init_ip.s_ivd_init_ip_t.u4_num_mem_rec = mNumMemRecords;
     s_init_ip.s_ivd_init_ip_t.e_output_format = mIvColorformat;
     s_init_op.s_ivd_init_op_t.u4_size = sizeof(ivdext_init_op_t);
@@ -571,6 +563,7 @@
     status_t ret = getNumMemRecords();
     if (OK != ret) return ret;
 
+    mKeepThreadsActive = android::media::swcodec::flags::mpeg2_keep_threads_active();
     ret = fillMemRecords();
     if (OK != ret) return ret;
 
diff --git a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h
index 3965bcc..6d09694 100644
--- a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h
+++ b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h
@@ -144,21 +144,22 @@
     };
 
     std::shared_ptr<IntfImpl> mIntf;
-    iv_obj_t *mDecHandle;
-    iv_mem_rec_t *mMemRecords;
-    size_t mNumMemRecords;
+    iv_obj_t *mDecHandle = nullptr;
+    iv_mem_rec_t *mMemRecords = nullptr;
+    size_t mNumMemRecords = 0;
     std::shared_ptr<C2GraphicBlock> mOutBlock;
-    uint8_t *mOutBufferDrain;
+    uint8_t *mOutBufferDrain = nullptr;
 
-    size_t mNumCores;
-    IV_COLOR_FORMAT_T mIvColorformat;
+    size_t mNumCores = 1;
+    IV_COLOR_FORMAT_T mIvColorformat = IV_YUV_420P;
 
-    uint32_t mWidth;
-    uint32_t mHeight;
-    uint32_t mStride;
-    bool mSignalledOutputEos;
-    bool mSignalledError;
-    std::atomic_uint64_t mOutIndex;
+    uint32_t mWidth = 320;
+    uint32_t mHeight = 240;
+    uint32_t mStride = 0;
+    bool mSignalledOutputEos = false;
+    bool mSignalledError = false;
+    bool mKeepThreadsActive = false;
+    std::atomic_uint64_t mOutIndex = 0;
 
     // Color aspects. These are ISO values and are meant to detect changes in aspects to avoid
     // converting them to C2 values for each frame
diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h
index e6782a9..fbd1b36 100644
--- a/media/codec2/core/include/C2Config.h
+++ b/media/codec2/core/include/C2Config.h
@@ -70,6 +70,7 @@
     enum platform_level_t : uint32_t;       ///< platform level
     enum prepend_header_mode_t : uint32_t;  ///< prepend header operational modes
     enum profile_t : uint32_t;              ///< coding profile
+    enum resource_kind_t : uint32_t;        ///< resource kinds
     enum scaling_method_t : uint32_t;       ///< scaling methods
     enum scan_order_t : uint32_t;           ///< scan orders
     enum secure_mode_t : uint32_t;          ///< secure/protected modes
@@ -101,6 +102,7 @@
     kParamIndexMasteringDisplayColorVolume,
     kParamIndexChromaOffset,
     kParamIndexGopLayer,
+    kParamIndexSystemResource,
 
     /* =================================== parameter indices =================================== */
 
@@ -167,6 +169,10 @@
     /* Region of Interest Encoding parameters */
     kParamIndexQpOffsetMapBuffer, // info-buffer, used to signal qp-offset map for a frame
 
+    /* resource capacity and resources excluded */
+    kParamIndexResourcesCapacity,
+    kParamIndexResourcesExcluded,
+
     // deprecated
     kParamIndexDelayRequest = kParamIndexDelay | C2Param::CoreIndex::IS_REQUEST_FLAG,
 
@@ -263,7 +269,7 @@
     kParamIndexSuspendAt, // input-surface, struct
     kParamIndexResumeAt, // input-surface, struct
     kParamIndexStopAt, // input-surface, struct
-    kParamIndexTimeOffset, // input-surface, struct
+    kParamIndexTimeOffset, // input-surface, int64_t
     kParamIndexMinFrameRate, // input-surface, float
     kParamIndexTimestampGapAdjustment, // input-surface, struct
 
@@ -293,6 +299,10 @@
 
     // allow tunnel peek behavior to be unspecified for app compatibility
     kParamIndexTunnelPeekMode, // tunnel mode, enum
+
+    // input surface
+    kParamIndexCaptureFrameRate, // input-surface, float
+    kParamIndexStopTimeOffset, // input-surface, int64_t
 };
 
 }
@@ -430,6 +440,7 @@
     _C2_PL_AV1_BASE  = 0x9000,
     _C2_PL_VP8_BASE  = 0xA000,
     _C2_PL_MPEGH_BASE = 0xB000,     // MPEG-H 3D Audio
+    _C2_PL_APV_BASE = 0xC000,     // APV
 
     C2_PROFILE_LEVEL_VENDOR_START = 0x70000000,
 };
@@ -597,6 +608,15 @@
     PROFILE_MPEGH_HIGH,                         ///< MPEG-H High
     PROFILE_MPEGH_LC,                           ///< MPEG-H Low-complexity
     PROFILE_MPEGH_BASELINE,                     ///< MPEG-H Baseline
+
+    // Advanced Professional VideoCodec (APV)
+    PROFILE_APV_422_10 = _C2_PL_APV_BASE,       ///< APV 422-10 Profile
+    PROFILE_APV_422_12,                         ///< APV 422-12 Profile
+    PROFILE_APV_444_10,                         ///< APV 444-10 Profile
+    PROFILE_APV_444_12,                         ///< APV 444-12 Profile
+    PROFILE_APV_4444_10,                        ///< APV 4444-10 Profile
+    PROFILE_APV_4444_12,                        ///< APV 4444-12 Profile
+    PROFILE_APV_400_10,                         ///< APV 400-10 Profile
 };
 
 enum C2Config::level_t : uint32_t {
@@ -752,6 +772,68 @@
     LEVEL_MPEGH_3,                              ///< MPEG-H L3
     LEVEL_MPEGH_4,                              ///< MPEG-H L4
     LEVEL_MPEGH_5,                              ///< MPEG-H L5
+
+    // Advanced Professional VideoCodec(APV) levels/bands
+    LEVEL_APV_1_BAND_0 = _C2_PL_APV_BASE,            ///< APV L 1, BAND 0
+    LEVEL_APV_1_1_BAND_0,                            ///< APV L 1.1, BAND 0
+    LEVEL_APV_2_BAND_0,                              ///< APV L 2, BAND 0
+    LEVEL_APV_2_1_BAND_0,                            ///< APV L 2.1, BAND 0
+    LEVEL_APV_3_BAND_0,                              ///< APV L 3, BAND 0
+    LEVEL_APV_3_1_BAND_0,                            ///< APV L 3.1, BAND 0
+    LEVEL_APV_4_BAND_0,                              ///< APV L 4, BAND 0
+    LEVEL_APV_4_1_BAND_0,                            ///< APV L 4.1, BAND 0
+    LEVEL_APV_5_BAND_0,                              ///< APV L 5, BAND 0
+    LEVEL_APV_5_1_BAND_0,                            ///< APV L 5.1, BAND 0
+    LEVEL_APV_6_BAND_0,                              ///< APV L 6, BAND 0
+    LEVEL_APV_6_1_BAND_0,                            ///< APV L 6.1, BAND 0
+    LEVEL_APV_7_BAND_0,                              ///< APV L 7, BAND 0
+    LEVEL_APV_7_1_BAND_0,                            ///< APV L 7.1, BAND 0
+
+    LEVEL_APV_1_BAND_1 = _C2_PL_APV_BASE + 0x100,    ///< APV L 1, BAND 1
+    LEVEL_APV_1_1_BAND_1,                            ///< APV L 1.1, BAND 1
+    LEVEL_APV_2_BAND_1,                              ///< APV L 2, BAND 1
+    LEVEL_APV_2_1_BAND_1,                            ///< APV L 2.1, BAND 1
+    LEVEL_APV_3_BAND_1,                              ///< APV L 3, BAND 1
+    LEVEL_APV_3_1_BAND_1,                            ///< APV L 3.1, BAND 1
+    LEVEL_APV_4_BAND_1,                              ///< APV L 4, BAND 1
+    LEVEL_APV_4_1_BAND_1,                            ///< APV L 4.1, BAND 1
+    LEVEL_APV_5_BAND_1,                              ///< APV L 5, BAND 1
+    LEVEL_APV_5_1_BAND_1,                            ///< APV L 5.1, BAND 1
+    LEVEL_APV_6_BAND_1,                              ///< APV L 6, BAND 1
+    LEVEL_APV_6_1_BAND_1,                            ///< APV L 6.1, BAND 1
+    LEVEL_APV_7_BAND_1,                              ///< APV L 7, BAND 1
+    LEVEL_APV_7_1_BAND_1,                            ///< APV L 7.1, BAND 1
+
+    LEVEL_APV_1_BAND_2 = _C2_PL_APV_BASE + 0x200,    ///< APV L 1, BAND 2
+    LEVEL_APV_1_1_BAND_2,                            ///< APV L 1.1, BAND 2
+    LEVEL_APV_2_BAND_2,                              ///< APV L 2, BAND 2
+    LEVEL_APV_2_1_BAND_2,                            ///< APV L 2.1, BAND 2
+    LEVEL_APV_3_BAND_2,                              ///< APV L 3, BAND 2
+    LEVEL_APV_3_1_BAND_2,                            ///< APV L 3.1, BAND 2
+    LEVEL_APV_4_BAND_2,                              ///< APV L 4, BAND 2
+    LEVEL_APV_4_1_BAND_2,                            ///< APV L 4.1, BAND 2
+    LEVEL_APV_5_BAND_2,                              ///< APV L 5, BAND 2
+    LEVEL_APV_5_1_BAND_2,                            ///< APV L 5.1, BAND 2
+    LEVEL_APV_6_BAND_2,                              ///< APV L 6, BAND 2
+    LEVEL_APV_6_1_BAND_2,                            ///< APV L 6.1, BAND 2
+    LEVEL_APV_7_BAND_2,                              ///< APV L 7, BAND 2
+    LEVEL_APV_7_1_BAND_2,                            ///< APV L 7.1, BAND 2
+
+    LEVEL_APV_1_BAND_3 = _C2_PL_APV_BASE + 0x300,    ///< APV L 1, BAND 3
+    LEVEL_APV_1_1_BAND_3,                            ///< APV L 1.1, BAND 3
+    LEVEL_APV_2_BAND_3,                              ///< APV L 2, BAND 3
+    LEVEL_APV_2_1_BAND_3,                            ///< APV L 2.1, BAND 3
+    LEVEL_APV_3_BAND_3,                              ///< APV L 3, BAND 3
+    LEVEL_APV_3_1_BAND_3,                            ///< APV L 3.1, BAND 3
+    LEVEL_APV_4_BAND_3,                              ///< APV L 4, BAND 3
+    LEVEL_APV_4_1_BAND_3,                            ///< APV L 4.1, BAND 3
+    LEVEL_APV_5_BAND_3,                              ///< APV L 5, BAND 3
+    LEVEL_APV_5_1_BAND_3,                            ///< APV L 5.1, BAND 3
+    LEVEL_APV_6_BAND_3,                              ///< APV L 6, BAND 3
+    LEVEL_APV_6_1_BAND_3,                            ///< APV L 6.1, BAND 3
+    LEVEL_APV_7_BAND_3,                              ///< APV L 7, BAND 3
+    LEVEL_APV_7_1_BAND_3,                            ///< APV L 7.1, BAND 3
+
 };
 
 struct C2ProfileLevelStruct {
@@ -1185,21 +1267,114 @@
 /* ----------------------------------------- resources ----------------------------------------- */
 
 /**
- * Resources needed and resources reserved for current configuration.
- *
- * Resources are tracked as a vector of positive numbers. Available resources are defined by
- * the vendor.
- *
- * By default, no resources are reserved for a component. If resource reservation is successful,
- * the component shall be able to use those resources exclusively. If however, the component is
- * not using all of the reserved resources, those may be shared with other components.
- *
- * TODO: define some of the resources.
+ * Resource kind.
  */
-typedef C2GlobalParam<C2Tuning, C2Uint64Array, kParamIndexResourcesNeeded> C2ResourcesNeededTuning;
-typedef C2GlobalParam<C2Tuning, C2Uint64Array, kParamIndexResourcesReserved>
-        C2ResourcesReservedTuning;
+C2ENUM(C2Config::resource_kind_t, uint32_t,
+    CONST,
+    PER_FRAME,
+    PER_INPUT_BLOCK,
+    PER_OUTPUT_BLOCK
+)
+
+/**
+ * Definition of a system resource use.
+ *
+ * [PROPOSED]
+ *
+ * System resources are defined by the default component store.
+ * They represent any physical or abstract entities of limited availability
+ * that is required for a component instance to execute and process work.
+ *
+ * Each defined resource has an id.
+ * The use of a resource is specified by the amount and the kind (e.g. whether the amount
+ * of resources is required for each frame processed, or whether they are required
+ * regardless of the processing rate (const amount)).
+ *
+ * Note: implementations can shadow this structure with their own custom resource
+ * structure where a uint32_t based enum is used for id.
+ * This can be used to provide a name for each resource, via parameter descriptors.
+ */
+
+struct C2SystemResourceStruct {
+    C2SystemResourceStruct(uint32_t id_,
+                           C2Config::resource_kind_t kind_,
+                           uint64_t amount_)
+        : id(id_), kind(kind_), amount(amount_) { }
+    uint32_t id;
+    C2Config::resource_kind_t kind;
+    uint64_t amount;
+
+    DEFINE_AND_DESCRIBE_C2STRUCT(SystemResource)
+    C2FIELD(id, "id")
+    C2FIELD(kind, "kind")
+    C2FIELD(amount, "amount")
+};
+
+/**
+ * Total system resource capacity.
+ *
+ * [PROPOSED]
+ *
+ * This setting is implemented by the default component store.
+ * The total resource capacity is specified as the maximum amount for each resource ID
+ * that is supported by the device hardware or firmware.
+ * As such, the kind must be CONST for each element.
+ */
+typedef C2GlobalParam<C2Tuning,
+                      C2SimpleArrayStruct<C2SystemResourceStruct>,
+                      kParamIndexResourcesCapacity> C2ResourcesCapacityTuning;
+constexpr char C2_PARAMKEY_RESOURCES_CAPACITY[] = "resources.capacity";
+
+/**
+ * Excluded system resources.
+ *
+ * [PROPOSED]
+ *
+ * This setting is implemented by the default component store.
+ * Some system resources may be used by components and not tracked by the Codec 2.0 API.
+ * This is communicated by this tuning.
+ * Excluded resources are the total resources that are used by non-Codec 2.0 components.
+ * It is specified as the excluded amount for each resource ID that is used by
+ * a non-Codec 2.0 component. As such, the kind must be CONST for each element.
+ *
+ * The platform can calculate the available resources as total capacity minus
+ * excluded resource minus sum of needed resources for each component.
+ */
+typedef C2GlobalParam<C2Tuning,
+                      C2SimpleArrayStruct<C2SystemResourceStruct>,
+                      kParamIndexResourcesExcluded> C2ResourcesExcludedTuning;
+constexpr char C2_PARAMKEY_RESOURCES_EXCLUDED[] = "resources.excluded";
+
+/**
+ * System resources needed for the current configuration.
+ *
+ * [PROPOSED]
+ *
+ * Resources are tracked as a list of individual resource use specifications.
+ * The resource kind can be CONST, PER_FRAME, PER_INPUT_BLODCK or PER_OUTPUT_BLOCK.
+ */
+typedef C2GlobalParam<C2Tuning,
+                      C2SimpleArrayStruct<C2SystemResourceStruct>,
+                      kParamIndexResourcesNeeded> C2ResourcesNeededTuning;
 constexpr char C2_PARAMKEY_RESOURCES_NEEDED[] = "resources.needed";
+
+/**
+ * System resources reserved for this component
+ *
+ * [FUTURE]
+ *
+ * This allows the platform to set aside system resources for the component.
+ * Since this is a static resource reservation, kind must be CONST for each element.
+ * This resource reservation only considers CONST and PER_FRAME use.
+ *
+ * By default, no resources are reserved for a component.
+ * If resource reservation is successful, the component shall be able to use those
+ * resources exclusively. If however, the component is not using all of the
+ * reserved resources, those may be shared with other components.
+ */
+typedef C2GlobalParam<C2Tuning,
+                      C2SimpleArrayStruct<C2SystemResourceStruct>,
+                      kParamIndexResourcesReserved> C2ResourcesReservedTuning;
 constexpr char C2_PARAMKEY_RESOURCES_RESERVED[] = "resources.reserved";
 
 /**
@@ -2480,6 +2655,14 @@
 constexpr char C2_PARAMKEY_INPUT_SURFACE_MIN_FRAME_RATE[] = "input-surface.min-frame-rate";
 
 /**
+ * Maximum fps for input surface.
+ *
+ * Drop frame to meet this.
+ */
+typedef C2PortParam<C2Tuning, C2FloatValue, kParamIndexMaxFrameRate> C2PortMaxFrameRateTuning;
+constexpr char C2_PARAMKEY_INPUT_SURFACE_MAX_FRAME_RATE[] = "input-surface.max-frame-rate";
+
+/**
  * Timestamp adjustment (override) for input surface buffers. These control the input timestamp
  * fed to the codec, but do not impact the output timestamp.
  */
@@ -2509,9 +2692,26 @@
 inline C2TimestampGapAdjustmentStruct::C2TimestampGapAdjustmentStruct()
     : mode(C2TimestampGapAdjustmentStruct::NONE), value(0) { }
 
-typedef C2PortParam<C2Tuning, C2TimestampGapAdjustmentStruct> C2PortTimestampGapTuning;
+typedef C2PortParam<C2Tuning, C2TimestampGapAdjustmentStruct, kParamIndexTimestampGapAdjustment>
+        C2PortTimestampGapTuning;
 constexpr char C2_PARAMKEY_INPUT_SURFACE_TIMESTAMP_ADJUSTMENT[] = "input-surface.timestamp-adjustment";
 
+/**
+ * Capture frame rate for input surface. During timelapse or slowmo encoding,
+ * this represents the frame rate of input surface.
+ */
+typedef C2PortParam<C2Tuning, C2FloatValue, kParamIndexCaptureFrameRate>
+        C2PortCaptureFrameRateTuning;
+constexpr char C2_PARAMKEY_INPUT_SURFACE_CAPTURE_FRAME_RATE[] = "input-surface.capture-frame-rate";
+
+/**
+ * Stop time offset for input surface. Stop time offset is the elapsed time
+ * offset to the last frame time from the stop time. This could be returned from
+ * IInputSurface when it is queried.
+ */
+typedef C2PortParam<C2Tuning, C2Int64Value, kParamIndexStopTimeOffset> C2PortStopTimeOffset;
+constexpr char C2_PARAMKEY_INPUT_SURFACE_STOP_TIME_OFFSET[] = "input-surface.stop-time-offset";
+
 /* ===================================== TUNNELED CODEC ==================================== */
 
 /**
diff --git a/media/codec2/hal/aidl/Android.bp b/media/codec2/hal/aidl/Android.bp
index e16e2b1..eaabc33 100644
--- a/media/codec2/hal/aidl/Android.bp
+++ b/media/codec2/hal/aidl/Android.bp
@@ -78,6 +78,8 @@
         "Configurable.cpp",
         "InputBufferManager.cpp",
         "ParamTypes.cpp",
+        "inputsurface/InputSurface.cpp",
+        "inputsurface/InputSurfaceConnection.cpp",
     ],
 
     header_libs: [
@@ -98,6 +100,7 @@
         "libhidlbase",
         "liblog",
         "libnativewindow",
+        "libmediandk",
         "libstagefright_aidl_bufferpool2",
         "libstagefright_bufferpool@2.0.1",
         "libui",
diff --git a/media/codec2/hal/aidl/include/codec2/aidl/inputsurface/InputSurface.h b/media/codec2/hal/aidl/include/codec2/aidl/inputsurface/InputSurface.h
new file mode 100644
index 0000000..5c2cc2e
--- /dev/null
+++ b/media/codec2/hal/aidl/include/codec2/aidl/inputsurface/InputSurface.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/media/c2/BnInputSurface.h>
+
+#include <codec2/aidl/Configurable.h>
+#include <util/C2InterfaceHelper.h>
+
+#include <C2.h>
+
+#include <memory>
+
+namespace aidl::android::hardware::media::c2::utils {
+
+struct InputSurface : public BnInputSurface {
+    InputSurface();
+    c2_status_t status() const;
+
+    // Methods from IInputSurface follow.
+    ::ndk::ScopedAStatus getSurface(
+            ::aidl::android::view::Surface* surface) override;
+    ::ndk::ScopedAStatus getConfigurable(
+            std::shared_ptr<IConfigurable>* configurable) override;
+    ::ndk::ScopedAStatus connect(
+            const std::shared_ptr<IInputSink>& sink,
+            std::shared_ptr<IInputSurfaceConnection>* connection) override;
+
+protected:
+    class Interface;
+    class ConfigurableIntf;
+
+    c2_status_t mInit;
+    std::shared_ptr<Interface> mIntf;
+    std::shared_ptr<CachedConfigurable> mConfigurable;
+
+    virtual ~InputSurface() override;
+
+
+    ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
+    static void OnBinderDied(void *cookie);
+    static void OnBinderUnlinked(void *cookie);
+    struct DeathContext;
+    DeathContext *mDeathContext;
+};
+
+}  // namespace aidl::android::hardware::media::c2::utils
diff --git a/media/codec2/hal/aidl/include/codec2/aidl/inputsurface/InputSurfaceConnection.h b/media/codec2/hal/aidl/include/codec2/aidl/inputsurface/InputSurfaceConnection.h
new file mode 100644
index 0000000..59361e1
--- /dev/null
+++ b/media/codec2/hal/aidl/include/codec2/aidl/inputsurface/InputSurfaceConnection.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/media/c2/BnInputSurfaceConnection.h>
+#include <media/NdkImage.h>
+
+#include <C2.h>
+
+#include <memory>
+
+namespace aidl::android::hardware::media::c2::utils {
+
+struct InputSurfaceConnection : public BnInputSurfaceConnection {
+    InputSurfaceConnection();
+    c2_status_t status() const;
+
+    // Methods from IInputSurfaceConnection follow.
+    ::ndk::ScopedAStatus disconnect() override;
+    ::ndk::ScopedAStatus signalEndOfStream() override;
+
+    // implementation specific interface.
+
+    // Submit a buffer to the connected component.
+    c2_status_t submitBuffer(
+            int32_t bufferId,
+            const AImage *buffer = nullptr,
+            int64_t timestamp = 0,
+            int fenceFd = -1);
+
+    // Submit eos to the connected component.
+    c2_status_t submitEos(int32_t bufferId);
+
+    // notify dataspace being changed to the component.
+    void dispatchDataSpaceChanged(
+            int32_t dataSpace, int32_t aspects, int32_t pixelFormat);
+
+protected:
+    virtual ~InputSurfaceConnection() override;
+};
+
+}  // namespace aidl::android::hardware::media::c2::utils
diff --git a/media/codec2/hal/aidl/inputsurface/InputSurface.cpp b/media/codec2/hal/aidl/inputsurface/InputSurface.cpp
new file mode 100644
index 0000000..5f6d176
--- /dev/null
+++ b/media/codec2/hal/aidl/inputsurface/InputSurface.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "Codec2-InputSurface"
+#include <android-base/logging.h>
+
+#include <codec2/aidl/inputsurface/InputSurface.h>
+
+namespace aidl::android::hardware::media::c2::utils {
+
+// Derived class of C2InterfaceHelper
+class InputSurface::Interface : public C2InterfaceHelper {
+public:
+    explicit Interface(
+            const std::shared_ptr<C2ReflectorHelper> &helper)
+        : C2InterfaceHelper(helper) {
+
+        setDerivedInstance(this);
+
+    }
+
+private:
+};
+
+class InputSurface::ConfigurableIntf : public ConfigurableC2Intf {
+public:
+};
+
+struct InputSurface::DeathContext {
+    // TODO;
+};
+
+void InputSurface::OnBinderDied(void *cookie) {
+    (void) cookie;
+}
+
+void InputSurface::OnBinderUnlinked(void *cookie) {
+    (void) cookie;
+}
+
+InputSurface::InputSurface() : mDeathContext(nullptr) {
+    mInit = C2_OK;
+}
+
+InputSurface::~InputSurface() {
+}
+
+::ndk::ScopedAStatus InputSurface::getSurface(::aidl::android::view::Surface* surface) {
+    (void) surface;
+    return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus InputSurface::getConfigurable(
+        std::shared_ptr<IConfigurable>* configurable) {
+    *configurable = mConfigurable;
+    return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus InputSurface::connect(
+        const std::shared_ptr<IInputSink>& sink,
+        std::shared_ptr<IInputSurfaceConnection>* connection) {
+    (void) sink;
+    (void) connection;
+    return ::ndk::ScopedAStatus::ok();
+}
+
+}  // namespace aidl::android::hardware::media::c2::utils
diff --git a/media/codec2/hal/aidl/inputsurface/InputSurfaceConnection.cpp b/media/codec2/hal/aidl/inputsurface/InputSurfaceConnection.cpp
new file mode 100644
index 0000000..44ca924
--- /dev/null
+++ b/media/codec2/hal/aidl/inputsurface/InputSurfaceConnection.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "Codec2-InputSurface"
+#include <android-base/logging.h>
+
+#include <codec2/aidl/inputsurface/InputSurfaceConnection.h>
+
+namespace aidl::android::hardware::media::c2::utils {
+
+InputSurfaceConnection::InputSurfaceConnection() {
+}
+
+InputSurfaceConnection::~InputSurfaceConnection() {
+}
+
+::ndk::ScopedAStatus InputSurfaceConnection::disconnect() {
+    return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus InputSurfaceConnection::signalEndOfStream() {
+    return ::ndk::ScopedAStatus::ok();
+}
+
+c2_status_t InputSurfaceConnection::submitBuffer(
+        int32_t bufferId, const AImage *buffer, int64_t timestamp, int fenceFd) {
+    (void)bufferId;
+    (void)buffer;
+    (void)timestamp;
+    (void)fenceFd;
+    return C2_OK;
+}
+
+c2_status_t InputSurfaceConnection::submitEos(int32_t bufferId) {
+    (void)bufferId;
+    return C2_OK;
+}
+
+void InputSurfaceConnection::dispatchDataSpaceChanged(
+            int32_t dataSpace, int32_t aspects, int32_t pixelFormat) {
+    (void)dataSpace;
+    (void)aspects;
+    (void)pixelFormat;
+}
+
+}  // namespace aidl::android::hardware::media::c2::utils
diff --git a/media/codec2/hal/client/Android.bp b/media/codec2/hal/client/Android.bp
index 864eeb8..029044f 100644
--- a/media/codec2/hal/client/Android.bp
+++ b/media/codec2/hal/client/Android.bp
@@ -23,6 +23,7 @@
     name: "libcodec2_client",
 
     srcs: [
+        "ApexCodecsLazy.cpp",
         "GraphicBufferAllocator.cpp",
         "GraphicsTracker.cpp",
         "client.cpp",
@@ -41,17 +42,18 @@
     cpp_std: "gnu++20",
 
     header_libs: [
+        "libapexcodecs-header",
         "libcodec2_internal", // private
     ],
 
     shared_libs: [
         "android.hardware.graphics.bufferqueue@1.0",
+        "android.hardware.media.bufferpool2-V2-ndk",
         "android.hardware.media.bufferpool@2.0",
+        "android.hardware.media.c2-V1-ndk",
         "android.hardware.media.c2@1.0",
         "android.hardware.media.c2@1.1",
         "android.hardware.media.c2@1.2",
-        "android.hardware.media.bufferpool2-V2-ndk",
-        "android.hardware.media.c2-V1-ndk",
         "libbase",
         "libbinder",
         "libbinder_ndk",
@@ -79,6 +81,10 @@
         "include",
     ],
 
+    export_header_lib_headers: [
+        "libapexcodecs-header",
+    ],
+
     export_shared_lib_headers: [
         "android.hardware.media.c2@1.0",
         "android.hardware.media.c2@1.1",
@@ -89,5 +95,4 @@
         "libcodec2_hidl_client@1.2",
         "libcodec2_vndk",
     ],
-
 }
diff --git a/media/codec2/hal/client/ApexCodecsLazy.cpp b/media/codec2/hal/client/ApexCodecsLazy.cpp
new file mode 100644
index 0000000..cd7953e
--- /dev/null
+++ b/media/codec2/hal/client/ApexCodecsLazy.cpp
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "ApexCodecsLazy"
+#include <log/log.h>
+
+#include <mutex>
+
+#include <dlfcn.h>
+
+#include <android-base/no_destructor.h>
+#include <apex/ApexCodecs.h>
+#include <utils/RWLock.h>
+
+using android::RWLock;
+
+namespace {
+
+// This file provides a lazy interface to libapexcodecs.so to address early boot dependencies.
+
+// Method pointers to libapexcodecs methods are held in an array which simplifies checking
+// all pointers are initialized.
+enum MethodIndex {
+    k_ApexCodec_Component_create,
+    k_ApexCodec_Component_destroy,
+    k_ApexCodec_Component_flush,
+    k_ApexCodec_Component_getConfigurable,
+    k_ApexCodec_Component_process,
+    k_ApexCodec_Component_start,
+    k_ApexCodec_Component_reset,
+    k_ApexCodec_Configurable_config,
+    k_ApexCodec_Configurable_query,
+    k_ApexCodec_Configurable_querySupportedParams,
+    k_ApexCodec_Configurable_querySupportedValues,
+    k_ApexCodec_GetComponentStore,
+    k_ApexCodec_ParamDescriptors_getDescriptor,
+    k_ApexCodec_ParamDescriptors_getIndices,
+    k_ApexCodec_ParamDescriptors_release,
+    k_ApexCodec_SettingResults_getResultAtIndex,
+    k_ApexCodec_SettingResults_release,
+    k_ApexCodec_SupportedValues_getTypeAndValues,
+    k_ApexCodec_SupportedValues_release,
+    k_ApexCodec_Traits_get,
+
+    // Marker for count of methods
+    k_MethodCount
+};
+
+class ApexCodecsLazyLoader {
+public:
+    ApexCodecsLazyLoader() = default;
+
+    static ApexCodecsLazyLoader &Get() {
+        static ::android::base::NoDestructor<ApexCodecsLazyLoader> sLoader;
+        return *sLoader;
+    }
+
+    void *getMethodAt(enum MethodIndex index) {
+        RWLock::AutoRLock l(mLock);
+        if (mInit) {
+            return mMethods[index];
+        } else {
+            mLock.unlock();
+            if (!init()) {
+                return nullptr;
+            }
+            mLock.readLock();
+            return mMethods[index];
+        }
+    }
+
+private:
+    static void* LoadLibapexcodecs(int dlopen_flags) {
+        return dlopen("libapexcodecs.so", dlopen_flags);
+    }
+
+    // Initialization and symbol binding.
+    void bindSymbol_l(void* handle, const char* name, enum MethodIndex index) {
+        void* symbol = dlsym(handle, name);
+        ALOGI_IF(symbol == nullptr, "Failed to find symbol '%s' in libapexcodecs.so: %s",
+                 name, dlerror());
+        mMethods[index] = symbol;
+    }
+
+    bool init() {
+        {
+            RWLock::AutoRLock l(mLock);
+            if (mInit) {
+                return true;
+            }
+        }
+        void* handle = LoadLibapexcodecs(RTLD_NOW);
+        if (handle == nullptr) {
+            ALOGI("Failed to load libapexcodecs.so: %s", dlerror());
+            return false;
+        }
+
+        RWLock::AutoWLock l(mLock);
+#undef BIND_SYMBOL
+#define BIND_SYMBOL(name) bindSymbol_l(handle, #name, k_##name);
+        BIND_SYMBOL(ApexCodec_Component_create);
+        BIND_SYMBOL(ApexCodec_Component_destroy);
+        BIND_SYMBOL(ApexCodec_Component_flush);
+        BIND_SYMBOL(ApexCodec_Component_getConfigurable);
+        BIND_SYMBOL(ApexCodec_Component_process);
+        BIND_SYMBOL(ApexCodec_Component_start);
+        BIND_SYMBOL(ApexCodec_Component_reset);
+        BIND_SYMBOL(ApexCodec_Configurable_config);
+        BIND_SYMBOL(ApexCodec_Configurable_query);
+        BIND_SYMBOL(ApexCodec_Configurable_querySupportedParams);
+        BIND_SYMBOL(ApexCodec_Configurable_querySupportedValues);
+        BIND_SYMBOL(ApexCodec_GetComponentStore);
+        BIND_SYMBOL(ApexCodec_ParamDescriptors_getDescriptor);
+        BIND_SYMBOL(ApexCodec_ParamDescriptors_getIndices);
+        BIND_SYMBOL(ApexCodec_ParamDescriptors_release);
+        BIND_SYMBOL(ApexCodec_SettingResults_getResultAtIndex);
+        BIND_SYMBOL(ApexCodec_SettingResults_release);
+        BIND_SYMBOL(ApexCodec_SupportedValues_getTypeAndValues);
+        BIND_SYMBOL(ApexCodec_SupportedValues_release);
+        BIND_SYMBOL(ApexCodec_Traits_get);
+#undef BIND_SYMBOL
+
+        // Check every symbol is bound.
+        for (int i = 0; i < k_MethodCount; ++i) {
+            if (mMethods[i] == nullptr) {
+                ALOGI("Uninitialized method in libapexcodecs_lazy at index: %d", i);
+                return false;
+            }
+        }
+        mInit = true;
+        return true;
+    }
+
+    RWLock mLock;
+    // Table of methods pointers in libapexcodecs APIs.
+    void* mMethods[k_MethodCount];
+    bool mInit{false};
+};
+
+}  // anonymous namespace
+
+#define INVOKE_METHOD(name, returnIfNull, args...)                          \
+    do {                                                                    \
+        void* method = ApexCodecsLazyLoader::Get().getMethodAt(k_##name);   \
+        if (!method) return (returnIfNull);                                 \
+        return reinterpret_cast<decltype(&name)>(method)(args);             \
+    } while (0)
+
+//
+// Forwarding for methods in ApexCodecs.h.
+//
+
+ApexCodec_ComponentStore *ApexCodec_GetComponentStore() {
+    INVOKE_METHOD(ApexCodec_GetComponentStore, nullptr);
+}
+
+ApexCodec_ComponentTraits *ApexCodec_Traits_get(
+        ApexCodec_ComponentStore *store, size_t index) {
+    INVOKE_METHOD(ApexCodec_Traits_get, nullptr, store, index);
+}
+
+ApexCodec_Status ApexCodec_Component_create(
+        ApexCodec_ComponentStore *store, const char *name, ApexCodec_Component **comp) {
+    INVOKE_METHOD(ApexCodec_Component_create, APEXCODEC_STATUS_OMITTED, store, name, comp);
+}
+
+void ApexCodec_Component_destroy(ApexCodec_Component *comp) {
+    INVOKE_METHOD(ApexCodec_Component_destroy, void(), comp);
+}
+
+ApexCodec_Status ApexCodec_Component_start(ApexCodec_Component *comp) {
+    INVOKE_METHOD(ApexCodec_Component_start, APEXCODEC_STATUS_OMITTED, comp);
+}
+
+ApexCodec_Status ApexCodec_Component_flush(ApexCodec_Component *comp) {
+    INVOKE_METHOD(ApexCodec_Component_flush, APEXCODEC_STATUS_OMITTED, comp);
+}
+
+ApexCodec_Status ApexCodec_Component_reset(ApexCodec_Component *comp) {
+    INVOKE_METHOD(ApexCodec_Component_reset, APEXCODEC_STATUS_OMITTED, comp);
+}
+
+ApexCodec_Configurable *ApexCodec_Component_getConfigurable(
+        ApexCodec_Component *comp) {
+    INVOKE_METHOD(ApexCodec_Component_getConfigurable, nullptr, comp);
+}
+
+ApexCodec_Status ApexCodec_SupportedValues_getTypeAndValues(
+        ApexCodec_SupportedValues *supportedValues,
+        ApexCodec_SupportedValuesType *type,
+        ApexCodec_SupportedValuesNumberType *numberType,
+        ApexCodec_Value **values,
+        uint32_t *numValues) {
+    INVOKE_METHOD(ApexCodec_SupportedValues_getTypeAndValues, APEXCODEC_STATUS_OMITTED,
+                  supportedValues, type, numberType, values, numValues);
+}
+
+void ApexCodec_SupportedValues_release(ApexCodec_SupportedValues *values) {
+    INVOKE_METHOD(ApexCodec_SupportedValues_release, void(), values);
+}
+
+ApexCodec_Status ApexCodec_SettingResults_getResultAtIndex(
+        ApexCodec_SettingResults *results,
+        size_t index,
+        ApexCodec_SettingResultFailure *failure,
+        ApexCodec_ParamFieldValues *field,
+        ApexCodec_ParamFieldValues **conflicts,
+        size_t *numConflicts) {
+    INVOKE_METHOD(ApexCodec_SettingResults_getResultAtIndex, APEXCODEC_STATUS_OMITTED,
+                  results, index, failure, field, conflicts, numConflicts);
+}
+
+void ApexCodec_SettingResults_release(ApexCodec_SettingResults *results) {
+    INVOKE_METHOD(ApexCodec_SettingResults_release, void(), results);
+}
+
+ApexCodec_Status ApexCodec_Component_process(
+        ApexCodec_Component *comp,
+        const ApexCodec_Buffer *input,
+        ApexCodec_Buffer *output,
+        size_t *consumed,
+        size_t *produced) {
+    INVOKE_METHOD(ApexCodec_Component_process, APEXCODEC_STATUS_OMITTED,
+                  comp, input, output, consumed, produced);
+}
+
+ApexCodec_Status ApexCodec_Configurable_config(
+        ApexCodec_Configurable *comp,
+        ApexCodec_LinearBuffer *config,
+        ApexCodec_SettingResults **results) {
+    INVOKE_METHOD(ApexCodec_Configurable_config, APEXCODEC_STATUS_OMITTED, comp, config, results);
+}
+
+ApexCodec_Status ApexCodec_Configurable_query(
+        ApexCodec_Configurable *comp,
+        uint32_t indices[],
+        size_t numIndices,
+        ApexCodec_LinearBuffer *config,
+        size_t *writtenOrRequested) {
+    INVOKE_METHOD(ApexCodec_Configurable_query, APEXCODEC_STATUS_OMITTED,
+                  comp, indices, numIndices, config, writtenOrRequested);
+}
+
+ApexCodec_Status ApexCodec_ParamDescriptors_getIndices(
+        ApexCodec_ParamDescriptors *descriptors,
+        uint32_t **indices,
+        size_t *numIndices) {
+    INVOKE_METHOD(ApexCodec_ParamDescriptors_getIndices, APEXCODEC_STATUS_OMITTED,
+                  descriptors, indices, numIndices);
+}
+
+ApexCodec_Status ApexCodec_ParamDescriptors_getDescriptor(
+        ApexCodec_ParamDescriptors *descriptors,
+        uint32_t index,
+        ApexCodec_ParamAttribute *attr,
+        const char **name,
+        uint32_t **dependencies,
+        size_t *numDependencies) {
+    INVOKE_METHOD(ApexCodec_ParamDescriptors_getDescriptor, APEXCODEC_STATUS_OMITTED,
+                  descriptors, index, attr, name, dependencies, numDependencies);
+}
+
+ApexCodec_Status ApexCodec_ParamDescriptors_release(
+        ApexCodec_ParamDescriptors *descriptors) {
+    INVOKE_METHOD(ApexCodec_ParamDescriptors_release, APEXCODEC_STATUS_OMITTED, descriptors);
+}
+
+ApexCodec_Status ApexCodec_Configurable_querySupportedParams(
+        ApexCodec_Configurable *comp,
+        ApexCodec_ParamDescriptors **descriptors) {
+    INVOKE_METHOD(ApexCodec_Configurable_querySupportedParams, APEXCODEC_STATUS_OMITTED,
+                  comp, descriptors);
+}
+
+ApexCodec_Status ApexCodec_Configurable_querySupportedValues(
+        ApexCodec_Configurable *comp,
+        ApexCodec_SupportedValuesQuery *queries,
+        size_t numQueries) {
+    INVOKE_METHOD(ApexCodec_Configurable_querySupportedValues, APEXCODEC_STATUS_OMITTED,
+                  comp, queries, numQueries);
+}
diff --git a/media/codec2/hal/client/GraphicBufferAllocator.cpp b/media/codec2/hal/client/GraphicBufferAllocator.cpp
index 6a6da0f..4055f9b 100644
--- a/media/codec2/hal/client/GraphicBufferAllocator.cpp
+++ b/media/codec2/hal/client/GraphicBufferAllocator.cpp
@@ -96,6 +96,10 @@
     mGraphicsTracker->onAttached(generation);
 }
 
+void GraphicBufferAllocator::pollForRenderedFrames(FrameEventHistoryDelta* delta) {
+    mGraphicsTracker->pollForRenderedFrames(delta);
+}
+
 c2_status_t GraphicBufferAllocator::allocate(
         uint32_t width, uint32_t height, ::android::PixelFormat format, uint64_t usage,
         AHardwareBuffer **buf, ::android::sp<::android::Fence> *fence) {
@@ -119,6 +123,10 @@
     return mGraphicsTracker->render(block, input, output);
 }
 
+void GraphicBufferAllocator::onRequestStop() {
+    mGraphicsTracker->onRequestStop();
+}
+
 GraphicBufferAllocator::~GraphicBufferAllocator() {}
 
 std::shared_ptr<GraphicBufferAllocator> GraphicBufferAllocator::CreateGraphicBufferAllocator(
diff --git a/media/codec2/hal/client/GraphicsTracker.cpp b/media/codec2/hal/client/GraphicsTracker.cpp
index bdfc409..ff356fc 100644
--- a/media/codec2/hal/client/GraphicsTracker.cpp
+++ b/media/codec2/hal/client/GraphicsTracker.cpp
@@ -32,6 +32,9 @@
 static constexpr int kMaxDequeueMin = 1;
 static constexpr int kMaxDequeueMax = ::android::BufferQueueDefs::NUM_BUFFER_SLOTS - 2;
 
+// Just some delay for HAL to receive the stop()/release() request.
+static constexpr int kAllocateDirectDelayUs = 16666;
+
 c2_status_t retrieveAHardwareBufferId(const C2ConstGraphicBlock &blk, uint64_t *bid) {
     std::shared_ptr<const _C2BlockPoolData> bpData = _C2BlockFactory::GetGraphicBlockPoolData(blk);
     if (!bpData || bpData->getType() != _C2BlockPoolData::TYPE_AHWBUFFER) {
@@ -177,7 +180,7 @@
     mMaxDequeueCommitted{maxDequeueCount},
     mDequeueable{maxDequeueCount},
     mTotalDequeued{0}, mTotalCancelled{0}, mTotalDropped{0}, mTotalReleased{0},
-    mInConfig{false}, mStopped{false} {
+    mInConfig{false}, mStopped{false}, mStopRequested{false}, mAllocAfterStopRequested{0} {
     if (maxDequeueCount < kMaxDequeueMin) {
         mMaxDequeue = kMaxDequeueMin;
         mMaxDequeueCommitted = kMaxDequeueMin;
@@ -490,6 +493,18 @@
     }
 }
 
+void GraphicsTracker::onRequestStop() {
+    std::unique_lock<std::mutex> l(mLock);
+    if (mStopped) {
+        return;
+    }
+    if (mStopRequested) {
+        return;
+    }
+    mStopRequested = true;
+    writeIncDequeueableLocked(kMaxDequeueMax - 1);
+}
+
 void GraphicsTracker::writeIncDequeueableLocked(int inc) {
     CHECK(inc > 0 && inc < kMaxDequeueMax);
     thread_local char buf[kMaxDequeueMax];
@@ -544,8 +559,7 @@
     return C2_OK;
 }
 
-c2_status_t GraphicsTracker::requestAllocate(std::shared_ptr<BufferCache> *cache) {
-    std::lock_guard<std::mutex> l(mLock);
+c2_status_t GraphicsTracker::requestAllocateLocked(std::shared_ptr<BufferCache> *cache) {
     if (mDequeueable > 0) {
         char buf[1];
         int ret = ::read(mReadPipeFd.get(), buf, 1);
@@ -728,6 +742,34 @@
     return C2_OK;
 }
 
+c2_status_t GraphicsTracker::_allocateDirect(
+        uint32_t width, uint32_t height, PixelFormat format, uint64_t usage,
+        AHardwareBuffer **buf, sp<Fence> *rFence) {
+    AHardwareBuffer_Desc desc;
+    desc.width = width;
+    desc.height = height;
+    desc.layers = 1u;
+    desc.format = ::android::AHardwareBuffer_convertFromPixelFormat(format);
+    desc.usage = ::android::AHardwareBuffer_convertFromGrallocUsageBits(usage);
+    desc.rfu0 = 0;
+    desc.rfu1 = 0;
+
+    int res = AHardwareBuffer_allocate(&desc, buf);
+    if (res != ::android::OK) {
+        ALOGE("_allocateDirect() failed(%d)", res);
+        if (res == ::android::NO_MEMORY) {
+            return C2_NO_MEMORY;
+        } else {
+            return C2_CORRUPTED;
+        }
+    }
+
+    int alloced = mAllocAfterStopRequested++;
+    *rFence = Fence::NO_FENCE;
+    ALOGD("_allocateDirect() allocated %d buffer", alloced);
+    return C2_OK;
+}
+
 c2_status_t GraphicsTracker::allocate(
         uint32_t width, uint32_t height, PixelFormat format, uint64_t usage,
         AHardwareBuffer **buf, sp<Fence> *rFence) {
@@ -735,10 +777,21 @@
         ALOGE("cannot allocate due to being stopped");
         return C2_BAD_STATE;
     }
+    c2_status_t res = C2_OK;
     std::shared_ptr<BufferCache> cache;
-    c2_status_t res = requestAllocate(&cache);
-    if (res != C2_OK) {
-        return res;
+    {
+        std::unique_lock<std::mutex> l(mLock);
+        if (mStopRequested) {
+            l.unlock();
+            res = _allocateDirect(width, height, format, usage, buf, rFence);
+            // Delay a little bit for HAL to receive stop()/release() request.
+            ::usleep(kAllocateDirectDelayUs);
+            return res;
+        }
+        c2_status_t res = requestAllocateLocked(&cache);
+        if (res != C2_OK) {
+            return res;
+        }
     }
     ALOGV("allocatable or dequeueable");
 
@@ -1003,6 +1056,19 @@
     return C2_OK;
 }
 
+void GraphicsTracker::pollForRenderedFrames(FrameEventHistoryDelta* delta) {
+    sp<IGraphicBufferProducer> igbp;
+    {
+        std::unique_lock<std::mutex> l(mLock);
+        if (mBufferCache) {
+            igbp = mBufferCache->mIgbp;
+        }
+    }
+    if (igbp) {
+        igbp->getFrameTimestamps(delta);
+    }
+}
+
 void GraphicsTracker::onReleased(uint32_t generation) {
     bool updateDequeue = false;
     {
diff --git a/media/codec2/hal/client/client.cpp b/media/codec2/hal/client/client.cpp
index 80735cb..17e5b62 100644
--- a/media/codec2/hal/client/client.cpp
+++ b/media/codec2/hal/client/client.cpp
@@ -20,8 +20,12 @@
 #include <android-base/logging.h>
 #include <utils/Trace.h>
 
+#include <android_media_codec.h>
+
 #include <codec2/aidl/GraphicBufferAllocator.h>
+#include <codec2/common/HalSelection.h>
 #include <codec2/hidl/client.h>
+
 #include <C2Debug.h>
 #include <C2BufferPriv.h>
 #include <C2Config.h> // for C2StreamUsageTuning
@@ -53,7 +57,9 @@
 #include <android/binder_ibinder.h>
 #include <android/binder_manager.h>
 #include <android-base/properties.h>
+#include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
+#include <apex/ApexCodecs.h>
 #include <bufferpool/ClientManager.h>
 #include <bufferpool2/ClientManager.h>
 #include <codec2/aidl/BufferTypes.h>
@@ -62,14 +68,14 @@
 #include <codec2/hidl/1.1/types.h>
 #include <codec2/hidl/1.2/types.h>
 #include <codec2/hidl/output.h>
-
 #include <cutils/native_handle.h>
 #include <gui/bufferqueue/2.0/B2HGraphicBufferProducer.h>
 #include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h>
 #include <hardware/gralloc.h> // for GRALLOC_USAGE_*
 #include <hidl/HidlSupport.h>
-#include <system/window.h> // for NATIVE_WINDOW_QUERY_*
 #include <media/stagefright/foundation/ADebug.h> // for asString(status_t)
+#include <private/android/AHardwareBufferHelpers.h>
+#include <system/window.h> // for NATIVE_WINDOW_QUERY_*
 
 #include <deque>
 #include <iterator>
@@ -797,6 +803,386 @@
     return status;
 }
 
+// Codec2ConfigurableClient::ApexImpl
+
+struct Codec2ConfigurableClient::ApexImpl : public Codec2ConfigurableClient::ImplBase {
+    ApexImpl(ApexCodec_Configurable *base, const C2String &name);
+
+    const C2String& getName() const override {
+        return mName;
+    }
+
+    c2_status_t query(
+            const std::vector<C2Param*>& stackParams,
+            const std::vector<C2Param::Index> &heapParamIndices,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2Param>>* const heapParams) const override;
+
+    c2_status_t config(
+            const std::vector<C2Param*> &params,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2SettingResult>>* const failures) override;
+
+    c2_status_t querySupportedParams(
+            std::vector<std::shared_ptr<C2ParamDescriptor>>* const params
+            ) const override;
+
+    c2_status_t querySupportedValues(
+            std::vector<C2FieldSupportedValuesQuery>& fields,
+            c2_blocking_t mayBlock) const override;
+
+private:
+    ApexCodec_Configurable* mBase;
+    const C2String mName;
+};
+
+Codec2ConfigurableClient::ApexImpl::ApexImpl(ApexCodec_Configurable *base, const C2String &name)
+      : mBase{base},
+        mName{name} {
+}
+
+c2_status_t Codec2ConfigurableClient::ApexImpl::query(
+        const std::vector<C2Param*> &stackParams,
+        const std::vector<C2Param::Index> &heapParamIndices,
+        [[maybe_unused]] c2_blocking_t mayBlock,
+        std::vector<std::unique_ptr<C2Param>>* const heapParams) const {
+    if (mBase == nullptr) {
+        return C2_OMITTED;
+    }
+
+    if (__builtin_available(android 36, *)) {
+        std::vector<uint32_t> indices(
+                stackParams.size() + heapParamIndices.size());
+        size_t numIndices = 0;
+        for (C2Param* const& stackParam : stackParams) {
+            if (!stackParam) {
+                LOG(WARNING) << "query -- null stack param encountered.";
+                continue;
+            }
+            indices[numIndices++] = uint32_t(stackParam->index());
+        }
+        size_t numStackIndices = numIndices;
+        for (const C2Param::Index& index : heapParamIndices) {
+            indices[numIndices++] = uint32_t(index);
+        }
+        indices.resize(numIndices);
+        if (heapParams) {
+            heapParams->reserve(heapParams->size() + numIndices);
+        }
+        if (numIndices == 0) {
+            return C2_OK;
+        }
+        thread_local std::vector<uint8_t> configBuffer(1024);
+        if (configBuffer.capacity() < numIndices * 16u) {
+            configBuffer.resize(numIndices * 16u);
+        }
+        ApexCodec_LinearBuffer config{configBuffer.data(), configBuffer.capacity()};
+        size_t writtenOrRequested = 0;
+        ApexCodec_Status status = ApexCodec_Configurable_query(
+                mBase, indices.data(), indices.size(), &config, &writtenOrRequested);
+        if (status == APEXCODEC_STATUS_NO_MEMORY) {
+            size_t requested = writtenOrRequested;
+            configBuffer.resize(align(requested, 1024));
+            config.data = configBuffer.data();
+            config.size = configBuffer.capacity();
+            status = ApexCodec_Configurable_query(
+                    mBase, indices.data(), indices.size(), &config, &writtenOrRequested);
+        }
+        size_t written = writtenOrRequested;
+        if (status != APEXCODEC_STATUS_OK && status != APEXCODEC_STATUS_BAD_INDEX) {
+            written = 0;
+        }
+        configBuffer.resize(written);
+        std::vector<C2Param*> paramPointers;
+        if (!::android::parseParamsBlob(&paramPointers, configBuffer)) {
+            LOG(ERROR) << "query -- error while parsing params.";
+            return C2_CORRUPTED;
+        }
+        size_t i = 0;
+        size_t numQueried = 0;
+        for (auto it = paramPointers.begin(); it != paramPointers.end(); ) {
+            C2Param* paramPointer = *it;
+            if (numStackIndices > 0) {
+                --numStackIndices;
+                if (!paramPointer) {
+                    LOG(DEBUG) << "query -- null stack param.";
+                    ++it;
+                    continue;
+                }
+                for (; i < stackParams.size() && !stackParams[i]; ) {
+                    ++i;
+                }
+                if (i >= stackParams.size()) {
+                    LOG(ERROR) << "query -- unexpected error.";
+                    status = APEXCODEC_STATUS_CORRUPTED;
+                    break;
+                }
+                if (stackParams[i]->index() != paramPointer->index()) {
+                    LOG(DEBUG) << "query -- param skipped: "
+                                "index = "
+                            << stackParams[i]->index() << ".";
+                    stackParams[i++]->invalidate();
+                    // this means that the param could not be queried.
+                    // signalling C2_BAD_INDEX to the client.
+                    status = APEXCODEC_STATUS_BAD_INDEX;
+                    continue;
+                }
+                if (stackParams[i++]->updateFrom(*paramPointer)) {
+                    ++numQueried;
+                } else {
+                    LOG(WARNING) << "query -- param update failed: "
+                                    "index = "
+                                << paramPointer->index() << ".";
+                }
+            } else {
+                if (!paramPointer) {
+                    LOG(DEBUG) << "query -- null heap param.";
+                    ++it;
+                    continue;
+                }
+                if (!heapParams) {
+                    LOG(WARNING) << "query -- "
+                                    "unexpected extra stack param.";
+                } else {
+                    heapParams->emplace_back(C2Param::Copy(*paramPointer));
+                    ++numQueried;
+                }
+            }
+            ++it;
+        }
+        if (status == APEXCODEC_STATUS_OK && indices.size() != numQueried) {
+            status = APEXCODEC_STATUS_BAD_INDEX;
+        }
+        return (c2_status_t)status;
+    } else {
+        return C2_OMITTED;
+    }
+}
+
+namespace {
+struct ParamOrField : public C2ParamField {
+    explicit ParamOrField(const ApexCodec_ParamFieldValues& field)
+            : C2ParamField(field.index, field.offset, field.size) {}
+};
+
+static bool FromApex(
+        ApexCodec_SupportedValues *apexValues,
+        C2FieldSupportedValues* c2Values) {
+    if (__builtin_available(android 36, *)) {
+        if (apexValues == nullptr) {
+            c2Values->type = C2FieldSupportedValues::EMPTY;
+            return true;
+        }
+        ApexCodec_SupportedValuesType type = APEXCODEC_SUPPORTED_VALUES_EMPTY;
+        ApexCodec_SupportedValuesNumberType numberType = APEXCODEC_SUPPORTED_VALUES_TYPE_NONE;
+        ApexCodec_Value* values = nullptr;
+        uint32_t numValues = 0;
+        ApexCodec_SupportedValues_getTypeAndValues(
+                apexValues, &type, &numberType, &values, &numValues);
+        c2Values->type = (C2FieldSupportedValues::type_t)type;
+        std::function<C2Value::Primitive(const ApexCodec_Value &)> getPrimitive;
+        switch (numberType) {
+            case APEXCODEC_SUPPORTED_VALUES_TYPE_NONE:
+                getPrimitive = [](const ApexCodec_Value &) -> C2Value::Primitive {
+                    return C2Value::Primitive();
+                };
+                break;
+            case APEXCODEC_SUPPORTED_VALUES_TYPE_INT32:
+                getPrimitive = [](const ApexCodec_Value &value) -> C2Value::Primitive {
+                    return C2Value::Primitive(value.i32);
+                };
+                break;
+            case APEXCODEC_SUPPORTED_VALUES_TYPE_UINT32:
+                getPrimitive = [](const ApexCodec_Value &value) -> C2Value::Primitive {
+                    return C2Value::Primitive(value.u32);
+                };
+                break;
+            case APEXCODEC_SUPPORTED_VALUES_TYPE_INT64:
+                getPrimitive = [](const ApexCodec_Value &value) -> C2Value::Primitive {
+                    return C2Value::Primitive(value.i64);
+                };
+                break;
+            case APEXCODEC_SUPPORTED_VALUES_TYPE_UINT64:
+                getPrimitive = [](const ApexCodec_Value &value) -> C2Value::Primitive {
+                    return C2Value::Primitive(value.u64);
+                };
+                break;
+            case APEXCODEC_SUPPORTED_VALUES_TYPE_FLOAT:
+                getPrimitive = [](const ApexCodec_Value &value) -> C2Value::Primitive {
+                    return C2Value::Primitive(value.f);
+                };
+                break;
+            default:
+                LOG(ERROR) << "Unsupported number type: " << numberType;
+                return false;
+        }
+        switch (type) {
+            case APEXCODEC_SUPPORTED_VALUES_EMPTY:
+                break;
+            case APEXCODEC_SUPPORTED_VALUES_RANGE:
+                c2Values->range.min   = getPrimitive(values[0]);
+                c2Values->range.max   = getPrimitive(values[1]);
+                c2Values->range.step  = getPrimitive(values[2]);
+                c2Values->range.num   = getPrimitive(values[3]);
+                c2Values->range.denom = getPrimitive(values[4]);
+                break;
+            case APEXCODEC_SUPPORTED_VALUES_VALUES:
+            case APEXCODEC_SUPPORTED_VALUES_FLAGS:
+                c2Values->values.clear();
+                for (uint32_t i = 0; i < numValues; ++i) {
+                    c2Values->values.push_back(getPrimitive(values[i]));
+                }
+                break;
+            default:
+                LOG(ERROR) << "Unsupported supported values type: " << type;
+                return false;
+        }
+        return true;
+    } else {
+        return false;
+    }
+}
+
+}  // anonymous namespace
+
+c2_status_t Codec2ConfigurableClient::ApexImpl::config(
+        const std::vector<C2Param*> &params,
+        c2_blocking_t mayBlock,
+        std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
+    (void)mayBlock;
+    if (mBase == nullptr) {
+        return C2_OMITTED;
+    }
+
+    if (__builtin_available(android 36, *)) {
+        std::vector<uint8_t> configBuffer;
+        if (!::android::_createParamsBlob(&configBuffer, params)) {
+            LOG(ERROR) << "config -- bad input.";
+            return C2_TRANSACTION_FAILED;
+        }
+        ApexCodec_SettingResults* result = nullptr;
+        ApexCodec_LinearBuffer config{configBuffer.data(), configBuffer.size()};
+        ApexCodec_Status status = ApexCodec_Configurable_config(
+                mBase, &config, &result);
+        base::ScopeGuard guard([result] {
+            if (result) {
+                ApexCodec_SettingResults_release(result);
+            }
+        });
+        size_t index = 0;
+        ApexCodec_SettingResultFailure failure;
+        ApexCodec_ParamFieldValues field;
+        ApexCodec_ParamFieldValues* conflicts = nullptr;
+        size_t numConflicts = 0;
+        ApexCodec_Status getResultStatus = ApexCodec_SettingResults_getResultAtIndex(
+            result, 0, &failure, &field, &conflicts, &numConflicts);
+        while (getResultStatus == APEXCODEC_STATUS_OK) {
+            std::unique_ptr<C2SettingResult> settingResult;
+            settingResult.reset(new C2SettingResult{
+                C2SettingResult::Failure(failure), C2ParamFieldValues(ParamOrField(field)), {}
+            });
+            // TODO: settingResult->field.values = ?
+            for (size_t i = 0; i < numConflicts; ++i) {
+                settingResult->conflicts.emplace_back(ParamOrField(conflicts[i]));
+                C2ParamFieldValues& conflict = settingResult->conflicts.back();
+                conflict.values = std::make_unique<C2FieldSupportedValues>();
+                FromApex(conflicts[i].values, conflict.values.get());
+            }
+            failures->push_back(std::move(settingResult));
+            getResultStatus = ApexCodec_SettingResults_getResultAtIndex(
+                    result, ++index, &failure, &field, &conflicts, &numConflicts);
+        }
+        if (!::android::updateParamsFromBlob(params, configBuffer)) {
+            LOG(ERROR) << "config -- "
+                    << "failed to parse returned params.";
+            status = APEXCODEC_STATUS_CORRUPTED;
+        }
+        return (c2_status_t)status;
+    } else {
+        return C2_OMITTED;
+    }
+}
+
+c2_status_t Codec2ConfigurableClient::ApexImpl::querySupportedParams(
+        std::vector<std::shared_ptr<C2ParamDescriptor>>* const params) const {
+    if (mBase == nullptr) {
+        return C2_OMITTED;
+    }
+
+    if (__builtin_available(android 36, *)) {
+        // TODO: Cache and query properly!
+        ApexCodec_ParamDescriptors* paramDescs = nullptr;
+        ApexCodec_Configurable_querySupportedParams(mBase, &paramDescs);
+        base::ScopeGuard guard([paramDescs] {
+            if (paramDescs) {
+                ApexCodec_ParamDescriptors_release(paramDescs);
+            }
+        });
+        uint32_t *indices = nullptr;
+        size_t numIndices = 0;
+        ApexCodec_Status status = ApexCodec_ParamDescriptors_getIndices(
+                paramDescs, &indices, &numIndices);
+        if (status != APEXCODEC_STATUS_OK) {
+            return (c2_status_t)status;
+        }
+        if (numIndices > 0) {
+            for (int i = 0; i < numIndices; ++i) {
+                uint32_t index = indices[i];
+                ApexCodec_ParamAttribute attr = (ApexCodec_ParamAttribute)0;
+                const char* name = nullptr;
+                uint32_t* dependencies = nullptr;
+                size_t numDependencies = 0;
+                ApexCodec_Status status = ApexCodec_ParamDescriptors_getDescriptor(
+                        paramDescs, index, &attr, &name, &dependencies, &numDependencies);
+                if (status != APEXCODEC_STATUS_OK) {
+                    LOG(WARNING) << "querySupportedParams -- "
+                                << "failed to get descriptor for index "
+                                << std::hex << index << std::dec << " with status " << status;
+                    continue;
+                }
+                params->push_back(std::make_shared<C2ParamDescriptor>(
+                        C2Param::Index(index), C2ParamDescriptor::attrib_t(attr), name,
+                        std::vector<C2Param::Index>(dependencies, dependencies + numDependencies)));
+            }
+        }
+        return (c2_status_t)status;
+    } else {
+        return C2_OMITTED;
+    }
+}
+
+c2_status_t Codec2ConfigurableClient::ApexImpl::querySupportedValues(
+        std::vector<C2FieldSupportedValuesQuery>& fields,
+        [[maybe_unused]] c2_blocking_t mayBlock) const {
+    if (mBase == nullptr) {
+        return C2_OMITTED;
+    }
+
+    if (__builtin_available(android 36, *)) {
+        std::vector<ApexCodec_SupportedValuesQuery> queries(fields.size());
+        for (size_t i = 0; i < fields.size(); ++i) {
+            queries[i].index  = _C2ParamInspector::GetIndex(fields[i].field());
+            queries[i].offset = _C2ParamInspector::GetOffset(fields[i].field());
+            queries[i].type   = (ApexCodec_SupportedValuesQueryType)fields[i].type();
+            queries[i].status = APEXCODEC_STATUS_OK;
+            queries[i].values = nullptr;
+        }
+        ApexCodec_Status status = ApexCodec_Configurable_querySupportedValues(
+                mBase, queries.data(), queries.size());
+        for (size_t i = 0; i < fields.size(); ++i) {
+            fields[i].status = (c2_status_t)queries[i].status;
+            FromApex(queries[i].values, &fields[i].values);
+            if (queries[i].values) {
+                ApexCodec_SupportedValues_release(queries[i].values);
+                queries[i].values = nullptr;
+            }
+        }
+        return (c2_status_t)status;
+    } else {
+        return C2_OMITTED;
+    }
+}
+
 // Codec2ConfigurableClient
 
 Codec2ConfigurableClient::Codec2ConfigurableClient(const sp<HidlBase> &hidlBase)
@@ -808,6 +1194,11 @@
     : mImpl(new Codec2ConfigurableClient::AidlImpl(aidlBase)) {
 }
 
+Codec2ConfigurableClient::Codec2ConfigurableClient(
+        ApexCodec_Configurable *apexBase, const C2String &name)
+    : mImpl(new Codec2ConfigurableClient::ApexImpl(apexBase, name)) {
+}
+
 const C2String& Codec2ConfigurableClient::getName() const {
     return mImpl->getName();
 }
@@ -1033,6 +1424,393 @@
 
 };
 
+// Codec2Client::Component::ApexHandler
+class Codec2Client::Component::ApexHandler {
+public:
+    ApexHandler(ApexCodec_Component *apexComponent,
+                const std::shared_ptr<Listener> &listener,
+                const std::shared_ptr<Component> &comp)
+          : mApexComponent(apexComponent),
+            mListener(listener),
+            mComponent(comp),
+            mStopped(false),
+            mOutputBufferType(APEXCODEC_BUFFER_TYPE_INVALID) {
+    }
+
+    void start() {
+        std::shared_ptr<Component> comp = mComponent.lock();
+        if (!comp) {
+            LOG(ERROR) << "ApexHandler::start -- component died.";
+            return;
+        }
+        C2ComponentDomainSetting domain;
+        C2ComponentKindSetting kind;
+        c2_status_t status = comp->query({&domain, &kind}, {}, C2_MAY_BLOCK, {});
+        if (status != C2_OK) {
+            LOG(ERROR) << "ApexHandler::start -- failed to query component domain and kind";
+            return;
+        }
+        if (kind.value != C2Component::KIND_DECODER
+                && kind.value != C2Component::KIND_ENCODER) {
+            LOG(ERROR) << "ApexHandler::start -- unrecognized component kind " << kind.value;
+            return;
+        }
+        ApexCodec_BufferType outputBufferType = APEXCODEC_BUFFER_TYPE_INVALID;
+        if (domain.value == C2Component::DOMAIN_AUDIO) {
+            // For both encoders and decoders the output buffer type is linear.
+            outputBufferType = APEXCODEC_BUFFER_TYPE_LINEAR;
+        } else if (domain.value == C2Component::DOMAIN_VIDEO
+                    || domain.value == C2Component::DOMAIN_IMAGE) {
+            // For video / image domain the decoder outputs a graphic buffer, and the encoder
+            // outputs a linear buffer.
+            outputBufferType = (kind.value == C2Component::KIND_DECODER)
+                    ? APEXCODEC_BUFFER_TYPE_GRAPHIC : APEXCODEC_BUFFER_TYPE_LINEAR;
+        } else {
+            LOG(ERROR) << "ApexHandler::start -- unrecognized component domain " << domain.value;
+            return;
+        }
+        {
+            std::unique_lock<std::mutex> l(mMutex);
+            mStopped = false;
+            mOutputBufferType = outputBufferType;
+        }
+        mThread = std::thread([this]() {
+            run();
+        });
+    }
+
+    void queue(std::list<std::unique_ptr<C2Work>>& workItems) {
+        std::unique_lock<std::mutex> l(mMutex);
+        mWorkQueue.splice(mWorkQueue.end(), workItems);
+        mCondition.notify_all();
+    }
+
+    void stop() {
+        std::unique_lock<std::mutex> l(mMutex);
+        mStopped = true;
+        mCondition.notify_all();
+        l.unlock();
+        mThread.join();
+    }
+
+private:
+    void run() {
+        while (true) {
+            std::unique_lock<std::mutex> l(mMutex);
+            mCondition.wait(l, [this]() {
+                return !mWorkQueue.empty() || mStopped;
+            });
+            if (mStopped) {
+                break;
+            }
+            if (mWorkQueue.empty()) {
+                continue;
+            }
+            std::list<std::unique_ptr<C2Work>> workItems;
+            mWorkQueue.swap(workItems);
+            for (std::unique_ptr<C2Work>& workItem : workItems) {
+                if (mStopped) {
+                    break;
+                }
+                l.unlock();
+                handleWork(std::move(workItem));
+                l.lock();
+            }
+        }
+        mWorkQueue.clear();
+        mWorkMap.clear();
+    }
+
+    void handleWork(std::unique_ptr<C2Work> &&workItem) {
+        if (__builtin_available(android 36, *)) {
+            std::shared_ptr<Listener> listener = mListener.lock();
+            if (!listener) {
+                LOG(DEBUG) << "handleWork -- listener died.";
+                return;
+            }
+            ApexCodec_Buffer input;
+            input.flags = (ApexCodec_BufferFlags)workItem->input.flags;
+            input.frameIndex = workItem->input.ordinal.frameIndex.peekll();
+            input.timestampUs = workItem->input.ordinal.timestamp.peekll();
+
+            if (workItem->input.buffers.size() > 1) {
+                LOG(ERROR) << "handleWork -- input buffer size is "
+                           << workItem->input.buffers.size();
+                return;
+            }
+            std::shared_ptr<C2Buffer> buffer;
+            std::optional<C2ReadView> linearView;
+            if (!workItem->input.buffers.empty()) {
+                buffer = workItem->input.buffers[0];
+            }
+            if (!FillMemory(buffer, &input, &linearView)) {
+                LOG(ERROR) << "handleWork -- failed to map input";
+                return;
+            }
+
+            std::vector<uint8_t> configUpdatesVector;
+            if (!_createParamsBlob(&configUpdatesVector, workItem->input.configUpdate)) {
+                listener->onError(mComponent, C2_CORRUPTED);
+                return;
+            }
+            input.configUpdates.data = configUpdatesVector.data();
+            input.configUpdates.size = configUpdatesVector.size();
+            mWorkMap.insert_or_assign(
+                    workItem->input.ordinal.frameIndex.peekll(), std::move(workItem));
+
+            std::list<std::unique_ptr<C2Work>> workItems;
+            bool inputDrained = false;
+            while (!inputDrained) {
+                ApexCodec_Buffer output;
+                std::shared_ptr<C2LinearBlock> linearBlock;
+                std::optional<C2WriteView> linearView;
+                std::shared_ptr<C2GraphicBlock> graphicBlock;
+                allocOutputBuffer(&output, &linearBlock, &linearView, &graphicBlock);
+                size_t consumed = 0;
+                size_t produced = 0;
+                ApexCodec_Status status = ApexCodec_Component_process(
+                        mApexComponent, &input, &output, &consumed, &produced);
+                if (status == APEXCODEC_STATUS_NO_MEMORY) {
+                    continue;
+                }
+                if (produced > 0) {
+                    auto it = mWorkMap.find(output.frameIndex);
+                    std::unique_ptr<C2Work> outputWorkItem;
+                    if (it != mWorkMap.end()) {
+                        if (output.flags & APEXCODEC_FLAG_INCOMPLETE) {
+                            outputWorkItem = std::make_unique<C2Work>();
+                            outputWorkItem->input.ordinal = it->second->input.ordinal;
+                            outputWorkItem->input.flags = it->second->input.flags;
+                        } else {
+                            outputWorkItem = std::move(it->second);
+                            mWorkMap.erase(it);
+                        }
+                    } else {
+                        LOG(WARNING) << "handleWork -- no work item found for output frame index "
+                                    << output.frameIndex;
+                        outputWorkItem = std::make_unique<C2Work>();
+                        outputWorkItem->input.ordinal.frameIndex = output.frameIndex;
+                        outputWorkItem->input.ordinal.timestamp = output.timestampUs;
+                    }
+                    outputWorkItem->worklets.emplace_back(new C2Worklet);
+                    const std::unique_ptr<C2Worklet> &worklet = outputWorkItem->worklets.front();
+                    if (worklet == nullptr) {
+                        LOG(ERROR) << "handleWork -- output work item has null worklet";
+                        return;
+                    }
+                    worklet->output.ordinal.frameIndex = output.frameIndex;
+                    worklet->output.ordinal.timestamp = output.timestampUs;
+                    // non-owning hidl_vec<> to wrap around the output config updates
+                    hidl_vec<uint8_t> outputConfigUpdates;
+                    outputConfigUpdates.setToExternal(
+                            output.configUpdates.data, output.configUpdates.size);
+                    std::vector<C2Param*> outputConfigUpdatePtrs;
+                    parseParamsBlob(&outputConfigUpdatePtrs, outputConfigUpdates);
+                    worklet->output.configUpdate.clear();
+                    std::ranges::transform(
+                            outputConfigUpdatePtrs,
+                            std::back_inserter(worklet->output.configUpdate),
+                            [](C2Param* param) { return C2Param::Copy(*param); });
+                    worklet->output.flags = (C2FrameData::flags_t)output.flags;
+
+                    workItems.push_back(std::move(outputWorkItem));
+                }
+
+                // determine whether the input buffer is drained
+                if (input.type == APEXCODEC_BUFFER_TYPE_LINEAR) {
+                    if (input.memory.linear.size < consumed) {
+                        LOG(WARNING) << "handleWork -- component consumed more bytes "
+                                    << "than the input buffer size";
+                        inputDrained = true;
+                    } else {
+                        input.memory.linear.data += consumed;
+                        input.memory.linear.size -= consumed;
+                    }
+                } else if (input.type == APEXCODEC_BUFFER_TYPE_GRAPHIC) {
+                    inputDrained = (consumed > 0);
+                }
+            }
+
+            if (!workItems.empty()) {
+                listener->onWorkDone(mComponent, workItems);
+            }
+        }
+    }
+
+    bool ensureBlockPool() {
+        std::shared_ptr<Component> comp = mComponent.lock();
+        if (!comp) {
+            return false;
+        }
+        std::vector<std::unique_ptr<C2Param>> heapParams;
+        comp->query({}, {C2PortBlockPoolsTuning::output::PARAM_TYPE}, C2_MAY_BLOCK, &heapParams);
+        if (heapParams.size() != 1) {
+            return false;
+        }
+        const C2Param* param = heapParams[0].get();
+        if (param->type() != C2PortBlockPoolsTuning::output::PARAM_TYPE) {
+            return false;
+        }
+        const C2PortBlockPoolsTuning::output *blockPools =
+                static_cast<const C2PortBlockPoolsTuning::output *>(param);
+        if (blockPools->flexCount() == 0) {
+            return false;
+        }
+        C2BlockPool::local_id_t blockPoolId = blockPools->m.values[0];
+        if (mBlockPool && mBlockPool->getLocalId() == blockPoolId) {
+            // no need to update
+            return true;
+        }
+        return C2_OK == GetCodec2BlockPool(blockPoolId, nullptr, &mBlockPool);
+    }
+
+    void allocOutputBuffer(
+            ApexCodec_Buffer* output,
+            std::shared_ptr<C2LinearBlock> *linearBlock,
+            std::optional<C2WriteView> *linearView,
+            std::shared_ptr<C2GraphicBlock> *graphicBlock) {
+        if (mOutputBufferType == APEXCODEC_BUFFER_TYPE_LINEAR) {
+            if (!ensureBlockPool()) {
+                return;
+            }
+            {
+                std::shared_ptr<Component> comp = mComponent.lock();
+                if (!comp) {
+                    return;
+                }
+                C2StreamMaxBufferSizeInfo::output maxBufferSize(0u /* stream */);
+                comp->query({&maxBufferSize}, {}, C2_MAY_BLOCK, {});
+                mLinearBlockCapacity = maxBufferSize ? maxBufferSize.value : 1024 * 1024;
+            }
+            output->type = APEXCODEC_BUFFER_TYPE_LINEAR;
+            c2_status_t status = mBlockPool->fetchLinearBlock(
+                    mLinearBlockCapacity,
+                    C2MemoryUsage(C2MemoryUsage::CPU_READ | C2MemoryUsage::CPU_WRITE),
+                    linearBlock);
+            if (!(*linearBlock)) {
+                return;
+            }
+            linearView->emplace((*linearBlock)->map().get());
+            if ((*linearView)->error() != C2_OK) {
+                return;
+            }
+            output->memory.linear.data = (*linearView)->data();
+            output->memory.linear.size = (*linearView)->capacity();
+        } else if (mOutputBufferType == APEXCODEC_BUFFER_TYPE_GRAPHIC) {
+            if (!ensureBlockPool()) {
+                return;
+            }
+            {
+                std::shared_ptr<Component> comp = mComponent.lock();
+                if (!comp) {
+                    return;
+                }
+                C2StreamMaxPictureSizeTuning::output maxPictureSize(0u /* stream */);
+                C2StreamPictureSizeInfo::output pictureSize(0u /* stream */);
+                C2StreamPixelFormatInfo::output pixelFormat(0u /* stream */);
+                comp->query({&maxPictureSize, &pictureSize, &pixelFormat}, {}, C2_MAY_BLOCK, {});
+                mWidth = maxPictureSize ? maxPictureSize.width : pictureSize.width;
+                mHeight = maxPictureSize ? maxPictureSize.height : pictureSize.height;
+                mFormat = pixelFormat ? pixelFormat.value : HAL_PIXEL_FORMAT_YCBCR_420_888;
+            }
+            output->type = APEXCODEC_BUFFER_TYPE_GRAPHIC;
+            c2_status_t status = mBlockPool->fetchGraphicBlock(
+                    mWidth, mHeight, mFormat,
+                    C2MemoryUsage(C2MemoryUsage::CPU_READ | C2MemoryUsage::CPU_WRITE),
+                    graphicBlock);
+            if (!(*graphicBlock)) {
+                return;
+            }
+            const C2Handle *handle = (*graphicBlock)->handle();
+            uint32_t width, height, format, stride, igbp_slot, generation;
+            uint64_t usage, igbp_id;
+            _UnwrapNativeCodec2GrallocMetadata(
+                    handle, &width, &height, &format, &usage, &stride, &generation,
+                    &igbp_id, &igbp_slot);
+            native_handle_t *grallocHandle = UnwrapNativeCodec2GrallocHandle(handle);
+            sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
+                    grallocHandle, GraphicBuffer::CLONE_HANDLE,
+                    width, height, format, 1, usage, stride);
+            native_handle_delete(grallocHandle);
+            AHardwareBuffer *hardwareBuffer =
+                AHardwareBuffer_from_GraphicBuffer(graphicBuffer.get());
+            AHardwareBuffer_acquire(hardwareBuffer);
+            output->memory.graphic = hardwareBuffer;
+        } else {
+            LOG(ERROR) << "allocOutputBuffer -- unsupported output buffer type: "
+                       << mOutputBufferType;
+            return;
+        }
+    }
+
+    static bool FillMemory(
+            const std::shared_ptr<C2Buffer>& buffer,
+            ApexCodec_Buffer* apexBuffer,
+            std::optional<C2ReadView>* linearView) {
+        if (buffer->data().type() == C2BufferData::LINEAR) {
+            apexBuffer->type = APEXCODEC_BUFFER_TYPE_LINEAR;
+            if (buffer->data().linearBlocks().empty()) {
+                apexBuffer->memory.linear.data = nullptr;
+                apexBuffer->memory.linear.size = 0;
+                return true;
+            } else if (buffer->data().linearBlocks().size() > 1) {
+                return false;
+            }
+            linearView->emplace(buffer->data().linearBlocks().front().map().get());
+            if ((*linearView)->error() != C2_OK) {
+                return false;
+            }
+            apexBuffer->memory.linear.data = const_cast<uint8_t*>((*linearView)->data());
+            apexBuffer->memory.linear.size = (*linearView)->capacity();
+            return true;
+        } else if (buffer->data().type() == C2BufferData::GRAPHIC) {
+            apexBuffer->type = APEXCODEC_BUFFER_TYPE_GRAPHIC;
+            if (buffer->data().graphicBlocks().empty()) {
+                apexBuffer->memory.graphic = nullptr;
+                return true;
+            } else if (buffer->data().graphicBlocks().size() > 1) {
+                return false;
+            }
+            const C2Handle *handle = buffer->data().graphicBlocks().front().handle();
+            uint32_t width, height, format, stride, igbp_slot, generation;
+            uint64_t usage, igbp_id;
+            _UnwrapNativeCodec2GrallocMetadata(
+                    handle, &width, &height, &format, &usage, &stride, &generation,
+                    &igbp_id, &igbp_slot);
+            native_handle_t *grallocHandle = UnwrapNativeCodec2GrallocHandle(handle);
+            sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
+                    grallocHandle, GraphicBuffer::CLONE_HANDLE,
+                    width, height, format, 1, usage, stride);
+            native_handle_delete(grallocHandle);
+            AHardwareBuffer *hardwareBuffer =
+                AHardwareBuffer_from_GraphicBuffer(graphicBuffer.get());
+            AHardwareBuffer_acquire(hardwareBuffer);
+            apexBuffer->memory.graphic = hardwareBuffer;
+            return true;
+        }
+        return false;
+    }
+
+    ApexCodec_Component *mApexComponent;
+    std::weak_ptr<Listener> mListener;
+    std::weak_ptr<Component> mComponent;
+
+    std::thread mThread;
+    std::mutex mMutex;
+    std::condition_variable mCondition;
+    bool mStopped;
+    ApexCodec_BufferType mOutputBufferType;
+
+    size_t mLinearBlockCapacity;
+    uint32_t mWidth;
+    uint32_t mHeight;
+    uint32_t mFormat;
+
+    std::shared_ptr<C2BlockPool> mBlockPool;
+    std::list<std::unique_ptr<C2Work>> mWorkQueue;
+    std::map<uint64_t, std::unique_ptr<C2Work>> mWorkMap;
+};
+
 // Codec2Client::Component::HidlBufferPoolSender
 struct Codec2Client::Component::HidlBufferPoolSender :
         hardware::media::c2::V1_1::utils::DefaultBufferPoolSender {
@@ -1166,6 +1944,13 @@
     }
 }
 
+Codec2Client::Codec2Client(ApexCodec_ComponentStore *base,
+                           size_t serviceIndex)
+      : Configurable{nullptr, "android.componentStore.apexCodecs"},
+        mApexBase{base},
+        mServiceIndex{serviceIndex} {
+}
+
 sp<Codec2Client::HidlBase> const& Codec2Client::getHidlBase() const {
     return mHidlBase1_0;
 }
@@ -1194,36 +1979,71 @@
         const C2String& name,
         const std::shared_ptr<Codec2Client::Listener>& listener,
         std::shared_ptr<Codec2Client::Component>* const component) {
-    if (mAidlBase) {
-        std::shared_ptr<Component::AidlListener> aidlListener =
-                Component::AidlListener::make<Component::AidlListener>();
-        aidlListener->base = listener;
-        std::shared_ptr<c2_aidl::IComponent> aidlComponent;
-        ::ndk::ScopedAStatus transStatus = mAidlBase->createComponent(
-                name,
-                aidlListener,
-                bufferpool2_aidl::implementation::ClientManager::getInstance(),
-                &aidlComponent);
-        c2_status_t status = GetC2Status(transStatus, "createComponent");
-        if (status != C2_OK) {
-            return status;
-        } else if (!aidlComponent) {
-            LOG(ERROR) << "createComponent(" << name.c_str()
-                       << ") -- null component.";
-            return C2_CORRUPTED;
-        }
-        *component = std::make_shared<Codec2Client::Component>(aidlComponent);
-        status = (*component)->setDeathListener((*component), listener);
-        if (status != C2_OK) {
-            LOG(ERROR) << "createComponent(" << name.c_str()
-                       << ") -- failed to set up death listener: "
-                       << status << ".";
-        }
-        (*component)->mAidlBufferPoolSender->setReceiver(mAidlHostPoolManager);
-        aidlListener->component = *component;
-        return status;
+    if (mApexBase) {
+        return createComponent_apex(name, listener, component);
+    } else if (mAidlBase) {
+        return createComponent_aidl(name, listener, component);
+    } else {
+        return createComponent_hidl(name, listener, component);
     }
+}
 
+c2_status_t Codec2Client::createComponent_apex(
+        const C2String& name,
+        const std::shared_ptr<Codec2Client::Listener>& listener,
+        std::shared_ptr<Codec2Client::Component>* const component) {
+    if (__builtin_available(android 36, *)) {
+        ApexCodec_Component *apexComponent = nullptr;
+        ApexCodec_Status status = ApexCodec_Component_create(
+                mApexBase, name.c_str(), &apexComponent);
+        if (status != APEXCODEC_STATUS_OK) {
+            return (c2_status_t)status;
+        }
+        *component = std::make_shared<Codec2Client::Component>(apexComponent, name);
+        (*component)->initApexHandler(listener, *component);
+        return C2_OK;
+    } else {
+        return C2_OMITTED;
+    }
+}
+
+c2_status_t Codec2Client::createComponent_aidl(
+        const C2String& name,
+        const std::shared_ptr<Codec2Client::Listener>& listener,
+        std::shared_ptr<Codec2Client::Component>* const component) {
+    std::shared_ptr<Component::AidlListener> aidlListener =
+            Component::AidlListener::make<Component::AidlListener>();
+    aidlListener->base = listener;
+    std::shared_ptr<c2_aidl::IComponent> aidlComponent;
+    ::ndk::ScopedAStatus transStatus = mAidlBase->createComponent(
+            name,
+            aidlListener,
+            bufferpool2_aidl::implementation::ClientManager::getInstance(),
+            &aidlComponent);
+    c2_status_t status = GetC2Status(transStatus, "createComponent");
+    if (status != C2_OK) {
+        return status;
+    } else if (!aidlComponent) {
+        LOG(ERROR) << "createComponent(" << name.c_str()
+                    << ") -- null component.";
+        return C2_CORRUPTED;
+    }
+    *component = std::make_shared<Codec2Client::Component>(aidlComponent);
+    status = (*component)->setDeathListener((*component), listener);
+    if (status != C2_OK) {
+        LOG(ERROR) << "createComponent(" << name.c_str()
+                    << ") -- failed to set up death listener: "
+                    << status << ".";
+    }
+    (*component)->mAidlBufferPoolSender->setReceiver(mAidlHostPoolManager);
+    aidlListener->component = *component;
+    return status;
+}
+
+c2_status_t Codec2Client::createComponent_hidl(
+        const C2String& name,
+        const std::shared_ptr<Codec2Client::Listener>& listener,
+        std::shared_ptr<Codec2Client::Component>* const component) {
     c2_status_t status;
     sp<Component::HidlListener> hidlListener = new Component::HidlListener{};
     hidlListener->base = listener;
@@ -1591,6 +2411,13 @@
             return a < b;
         });
 
+    if (__builtin_available(android 36, *)) {
+        if (android::media::codec::provider_->in_process_sw_audio_codec_support()
+                && nullptr != ApexCodec_GetComponentStore()) {
+            names.push_back("__ApexCodecs__");
+        }
+    }
+
     // Summarize to logcat.
     if (names.empty()) {
         LOG(INFO) << "No Codec2 services declared in the manifest.";
@@ -1647,7 +2474,13 @@
     std::string const& name = GetServiceNames()[index];
     LOG(VERBOSE) << "Creating a Codec2 client to service \"" << name << "\"";
 
-    if (c2_aidl::utils::IsSelected()) {
+    if (name == "__ApexCodecs__") {
+        if (__builtin_available(android 36, *)) {
+            return std::make_shared<Codec2Client>(ApexCodec_GetComponentStore(), index);
+        } else {
+            LOG(FATAL) << "ApexCodecs not supported on Android version older than 36";
+        }
+    } else if (c2_aidl::utils::IsSelected()) {
         if (__builtin_available(android __ANDROID_API_S__, *)) {
             std::string instanceName =
                 ::android::base::StringPrintf("%s/%s", AidlBase::descriptor, name.c_str());
@@ -1832,9 +2665,7 @@
 
 std::shared_ptr<Codec2Client::InputSurface> Codec2Client::CreateInputSurface(
         char const* serviceName) {
-    int32_t inputSurfaceSetting = ::android::base::GetIntProperty(
-            "debug.stagefright.c2inputsurface", int32_t(0));
-    if (inputSurfaceSetting <= 0) {
+    if (!IsCodec2AidlInputSurfaceSelected()) {
         return nullptr;
     }
     size_t index = GetServiceNames().size();
@@ -2054,16 +2885,41 @@
         mGraphicBufferAllocators{std::make_unique<GraphicBufferAllocators>()} {
 }
 
+Codec2Client::Component::Component(ApexCodec_Component *base, const C2String &name)
+      : Configurable{[base]() -> ApexCodec_Configurable * {
+            if (__builtin_available(android 36, *)) {
+                return ApexCodec_Component_getConfigurable(base);
+            } else {
+                return nullptr;
+            }
+        }(), name},
+        mApexBase{base} {
+}
+
 Codec2Client::Component::~Component() {
     if (mAidlDeathSeq) {
         GetAidlDeathManager()->unlinkToDeath(*mAidlDeathSeq, mAidlBase);
     }
+    if (mApexBase) {
+        if (__builtin_available(android 36, *)) {
+            ApexCodec_Component_destroy(mApexBase);
+        }
+        mApexBase = nullptr;
+    }
 }
 
 c2_status_t Codec2Client::Component::createBlockPool(
         C2Allocator::id_t id,
         C2BlockPool::local_id_t* blockPoolId,
         std::shared_ptr<Codec2Client::Configurable>* configurable) {
+    if (mApexBase) {
+        std::shared_ptr<C2BlockPool> blockPool;
+        CreateCodec2BlockPool(id, nullptr, &blockPool);
+        *blockPoolId = blockPool->getLocalId();
+        *configurable = nullptr;
+        mBlockPools[*blockPoolId] = blockPool;
+        return C2_OK;
+    }
     if (mAidlBase) {
         c2_aidl::IComponent::BlockPool aidlBlockPool;
         c2_status_t status = C2_OK;
@@ -2134,6 +2990,10 @@
 
 c2_status_t Codec2Client::Component::destroyBlockPool(
         C2BlockPool::local_id_t localId) {
+    if (mApexBase) {
+        mBlockPools.erase(localId);
+        return C2_OK;
+    }
     if (mAidlBase) {
         mGraphicBufferAllocators->remove(localId);
         ::ndk::ScopedAStatus transStatus = mAidlBase->destroyBlockPool(localId);
@@ -2150,7 +3010,10 @@
 
 void Codec2Client::Component::handleOnWorkDone(
         const std::list<std::unique_ptr<C2Work>> &workItems) {
-    if (mAidlBase) {
+    if (mApexBase) {
+        // no-op
+        return;
+    } else if (mAidlBase) {
         holdIgbaBlocks(workItems);
     } else {
         // Output bufferqueue-based blocks' lifetime management
@@ -2160,6 +3023,10 @@
 
 c2_status_t Codec2Client::Component::queue(
         std::list<std::unique_ptr<C2Work>>* const items) {
+    if (mApexBase) {
+        mApexHandler->queue(*items);
+        return C2_OK;
+    }
     if (mAidlBase) {
         c2_aidl::WorkBundle workBundle;
         if (!c2_aidl::utils::ToAidl(&workBundle, *items, mAidlBufferPoolSender.get())) {
@@ -2191,6 +3058,13 @@
         C2Component::flush_mode_t mode,
         std::list<std::unique_ptr<C2Work>>* const flushedWork) {
     (void)mode; // Flush mode isn't supported in HIDL/AIDL yet.
+    if (mApexBase) {
+        if (__builtin_available(android 36, *)) {
+            return (c2_status_t)ApexCodec_Component_flush(mApexBase);
+        } else {
+            return C2_OMITTED;
+        }
+    }
     c2_status_t status = C2_OK;
     if (mAidlBase) {
         c2_aidl::WorkBundle workBundle;
@@ -2250,6 +3124,9 @@
 }
 
 c2_status_t Codec2Client::Component::drain(C2Component::drain_mode_t mode) {
+    if (mApexBase) {
+        return C2_OMITTED;
+    }
     if (mAidlBase) {
         ::ndk::ScopedAStatus transStatus = mAidlBase->drain(
                 mode == C2Component::DRAIN_COMPONENT_WITH_EOS);
@@ -2270,6 +3147,10 @@
 }
 
 c2_status_t Codec2Client::Component::start() {
+    if (mApexBase) {
+        // no-op
+        return C2_OK;
+    }
     if (mAidlBase) {
         ::ndk::ScopedAStatus transStatus = mAidlBase->start();
         return GetC2Status(transStatus, "start");
@@ -2289,6 +3170,11 @@
 
 c2_status_t Codec2Client::Component::stop() {
     if (mAidlBase) {
+        std::shared_ptr<AidlGraphicBufferAllocator> gba =
+                mGraphicBufferAllocators->current();
+        if (gba) {
+            gba->onRequestStop();
+        }
         ::ndk::ScopedAStatus transStatus = mAidlBase->stop();
         return GetC2Status(transStatus, "stop");
     }
@@ -2306,6 +3192,13 @@
 }
 
 c2_status_t Codec2Client::Component::reset() {
+    if (mApexBase) {
+        if (__builtin_available(android 36, *)) {
+            return (c2_status_t)ApexCodec_Component_reset(mApexBase);
+        } else {
+            return C2_OMITTED;
+        }
+    }
     if (mAidlBase) {
         ::ndk::ScopedAStatus transStatus = mAidlBase->reset();
         return GetC2Status(transStatus, "reset");
@@ -2324,7 +3217,19 @@
 }
 
 c2_status_t Codec2Client::Component::release() {
+    if (mApexBase) {
+        if (__builtin_available(android 36, *)) {
+            return (c2_status_t)ApexCodec_Component_reset(mApexBase);
+        } else {
+            return C2_OMITTED;
+        }
+    }
     if (mAidlBase) {
+        std::shared_ptr<AidlGraphicBufferAllocator> gba =
+                mGraphicBufferAllocators->current();
+        if (gba) {
+            gba->onRequestStop();
+        }
         ::ndk::ScopedAStatus transStatus = mAidlBase->release();
         return GetC2Status(transStatus, "release");
     }
@@ -2345,6 +3250,10 @@
         uint32_t avSyncHwId,
         native_handle_t** sidebandHandle) {
     *sidebandHandle = nullptr;
+    if (mApexBase) {
+        // tunneling is not supported in APEX
+        return C2_OMITTED;
+    }
     if (mAidlBase) {
         ::aidl::android::hardware::common::NativeHandle handle;
         ::ndk::ScopedAStatus transStatus = mAidlBase->configureVideoTunnel(avSyncHwId, &handle);
@@ -2508,7 +3417,11 @@
 
 void Codec2Client::Component::pollForRenderedFrames(FrameEventHistoryDelta* delta) {
     if (mAidlBase) {
-        // TODO b/311348680
+        std::shared_ptr<AidlGraphicBufferAllocator> gba =
+                mGraphicBufferAllocators->current();
+        if (gba) {
+            gba->pollForRenderedFrames(delta);
+        }
         return;
     }
     mOutputBufferQueue->pollForRenderedFrames(delta);
@@ -2616,6 +3529,10 @@
 c2_status_t Codec2Client::Component::connectToInputSurface(
         const std::shared_ptr<InputSurface>& inputSurface,
         std::shared_ptr<InputSurfaceConnection>* connection) {
+    if (mApexBase) {
+        // FIXME
+        return C2_OMITTED;
+    }
     if (mAidlBase) {
         // FIXME
         return C2_OMITTED;
@@ -2644,6 +3561,10 @@
         const sp<HGraphicBufferProducer1>& producer,
         const sp<HGraphicBufferSource>& source,
         std::shared_ptr<InputSurfaceConnection>* connection) {
+    if (mApexBase) {
+        LOG(WARNING) << "Connecting to OMX input surface is not supported for AIDL C2 HAL";
+        return C2_OMITTED;
+    }
     if (mAidlBase) {
         LOG(WARNING) << "Connecting to OMX input surface is not supported for AIDL C2 HAL";
         return C2_OMITTED;
@@ -2669,6 +3590,10 @@
 }
 
 c2_status_t Codec2Client::Component::disconnectFromInputSurface() {
+    if (mApexBase) {
+        // FIXME
+        return C2_OMITTED;
+    }
     if (mAidlBase) {
         // FIXME
         return C2_OMITTED;
@@ -2693,6 +3618,16 @@
     return sManager;
 }
 
+c2_status_t Codec2Client::Component::initApexHandler(
+            const std::shared_ptr<Listener> &listener,
+            const std::shared_ptr<Component> &comp) {
+    if (!mApexBase) {
+        return C2_BAD_STATE;
+    }
+    mApexHandler = std::make_unique<ApexHandler>(mApexBase, listener, comp);
+    return C2_OK;
+}
+
 c2_status_t Codec2Client::Component::setDeathListener(
         const std::shared_ptr<Component>& component,
         const std::shared_ptr<Listener>& listener) {
diff --git a/media/codec2/hal/client/include/codec2/aidl/GraphicBufferAllocator.h b/media/codec2/hal/client/include/codec2/aidl/GraphicBufferAllocator.h
index a797cb7..a70ffef 100644
--- a/media/codec2/hal/client/include/codec2/aidl/GraphicBufferAllocator.h
+++ b/media/codec2/hal/client/include/codec2/aidl/GraphicBufferAllocator.h
@@ -85,6 +85,11 @@
     void onBufferAttached(uint32_t generation);
 
     /**
+     * Retrieve frame event history from the crurrent surface if any.
+     */
+    void pollForRenderedFrames(::android::FrameEventHistoryDelta* delta);
+
+    /**
      * Allocates a buffer.
      *
      * @param   width             width of the requested buffer.
@@ -125,6 +130,11 @@
             const ::android::IGraphicBufferProducer::QueueBufferInput& input,
             ::android::IGraphicBufferProducer::QueueBufferOutput *output);
 
+    /**
+     * Notify stop()/release() is in progress.
+     */
+    void onRequestStop();
+
     ~GraphicBufferAllocator();
 
     /**
diff --git a/media/codec2/hal/client/include/codec2/aidl/GraphicsTracker.h b/media/codec2/hal/client/include/codec2/aidl/GraphicsTracker.h
index 9a4fa12..536caaa 100644
--- a/media/codec2/hal/client/include/codec2/aidl/GraphicsTracker.h
+++ b/media/codec2/hal/client/include/codec2/aidl/GraphicsTracker.h
@@ -35,6 +35,7 @@
 
 using ::android::IGraphicBufferProducer;
 using ::android::GraphicBuffer;
+using ::android::FrameEventHistoryDelta;
 using ::android::Fence;
 using ::android::PixelFormat;
 using ::android::sp;
@@ -133,6 +134,11 @@
                        IGraphicBufferProducer::QueueBufferOutput *output);
 
     /**
+     * Retrieve frame event history from the crurrent surface if any.
+     */
+    void pollForRenderedFrames(FrameEventHistoryDelta* delta);
+
+    /**
      * Notifies when a Buffer is ready to allocate from Graphics.
      * If generation does not match to the current, notifications via the interface
      * will be ignored. (In the case, the notifications are from one of the old surfaces
@@ -175,6 +181,14 @@
      */
     void stop();
 
+    /**
+     * stop()/release() request to HAL is in process from the client.
+     * The class will never be active again after the request.
+     * Still, allocation requests from HAL should be served until stop()
+     * is being called.
+     */
+    void onRequestStop();
+
 private:
     struct BufferCache;
 
@@ -290,6 +304,10 @@
 
     std::atomic<bool> mStopped;
 
+    bool mStopRequested;
+    std::atomic<int> mAllocAfterStopRequested;
+
+
 private:
     explicit GraphicsTracker(int maxDequeueCount);
 
@@ -304,7 +322,7 @@
             const std::shared_ptr<BufferCache> &cache,
             int maxDequeueCommitted);
 
-    c2_status_t requestAllocate(std::shared_ptr<BufferCache> *cache);
+    c2_status_t requestAllocateLocked(std::shared_ptr<BufferCache> *cache);
     c2_status_t requestDeallocate(uint64_t bid, const sp<Fence> &fence,
                                   bool *completed, bool *updateDequeue,
                                   std::shared_ptr<BufferCache> *cache, int *slotId,
@@ -334,6 +352,10 @@
             bool *cached, int *rSlotId, sp<Fence> *rFence,
             std::shared_ptr<BufferItem> *buffer);
 
+    c2_status_t _allocateDirect(
+            uint32_t width, uint32_t height, PixelFormat format, uint64_t usage,
+            AHardwareBuffer **buf, sp<Fence> *fence);
+
     void writeIncDequeueableLocked(int inc);
     void drainDequeueableLocked(int dec);
 };
diff --git a/media/codec2/hal/client/include/codec2/hidl/client.h b/media/codec2/hal/client/include/codec2/hidl/client.h
index 7923f04..35c87e0 100644
--- a/media/codec2/hal/client/include/codec2/hidl/client.h
+++ b/media/codec2/hal/client/include/codec2/hidl/client.h
@@ -112,6 +112,10 @@
 struct IGraphicBufferSource;
 }  // namespace android::hardware::media::omx::V1_0
 
+struct ApexCodec_ComponentStore;
+struct ApexCodec_Component;
+struct ApexCodec_Configurable;
+
 namespace android {
 
 // This class is supposed to be called Codec2Client::Configurable, but forward
@@ -148,6 +152,7 @@
 
     explicit Codec2ConfigurableClient(const sp<HidlBase> &hidlBase);
     explicit Codec2ConfigurableClient(const std::shared_ptr<AidlBase> &aidlBase);
+    Codec2ConfigurableClient(ApexCodec_Configurable *base, const C2String &name);
 
     const C2String& getName() const;
 
@@ -172,6 +177,7 @@
 private:
     struct HidlImpl;
     struct AidlImpl;
+    struct ApexImpl;
 
     const std::unique_ptr<ImplBase> mImpl;
 };
@@ -282,12 +288,16 @@
             std::shared_ptr<AidlBase> const& base,
             std::shared_ptr<Codec2ConfigurableClient::AidlBase> const& configurable,
             size_t serviceIndex);
+    Codec2Client(
+            ApexCodec_ComponentStore* base,
+            size_t serviceIndex);
 
 protected:
     sp<HidlBase1_0> mHidlBase1_0;
     sp<HidlBase1_1> mHidlBase1_1;
     sp<HidlBase1_2> mHidlBase1_2;
     std::shared_ptr<AidlBase> mAidlBase;
+    ApexCodec_ComponentStore* mApexBase{nullptr};
 
     // Finds the first store where the predicate returns C2_OK and returns the
     // last predicate result. The predicate will be tried on all stores. The
@@ -325,6 +335,20 @@
     std::vector<C2Component::Traits> _listComponents(bool* success) const;
 
     class Cache;
+
+private:
+    c2_status_t createComponent_aidl(
+            C2String const& name,
+            std::shared_ptr<Listener> const& listener,
+            std::shared_ptr<Component>* const component);
+    c2_status_t createComponent_hidl(
+            C2String const& name,
+            std::shared_ptr<Listener> const& listener,
+            std::shared_ptr<Component>* const component);
+    c2_status_t createComponent_apex(
+            C2String const& name,
+            std::shared_ptr<Listener> const& listener,
+            std::shared_ptr<Component>* const component);
 };
 
 struct Codec2Client::Interface : public Codec2Client::Configurable {
@@ -508,11 +532,16 @@
 
     c2_status_t disconnectFromInputSurface();
 
+    c2_status_t initApexHandler(
+            const std::shared_ptr<Listener> &listener,
+            const std::shared_ptr<Component> &comp);
+
     // base cannot be null.
     Component(const sp<HidlBase>& base);
     Component(const sp<HidlBase1_1>& base);
     Component(const sp<HidlBase1_2>& base);
     Component(const std::shared_ptr<AidlBase>& base);
+    Component(ApexCodec_Component* base, const C2String& name);
 
     ~Component();
 
@@ -521,12 +550,16 @@
     sp<HidlBase1_1> mHidlBase1_1;
     sp<HidlBase1_2> mHidlBase1_2;
     std::shared_ptr<AidlBase> mAidlBase;
+    ApexCodec_Component *mApexBase{nullptr};
 
     struct HidlBufferPoolSender;
     struct AidlBufferPoolSender;
     std::unique_ptr<HidlBufferPoolSender> mHidlBufferPoolSender;
     std::unique_ptr<AidlBufferPoolSender> mAidlBufferPoolSender;
 
+    class ApexHandler;
+    std::unique_ptr<ApexHandler> mApexHandler;
+
     struct OutputBufferQueue;
     std::unique_ptr<OutputBufferQueue> mOutputBufferQueue;
 
@@ -547,6 +580,11 @@
             const std::shared_ptr<Listener>& listener);
     sp<::android::hardware::hidl_death_recipient> mDeathRecipient;
 
+    // This is a map of block pools created for APEX components in the client.
+    // Note that the APEX codec API requires output buffers to be passed from the client,
+    // so the client creates and keeps track of the block pools here.
+    std::map<C2BlockPool::local_id_t, std::shared_ptr<C2BlockPool>> mBlockPools;
+
     friend struct Codec2Client;
 
     struct HidlListener;
diff --git a/media/codec2/hal/common/Android.bp b/media/codec2/hal/common/Android.bp
index 0638363..886391b 100644
--- a/media/codec2/hal/common/Android.bp
+++ b/media/codec2/hal/common/Android.bp
@@ -56,13 +56,17 @@
         "libaconfig_storage_read_api_cc",
     ],
 
-    static_libs: ["aconfig_mediacodec_flags_c_lib"],
+    static_libs: [
+        "aconfig_mediacodec_flags_c_lib",
+        "android.media.codec-aconfig-cc",
+    ],
 }
 
 cc_defaults {
     name: "libcodec2_hal_selection",
     static_libs: [
         "aconfig_mediacodec_flags_c_lib",
+        "android.media.codec-aconfig-cc",
         "libcodec2_hal_selection_static",
     ],
     shared_libs: [
diff --git a/media/codec2/hal/common/HalSelection.cpp b/media/codec2/hal/common/HalSelection.cpp
index d3ea181..5bf4fbe 100644
--- a/media/codec2/hal/common/HalSelection.cpp
+++ b/media/codec2/hal/common/HalSelection.cpp
@@ -21,6 +21,7 @@
 // NOTE: due to dependency from mainline modules cannot use libsysprop
 // #include <android/sysprop/MediaProperties.sysprop.h>
 #include <android-base/properties.h>
+#include <android_media_codec.h>
 #include <com_android_media_codec_flags.h>
 
 #include <codec2/common/HalSelection.h>
@@ -66,4 +67,20 @@
     return false;
 }
 
+bool IsCodec2AidlInputSurfaceSelected() {
+    if (!IsCodec2AidlHalSelected()) {
+        return false;
+    }
+
+    int32_t inputSurfaceSetting = ::android::base::GetIntProperty(
+            "debug.stagefright.c2inputsurface", int32_t(0));
+    if (inputSurfaceSetting <= 0) {
+        return false;
+    }
+    if (!android::media::codec::provider_->aidl_hal_input_surface()) {
+        return false;
+    }
+    return true;
+}
+
 }  // namespace android
diff --git a/media/codec2/hal/common/include/codec2/common/HalSelection.h b/media/codec2/hal/common/include/codec2/common/HalSelection.h
index 7c77515..bf0c7c5 100644
--- a/media/codec2/hal/common/include/codec2/common/HalSelection.h
+++ b/media/codec2/hal/common/include/codec2/common/HalSelection.h
@@ -22,6 +22,9 @@
 // Returns true iff AIDL c2 HAL is selected for the system
 bool IsCodec2AidlHalSelected();
 
+// Returns true iff AIDL c2 InputSurface interface is selected for the system
+bool IsCodec2AidlInputSurfaceSelected();
+
 }  // namespace android
 
 #endif  // CODEC2_HAL_SELECTION_H
diff --git a/media/codec2/sfplugin/Android.bp b/media/codec2/sfplugin/Android.bp
index 4f6fe51..cc5d10c 100644
--- a/media/codec2/sfplugin/Android.bp
+++ b/media/codec2/sfplugin/Android.bp
@@ -16,6 +16,10 @@
 cc_library_shared {
     name: "libsfplugin_ccodec",
 
+    defaults: [
+        "android.hardware.graphics.common-ndk_shared",
+    ],
+
     export_include_dirs: ["include"],
 
     srcs: [
@@ -57,7 +61,6 @@
         "android.hardware.drm@1.0",
         "android.hardware.media.c2@1.0",
         "android.hardware.media.omx@1.0",
-        "android.hardware.graphics.common-V5-ndk",
         "graphicbuffersource-aidl-ndk",
         "libbase",
         "libbinder",
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 632eaed..72b5a61 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -2877,6 +2877,7 @@
 
             // handle configuration changes in work done
             std::shared_ptr<const C2StreamInitDataInfo::output> initData;
+            sp<AMessage> inputFormat = nullptr;
             sp<AMessage> outputFormat = nullptr;
             {
                 Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
@@ -2964,10 +2965,12 @@
                             initData->m.value, initData->flexCount(), config->mCodingMediaType,
                             config->mOutputFormat);
                 }
+                inputFormat = config->mInputFormat;
                 outputFormat = config->mOutputFormat;
             }
             mChannel->onWorkDone(
-                    std::move(work), outputFormat, initData ? initData.get() : nullptr);
+                    std::move(work), inputFormat, outputFormat,
+                    initData ? initData.get() : nullptr);
             // log metrics to MediaCodec
             if (mMetrics->countEntries() == 0) {
                 Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 4353521..d67a876 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -335,7 +335,7 @@
 
 void CCodecBufferChannel::setComponent(
         const std::shared_ptr<Codec2Client::Component> &component) {
-    mComponent = component;
+    std::atomic_store(&mComponent, component);
     mComponentName = component->getName() + StringPrintf("#%d", int(uintptr_t(component.get()) % 997));
     mName = mComponentName.c_str();
 }
@@ -351,7 +351,7 @@
     inputSurface->numProcessingBuffersBalance = 0;
     inputSurface->surface = surface;
     mHasInputSurface = true;
-    return inputSurface->surface->connect(mComponent);
+    return inputSurface->surface->connect(std::atomic_load(&mComponent));
 }
 
 status_t CCodecBufferChannel::signalEndOfInputStream() {
@@ -547,7 +547,7 @@
                         now);
             }
         }
-        err = mComponent->queue(&items);
+        err = std::atomic_load(&mComponent)->queue(&items);
     }
     if (err != C2_OK) {
         Mutexed<PipelineWatcher>::Locked watcher(mPipelineWatcher);
@@ -1457,7 +1457,7 @@
     qbi.setSurfaceDamage(Region::INVALID_REGION); // we don't have dirty regions
     qbi.getFrameTimestamps = true; // we need to know when a frame is rendered
     IGraphicBufferProducer::QueueBufferOutput qbo;
-    status_t result = mComponent->queueToOutputSurface(block, qbi, &qbo);
+    status_t result = std::atomic_load(&mComponent)->queueToOutputSurface(block, qbi, &qbo);
     if (result != OK) {
         ALOGI("[%s] queueBuffer failed: %d", mName, result);
         if (result == NO_INIT) {
@@ -1596,7 +1596,7 @@
 
 void CCodecBufferChannel::pollForRenderedBuffers() {
     FrameEventHistoryDelta delta;
-    mComponent->pollForRenderedFrames(&delta);
+    std::atomic_load(&mComponent)->pollForRenderedFrames(&delta);
     processRenderedFrames(delta);
 }
 
@@ -1605,7 +1605,7 @@
     // knowing the internal state of CCodec/CCodecBufferChannel,
     // prevent mComponent from being destroyed by holding the shared reference
     // during this interface being executed.
-    std::shared_ptr<Codec2Client::Component> comp = mComponent;
+    std::shared_ptr<Codec2Client::Component> comp = std::atomic_load(&mComponent);
     if (comp) {
       SurfaceCallbackHandler::GetInstance().post(
                 SurfaceCallbackHandler::ON_BUFFER_RELEASED, comp, generation);
@@ -1617,7 +1617,7 @@
     // knowing the internal state of CCodec/CCodecBufferChannel,
     // prevent mComponent from being destroyed by holding the shared reference
     // during this interface being executed.
-    std::shared_ptr<Codec2Client::Component> comp = mComponent;
+    std::shared_ptr<Codec2Client::Component> comp = std::atomic_load(&mComponent);
     if (comp) {
       SurfaceCallbackHandler::GetInstance().post(
                 SurfaceCallbackHandler::ON_BUFFER_ATTACHED, comp, generation);
@@ -1691,7 +1691,7 @@
     C2ActualPipelineDelayTuning pipelineDelay(0);
     C2SecureModeTuning secureMode(C2Config::SM_UNPROTECTED);
 
-    c2_status_t err = mComponent->query(
+    c2_status_t err = std::atomic_load(&mComponent)->query(
             {
                 &iStreamFormat,
                 &oStreamFormat,
@@ -1722,7 +1722,7 @@
     size_t numOutputSlots = outputDelayValue + kSmoothnessFactor;
 
     // TODO: get this from input format
-    bool secure = mComponent->getName().find(".secure") != std::string::npos;
+    bool secure = std::atomic_load(&mComponent)->getName().find(".secure") != std::string::npos;
 
     // secure mode is a static parameter (shall not change in the executing state)
     mSendEncryptedInfoBuffer = secureMode.value == C2Config::SM_READ_PROTECTED_WITH_ENCRYPTED;
@@ -1768,7 +1768,7 @@
                 channelCount.invalidate();
                 pcmEncoding.invalidate();
             }
-            err = mComponent->query(stackParams,
+            err = std::atomic_load(&mComponent)->query(stackParams,
                                     { C2PortAllocatorsTuning::input::PARAM_TYPE },
                                     C2_DONT_BLOCK,
                                     &params);
@@ -1836,6 +1836,9 @@
                     channelCount.value,
                     pcmEncoding ? pcmEncoding.value : C2Config::PCM_16);
         }
+        if (!buffersBoundToCodec) {
+            inputFormat->setInt32(KEY_NUM_SLOTS, numInputSlots);
+        }
         bool conforming = (apiFeatures & API_SAME_INPUT_BUFFER);
         // For encrypted content, framework decrypts source buffer (ashmem) into
         // C2Buffers. Thus non-conforming codecs can process these.
@@ -1929,7 +1932,7 @@
             // query C2PortAllocatorsTuning::output from component, or use default allocator if
             // unsuccessful.
             std::vector<std::unique_ptr<C2Param>> params;
-            err = mComponent->query({ },
+            err = std::atomic_load(&mComponent)->query({ },
                                     { C2PortAllocatorsTuning::output::PARAM_TYPE },
                                     C2_DONT_BLOCK,
                                     &params);
@@ -1957,7 +1960,7 @@
             // if unsuccessful.
             if (outputSurface) {
                 params.clear();
-                err = mComponent->query({ },
+                err = std::atomic_load(&mComponent)->query({ },
                                         { C2PortSurfaceAllocatorTuning::output::PARAM_TYPE },
                                         C2_DONT_BLOCK,
                                         &params);
@@ -1988,7 +1991,7 @@
             }
 
             if ((poolMask >> pools->outputAllocatorId) & 1) {
-                err = mComponent->createBlockPool(
+                err = std::atomic_load(&mComponent)->createBlockPool(
                         pools->outputAllocatorId, &pools->outputPoolId, &pools->outputPoolIntf);
                 ALOGI("[%s] Created output block pool with allocatorID %u => poolID %llu - %s",
                         mName, pools->outputAllocatorId,
@@ -2009,7 +2012,8 @@
                     C2PortBlockPoolsTuning::output::AllocUnique({ pools->outputPoolId });
 
             std::vector<std::unique_ptr<C2SettingResult>> failures;
-            err = mComponent->config({ poolIdsTuning.get() }, C2_MAY_BLOCK, &failures);
+            err = std::atomic_load(&mComponent)->config(
+                    { poolIdsTuning.get() }, C2_MAY_BLOCK, &failures);
             ALOGD("[%s] Configured output block pool ids %llu => %s",
                     mName, (unsigned long long)poolIdsTuning->m.values[0], asString(err));
             outputPoolId_ = pools->outputPoolId;
@@ -2017,7 +2021,7 @@
 
         if (prevOutputPoolId != C2BlockPool::BASIC_LINEAR
                 && prevOutputPoolId != C2BlockPool::BASIC_GRAPHIC) {
-            c2_status_t err = mComponent->destroyBlockPool(prevOutputPoolId);
+            c2_status_t err = std::atomic_load(&mComponent)->destroyBlockPool(prevOutputPoolId);
             if (err != C2_OK) {
                 ALOGW("Failed to clean up previous block pool %llu - %s (%d)\n",
                         (unsigned long long) prevOutputPoolId, asString(err), err);
@@ -2049,7 +2053,7 @@
 
         // Try to set output surface to created block pool if given.
         if (outputSurface) {
-            mComponent->setOutputSurface(
+            std::atomic_load(&mComponent)->setOutputSurface(
                     outputPoolId_,
                     outputSurface,
                     outputGeneration,
@@ -2058,7 +2062,7 @@
             // configure CPU read consumer usage
             C2StreamUsageTuning::output outputUsage{0u, C2MemoryUsage::CPU_READ};
             std::vector<std::unique_ptr<C2SettingResult>> failures;
-            err = mComponent->config({ &outputUsage }, C2_MAY_BLOCK, &failures);
+            err = std::atomic_load(&mComponent)->config({ &outputUsage }, C2_MAY_BLOCK, &failures);
             // do not print error message for now as most components may not yet
             // support this setting
             ALOGD_IF(err != C2_BAD_INDEX, "[%s] Configured output usage [%#llx]",
@@ -2180,7 +2184,8 @@
     }
     C2StreamBufferTypeSetting::output oStreamFormat(0u);
     C2PrependHeaderModeSetting prepend(PREPEND_HEADER_TO_NONE);
-    c2_status_t err = mComponent->query({ &oStreamFormat, &prepend }, {}, C2_DONT_BLOCK, nullptr);
+    c2_status_t err = std::atomic_load(&mComponent)->query(
+            { &oStreamFormat, &prepend }, {}, C2_DONT_BLOCK, nullptr);
     if (err != C2_OK && err != C2_BAD_INDEX) {
         return UNKNOWN_ERROR;
     }
@@ -2198,7 +2203,7 @@
                         now);
             }
         }
-        err = mComponent->queue(&flushedConfigs);
+        err = std::atomic_load(&mComponent)->queue(&flushedConfigs);
         if (err != C2_OK) {
             ALOGW("[%s] Error while queueing a flushed config", mName);
             return UNKNOWN_ERROR;
@@ -2249,7 +2254,8 @@
             Mutexed<BlockPools>::Locked pools(mBlockPools);
             outputPoolId = pools->outputPoolId;
         }
-        if (mComponent) mComponent->stopUsingOutputSurface(outputPoolId);
+        std::shared_ptr<Codec2Client::Component> comp = std::atomic_load(&mComponent);
+        if (comp) comp->stopUsingOutputSurface(outputPoolId);
 
         if (pushBlankBuffer) {
             sp<ANativeWindow> anw = static_cast<ANativeWindow *>(surface.get());
@@ -2283,7 +2289,8 @@
 
 void CCodecBufferChannel::release() {
     mInfoBuffers.clear();
-    mComponent.reset();
+    std::shared_ptr<Codec2Client::Component> nullComp;
+    std::atomic_store(&mComponent, nullComp);
     mInputAllocator.reset();
     mOutputSurface.lock()->surface.clear();
     {
@@ -2352,9 +2359,11 @@
 }
 
 void CCodecBufferChannel::onWorkDone(
-        std::unique_ptr<C2Work> work, const sp<AMessage> &outputFormat,
+        std::unique_ptr<C2Work> work,
+        const sp<AMessage> &inputFormat,
+        const sp<AMessage> &outputFormat,
         const C2StreamInitDataInfo::output *initData) {
-    if (handleWork(std::move(work), outputFormat, initData)) {
+    if (handleWork(std::move(work), inputFormat, outputFormat, initData)) {
         feedInputBufferIfAvailable();
     }
 }
@@ -2384,6 +2393,7 @@
 
 bool CCodecBufferChannel::handleWork(
         std::unique_ptr<C2Work> work,
+        const sp<AMessage> &inputFormat,
         const sp<AMessage> &outputFormat,
         const C2StreamInitDataInfo::output *initData) {
     {
@@ -2557,6 +2567,9 @@
         } else {
             input->numSlots = newNumSlots;
         }
+        if (inputFormat->contains(KEY_NUM_SLOTS)) {
+            inputFormat->setInt32(KEY_NUM_SLOTS, input->numSlots);
+        }
     }
     size_t numOutputSlots = 0;
     uint32_t reorderDepth = 0;
@@ -2605,7 +2618,7 @@
             }
         }
         if (maxDequeueCount > 0) {
-            mComponent->setOutputSurfaceMaxDequeueCount(maxDequeueCount);
+            std::atomic_load(&mComponent)->setOutputSurfaceMaxDequeueCount(maxDequeueCount);
         }
     }
 
@@ -2853,7 +2866,7 @@
     }
 
     if (outputPoolIntf) {
-        if (mComponent->setOutputSurface(
+        if (std::atomic_load(&mComponent)->setOutputSurface(
                 outputPoolId,
                 producer,
                 generation,
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h
index 4d296fd..6493b87 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.h
+++ b/media/codec2/sfplugin/CCodecBufferChannel.h
@@ -193,12 +193,15 @@
     /**
      * Notify input client about work done.
      *
-     * @param workItems   finished work item.
+     * @param workItems    finished work item.
+     * @param inputFormat  input format
      * @param outputFormat new output format if it has changed, otherwise nullptr
-     * @param initData    new init data (CSD) if it has changed, otherwise nullptr
+     * @param initData     new init data (CSD) if it has changed, otherwise nullptr
      */
     void onWorkDone(
-            std::unique_ptr<C2Work> work, const sp<AMessage> &outputFormat,
+            std::unique_ptr<C2Work> work,
+            const sp<AMessage> &inputFormat,
+            const sp<AMessage> &outputFormat,
             const C2StreamInitDataInfo::output *initData);
 
     /**
@@ -311,7 +314,9 @@
                                       std::shared_ptr<C2LinearBlock> encryptedBlock = nullptr,
                                       size_t blockSize = 0);
     bool handleWork(
-            std::unique_ptr<C2Work> work, const sp<AMessage> &outputFormat,
+            std::unique_ptr<C2Work> work,
+            const sp<AMessage> &inputFormat,
+            const sp<AMessage> &outputFormat,
             const C2StreamInitDataInfo::output *initData);
     void sendOutputBuffers();
     void ensureDecryptDestination(size_t size);
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index a943626..897a696 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -1907,7 +1907,7 @@
                         bottom = c2_min(bottom, height);
                         if (right > left && bottom > top) {
                             C2Rect rect(right - left, bottom - top);
-                            rect.at(left, top);
+                            rect = rect.at(left, top);
                             c2QpOffsetRects.push_back(C2QpOffsetRectStruct(rect, offset));
                         } else {
                             ALOGE("Rects configuration %s is not valid.", box);
diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
index 692f700..0f5cdd6 100644
--- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp
+++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
@@ -16,6 +16,9 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "Codec2InfoBuilder"
+
+#include <cstdlib>
+
 #include <log/log.h>
 
 #include <strings.h>
@@ -508,6 +511,10 @@
                 && !hasPrefix(v.first, "domain-")
                 && !hasPrefix(v.first, "variant-")) {
             writer->addGlobalSetting(v.first.c_str(), v.second.c_str());
+            if (v.first == "max-concurrent-instances") {
+                MediaCodecInfoWriter::SetMaxSupportedInstances(
+                        (int32_t)strtol(v.second.c_str(), NULL, 10));
+            }
         }
     }
 
@@ -740,6 +747,7 @@
                     pixelFormatMap[HAL_PIXEL_FORMAT_YCBCR_P010]    = COLOR_FormatYUVP010;
                     pixelFormatMap[HAL_PIXEL_FORMAT_RGBA_1010102]  = COLOR_Format32bitABGR2101010;
                     pixelFormatMap[HAL_PIXEL_FORMAT_RGBA_FP16]     = COLOR_Format64bitABGRFloat;
+                    pixelFormatMap[AHARDWAREBUFFER_FORMAT_YCbCr_P210]    = COLOR_FormatYUVP210;
 
                     std::shared_ptr<C2StoreFlexiblePixelFormatDescriptorsInfo> pixelFormatInfo;
                     std::vector<std::unique_ptr<C2Param>> heapParams;
@@ -796,6 +804,7 @@
                     }
                 }
             }
+            codecInfo->createCodecCaps();
         }
     }
     return OK;
diff --git a/media/codec2/sfplugin/utils/Codec2CommonUtils.cpp b/media/codec2/sfplugin/utils/Codec2CommonUtils.cpp
index 7a33af4..aa87e97 100644
--- a/media/codec2/sfplugin/utils/Codec2CommonUtils.cpp
+++ b/media/codec2/sfplugin/utils/Codec2CommonUtils.cpp
@@ -64,6 +64,13 @@
     return kVendorApiLevel >= __ANDROID_API_T__;
 }
 
+static bool isP210Allowed() {
+    static const int32_t kVendorApiLevel =
+        base::GetIntProperty<int32_t>("ro.vendor.api_level", 0);
+
+    return kVendorApiLevel > __ANDROID_API_V__;
+}
+
 bool isHalPixelFormatSupported(AHardwareBuffer_Format format) {
     // HAL_PIXEL_FORMAT_YCBCR_P010 requirement was added in T VSR, although it could have been
     // supported prior to this.
@@ -76,6 +83,12 @@
         return false;
     }
 
+    // P210 is not available before Android B
+    if (format == (AHardwareBuffer_Format)AHARDWAREBUFFER_FORMAT_YCbCr_P210 &&
+            !isP210Allowed()) {
+        return false;
+    }
+
     // Default scenario --- the consumer is display or GPU
     const AHardwareBuffer_Desc consumableForDisplayOrGpu = {
             .width = 320,
diff --git a/media/codec2/sfplugin/utils/Codec2Mapper.cpp b/media/codec2/sfplugin/utils/Codec2Mapper.cpp
index 9297520..3841831 100644
--- a/media/codec2/sfplugin/utils/Codec2Mapper.cpp
+++ b/media/codec2/sfplugin/utils/Codec2Mapper.cpp
@@ -436,6 +436,86 @@
     { C2Config::hdr_format_t::HDR10_PLUS, AV1ProfileMain10HDR10Plus },
 };
 
+// APV
+ALookup<C2Config::profile_t, int32_t> sApvProfiles = {
+    { C2Config::PROFILE_APV_422_10, APVProfile422_10 },
+    { C2Config::PROFILE_APV_422_10, APVProfile422_10HDR10 },
+    { C2Config::PROFILE_APV_422_10, APVProfile422_10HDR10Plus },
+};
+
+ALookup<C2Config::profile_t, int32_t> sApvHdrProfiles = {
+    { C2Config::PROFILE_APV_422_10, APVProfile422_10HDR10 },
+};
+
+ALookup<C2Config::profile_t, int32_t> sApvHdr10PlusProfiles = {
+    { C2Config::PROFILE_APV_422_10, APVProfile422_10HDR10Plus },
+};
+
+ALookup<C2Config::level_t, int32_t> sApvLevels = {
+    { C2Config::LEVEL_APV_1_BAND_0, APVLevel1Band0 },
+    { C2Config::LEVEL_APV_1_BAND_1, APVLevel1Band1 },
+    { C2Config::LEVEL_APV_1_BAND_2, APVLevel1Band2 },
+    { C2Config::LEVEL_APV_1_BAND_3, APVLevel1Band3 },
+    { C2Config::LEVEL_APV_1_1_BAND_0, APVLevel11Band0 },
+    { C2Config::LEVEL_APV_1_1_BAND_1, APVLevel11Band1 },
+    { C2Config::LEVEL_APV_1_1_BAND_2, APVLevel11Band2 },
+    { C2Config::LEVEL_APV_1_1_BAND_3, APVLevel11Band3 },
+    { C2Config::LEVEL_APV_2_BAND_0, APVLevel2Band0 },
+    { C2Config::LEVEL_APV_2_BAND_1, APVLevel2Band1 },
+    { C2Config::LEVEL_APV_2_BAND_2, APVLevel2Band2 },
+    { C2Config::LEVEL_APV_2_BAND_3, APVLevel2Band3 },
+    { C2Config::LEVEL_APV_2_1_BAND_0, APVLevel21Band0 },
+    { C2Config::LEVEL_APV_2_1_BAND_1, APVLevel21Band1 },
+    { C2Config::LEVEL_APV_2_1_BAND_2, APVLevel21Band2 },
+    { C2Config::LEVEL_APV_2_1_BAND_3, APVLevel21Band3 },
+    { C2Config::LEVEL_APV_3_BAND_0, APVLevel3Band0 },
+    { C2Config::LEVEL_APV_3_BAND_1, APVLevel3Band1 },
+    { C2Config::LEVEL_APV_3_BAND_2, APVLevel3Band2 },
+    { C2Config::LEVEL_APV_3_BAND_3, APVLevel3Band3 },
+    { C2Config::LEVEL_APV_3_1_BAND_0, APVLevel31Band0 },
+    { C2Config::LEVEL_APV_3_1_BAND_1, APVLevel31Band1 },
+    { C2Config::LEVEL_APV_3_1_BAND_2, APVLevel31Band2 },
+    { C2Config::LEVEL_APV_3_1_BAND_3, APVLevel31Band3 },
+    { C2Config::LEVEL_APV_4_BAND_0, APVLevel4Band0 },
+    { C2Config::LEVEL_APV_4_BAND_1, APVLevel4Band1 },
+    { C2Config::LEVEL_APV_4_BAND_2, APVLevel4Band2 },
+    { C2Config::LEVEL_APV_4_BAND_3, APVLevel4Band3 },
+    { C2Config::LEVEL_APV_4_1_BAND_0, APVLevel41Band0 },
+    { C2Config::LEVEL_APV_4_1_BAND_1, APVLevel41Band1 },
+    { C2Config::LEVEL_APV_4_1_BAND_2, APVLevel41Band2 },
+    { C2Config::LEVEL_APV_4_1_BAND_3, APVLevel41Band3 },
+    { C2Config::LEVEL_APV_5_BAND_0, APVLevel5Band0 },
+    { C2Config::LEVEL_APV_5_BAND_1, APVLevel5Band1 },
+    { C2Config::LEVEL_APV_5_BAND_2, APVLevel5Band2 },
+    { C2Config::LEVEL_APV_5_BAND_3, APVLevel5Band3 },
+    { C2Config::LEVEL_APV_5_1_BAND_0, APVLevel51Band0 },
+    { C2Config::LEVEL_APV_5_1_BAND_1, APVLevel51Band1 },
+    { C2Config::LEVEL_APV_5_1_BAND_2, APVLevel51Band2 },
+    { C2Config::LEVEL_APV_5_1_BAND_3, APVLevel51Band3 },
+    { C2Config::LEVEL_APV_6_BAND_0, APVLevel6Band0 },
+    { C2Config::LEVEL_APV_6_BAND_1, APVLevel6Band1 },
+    { C2Config::LEVEL_APV_6_BAND_2, APVLevel6Band2 },
+    { C2Config::LEVEL_APV_6_BAND_3, APVLevel6Band3 },
+    { C2Config::LEVEL_APV_6_1_BAND_0, APVLevel61Band0 },
+    { C2Config::LEVEL_APV_6_1_BAND_1, APVLevel61Band1 },
+    { C2Config::LEVEL_APV_6_1_BAND_2, APVLevel61Band2 },
+    { C2Config::LEVEL_APV_6_1_BAND_3, APVLevel61Band3 },
+    { C2Config::LEVEL_APV_7_BAND_0, APVLevel7Band0 },
+    { C2Config::LEVEL_APV_7_BAND_1, APVLevel7Band1 },
+    { C2Config::LEVEL_APV_7_BAND_2, APVLevel7Band2 },
+    { C2Config::LEVEL_APV_7_BAND_3, APVLevel7Band3 },
+    { C2Config::LEVEL_APV_7_1_BAND_0, APVLevel71Band0 },
+    { C2Config::LEVEL_APV_7_1_BAND_1, APVLevel71Band1 },
+    { C2Config::LEVEL_APV_7_1_BAND_2, APVLevel71Band2 },
+    { C2Config::LEVEL_APV_7_1_BAND_3, APVLevel71Band3 },
+};
+
+ALookup<C2Config::hdr_format_t, int32_t> sApvHdrFormats = {
+    { C2Config::hdr_format_t::HLG, APVProfile422_10 },
+    { C2Config::hdr_format_t::HDR10, APVProfile422_10HDR10 },
+    { C2Config::hdr_format_t::HDR10_PLUS, APVProfile422_10HDR10Plus },
+};
+
 // HAL_PIXEL_FORMAT_* -> COLOR_Format*
 ALookup<uint32_t, int32_t> sPixelFormats = {
     { HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, COLOR_FormatSurface },
@@ -720,6 +800,37 @@
     int32_t mBitDepth;
 };
 
+// APV
+struct ApvProfileLevelMapper : ProfileLevelMapperHelper {
+    ApvProfileLevelMapper(bool isHdr = false, bool isHdr10Plus = false) :
+        ProfileLevelMapperHelper(),
+        mIsHdr(isHdr), mIsHdr10Plus(isHdr10Plus) {}
+
+    virtual bool simpleMap(C2Config::level_t from, int32_t *to) {
+        return sApvLevels.map(from, to);
+    }
+    virtual bool simpleMap(int32_t from, C2Config::level_t *to) {
+        return sApvLevels.map(from, to);
+    }
+    virtual bool simpleMap(C2Config::profile_t from, int32_t *to) {
+        return mIsHdr10Plus ? sApvHdr10PlusProfiles.map(from, to) :
+                     mIsHdr ? sApvHdrProfiles.map(from, to) :
+                              sApvProfiles.map(from, to);
+    }
+    virtual bool simpleMap(int32_t from, C2Config::profile_t *to) {
+        return mIsHdr10Plus ? sApvHdr10PlusProfiles.map(from, to) :
+                     mIsHdr ? sApvHdrProfiles.map(from, to) :
+                              sApvProfiles.map(from, to);
+    }
+    virtual bool mapHdrFormat(int32_t from, C2Config::hdr_format_t *to) override {
+        return sApvHdrFormats.map(from, to);
+    }
+
+private:
+    bool mIsHdr;
+    bool mIsHdr10Plus;
+};
+
 } // namespace
 
 // the default mapper is used for media types that do not support HDR
@@ -753,6 +864,8 @@
         return std::make_shared<Vp9ProfileLevelMapper>();
     } else if (mediaType == MIMETYPE_VIDEO_AV1) {
         return std::make_shared<Av1ProfileLevelMapper>();
+    } else if (mediaType == MIMETYPE_VIDEO_APV) {
+        return std::make_shared<ApvProfileLevelMapper>();
     }
     return nullptr;
 }
@@ -767,6 +880,8 @@
         return std::make_shared<Vp9ProfileLevelMapper>(true, isHdr10Plus);
     } else if (mediaType == MIMETYPE_VIDEO_AV1) {
         return std::make_shared<Av1ProfileLevelMapper>(true, isHdr10Plus);
+    } else if (mediaType == MIMETYPE_VIDEO_APV) {
+        return std::make_shared<ApvProfileLevelMapper>(true, isHdr10Plus);
     }
     return nullptr;
 }
@@ -779,6 +894,8 @@
         return GetProfileLevelMapper(mediaType);
     } else if (mediaType == MIMETYPE_VIDEO_AV1 && bitDepth == 10) {
         return std::make_shared<Av1ProfileLevelMapper>(false, false, bitDepth);
+    } else if (mediaType == MIMETYPE_VIDEO_APV) {
+        return std::make_shared<ApvProfileLevelMapper>();
     }
     return nullptr;
 }
diff --git a/media/codec2/vndk/C2Store.cpp b/media/codec2/vndk/C2Store.cpp
index 0987da2..6ec9d6b 100644
--- a/media/codec2/vndk/C2Store.cpp
+++ b/media/codec2/vndk/C2Store.cpp
@@ -1206,7 +1206,8 @@
     emplace("libcodec2_soft_vp8enc.so");
     emplace("libcodec2_soft_vp9dec.so");
     emplace("libcodec2_soft_vp9enc.so");
-
+    emplace("libcodec2_soft_apvenc.so");
+    emplace("libcodec2_soft_apvdec.so");
 }
 
 // For testing only
diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp
index 665f9fc..17dfe9c 100644
--- a/media/codec2/vndk/platform/C2BqBuffer.cpp
+++ b/media/codec2/vndk/platform/C2BqBuffer.cpp
@@ -997,7 +997,7 @@
         return -1;
     }
 
-    if (toUsage != graphicBuffer->getUsage()) {
+    if ((toUsage & graphicBuffer->getUsage()) != toUsage) {
         sp<GraphicBuffer> newBuffer = new GraphicBuffer(
             graphicBuffer->handle, GraphicBuffer::CLONE_HANDLE,
             graphicBuffer->width, graphicBuffer->height, graphicBuffer->format,
diff --git a/media/janitors/better_together_OWNERS b/media/janitors/better_together_OWNERS
new file mode 100644
index 0000000..70723cb
--- /dev/null
+++ b/media/janitors/better_together_OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 137631
+
+aquilescanta@google.com
+asapperstein@google.com
+halliwell@google.com
diff --git a/media/janitors/media_solutions_OWNERS b/media/janitors/media_solutions_OWNERS
index 3243726..004fa30 100644
--- a/media/janitors/media_solutions_OWNERS
+++ b/media/janitors/media_solutions_OWNERS
@@ -4,6 +4,7 @@
 andrewlewis@google.com
 bachinger@google.com
 claincly@google.com
+dancho@google.com
 ibaker@google.com
 ivanbuper@google.com
 jbibik@google.com
@@ -16,3 +17,8 @@
 
 # MediaRouter and native mirroring only:
 aquilescanta@google.com
+
+# Emergency rollbacks and fixes outside LON timezone
+jmtrivi@google.com # US-MTV
+lajos@google.com # US-MTV
+scottnien@google.com # TW-NTC
diff --git a/media/libaaudio/fuzzer/Android.bp b/media/libaaudio/fuzzer/Android.bp
index b369a62..67c6715 100644
--- a/media/libaaudio/fuzzer/Android.bp
+++ b/media/libaaudio/fuzzer/Android.bp
@@ -58,8 +58,6 @@
         "libaudioclient",
         "libaudioutils",
         "libbase_ndk",
-        "libcgrouprc",
-        "libcgrouprc_format",
         "libcutils",
         "libjsoncpp",
         "liblog",
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index 61204ae..6dfb327 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -359,6 +359,8 @@
         "aidl/android/media/AudioMixerBehavior.aidl",
         "aidl/android/media/AudioOffloadMode.aidl",
         "aidl/android/media/AudioPolicyDeviceState.aidl",
+        "aidl/android/media/AudioPolicyForceUse.aidl",
+        "aidl/android/media/AudioPolicyForcedConfig.aidl",
         "aidl/android/media/AudioProductStrategy.aidl",
         "aidl/android/media/AudioVolumeGroup.aidl",
         "aidl/android/media/DeviceRole.aidl",
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 85f4a86..3f4fcfd 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -60,8 +60,6 @@
 using media::audio::common::AudioMMapPolicyInfo;
 using media::audio::common::AudioMMapPolicyType;
 using media::audio::common::AudioOffloadInfo;
-using media::audio::common::AudioPolicyForceUse;
-using media::audio::common::AudioPolicyForcedConfig;
 using media::audio::common::AudioSource;
 using media::audio::common::AudioStreamType;
 using media::audio::common::AudioUsage;
@@ -1061,9 +1059,9 @@
     if (aps == 0) return AUDIO_POLICY_FORCE_NONE;
 
     auto result = [&]() -> ConversionResult<audio_policy_forced_cfg_t> {
-        AudioPolicyForceUse usageAidl = VALUE_OR_RETURN(
+        media::AudioPolicyForceUse usageAidl = VALUE_OR_RETURN(
                 legacy2aidl_audio_policy_force_use_t_AudioPolicyForceUse(usage));
-        AudioPolicyForcedConfig configAidl;
+        media::AudioPolicyForcedConfig configAidl;
         RETURN_IF_ERROR(statusTFromBinderStatus(
                 aps->getForceUse(usageAidl, &configAidl)));
         return aidl2legacy_AudioPolicyForcedConfig_audio_policy_forced_cfg_t(configAidl);
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index ecf7436..fd55bf3 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -1300,7 +1300,7 @@
     if (isAudioPlaybackRateEqual(playbackRate, mPlaybackRate)) {
         return NO_ERROR;
     }
-    if (isOffloadedOrDirect_l()) {
+    if (isAfTrackOffloadedOrDirect_l()) {
         const status_t status = statusTFromBinderStatus(mAudioTrack->setPlaybackRateParameters(
                 VALUE_OR_RETURN_STATUS(
                         legacy2aidl_audio_playback_rate_t_AudioPlaybackRate(playbackRate))));
diff --git a/media/libaudioclient/AudioTrackShared.cpp b/media/libaudioclient/AudioTrackShared.cpp
index e3b79b2..359f3c1 100644
--- a/media/libaudioclient/AudioTrackShared.cpp
+++ b/media/libaudioclient/AudioTrackShared.cpp
@@ -310,8 +310,16 @@
             ts = NULL;
             break;
         }
+
         int32_t old = android_atomic_and(~CBLK_FUTEX_WAKE, &cblk->mFutex);
-        if (!(old & CBLK_FUTEX_WAKE)) {
+
+        // Check inactive to prevent waiting if the track has been disabled due to underrun
+        // (or invalidated).  The subsequent call to obtainBufer will return NOT_ENOUGH_DATA
+        // (or DEAD_OBJECT) and restart (or restore) the track.
+        const int32_t current_flags = android_atomic_acquire_load(&cblk->mFlags);
+        const bool inactive = current_flags & (CBLK_INVALID | CBLK_DISABLED);
+
+        if (!(old & CBLK_FUTEX_WAKE) && !inactive) {
             if (measure && !beforeIsValid) {
                 clock_gettime(CLOCK_MONOTONIC, &before);
                 beforeIsValid = true;
diff --git a/media/libaudioclient/PolicyAidlConversion.cpp b/media/libaudioclient/PolicyAidlConversion.cpp
index a414cb7..163a359 100644
--- a/media/libaudioclient/PolicyAidlConversion.cpp
+++ b/media/libaudioclient/PolicyAidlConversion.cpp
@@ -296,6 +296,138 @@
     return unexpected(BAD_VALUE);
 }
 
+ConversionResult<audio_policy_force_use_t>
+aidl2legacy_AudioPolicyForceUse_audio_policy_force_use_t(media::AudioPolicyForceUse aidl) {
+    switch (aidl) {
+        case media::AudioPolicyForceUse::COMMUNICATION:
+            return AUDIO_POLICY_FORCE_FOR_COMMUNICATION;
+        case media::AudioPolicyForceUse::MEDIA:
+            return AUDIO_POLICY_FORCE_FOR_MEDIA;
+        case media::AudioPolicyForceUse::RECORD:
+            return AUDIO_POLICY_FORCE_FOR_RECORD;
+        case media::AudioPolicyForceUse::DOCK:
+            return AUDIO_POLICY_FORCE_FOR_DOCK;
+        case media::AudioPolicyForceUse::SYSTEM:
+            return AUDIO_POLICY_FORCE_FOR_SYSTEM;
+        case media::AudioPolicyForceUse::HDMI_SYSTEM_AUDIO:
+            return AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO;
+        case media::AudioPolicyForceUse::ENCODED_SURROUND:
+            return AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND;
+        case media::AudioPolicyForceUse::VIBRATE_RINGING:
+            return AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING;
+    }
+    return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioPolicyForceUse>
+legacy2aidl_audio_policy_force_use_t_AudioPolicyForceUse(audio_policy_force_use_t legacy) {
+    switch (legacy) {
+        case AUDIO_POLICY_FORCE_FOR_COMMUNICATION:
+            return media::AudioPolicyForceUse::COMMUNICATION;
+        case AUDIO_POLICY_FORCE_FOR_MEDIA:
+            return media::AudioPolicyForceUse::MEDIA;
+        case AUDIO_POLICY_FORCE_FOR_RECORD:
+            return media::AudioPolicyForceUse::RECORD;
+        case AUDIO_POLICY_FORCE_FOR_DOCK:
+            return media::AudioPolicyForceUse::DOCK;
+        case AUDIO_POLICY_FORCE_FOR_SYSTEM:
+            return media::AudioPolicyForceUse::SYSTEM;
+        case AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO:
+            return media::AudioPolicyForceUse::HDMI_SYSTEM_AUDIO;
+        case AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND:
+            return media::AudioPolicyForceUse::ENCODED_SURROUND;
+        case AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING:
+            return media::AudioPolicyForceUse::VIBRATE_RINGING;
+        case AUDIO_POLICY_FORCE_USE_CNT:
+            break;
+    }
+    return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_policy_forced_cfg_t>
+aidl2legacy_AudioPolicyForcedConfig_audio_policy_forced_cfg_t(media::AudioPolicyForcedConfig aidl) {
+    switch (aidl) {
+        case media::AudioPolicyForcedConfig::NONE:
+            return AUDIO_POLICY_FORCE_NONE;
+        case media::AudioPolicyForcedConfig::SPEAKER:
+            return AUDIO_POLICY_FORCE_SPEAKER;
+        case media::AudioPolicyForcedConfig::HEADPHONES:
+            return AUDIO_POLICY_FORCE_HEADPHONES;
+        case media::AudioPolicyForcedConfig::BT_SCO:
+            return AUDIO_POLICY_FORCE_BT_SCO;
+        case media::AudioPolicyForcedConfig::BT_A2DP:
+            return AUDIO_POLICY_FORCE_BT_A2DP;
+        case media::AudioPolicyForcedConfig::WIRED_ACCESSORY:
+            return AUDIO_POLICY_FORCE_WIRED_ACCESSORY;
+        case media::AudioPolicyForcedConfig::BT_CAR_DOCK:
+            return AUDIO_POLICY_FORCE_BT_CAR_DOCK;
+        case media::AudioPolicyForcedConfig::BT_DESK_DOCK:
+            return AUDIO_POLICY_FORCE_BT_DESK_DOCK;
+        case media::AudioPolicyForcedConfig::ANALOG_DOCK:
+            return AUDIO_POLICY_FORCE_ANALOG_DOCK;
+        case media::AudioPolicyForcedConfig::DIGITAL_DOCK:
+            return AUDIO_POLICY_FORCE_DIGITAL_DOCK;
+        case media::AudioPolicyForcedConfig::NO_BT_A2DP:
+            return AUDIO_POLICY_FORCE_NO_BT_A2DP;
+        case media::AudioPolicyForcedConfig::SYSTEM_ENFORCED:
+            return AUDIO_POLICY_FORCE_SYSTEM_ENFORCED;
+        case media::AudioPolicyForcedConfig::HDMI_SYSTEM_AUDIO_ENFORCED:
+            return AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED;
+        case media::AudioPolicyForcedConfig::ENCODED_SURROUND_NEVER:
+            return AUDIO_POLICY_FORCE_ENCODED_SURROUND_NEVER;
+        case media::AudioPolicyForcedConfig::ENCODED_SURROUND_ALWAYS:
+            return AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS;
+        case media::AudioPolicyForcedConfig::ENCODED_SURROUND_MANUAL:
+            return AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL;
+        case media::AudioPolicyForcedConfig::BT_BLE:
+            return AUDIO_POLICY_FORCE_BT_BLE;
+    }
+    return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioPolicyForcedConfig>
+legacy2aidl_audio_policy_forced_cfg_t_AudioPolicyForcedConfig(audio_policy_forced_cfg_t legacy) {
+    switch (legacy) {
+        case AUDIO_POLICY_FORCE_NONE:
+            return media::AudioPolicyForcedConfig::NONE;
+        case AUDIO_POLICY_FORCE_SPEAKER:
+            return media::AudioPolicyForcedConfig::SPEAKER;
+        case AUDIO_POLICY_FORCE_HEADPHONES:
+            return media::AudioPolicyForcedConfig::HEADPHONES;
+        case AUDIO_POLICY_FORCE_BT_SCO:
+            return media::AudioPolicyForcedConfig::BT_SCO;
+        case AUDIO_POLICY_FORCE_BT_A2DP:
+            return media::AudioPolicyForcedConfig::BT_A2DP;
+        case AUDIO_POLICY_FORCE_WIRED_ACCESSORY:
+            return media::AudioPolicyForcedConfig::WIRED_ACCESSORY;
+        case AUDIO_POLICY_FORCE_BT_CAR_DOCK:
+            return media::AudioPolicyForcedConfig::BT_CAR_DOCK;
+        case AUDIO_POLICY_FORCE_BT_DESK_DOCK:
+            return media::AudioPolicyForcedConfig::BT_DESK_DOCK;
+        case AUDIO_POLICY_FORCE_ANALOG_DOCK:
+            return media::AudioPolicyForcedConfig::ANALOG_DOCK;
+        case AUDIO_POLICY_FORCE_DIGITAL_DOCK:
+            return media::AudioPolicyForcedConfig::DIGITAL_DOCK;
+        case AUDIO_POLICY_FORCE_NO_BT_A2DP:
+            return media::AudioPolicyForcedConfig::NO_BT_A2DP;
+        case AUDIO_POLICY_FORCE_SYSTEM_ENFORCED:
+            return media::AudioPolicyForcedConfig::SYSTEM_ENFORCED;
+        case AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED:
+            return media::AudioPolicyForcedConfig::HDMI_SYSTEM_AUDIO_ENFORCED;
+        case AUDIO_POLICY_FORCE_ENCODED_SURROUND_NEVER:
+            return media::AudioPolicyForcedConfig::ENCODED_SURROUND_NEVER;
+        case AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS:
+            return media::AudioPolicyForcedConfig::ENCODED_SURROUND_ALWAYS;
+        case AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL:
+            return media::AudioPolicyForcedConfig::ENCODED_SURROUND_MANUAL;
+        case AUDIO_POLICY_FORCE_BT_BLE:
+            return media::AudioPolicyForcedConfig::BT_BLE;
+        case AUDIO_POLICY_FORCE_CFG_CNT:
+            break;
+    }
+    return unexpected(BAD_VALUE);
+}
+
 ConversionResult<device_role_t>
 aidl2legacy_DeviceRole_device_role_t(media::DeviceRole aidl) {
     switch (aidl) {
diff --git a/media/libaudioclient/aidl/android/media/AudioPolicyForceUse.aidl b/media/libaudioclient/aidl/android/media/AudioPolicyForceUse.aidl
new file mode 100644
index 0000000..9bb0605
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPolicyForceUse.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioPolicyForceUse {
+    COMMUNICATION = 0,
+    MEDIA = 1,
+    RECORD = 2,
+    DOCK = 3,
+    SYSTEM = 4,
+    HDMI_SYSTEM_AUDIO = 5,
+    ENCODED_SURROUND = 6,
+    VIBRATE_RINGING = 7,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioPolicyForcedConfig.aidl b/media/libaudioclient/aidl/android/media/AudioPolicyForcedConfig.aidl
new file mode 100644
index 0000000..111bb2f
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPolicyForcedConfig.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioPolicyForcedConfig {
+    NONE = 0,
+    SPEAKER = 1,
+    HEADPHONES = 2,
+    BT_SCO = 3,
+    BT_A2DP = 4,
+    WIRED_ACCESSORY = 5,
+    BT_CAR_DOCK = 6,
+    BT_DESK_DOCK = 7,
+    ANALOG_DOCK = 8,
+    DIGITAL_DOCK = 9,
+    NO_BT_A2DP = 10, /* A2DP sink is not preferred to speaker or wired HS */
+    SYSTEM_ENFORCED = 11,
+    HDMI_SYSTEM_AUDIO_ENFORCED = 12,
+    ENCODED_SURROUND_NEVER = 13,
+    ENCODED_SURROUND_ALWAYS = 14,
+    ENCODED_SURROUND_MANUAL = 15,
+    BT_BLE = 16,
+}
diff --git a/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl b/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
index 7f5e8e2..ac42ea9 100644
--- a/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
@@ -25,6 +25,8 @@
 import android.media.AudioOffloadMode;
 import android.media.AudioPatchFw;
 import android.media.AudioPolicyDeviceState;
+import android.media.AudioPolicyForcedConfig;
+import android.media.AudioPolicyForceUse;
 import android.media.AudioPortFw;
 import android.media.AudioPortConfigFw;
 import android.media.AudioPortRole;
@@ -47,8 +49,6 @@
 import android.media.audio.common.AudioDeviceDescription;
 import android.media.audio.common.AudioFormatDescription;
 import android.media.audio.common.AudioMode;
-import android.media.audio.common.AudioPolicyForcedConfig;
-import android.media.audio.common.AudioPolicyForceUse;
 import android.media.audio.common.AudioProfile;
 import android.media.audio.common.AudioOffloadInfo;
 import android.media.audio.common.AudioPort;
diff --git a/media/libaudioclient/aidl/fuzzer/Android.bp b/media/libaudioclient/aidl/fuzzer/Android.bp
index a215c0b..14e528f 100644
--- a/media/libaudioclient/aidl/fuzzer/Android.bp
+++ b/media/libaudioclient/aidl/fuzzer/Android.bp
@@ -24,8 +24,6 @@
         "android.hardware.audio.common@7.0-enums",
         "audiopermissioncontroller",
         "libaudiomockhal",
-        "libcgrouprc",
-        "libcgrouprc_format",
         "libfakeservicemanager",
         "libjsoncpp",
         "libmediametricsservice",
diff --git a/media/libaudioclient/fuzzer/Android.bp b/media/libaudioclient/fuzzer/Android.bp
index 8bca8df..65ada70 100644
--- a/media/libaudioclient/fuzzer/Android.bp
+++ b/media/libaudioclient/fuzzer/Android.bp
@@ -37,8 +37,6 @@
         "effect-aidl-cpp",
         "libaudioclient",
         "libbase",
-        "libcgrouprc",
-        "libcgrouprc_format",
         "libcutils",
         "libjsoncpp",
         "liblog",
diff --git a/media/libaudioclient/include/media/PolicyAidlConversion.h b/media/libaudioclient/include/media/PolicyAidlConversion.h
index 1b90d6b..ed9ddd6 100644
--- a/media/libaudioclient/include/media/PolicyAidlConversion.h
+++ b/media/libaudioclient/include/media/PolicyAidlConversion.h
@@ -28,6 +28,8 @@
 #include <android/media/AudioMixRouteFlag.h>
 #include <android/media/AudioMixType.h>
 #include <android/media/AudioOffloadMode.h>
+#include <android/media/AudioPolicyForceUse.h>
+#include <android/media/AudioPolicyForcedConfig.h>
 #include <android/media/DeviceRole.h>
 
 #include <media/AidlConversionUtil.h>
@@ -82,6 +84,16 @@
 ConversionResult<media::AudioPolicyDeviceState>
 legacy2aidl_audio_policy_dev_state_t_AudioPolicyDeviceState(audio_policy_dev_state_t legacy);
 
+ConversionResult<audio_policy_force_use_t>
+aidl2legacy_AudioPolicyForceUse_audio_policy_force_use_t(media::AudioPolicyForceUse aidl);
+ConversionResult<media::AudioPolicyForceUse>
+legacy2aidl_audio_policy_force_use_t_AudioPolicyForceUse(audio_policy_force_use_t legacy);
+
+ConversionResult<audio_policy_forced_cfg_t>
+aidl2legacy_AudioPolicyForcedConfig_audio_policy_forced_cfg_t(media::AudioPolicyForcedConfig aidl);
+ConversionResult<media::AudioPolicyForcedConfig>
+legacy2aidl_audio_policy_forced_cfg_t_AudioPolicyForcedConfig(audio_policy_forced_cfg_t legacy);
+
 ConversionResult<device_role_t>
 aidl2legacy_DeviceRole_device_role_t(media::DeviceRole aidl);
 ConversionResult<media::DeviceRole>
diff --git a/media/libaudioclient/tests/Android.bp b/media/libaudioclient/tests/Android.bp
index ddf14a3..3941280 100644
--- a/media/libaudioclient/tests/Android.bp
+++ b/media/libaudioclient/tests/Android.bp
@@ -107,7 +107,6 @@
         "framework-permission-aidl-cpp",
         "libaudioutils",
         "libbase",
-        "libcgrouprc",
         "libdl",
         "libmedia",
         "libmedia_helper",
diff --git a/media/libaudioclient/tests/audio_aidl_legacy_conversion_tests.cpp b/media/libaudioclient/tests/audio_aidl_legacy_conversion_tests.cpp
index 7f55e48..aa6cb0d 100644
--- a/media/libaudioclient/tests/audio_aidl_legacy_conversion_tests.cpp
+++ b/media/libaudioclient/tests/audio_aidl_legacy_conversion_tests.cpp
@@ -670,6 +670,25 @@
                                          AudioEncapsulationMetadataType::FRAMEWORK_TUNER,
                                          AudioEncapsulationMetadataType::DVB_AD_DESCRIPTOR));
 
+TEST(AudioPortDeviceExt_speakerLayoutRoundTripTest, Aidl2Legacy2Aidl_layoutMask) {
+    AudioPortDeviceExt initial{};
+    initial.speakerLayout = make_ACL_Stereo();
+    auto conv = aidl2legacy_AudioPortDeviceExt_audio_port_config_device_ext(initial);
+    ASSERT_TRUE(conv.ok());
+    auto convBack = legacy2aidl_audio_port_config_device_ext_AudioPortDeviceExt(conv.value());
+    ASSERT_TRUE(convBack.ok());
+    EXPECT_EQ(initial, convBack.value());
+}
+
+TEST(AudioPortDeviceExt_speakerLayoutRoundTripTest, Aidl2Legacy2Aidl_null) {
+    const AudioPortDeviceExt initial{};  // speakerLayout is null
+    auto conv = aidl2legacy_AudioPortDeviceExt_audio_port_config_device_ext(initial);
+    ASSERT_TRUE(conv.ok());
+    auto convBack = legacy2aidl_audio_port_config_device_ext_AudioPortDeviceExt(conv.value());
+    ASSERT_TRUE(convBack.ok());
+    EXPECT_EQ(initial, convBack.value());
+}
+
 class AudioGainModeRoundTripTest : public testing::TestWithParam<AudioGainMode> {};
 TEST_P(AudioGainModeRoundTripTest, Aidl2Legacy2Aidl) {
     const auto initial = GetParam();
diff --git a/media/libaudiohal/impl/Android.bp b/media/libaudiohal/impl/Android.bp
index f5dec56..0dd0f74 100644
--- a/media/libaudiohal/impl/Android.bp
+++ b/media/libaudiohal/impl/Android.bp
@@ -201,11 +201,11 @@
         "latest_android_hardware_audio_core_sounddose_ndk_shared",
         "latest_android_hardware_audio_effect_ndk_shared",
         "latest_android_media_audio_common_types_ndk_shared",
-        "latest_av_audio_types_aidl_ndk_shared",
     ],
     shared_libs: [
         "android.hardware.common-V2-ndk",
         "android.hardware.common.fmq-V1-ndk",
+        "av-audio-types-aidl-ndk",
         "libaudio_aidl_conversion_common_cpp",
         "libaudio_aidl_conversion_common_ndk",
         "libaudio_aidl_conversion_common_ndk_cpp",
diff --git a/media/libaudiohal/impl/DeviceHalAidl.cpp b/media/libaudiohal/impl/DeviceHalAidl.cpp
index 629cd7c..0a131fa 100644
--- a/media/libaudiohal/impl/DeviceHalAidl.cpp
+++ b/media/libaudiohal/impl/DeviceHalAidl.cpp
@@ -79,13 +79,13 @@
 using aidl::android::hardware::audio::core::VendorParameter;
 
 #define RETURN_IF_MODULE_NOT_INIT(retVal)         \
-    if (mModule == nullptr) {                     \
+    if (!isModuleInitialized()) {                 \
         AUGMENT_LOG(E, "module not initialized"); \
         return retVal;                            \
     }
 
 #define RETURN_IF_TELEPHONY_NOT_INIT(retVal)         \
-    if (mTelephony == nullptr) {                     \
+    if (!isTelephonyInitialized()) {                  \
         AUGMENT_LOG(E, "telephony not initialized"); \
         return retVal;                               \
     }
@@ -124,12 +124,12 @@
                              const std::shared_ptr<IHalAdapterVendorExtension>& vext)
     : ConversionHelperAidl("DeviceHalAidl", instance),
       mModule(module),
-      mVendorExt(vext),
       mTelephony(retrieveSubInterface<ITelephony>(module, &IModule::getTelephony)),
       mBluetooth(retrieveSubInterface<IBluetooth>(module, &IModule::getBluetooth)),
       mBluetoothA2dp(retrieveSubInterface<IBluetoothA2dp>(module, &IModule::getBluetoothA2dp)),
       mBluetoothLe(retrieveSubInterface<IBluetoothLe>(module, &IModule::getBluetoothLe)),
       mSoundDose(retrieveSubInterface<ISoundDose>(module, &IModule::getSoundDose)),
+      mVendorExt(vext),
       mMapper(instance, module),
       mMapperAccessor(mMapper, mLock) {}
 
@@ -154,8 +154,11 @@
         return BAD_VALUE;
     }
     std::vector<AudioMode> aidlModes;
-    RETURN_STATUS_IF_ERROR(
-            statusTFromBinderStatus(mTelephony->getSupportedAudioModes(&aidlModes)));
+    {
+        std::lock_guard l(mLock);
+        RETURN_STATUS_IF_ERROR(
+                statusTFromBinderStatus(mTelephony->getSupportedAudioModes(&aidlModes)));
+    }
     *modes = VALUE_OR_RETURN_STATUS(
             ::aidl::android::convertContainer<std::vector<media::audio::common::AudioMode>>(
                     aidlModes, ndk2cpp_AudioMode));
@@ -182,8 +185,11 @@
     RETURN_IF_TELEPHONY_NOT_INIT(INVALID_OPERATION);
 
     ITelephony::TelecomConfig inConfig{.voiceVolume = Float{volume}}, outConfig;
-    RETURN_STATUS_IF_ERROR(
-            statusTFromBinderStatus(mTelephony->setTelecomConfig(inConfig, &outConfig)));
+    {
+        std::lock_guard l(mLock);
+        RETURN_STATUS_IF_ERROR(
+                statusTFromBinderStatus(mTelephony->setTelecomConfig(inConfig, &outConfig)));
+    }
     AUGMENT_LOG_IF(
             W, outConfig.voiceVolume.has_value() && volume != outConfig.voiceVolume.value().value,
             "the resulting voice volume %f is not the same as requested %f",
@@ -196,6 +202,7 @@
 
     TIME_CHECK();
     RETURN_IF_MODULE_NOT_INIT(NO_INIT);
+    std::lock_guard l(mLock);
     return statusTFromBinderStatus(mModule->setMasterVolume(volume));
 }
 
@@ -207,6 +214,7 @@
         AUGMENT_LOG(E, "uninitialized volumes");
         return BAD_VALUE;
     }
+    std::lock_guard l(mLock);
     return statusTFromBinderStatus(mModule->getMasterVolume(volume));
 }
 
@@ -216,6 +224,7 @@
     TIME_CHECK();
     RETURN_IF_MODULE_NOT_INIT(NO_INIT);
     AudioMode audioMode = VALUE_OR_FATAL(::aidl::android::legacy2aidl_audio_mode_t_AudioMode(mode));
+    std::lock_guard l(mLock);
     if (mTelephony != nullptr) {
         RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mTelephony->switchAudioMode(audioMode)));
     }
@@ -227,6 +236,7 @@
 
     TIME_CHECK();
     RETURN_IF_MODULE_NOT_INIT(NO_INIT);
+    std::lock_guard l(mLock);
     return statusTFromBinderStatus(mModule->setMicMute(state));
 }
 
@@ -239,6 +249,7 @@
         AUGMENT_LOG(E, "uninitialized mute state");
         return BAD_VALUE;
     }
+    std::lock_guard l(mLock);
     return statusTFromBinderStatus(mModule->getMicMute(state));
 }
 
@@ -247,6 +258,7 @@
 
     TIME_CHECK();
     RETURN_IF_MODULE_NOT_INIT(NO_INIT);
+    std::lock_guard l(mLock);
     return statusTFromBinderStatus(mModule->setMasterMute(state));
 }
 
@@ -259,6 +271,7 @@
         AUGMENT_LOG(E, "uninitialized mute state");
         return BAD_VALUE;
     }
+    std::lock_guard l(mLock);
     return statusTFromBinderStatus(mModule->getMasterMute(state));
 }
 
@@ -286,6 +299,7 @@
     if (status_t status = filterAndUpdateTelephonyParameters(parameters); status != OK) {
         AUGMENT_LOG(W, "filterAndUpdateTelephonyParameters failed: %d", status);
     }
+    std::lock_guard l(mLock);
     return parseAndSetVendorParameters(mVendorExt, mModule, parameters);
 }
 
@@ -306,6 +320,7 @@
         AUGMENT_LOG(W, "filterAndRetrieveBtLeParameters failed: %d", status);
     }
     *values = result.toString();
+    std::lock_guard l(mLock);
     return parseAndGetVendorParameters(mVendorExt, mModule, parameterKeys, values);
 }
 
@@ -526,7 +541,10 @@
     args.eventCallback = eventCb;
     args.sourceMetadata = aidlMetadata;
     ::aidl::android::hardware::audio::core::IModule::OpenOutputStreamReturn ret;
-    RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->openOutputStream(args, &ret)));
+    {
+        std::lock_guard l(mLock);
+        RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->openOutputStream(args, &ret)));
+    }
     StreamContextAidl context(ret.desc, isOffload, aidlHandle);
     if (!context.isValid()) {
         AUGMENT_LOG(E, "Failed to created a valid stream context from the descriptor: %s",
@@ -605,7 +623,10 @@
     args.sinkMetadata.tracks.push_back(std::move(aidlTrackMetadata));
     args.bufferSizeFrames = aidlConfig.frameCount;
     ::aidl::android::hardware::audio::core::IModule::OpenInputStreamReturn ret;
-    RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->openInputStream(args, &ret)));
+    {
+        std::lock_guard l(mLock);
+        RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->openInputStream(args, &ret)));
+    }
     StreamContextAidl context(ret.desc, false /*isAsynchronous*/, aidlHandle);
     if (!context.isValid()) {
         AUGMENT_LOG(E, "Failed to created a valid stream context from the descriptor: %s",
@@ -904,8 +925,11 @@
                     requestedPortConfig, {} /*destinationPortIds*/, &devicePortConfig, &cleanups));
     }
     auto aidlEffect = sp<effect::EffectHalAidl>::cast(effect);
-    RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->addDeviceEffect(
-                            devicePortConfig.id, aidlEffect->getIEffect())));
+    {
+        std::lock_guard l(mLock);
+        RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+                mModule->addDeviceEffect(devicePortConfig.id, aidlEffect->getIEffect())));
+    }
     cleanups.disarmAll();
     return OK;
 }
@@ -936,6 +960,7 @@
                         &devicePortConfig));
     }
     auto aidlEffect = sp<effect::EffectHalAidl>::cast(effect);
+    std::lock_guard l(mLock);
     return statusTFromBinderStatus(mModule->removeDeviceEffect(
                     devicePortConfig.id, aidlEffect->getIEffect()));
 }
@@ -953,9 +978,10 @@
 
     std::vector<AudioMMapPolicyInfo> mmapPolicyInfos;
 
-    if (status_t status = statusTFromBinderStatus(
-            mModule->getMmapPolicyInfos(mmapPolicyType, &mmapPolicyInfos)); status != OK) {
-        return status;
+    {
+        std::lock_guard l(mLock);
+        RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+                        mModule->getMmapPolicyInfos(mmapPolicyType, &mmapPolicyInfos)));
     }
 
     *policyInfos = VALUE_OR_RETURN_STATUS(
@@ -970,10 +996,8 @@
     TIME_CHECK();
     RETURN_IF_MODULE_NOT_INIT(NO_INIT);
     int32_t mixerBurstCount = 0;
-    if (mModule->getAAudioMixerBurstCount(&mixerBurstCount).isOk()) {
-        return mixerBurstCount;
-    }
-    return 0;
+    std::lock_guard l(mLock);
+    return mModule->getAAudioMixerBurstCount(&mixerBurstCount).isOk() ? mixerBurstCount : 0;
 }
 
 int32_t DeviceHalAidl::getAAudioHardwareBurstMinUsec() {
@@ -982,10 +1006,9 @@
     TIME_CHECK();
     RETURN_IF_MODULE_NOT_INIT(NO_INIT);
     int32_t hardwareBurstMinUsec = 0;
-    if (mModule->getAAudioHardwareBurstMinUsec(&hardwareBurstMinUsec).isOk()) {
-        return hardwareBurstMinUsec;
-    }
-    return 0;
+    std::lock_guard l(mLock);
+    return mModule->getAAudioHardwareBurstMinUsec(&hardwareBurstMinUsec).isOk() ?
+            hardwareBurstMinUsec : 0;
 }
 
 error::Result<audio_hw_sync_t> DeviceHalAidl::getHwAvSync() {
@@ -994,6 +1017,7 @@
     TIME_CHECK();
     RETURN_IF_MODULE_NOT_INIT(NO_INIT);
     int32_t aidlHwAvSync;
+    std::lock_guard l(mLock);
     RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->generateHwAvSyncId(&aidlHwAvSync)));
     return VALUE_OR_RETURN_STATUS(
             ::aidl::android::aidl2legacy_int32_t_audio_hw_sync_t(aidlHwAvSync));
@@ -1001,9 +1025,10 @@
 
 status_t DeviceHalAidl::dump(int fd, const Vector<String16>& args) {
     TIME_CHECK();
-    if (mModule == nullptr) return NO_INIT;
+    if (!isModuleInitialized()) return NO_INIT;
     Vector<String16> newArgs = args;
     newArgs.push(String16(kDumpFromAudioServerArgument));
+    std::lock_guard l(mLock);
     return mModule->dump(fd, Args(newArgs).args(), newArgs.size());
 }
 
@@ -1015,6 +1040,7 @@
     if (supports == nullptr) {
         return BAD_VALUE;
     }
+    std::lock_guard l(mLock);
     return statusTFromBinderStatus(mModule->supportsVariableLatency(supports));
 }
 
@@ -1028,15 +1054,15 @@
     }
     if (mSoundDose == nullptr) {
         AUGMENT_LOG(E, "failed to retrieve the sound dose interface");
-        return BAD_VALUE;
-    }
-
-    if (mSoundDose == nullptr) {
-        AUGMENT_LOG(E, "failed to return the sound dose interface not implemented");
         return NO_INIT;
     }
 
     *soundDoseBinder = mSoundDose->asBinder();
+    if (soundDoseBinder == nullptr) {
+        AUGMENT_LOG(E, "failed to return the sound dose interface not implemented");
+        return NO_INIT;
+    }
+
     AUGMENT_LOG(I, "using audio AIDL HAL sound dose interface");
     return OK;
 }
@@ -1116,10 +1142,8 @@
     AUGMENT_LOG(V);
     TIME_CHECK();
     RETURN_IF_MODULE_NOT_INIT(NO_INIT);
-    {
-        std::lock_guard l(mLock);
-        mMapper.resetUnusedPatchesAndPortConfigs();
-    }
+    std::lock_guard l(mLock);
+    mMapper.resetUnusedPatchesAndPortConfigs();
     ModuleDebug debug{ .simulateDeviceConnections = enabled };
     status_t status = statusTFromBinderStatus(mModule->setModuleDebug(debug));
     // This is important to log as it affects HAL behavior.
@@ -1135,6 +1159,7 @@
         AudioParameter &keys, AudioParameter *result) {
     if (String8 key = String8(AudioParameter::keyReconfigA2dpSupported); keys.containsKey(key)) {
         keys.remove(key);
+        std::lock_guard l(mLock);
         if (mBluetoothA2dp != nullptr) {
             bool supports;
             RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
@@ -1152,6 +1177,7 @@
         AudioParameter &keys, AudioParameter *result) {
     if (String8 key = String8(AudioParameter::keyReconfigLeSupported); keys.containsKey(key)) {
         keys.remove(key);
+        std::lock_guard l(mLock);
         if (mBluetoothLe != nullptr) {
             bool supports;
             RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
@@ -1192,6 +1218,7 @@
                 reconfigureOffload = std::move(result);
                 return OK;
             }));
+    std::lock_guard l(mLock);
     if (mBluetoothA2dp != nullptr && a2dpEnabled.has_value()) {
         return statusTFromBinderStatus(mBluetoothA2dp->setEnabled(a2dpEnabled.value()));
     }
@@ -1232,6 +1259,7 @@
                 }
                 return BAD_VALUE;
             }));
+    std::lock_guard l(mLock);
     if (mBluetooth != nullptr && hfpConfig != IBluetooth::HfpConfig{}) {
         IBluetooth::HfpConfig newHfpConfig;
         return statusTFromBinderStatus(mBluetooth->setHfpConfig(hfpConfig, &newHfpConfig));
@@ -1270,6 +1298,7 @@
                 }
                 return OK;
             }));
+    std::lock_guard l(mLock);
     if (mBluetoothLe != nullptr && leEnabled.has_value()) {
         return statusTFromBinderStatus(mBluetoothLe->setEnabled(leEnabled.value()));
     }
@@ -1330,6 +1359,7 @@
                             AudioParameter::keyBtScoWb, onOrOff.c_str());
                 return BAD_VALUE;
             }));
+    std::lock_guard l(mLock);
     if (mBluetooth != nullptr && scoConfig != IBluetooth::ScoConfig{}) {
         IBluetooth::ScoConfig newScoConfig;
         return statusTFromBinderStatus(mBluetooth->setScoConfig(scoConfig, &newScoConfig));
@@ -1352,6 +1382,7 @@
                                 AudioParameter::keyScreenState, onOrOff.c_str());
                     return BAD_VALUE;
                 }
+                std::lock_guard l(mLock);
                 return statusTFromBinderStatus(mModule->updateScreenState(isTurnedOn.value()));
             }));
     (void)VALUE_OR_RETURN_STATUS(filterOutAndProcessParameter<int>(
@@ -1376,6 +1407,7 @@
                                     AudioParameter::keyScreenRotation, rotationDegrees);
                         return BAD_VALUE;
                 }
+                std::lock_guard l(mLock);
                 return statusTFromBinderStatus(mModule->updateScreenRotation(rotation));
             }));
     return OK;
@@ -1418,6 +1450,7 @@
                             AudioParameter::keyHacSetting, onOrOff.c_str());
                 return BAD_VALUE;
             }));
+    std::lock_guard l(mLock);
     if (mTelephony != nullptr && telConfig != ITelephony::TelecomConfig{}) {
         ITelephony::TelecomConfig newTelConfig;
         return statusTFromBinderStatus(mTelephony->setTelecomConfig(telConfig, &newTelConfig));
diff --git a/media/libaudiohal/impl/DeviceHalAidl.h b/media/libaudiohal/impl/DeviceHalAidl.h
index 6ae6402..af8b423 100644
--- a/media/libaudiohal/impl/DeviceHalAidl.h
+++ b/media/libaudiohal/impl/DeviceHalAidl.h
@@ -235,19 +235,32 @@
     // MicrophoneInfoProvider implementation
     MicrophoneInfoProvider::Info const* getMicrophoneInfo() override;
 
-    const std::shared_ptr<::aidl::android::hardware::audio::core::IModule> mModule;
-    const std::shared_ptr<::aidl::android::media::audio::IHalAdapterVendorExtension> mVendorExt;
-    const std::shared_ptr<::aidl::android::hardware::audio::core::ITelephony> mTelephony;
-    const std::shared_ptr<::aidl::android::hardware::audio::core::IBluetooth> mBluetooth;
-    const std::shared_ptr<::aidl::android::hardware::audio::core::IBluetoothA2dp> mBluetoothA2dp;
-    const std::shared_ptr<::aidl::android::hardware::audio::core::IBluetoothLe> mBluetoothLe;
+    // See below, the lock is only used to serialize calling into the interface.
+    bool isModuleInitialized() const NO_THREAD_SAFETY_ANALYSIS { return mModule != nullptr; }
+    bool isTelephonyInitialized() const NO_THREAD_SAFETY_ANALYSIS { return mTelephony != nullptr; }
+
+    mutable std::mutex mLock;
+    // GUARDED_BY is used to prevent concurrent calls into these interfaces from multiple threads.
+    // There is no requirement for IModule and its helper interfaces implementations
+    // to be thread-safe.
+    const std::shared_ptr<::aidl::android::hardware::audio::core::IModule> mModule
+            GUARDED_BY(mLock);
+    const std::shared_ptr<::aidl::android::hardware::audio::core::ITelephony> mTelephony
+            GUARDED_BY(mLock);
+    const std::shared_ptr<::aidl::android::hardware::audio::core::IBluetooth> mBluetooth
+            GUARDED_BY(mLock);
+    const std::shared_ptr<::aidl::android::hardware::audio::core::IBluetoothA2dp> mBluetoothA2dp
+            GUARDED_BY(mLock);
+    const std::shared_ptr<::aidl::android::hardware::audio::core::IBluetoothLe> mBluetoothLe
+            GUARDED_BY(mLock);
+
     const std::shared_ptr<::aidl::android::hardware::audio::core::sounddose::ISoundDose> mSoundDose;
+    const std::shared_ptr<::aidl::android::media::audio::IHalAdapterVendorExtension> mVendorExt;
 
     std::mutex mCallbacksLock;
     // Use 'mCallbacksLock' only to implement exclusive access to 'mCallbacks'. Never hold it
     // while making any calls.
     std::map<void*, Callbacks> mCallbacks GUARDED_BY(mCallbacksLock);
-    std::mutex mLock;
     std::set<audio_port_handle_t> mDeviceDisconnectionNotified GUARDED_BY(mLock);
     Hal2AidlMapper mMapper GUARDED_BY(mLock);
     LockedAccessor<Hal2AidlMapper> mMapperAccessor;
diff --git a/media/libaudiohal/impl/EffectHalAidl.cpp b/media/libaudiohal/impl/EffectHalAidl.cpp
index 9fdde49..658fc18b 100644
--- a/media/libaudiohal/impl/EffectHalAidl.cpp
+++ b/media/libaudiohal/impl/EffectHalAidl.cpp
@@ -184,7 +184,7 @@
 status_t EffectHalAidl::process() {
     State state = State::INIT;
     if (mConversion->isBypassing() || !mEffect->getState(&state).isOk() ||
-        state != State::PROCESSING) {
+        (state != State::PROCESSING && state != State::DRAINING)) {
         ALOGI("%s skipping process because it's %s", mEffectName.c_str(),
               mConversion->isBypassing()
                       ? "bypassing"
diff --git a/media/libaudiohal/impl/StreamHalAidl.cpp b/media/libaudiohal/impl/StreamHalAidl.cpp
index 0d65f8c..e138cea 100644
--- a/media/libaudiohal/impl/StreamHalAidl.cpp
+++ b/media/libaudiohal/impl/StreamHalAidl.cpp
@@ -232,7 +232,9 @@
             RETURN_STATUS_IF_ERROR(pause(&reply));
             if (reply.state != StreamDescriptor::State::PAUSED &&
                     reply.state != StreamDescriptor::State::DRAIN_PAUSED &&
-                    reply.state != StreamDescriptor::State::TRANSFER_PAUSED) {
+                    reply.state != StreamDescriptor::State::TRANSFER_PAUSED &&
+                    (state != StreamDescriptor::State::DRAINING ||
+                        reply.state != StreamDescriptor::State::IDLE)) {
                 AUGMENT_LOG(E, "unexpected stream state: %s (expected PAUSED)",
                             toString(reply.state).c_str());
                 return INVALID_OPERATION;
diff --git a/media/libaudiohal/tests/EffectHalVersionCompatibility_test.cpp b/media/libaudiohal/tests/EffectHalVersionCompatibility_test.cpp
index e8731ea..c11f908 100644
--- a/media/libaudiohal/tests/EffectHalVersionCompatibility_test.cpp
+++ b/media/libaudiohal/tests/EffectHalVersionCompatibility_test.cpp
@@ -83,6 +83,7 @@
         {Parameter::Id::visualizerTag, 1},
         {Parameter::Id::volumeTag, 1},
         {Parameter::Id::spatializerTag, 2},
+        {Parameter::Id::eraserTag, 3},
 };
 // Tags defined Parameter::Specific union.
 static const std::unordered_map<Parameter::Specific::Tag, int /* version */>
@@ -104,6 +105,7 @@
                 {Parameter::Specific::visualizer, 1},
                 {Parameter::Specific::volume, 1},
                 {Parameter::Specific::spatializer, 2},
+                {Parameter::Specific::eraser, 3},
 };
 
 class MockFactory : public IFactory {
@@ -223,6 +225,7 @@
             case Parameter::Id::virtualizerTag:
             case Parameter::Id::visualizerTag:
             case Parameter::Id::volumeTag:
+            case Parameter::Id::eraserTag:
                 FALLTHROUGH_INTENDED;
             case Parameter::Id::spatializerTag: {
                 if (kParamIdEffectVersionMap.find(idTag) != kParamIdEffectVersionMap.end() &&
diff --git a/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp b/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp
index d5e3cf7..5574ea1 100644
--- a/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp
+++ b/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp
@@ -90,6 +90,23 @@
     }
 }
 
+RetCode BundleContext::setCommon(const Parameter::Common& common) {
+    RetCode ret = EffectContext::setCommon(common);
+    RETURN_VALUE_IF(ret != RetCode::SUCCESS, ret, " setCommonFailed");
+    if (mInstance) {
+        LVM_ControlParams_t params;
+        RETURN_VALUE_IF(LVM_SUCCESS != LVM_GetControlParameters(mInstance, &params),
+                        RetCode::ERROR_EFFECT_LIB_ERROR, "failGetControlParams");
+        RETURN_VALUE_IF(RetCode::SUCCESS != applyCommonParameter(params),
+                        RetCode::ERROR_EFFECT_LIB_ERROR, " applyCommonParameterFailed");
+        RETURN_VALUE_IF(LVM_SUCCESS != LVM_SetControlParameters(mInstance, &params),
+                        RetCode::ERROR_EFFECT_LIB_ERROR, "failSetControlParams");
+    } else {
+        RETURN_VALUE_IF(RetCode::SUCCESS != init(), RetCode::ERROR_EFFECT_LIB_ERROR, " initFailed");
+    }
+    return RetCode::SUCCESS;
+}
+
 RetCode BundleContext::enable() {
     if (mEnabled) return RetCode::ERROR_ILLEGAL_PARAMETER;
     // Bass boost or Virtualizer can be temporarily disabled if playing over device speaker due to
@@ -599,7 +616,7 @@
     return ret;
 }
 
-RetCode BundleContext::initControlParameter(LVM_ControlParams_t& params) const {
+RetCode BundleContext::applyCommonParameter(LVM_ControlParams_t& params) const {
     int outputChannelCount = ::aidl::android::hardware::audio::common::getChannelCount(
             mCommon.output.base.channelMask);
     auto outputChannelMaskConv = aidl2legacy_AudioChannelLayout_audio_channel_mask_t(
@@ -621,6 +638,13 @@
         params.SourceFormat = LVM_MULTICHANNEL;
     }
 
+    return RetCode::SUCCESS;
+}
+
+RetCode BundleContext::initControlParameter(LVM_ControlParams_t& params) const {
+    RETURN_VALUE_IF(RetCode::SUCCESS != applyCommonParameter(params),
+                    RetCode::ERROR_EFFECT_LIB_ERROR, " applyCommonParameterFailed");
+
     /* General parameters */
     params.OperatingMode = LVM_MODE_ON;
     params.SpeakerType = LVM_HEADPHONES;
diff --git a/media/libeffects/lvm/wrapper/Aidl/BundleContext.h b/media/libeffects/lvm/wrapper/Aidl/BundleContext.h
index e5ab40d..96f63cd 100644
--- a/media/libeffects/lvm/wrapper/Aidl/BundleContext.h
+++ b/media/libeffects/lvm/wrapper/Aidl/BundleContext.h
@@ -35,6 +35,8 @@
     void deInit();
     lvm::BundleEffectType getBundleType() const { return mType; }
 
+    RetCode setCommon(const Parameter::Common& common) override;
+
     RetCode enable() override;
     RetCode enableOperatingMode();
     RetCode disable() override;
@@ -133,6 +135,7 @@
     bool isBandLevelIndexInRange(const std::vector<Equalizer::BandLevel>& bandLevels) const;
     static LVM_EQNB_BandDef_t* getDefaultEqualizerBandDefs();
     static LVM_HeadroomBandDef_t* getDefaultEqualizerHeadroomBanDefs();
+    RetCode applyCommonParameter(LVM_ControlParams_t& params) const;
 };
 
 }  // namespace aidl::android::hardware::audio::effect
diff --git a/media/libeffects/preprocessing/Android.bp b/media/libeffects/preprocessing/Android.bp
index 44b7d97..d791fab 100644
--- a/media/libeffects/preprocessing/Android.bp
+++ b/media/libeffects/preprocessing/Android.bp
@@ -55,8 +55,8 @@
     defaults: ["libaudiopreprocessing-defaults"],
     relative_install_path: "soundfx",
     srcs: ["PreProcessing.cpp"],
-    header_libs: [
-        "libwebrtc_absl_headers",
+    static_libs: [
+        "libabsl",
     ],
 }
 
@@ -77,6 +77,7 @@
         "libutils",
     ],
     static_libs: [
+        "libabsl",
         "webrtc_audio_processing",
     ],
     header_libs: [
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 8a962c6..ee6ad00 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -260,6 +260,8 @@
     srcs: [
         "AudioCapabilities.cpp",
         "CodecCapabilities.cpp",
+        "EncoderCapabilities.cpp",
+        "VideoCapabilities.cpp",
         "CodecCapabilitiesUtils.cpp",
     ],
 
diff --git a/media/libmedia/AudioCapabilities.cpp b/media/libmedia/AudioCapabilities.cpp
index 908989f..dbbe9e8 100644
--- a/media/libmedia/AudioCapabilities.cpp
+++ b/media/libmedia/AudioCapabilities.cpp
@@ -26,23 +26,23 @@
 
 namespace android {
 
-const Range<int>& AudioCapabilities::getBitrateRange() const {
+const Range<int32_t>& AudioCapabilities::getBitrateRange() const {
     return mBitrateRange;
 }
 
-const std::vector<int>& AudioCapabilities::getSupportedSampleRates() const {
+const std::vector<int32_t>& AudioCapabilities::getSupportedSampleRates() const {
     return mSampleRates;
 }
 
-const std::vector<Range<int>>&
+const std::vector<Range<int32_t>>&
         AudioCapabilities::getSupportedSampleRateRanges() const {
     return mSampleRateRanges;
 }
 
-int AudioCapabilities::getMaxInputChannelCount() const {
-    int overallMax = 0;
+int32_t AudioCapabilities::getMaxInputChannelCount() const {
+    int32_t overallMax = 0;
     for (int i = mInputChannelRanges.size() - 1; i >= 0; i--) {
-        int lmax = mInputChannelRanges[i].upper();
+        int32_t lmax = mInputChannelRanges[i].upper();
         if (lmax > overallMax) {
             overallMax = lmax;
         }
@@ -50,10 +50,10 @@
     return overallMax;
 }
 
-int AudioCapabilities::getMinInputChannelCount() const {
-    int overallMin = MAX_INPUT_CHANNEL_COUNT;
+int32_t AudioCapabilities::getMinInputChannelCount() const {
+    int32_t overallMin = MAX_INPUT_CHANNEL_COUNT;
     for (int i = mInputChannelRanges.size() - 1; i >= 0; i--) {
-        int lmin = mInputChannelRanges[i].lower();
+        int32_t lmin = mInputChannelRanges[i].lower();
         if (lmin < overallMin) {
             overallMin = lmin;
         }
@@ -61,7 +61,7 @@
     return overallMin;
 }
 
-const std::vector<Range<int>>&
+const std::vector<Range<int32_t>>&
         AudioCapabilities::getInputChannelCountRanges() const {
     return mInputChannelRanges;
 }
@@ -86,40 +86,41 @@
 }
 
 void AudioCapabilities::initWithPlatformLimits() {
-    mBitrateRange = Range<int>(0, INT_MAX);
-    mInputChannelRanges.push_back(Range<int>(1, MAX_INPUT_CHANNEL_COUNT));
+    mBitrateRange = Range<int32_t>(0, INT32_MAX);
+    mInputChannelRanges.push_back(Range<int32_t>(1, MAX_INPUT_CHANNEL_COUNT));
 
-    const int minSampleRate = base::GetIntProperty("ro.mediacodec.min_sample_rate", 7350);
-    const int maxSampleRate = base::GetIntProperty("ro.mediacodec.max_sample_rate", 192000);
-    mSampleRateRanges.push_back(Range<int>(minSampleRate, maxSampleRate));
+    const int32_t minSampleRate = base::GetIntProperty("ro.mediacodec.min_sample_rate", 7350);
+    const int32_t maxSampleRate = base::GetIntProperty("ro.mediacodec.max_sample_rate", 192000);
+    mSampleRateRanges.push_back(Range<int32_t>(minSampleRate, maxSampleRate));
 }
 
-bool AudioCapabilities::supports(std::optional<int> sampleRate,
-        std::optional<int> inputChannels) {
+bool AudioCapabilities::supports(std::optional<int32_t> sampleRate,
+        std::optional<int32_t> inputChannels) {
     // channels and sample rates are checked orthogonally
     if (inputChannels
             && !std::any_of(mInputChannelRanges.begin(), mInputChannelRanges.end(),
-            [inputChannels](const Range<int> &a) { return a.contains(inputChannels.value()); })) {
+            [inputChannels](const Range<int32_t> &a) {
+                    return a.contains(inputChannels.value()); })) {
         return false;
     }
     if (sampleRate
             && !std::any_of(mSampleRateRanges.begin(), mSampleRateRanges.end(),
-            [sampleRate](const Range<int> &a) { return a.contains(sampleRate.value()); })) {
+            [sampleRate](const Range<int32_t> &a) { return a.contains(sampleRate.value()); })) {
         return false;
     }
     return true;
 }
 
-bool AudioCapabilities::isSampleRateSupported(int sampleRate) {
-    return supports(std::make_optional<int>(sampleRate), std::nullopt);
+bool AudioCapabilities::isSampleRateSupported(int32_t sampleRate) {
+    return supports(std::make_optional<int32_t>(sampleRate), std::nullopt);
 }
 
-void AudioCapabilities::limitSampleRates(std::vector<int> rates) {
-    std::vector<Range<int>> sampleRateRanges;
+void AudioCapabilities::limitSampleRates(std::vector<int32_t> rates) {
+    std::vector<Range<int32_t>> sampleRateRanges;
     std::sort(rates.begin(), rates.end());
-    for (int rate : rates) {
-        if (supports(std::make_optional<int>(rate), std::nullopt /* channels */)) {
-            sampleRateRanges.push_back(Range<int>(rate, rate));
+    for (int32_t rate : rates) {
+        if (supports(std::make_optional<int32_t>(rate), std::nullopt /* channels */)) {
+            sampleRateRanges.push_back(Range<int32_t>(rate, rate));
         }
     }
     mSampleRateRanges = intersectSortedDistinctRanges(mSampleRateRanges, sampleRateRanges);
@@ -133,11 +134,11 @@
     }
 }
 
-void AudioCapabilities::limitSampleRates(std::vector<Range<int>> rateRanges) {
+void AudioCapabilities::limitSampleRates(std::vector<Range<int32_t>> rateRanges) {
     sortDistinctRanges(&rateRanges);
     mSampleRateRanges = intersectSortedDistinctRanges(mSampleRateRanges, rateRanges);
     // check if all values are discrete
-    for (Range<int> range: mSampleRateRanges) {
+    for (Range<int32_t> range: mSampleRateRanges) {
         if (range.lower() != range.upper()) {
             mSampleRates.clear();
             return;
@@ -147,10 +148,10 @@
 }
 
 void AudioCapabilities::applyLevelLimits() {
-    std::vector<int> sampleRates;
-    std::optional<Range<int>> sampleRateRange;
-    std::optional<Range<int>> bitRates;
-    int maxChannels = MAX_INPUT_CHANNEL_COUNT;
+    std::vector<int32_t> sampleRates;
+    std::optional<Range<int32_t>> sampleRateRange;
+    std::optional<Range<int32_t>> bitRates;
+    int32_t maxChannels = MAX_INPUT_CHANNEL_COUNT;
 
     // const char *mediaType = mMediaType.c_str();
     if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_MPEG)) {
@@ -158,15 +159,15 @@
                 8000, 11025, 12000,
                 16000, 22050, 24000,
                 32000, 44100, 48000 };
-        bitRates = Range<int>(8000, 320000);
+        bitRates = Range<int32_t>(8000, 320000);
         maxChannels = 2;
     } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AMR_NB)) {
         sampleRates = { 8000 };
-        bitRates = Range<int>(4750, 12200);
+        bitRates = Range<int32_t>(4750, 12200);
         maxChannels = 1;
     } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AMR_WB)) {
         sampleRates = { 16000 };
-        bitRates = Range<int>(6600, 23850);
+        bitRates = Range<int32_t>(6600, 23850);
         maxChannels = 1;
     } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AAC)) {
         sampleRates = {
@@ -175,32 +176,32 @@
                 22050, 24000, 32000,
                 44100, 48000, 64000,
                 88200, 96000 };
-        bitRates = Range<int>(8000, 510000);
+        bitRates = Range<int32_t>(8000, 510000);
         maxChannels = 48;
     } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_VORBIS)) {
-        bitRates = Range<int>(32000, 500000);
-        sampleRateRange = Range<int>(8000, 192000);
+        bitRates = Range<int32_t>(32000, 500000);
+        sampleRateRange = Range<int32_t>(8000, 192000);
         maxChannels = 255;
     } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_OPUS)) {
-        bitRates = Range<int>(6000, 510000);
+        bitRates = Range<int32_t>(6000, 510000);
         sampleRates = { 8000, 12000, 16000, 24000, 48000 };
         maxChannels = 255;
     } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_RAW)) {
-        sampleRateRange = Range<int>(1, 192000);
-        bitRates = Range<int>(1, 10000000);
+        sampleRateRange = Range<int32_t>(1, 192000);
+        bitRates = Range<int32_t>(1, 10000000);
         maxChannels = MAX_NUM_CHANNELS;
     } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_FLAC)) {
-        sampleRateRange = Range<int>(1, 655350);
+        sampleRateRange = Range<int32_t>(1, 655350);
         // lossless codec, so bitrate is ignored
         maxChannels = 255;
     } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_G711_ALAW)
             || base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_G711_MLAW)) {
         sampleRates = { 8000 };
-        bitRates = Range<int>(64000, 64000);
+        bitRates = Range<int32_t>(64000, 64000);
         // platform allows multiple channels for this format
     } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_MSGSM)) {
         sampleRates = { 8000 };
-        bitRates = Range<int>(13000, 13000);
+        bitRates = Range<int32_t>(13000, 13000);
         maxChannels = 1;
     } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AC3)) {
         maxChannels = 6;
@@ -208,34 +209,34 @@
         maxChannels = 16;
     } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_EAC3_JOC)) {
         sampleRates = { 48000 };
-        bitRates = Range<int>(32000, 6144000);
+        bitRates = Range<int32_t>(32000, 6144000);
         maxChannels = 16;
     } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AC4)) {
         sampleRates = { 44100, 48000, 96000, 192000 };
-        bitRates = Range<int>(16000, 2688000);
+        bitRates = Range<int32_t>(16000, 2688000);
         maxChannels = 24;
     } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_DTS)) {
         sampleRates = { 44100, 48000 };
-        bitRates = Range<int>(96000, 1524000);
+        bitRates = Range<int32_t>(96000, 1524000);
         maxChannels = 6;
     } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_DTS_HD)) {
         for (ProfileLevel profileLevel: mProfileLevels) {
             switch (profileLevel.mProfile) {
                 case DTS_HDProfileLBR:
                     sampleRates = { 22050, 24000, 44100, 48000 };
-                    bitRates = Range<int>(32000, 768000);
+                    bitRates = Range<int32_t>(32000, 768000);
                     break;
                 case DTS_HDProfileHRA:
                 case DTS_HDProfileMA:
                     sampleRates = { 44100, 48000, 88200, 96000, 176400, 192000 };
-                    bitRates = Range<int>(96000, 24500000);
+                    bitRates = Range<int32_t>(96000, 24500000);
                     break;
                 default:
                     ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile,
                             mMediaType.c_str());
                     mError |= ERROR_CAPABILITIES_UNRECOGNIZED;
                     sampleRates = { 44100, 48000, 88200, 96000, 176400, 192000 };
-                    bitRates = Range<int>(96000, 24500000);
+                    bitRates = Range<int32_t>(96000, 24500000);
             }
         }
         maxChannels = 8;
@@ -244,12 +245,12 @@
             switch (profileLevel.mProfile) {
                 case DTS_UHDProfileP2:
                     sampleRates = { 48000 };
-                    bitRates = Range<int>(96000, 768000);
+                    bitRates = Range<int32_t>(96000, 768000);
                     maxChannels = 10;
                     break;
                 case DTS_UHDProfileP1:
                     sampleRates = { 44100, 48000, 88200, 96000, 176400, 192000 };
-                    bitRates = Range<int>(96000, 24500000);
+                    bitRates = Range<int32_t>(96000, 24500000);
                     maxChannels = 32;
                     break;
                 default:
@@ -257,7 +258,7 @@
                             mMediaType.c_str());
                     mError |= ERROR_CAPABILITIES_UNRECOGNIZED;
                     sampleRates = { 44100, 48000, 88200, 96000, 176400, 192000 };
-                    bitRates = Range<int>(96000, 24500000);
+                    bitRates = Range<int32_t>(96000, 24500000);
                     maxChannels = 32;
             }
         }
@@ -270,24 +271,24 @@
     if (!sampleRates.empty()) {
         limitSampleRates(sampleRates);
     } else if (sampleRateRange) {
-        std::vector<Range<int>> rateRanges = { sampleRateRange.value() };
+        std::vector<Range<int32_t>> rateRanges = { sampleRateRange.value() };
         limitSampleRates(rateRanges);
     }
 
-    Range<int> channelRange = Range<int>(1, maxChannels);
-    std::vector<Range<int>> inputChannels = { channelRange };
+    Range<int32_t> channelRange = Range<int32_t>(1, maxChannels);
+    std::vector<Range<int32_t>> inputChannels = { channelRange };
     applyLimits(inputChannels, bitRates);
 }
 
 void AudioCapabilities::applyLimits(
-        const std::vector<Range<int>> &inputChannels,
-        const std::optional<Range<int>> &bitRates) {
+        const std::vector<Range<int32_t>> &inputChannels,
+        const std::optional<Range<int32_t>> &bitRates) {
     // clamp & make a local copy
-    std::vector<Range<int>> inputChannelsCopy(inputChannels.size());
+    std::vector<Range<int32_t>> inputChannelsCopy(inputChannels.size());
     for (int i = 0; i < inputChannels.size(); i++) {
-        int lower = inputChannels[i].clamp(1);
-        int upper = inputChannels[i].clamp(MAX_INPUT_CHANNEL_COUNT);
-        inputChannelsCopy[i] = Range<int>(lower, upper);
+        int32_t lower = inputChannels[i].clamp(1);
+        int32_t upper = inputChannels[i].clamp(MAX_INPUT_CHANNEL_COUNT);
+        inputChannelsCopy[i] = Range<int32_t>(lower, upper);
     }
 
     // sort, intersect with existing, & save channel list
@@ -300,16 +301,16 @@
 }
 
 void AudioCapabilities::parseFromInfo(const sp<AMessage> &format) {
-    int maxInputChannels = MAX_INPUT_CHANNEL_COUNT;
-    std::vector<Range<int>> channels = { Range<int>(1, maxInputChannels) };
-    std::optional<Range<int>> bitRates = POSITIVE_INTEGERS;
+    int32_t maxInputChannels = MAX_INPUT_CHANNEL_COUNT;
+    std::vector<Range<int32_t>> channels = { Range<int32_t>(1, maxInputChannels) };
+    std::optional<Range<int32_t>> bitRates = POSITIVE_INT32;
 
     AString rateAString;
     if (format->findString("sample-rate-ranges", &rateAString)) {
         std::vector<std::string> rateStrings = base::Split(std::string(rateAString.c_str()), ",");
-        std::vector<Range<int>> rateRanges;
+        std::vector<Range<int32_t>> rateRanges;
         for (std::string rateString : rateStrings) {
-            std::optional<Range<int>> rateRange = ParseIntRange(rateString);
+            std::optional<Range<int32_t>> rateRange = Range<int32_t>::Parse(rateString);
             if (!rateRange) {
                 continue;
             }
@@ -322,9 +323,9 @@
     AString valueStr;
     if (format->findString("channel-ranges", &valueStr)) {
         std::vector<std::string> channelStrings = base::Split(std::string(valueStr.c_str()), ",");
-        std::vector<Range<int>> channelRanges;
+        std::vector<Range<int32_t>> channelRanges;
         for (std::string channelString : channelStrings) {
-            std::optional<Range<int>> channelRange = ParseIntRange(channelString);
+            std::optional<Range<int32_t>> channelRange = Range<int32_t>::Parse(channelString);
             if (!channelRange) {
                 continue;
             }
@@ -332,24 +333,25 @@
         }
         channels = channelRanges;
     } else if (format->findString("channel-range", &valueStr)) {
-        std::optional<Range<int>> oneRange = ParseIntRange(std::string(valueStr.c_str()));
+        std::optional<Range<int32_t>> oneRange
+                = Range<int32_t>::Parse(std::string(valueStr.c_str()));
         if (oneRange) {
             channels = { oneRange.value() };
         }
     } else if (format->findString("max-channel-count", &valueStr)) {
         maxInputChannels = std::atoi(valueStr.c_str());
         if (maxInputChannels == 0) {
-            channels = { Range<int>(0, 0) };
+            channels = { Range<int32_t>(0, 0) };
         } else {
-            channels = { Range<int>(1, maxInputChannels) };
+            channels = { Range<int32_t>(1, maxInputChannels) };
         }
     } else if ((mError & ERROR_CAPABILITIES_UNSUPPORTED) != 0) {
         maxInputChannels = 0;
-        channels = { Range<int>(0, 0) };
+        channels = { Range<int32_t>(0, 0) };
     }
 
     if (format->findString("bitrate-range", &valueStr)) {
-        std::optional<Range<int>> parsedBitrate = ParseIntRange(valueStr.c_str());
+        std::optional<Range<int32_t>> parsedBitrate = Range<int32_t>::Parse(valueStr.c_str());
         if (parsedBitrate) {
             bitRates = bitRates.value().intersect(parsedBitrate.value());
         }
@@ -374,11 +376,11 @@
 
 bool AudioCapabilities::supportsFormat(const sp<AMessage> &format) {
     int32_t sampleRateValue;
-    std::optional<int> sampleRate = format->findInt32(KEY_SAMPLE_RATE, &sampleRateValue)
-            ? std::make_optional<int>(sampleRateValue) : std::nullopt;
+    std::optional<int32_t> sampleRate = format->findInt32(KEY_SAMPLE_RATE, &sampleRateValue)
+            ? std::make_optional<int32_t>(sampleRateValue) : std::nullopt;
     int32_t channelsValue;
-    std::optional<int> channels = format->findInt32(KEY_CHANNEL_COUNT, &channelsValue)
-            ? std::make_optional<int>(channelsValue) : std::nullopt;
+    std::optional<int32_t> channels = format->findInt32(KEY_CHANNEL_COUNT, &channelsValue)
+            ? std::make_optional<int32_t>(channelsValue) : std::nullopt;
 
     if (!supports(sampleRate, channels)) {
         return false;
diff --git a/media/libmedia/CodecCapabilities.cpp b/media/libmedia/CodecCapabilities.cpp
index 5bed1c4..407d376 100644
--- a/media/libmedia/CodecCapabilities.cpp
+++ b/media/libmedia/CodecCapabilities.cpp
@@ -17,6 +17,7 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "CodecCapabilities"
 
+#include <android-base/strings.h>
 #include <utils/Log.h>
 #include <media/CodecCapabilities.h>
 #include <media/CodecCapabilitiesUtils.h>
@@ -25,7 +26,59 @@
 
 namespace android {
 
-bool CodecCapabilities::SupportsBitrate(Range<int> bitrateRange,
+static const int32_t HEVCHighTierLevels =
+        HEVCHighTierLevel1 | HEVCHighTierLevel2 | HEVCHighTierLevel21 | HEVCHighTierLevel3 |
+        HEVCHighTierLevel31 | HEVCHighTierLevel4 | HEVCHighTierLevel41 | HEVCHighTierLevel5 |
+        HEVCHighTierLevel51 | HEVCHighTierLevel52 | HEVCHighTierLevel6 | HEVCHighTierLevel61 |
+        HEVCHighTierLevel62;
+
+static const int32_t DEFAULT_MAX_SUPPORTED_INSTANCES = 32;
+static const int32_t MAX_SUPPORTED_INSTANCES_LIMIT = 256;
+
+// must not contain KEY_PROFILE
+static const std::set<std::pair<std::string, AMessage::Type>> AUDIO_LEVEL_CRITICAL_FORMAT_KEYS = {
+    // We don't set level-specific limits for audio codecs today. Key candidates would
+    // be sample rate, bit rate or channel count.
+    // MediaFormat.KEY_SAMPLE_RATE,
+    // MediaFormat.KEY_CHANNEL_COUNT,
+    // MediaFormat.KEY_BIT_RATE,
+    { KEY_MIME, AMessage::kTypeString }
+};
+
+// CodecCapabilities Features
+static const std::vector<Feature> DECODER_FEATURES = {
+    Feature(FEATURE_AdaptivePlayback, (1 << 0), true),
+    Feature(FEATURE_SecurePlayback,   (1 << 1), false),
+    Feature(FEATURE_TunneledPlayback, (1 << 2), false),
+    Feature(FEATURE_PartialFrame,     (1 << 3), false),
+    Feature(FEATURE_FrameParsing,     (1 << 4), false),
+    Feature(FEATURE_MultipleFrames,   (1 << 5), false),
+    Feature(FEATURE_DynamicTimestamp, (1 << 6), false),
+    Feature(FEATURE_LowLatency,       (1 << 7), true),
+    // feature to exclude codec from REGULAR codec list
+    Feature(FEATURE_SpecialCodec,     (1 << 30), false, true),
+};
+static const std::vector<Feature> ENCODER_FEATURES = {
+    Feature(FEATURE_IntraRefresh, (1 << 0), false),
+    Feature(FEATURE_MultipleFrames, (1 << 1), false),
+    Feature(FEATURE_DynamicTimestamp, (1 << 2), false),
+    Feature(FEATURE_QpBounds, (1 << 3), false),
+    Feature(FEATURE_EncodingStatistics, (1 << 4), false),
+    Feature(FEATURE_HdrEditing, (1 << 5), false),
+    // feature to exclude codec from REGULAR codec list
+    Feature(FEATURE_SpecialCodec,     (1 << 30), false, true),
+};
+
+// must not contain KEY_PROFILE
+static const std::set<std::pair<std::string, AMessage::Type>> VIDEO_LEVEL_CRITICAL_FORMAT_KEYS = {
+    { KEY_WIDTH, AMessage::kTypeInt32 },
+    { KEY_HEIGHT, AMessage::kTypeInt32 },
+    { KEY_FRAME_RATE, AMessage::kTypeInt32 },
+    { KEY_BIT_RATE, AMessage::kTypeInt32 },
+    { KEY_MIME, AMessage::kTypeString }
+};
+
+bool CodecCapabilities::SupportsBitrate(Range<int32_t> bitrateRange,
         const sp<AMessage> &format) {
     // consider max bitrate over average bitrate for support
     int32_t maxBitrate = 0;
@@ -46,6 +99,212 @@
     return true;
 }
 
+bool CodecCapabilities::isFeatureSupported(const std::string &name) const {
+    return mFeaturesSupported.contains(name);
+}
+
+bool CodecCapabilities::isFeatureRequired(const std::string &name) const {
+    return mFeaturesRequired.contains(name);
+}
+
+std::vector<std::string> CodecCapabilities::validFeatures() const {
+    std::vector<std::string> res;
+    for (const Feature& feature : getValidFeatures()) {
+        if (!feature.mInternal) {
+            res.push_back(feature.mName);
+        }
+    }
+    return res;
+}
+
+std::vector<Feature> CodecCapabilities::getValidFeatures() const {
+    if (isEncoder()) {
+        return ENCODER_FEATURES;
+    } else {
+        return DECODER_FEATURES;
+    }
+}
+
+bool CodecCapabilities::isRegular() const {
+    // regular codecs only require default features
+    std::vector<Feature> features = getValidFeatures();
+    return std::all_of(features.begin(), features.end(),
+            [this](Feature feat){ return (feat.mDefault || !isFeatureRequired(feat.mName)); });
+}
+
+bool CodecCapabilities::isFormatSupported(const sp<AMessage> &format) const {
+    AString mediaType;
+    format->findString(KEY_MIME, &mediaType);
+    // mediaType must match if present
+    if (!base::EqualsIgnoreCase(mMediaType, mediaType.c_str())) {
+        return false;
+    }
+
+    // check feature support
+    for (Feature feat: getValidFeatures()) {
+        if (feat.mInternal) {
+            continue;
+        }
+
+        int32_t yesNo;
+        std::string key = KEY_FEATURE_;
+        key = key + feat.mName;
+        if (format->findInt32(key.c_str(), &yesNo)) {
+            continue;
+        }
+        if ((yesNo == 1 && !isFeatureSupported(feat.mName)) ||
+                (yesNo == 0 && isFeatureRequired(feat.mName))) {
+            return false;
+        }
+    }
+
+    int32_t profile;
+    if (format->findInt32(KEY_PROFILE, &profile)) {
+        int32_t level = -1;
+        format->findInt32(KEY_LEVEL, &level);
+        if (!supportsProfileLevel(profile, level)) {
+            return false;
+        }
+
+        // If we recognize this profile, check that this format is supported by the
+        // highest level supported by the codec for that profile. (Ignore specified
+        // level beyond the above profile/level check as level is only used as a
+        // guidance. E.g. AVC Level 1 CIF format is supported if codec supports level 1.1
+        // even though max size for Level 1 is QCIF. However, MPEG2 Simple Profile
+        // 1080p format is not supported even if codec supports Main Profile Level High,
+        // as Simple Profile does not support 1080p.
+        int32_t maxLevel = 0;
+        for (ProfileLevel pl : mProfileLevels) {
+            if (pl.mProfile == profile && pl.mLevel > maxLevel) {
+                // H.263 levels are not completely ordered:
+                // Level45 support only implies Level10 support
+                if (!base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_H263)
+                        || pl.mLevel != H263Level45
+                        || maxLevel == H263Level10) {
+                    maxLevel = pl.mLevel;
+                }
+            }
+        }
+        std::shared_ptr<CodecCapabilities> levelCaps
+                = CreateFromProfileLevel(mMediaType, profile, maxLevel);
+        // We must remove the profile from this format otherwise levelCaps.isFormatSupported
+        // will get into this same condition and loop forever. Furthermore, since levelCaps
+        // does not contain features and bitrate specific keys, keep only keys relevant for
+        // a level check.
+        sp<AMessage> levelCriticalFormat = new AMessage;
+
+        // critical keys will always contain KEY_MIME, but should also contain others to be
+        // meaningful
+        if ((isVideo() || isAudio()) && levelCaps != nullptr) {
+            const std::set<std::pair<std::string, AMessage::Type>> criticalKeys =
+                isVideo() ? VIDEO_LEVEL_CRITICAL_FORMAT_KEYS : AUDIO_LEVEL_CRITICAL_FORMAT_KEYS;
+            for (std::pair<std::string, AMessage::Type> key : criticalKeys) {
+                if (format->contains(key.first.c_str())) {
+                    // AMessage::ItemData value = format->findItem(key.c_str());
+                    // levelCriticalFormat->setItem(key.c_str(), value);
+                    switch (key.second) {
+                        case AMessage::kTypeInt32: {
+                            int32_t value;
+                            format->findInt32(key.first.c_str(), &value);
+                            levelCriticalFormat->setInt32(key.first.c_str(), value);
+                            break;
+                        }
+                        case AMessage::kTypeString: {
+                            AString value;
+                            format->findString(key.first.c_str(), &value);
+                            levelCriticalFormat->setString(key.first.c_str(), value);
+                            break;
+                        }
+                        default:
+                            ALOGE("Unsupported type");
+                    }
+                }
+            }
+            if (!levelCaps->isFormatSupported(levelCriticalFormat)) {
+                return false;
+            }
+        }
+    }
+    if (mAudioCaps && !mAudioCaps->supportsFormat(format)) {
+        return false;
+    }
+    if (mVideoCaps && !mVideoCaps->supportsFormat(format)) {
+        return false;
+    }
+    if (mEncoderCaps && !mEncoderCaps->supportsFormat(format)) {
+        return false;
+    }
+    return true;
+}
+
+bool CodecCapabilities::supportsProfileLevel(int32_t profile, int32_t level) const {
+    for (ProfileLevel pl: mProfileLevels) {
+        if (pl.mProfile != profile) {
+            continue;
+        }
+
+        // No specific level requested
+        if (level == -1) {
+            return true;
+        }
+
+        // AAC doesn't use levels
+        if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AAC)) {
+            return true;
+        }
+
+        // DTS doesn't use levels
+        if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_DTS)
+                || base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_DTS_HD)
+                || base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_DTS_UHD)) {
+            return true;
+        }
+
+        // H.263 levels are not completely ordered:
+        // Level45 support only implies Level10 support
+        if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_H263)) {
+            if (pl.mLevel != level && pl.mLevel == H263Level45
+                    && level > H263Level10) {
+                continue;
+            }
+        }
+
+        // MPEG4 levels are not completely ordered:
+        // Level1 support only implies Level0 (and not Level0b) support
+        if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_MPEG4)) {
+            if (pl.mLevel != level && pl.mLevel == MPEG4Level1
+                    && level > MPEG4Level0) {
+                continue;
+            }
+        }
+
+        // HEVC levels incorporate both tiers and levels. Verify tier support.
+        if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_HEVC)) {
+            bool supportsHighTier =
+                (pl.mLevel & HEVCHighTierLevels) != 0;
+            bool checkingHighTier = (level & HEVCHighTierLevels) != 0;
+            // high tier levels are only supported by other high tier levels
+            if (checkingHighTier && !supportsHighTier) {
+                continue;
+            }
+        }
+
+        if (pl.mLevel >= level) {
+            // if we recognize the listed profile/level, we must also recognize the
+            // profile/level arguments.
+            if (CreateFromProfileLevel(mMediaType, profile, pl.mLevel) != nullptr) {
+                return CreateFromProfileLevel(mMediaType, profile, level) != nullptr;
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+sp<AMessage> CodecCapabilities::getDefaultFormat() const {
+    return mDefaultFormat;
+}
+
 const std::string& CodecCapabilities::getMediaType() {
     return mMediaType;
 }
@@ -54,4 +313,130 @@
     return mProfileLevels;
 }
 
+std::vector<uint32_t> CodecCapabilities::getColorFormats() const {
+    return mColorFormats;
+}
+
+int32_t CodecCapabilities::getMaxSupportedInstances() const {
+    return mMaxSupportedInstances;
+}
+
+bool CodecCapabilities::isAudio() const {
+    return mAudioCaps != nullptr;
+}
+
+std::shared_ptr<AudioCapabilities>
+        CodecCapabilities::getAudioCapabilities() const {
+    return mAudioCaps;
+}
+
+bool CodecCapabilities::isEncoder() const {
+    return mEncoderCaps != nullptr;
+}
+
+std::shared_ptr<EncoderCapabilities>
+        CodecCapabilities::getEncoderCapabilities() const {
+    return mEncoderCaps;
+}
+
+bool CodecCapabilities::isVideo() const {
+    return mVideoCaps != nullptr;
+}
+
+std::shared_ptr<VideoCapabilities> CodecCapabilities::getVideoCapabilities() const {
+    return mVideoCaps;
+}
+
+// static
+std::shared_ptr<CodecCapabilities> CodecCapabilities::CreateFromProfileLevel(
+        std::string mediaType, int32_t profile, int32_t level, int32_t maxConcurrentInstances) {
+    ProfileLevel pl;
+    pl.mProfile = profile;
+    pl.mLevel = level;
+    sp<AMessage> defaultFormat = new AMessage;
+    defaultFormat->setString(KEY_MIME, mediaType.c_str());
+
+    std::vector<ProfileLevel> pls;
+    pls.push_back(pl);
+    std::vector<uint32_t> colFmts;
+    sp<AMessage> capabilitiesInfo = new AMessage;
+    std::shared_ptr<CodecCapabilities> ret(new CodecCapabilities());
+    ret->init(pls, colFmts, true /* encoder */, defaultFormat, capabilitiesInfo,
+            maxConcurrentInstances);
+    if (ret->getErrors() != 0) {
+        return nullptr;
+    }
+    return ret;
+}
+
+void CodecCapabilities::init(std::vector<ProfileLevel> profLevs, std::vector<uint32_t> colFmts,
+        bool encoder, sp<AMessage> &defaultFormat, sp<AMessage> &capabilitiesInfo,
+        int32_t maxConcurrentInstances) {
+    mColorFormats = colFmts;
+    mDefaultFormat = defaultFormat;
+    mCapabilitiesInfo = capabilitiesInfo;
+
+    AString mediaTypeAStr;
+    mDefaultFormat->findString(KEY_MIME, &mediaTypeAStr);
+    mMediaType = mediaTypeAStr.c_str();
+
+    /* VP9 introduced profiles around 2016, so some VP9 codecs may not advertise any
+       supported profiles. Determine the level for them using the info they provide. */
+    if (profLevs.size() == 0 && mMediaType == MIMETYPE_VIDEO_VP9) {
+        ProfileLevel profLev;
+        profLev.mProfile = VP9Profile0;
+        profLev.mLevel = VideoCapabilities::EquivalentVP9Level(capabilitiesInfo);
+        profLevs.push_back(profLev);
+    }
+    mProfileLevels = profLevs;
+
+    if (mediaTypeAStr.startsWithIgnoreCase("audio/")) {
+        mAudioCaps = AudioCapabilities::Create(mMediaType, profLevs, capabilitiesInfo);
+        mAudioCaps->getDefaultFormat(mDefaultFormat);
+    } else if (mediaTypeAStr.startsWithIgnoreCase("video/")
+            || mediaTypeAStr.equalsIgnoreCase(MIMETYPE_IMAGE_ANDROID_HEIC)) {
+        mVideoCaps = VideoCapabilities::Create(mMediaType, profLevs, capabilitiesInfo);
+    }
+
+    if (encoder) {
+        mEncoderCaps = EncoderCapabilities::Create(mMediaType, profLevs, capabilitiesInfo);
+        mEncoderCaps->getDefaultFormat(mDefaultFormat);
+    }
+
+    mMaxSupportedInstances = maxConcurrentInstances > 0
+            ? maxConcurrentInstances : DEFAULT_MAX_SUPPORTED_INSTANCES;
+
+    int32_t maxInstances = mMaxSupportedInstances;
+    capabilitiesInfo->findInt32("max-concurrent-instances", &maxInstances);
+    mMaxSupportedInstances =
+            Range(1, MAX_SUPPORTED_INSTANCES_LIMIT).clamp(maxInstances);
+
+    mFeaturesRequired.clear();
+    mFeaturesSupported.clear();
+    for (Feature feat: getValidFeatures()) {
+        std::string key = KEY_FEATURE_;
+        key = key + feat.mName;
+        int yesNo = -1;
+        if (!capabilitiesInfo->findInt32(key.c_str(), &yesNo)) {
+            continue;
+        }
+        if (yesNo > 0) {
+            mFeaturesRequired.insert(feat.mName);
+        }
+        mFeaturesSupported.insert(feat.mName);
+        if (!feat.mInternal) {
+            mDefaultFormat->setInt32(key.c_str(), 1);
+        }
+    }
+}
+
+int32_t CodecCapabilities::getErrors() const {
+    if (mAudioCaps) {
+        return mAudioCaps->mError;
+    } else if (mVideoCaps) {
+        return mVideoCaps->mError;
+    }
+    return 0;
+}
+
 }  // namespace android
\ No newline at end of file
diff --git a/media/libmedia/CodecCapabilitiesUtils.cpp b/media/libmedia/CodecCapabilitiesUtils.cpp
index edfc9be..01bb24e 100644
--- a/media/libmedia/CodecCapabilitiesUtils.cpp
+++ b/media/libmedia/CodecCapabilitiesUtils.cpp
@@ -16,38 +16,146 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "CodecCapabilitiesUtils"
+
+#include <android-base/properties.h>
 #include <utils/Log.h>
 
 #include <algorithm>
 #include <cmath>
-#include <regex>
+#include <cstdlib>
 #include <string>
 #include <vector>
 
 #include <media/CodecCapabilitiesUtils.h>
-
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AUtils.h>
 
 namespace android {
 
-std::optional<Range<int>> ParseIntRange(const std::string &str) {
+// VideoSize
+
+VideoSize::VideoSize(int32_t width, int32_t height) : mWidth(width), mHeight(height) {}
+
+VideoSize::VideoSize() : mWidth(0), mHeight(0) {}
+
+int32_t VideoSize::getWidth() const { return mWidth; }
+
+int32_t VideoSize::getHeight() const { return mHeight; }
+
+bool VideoSize::equals(VideoSize other) const {
+    return mWidth == other.mWidth && mHeight == other.mHeight;
+}
+
+bool VideoSize::empty() const {
+    return mWidth <= 0 || mHeight <= 0;
+}
+
+std::string VideoSize::toString() const {
+    return std::to_string(mWidth) + "x" + std::to_string(mHeight);
+}
+
+std::optional<VideoSize> VideoSize::ParseSize(std::string str) {
     if (str.empty()) {
-        ALOGW("could not parse empty integer range");
         return std::nullopt;
     }
-    int lower, upper;
-    std::regex regex("([0-9]+)-([0-9]+)");
+
+    std::regex regex("([0-9]+)([*x])([0-9]+)");
     std::smatch match;
     if (std::regex_match(str, match, regex)) {
-        lower = std::atoi(match[1].str().c_str());
-        upper = std::atoi(match[2].str().c_str());
-    } else if (std::atoi(str.c_str()) != 0) {
-        lower = upper = std::atoi(str.c_str());
+        long int w = strtol(match[1].str().c_str(), NULL, 10);
+        long int h = strtol(match[3].str().c_str(), NULL, 10);
+        return std::make_optional(VideoSize(w, h));
     } else {
-        ALOGW("could not parse integer range: %s", str.c_str());
+        ALOGW("could not parse size %s", str.c_str());
         return std::nullopt;
     }
-    return std::make_optional<Range<int>>(lower, upper);
+}
+
+std::optional<std::pair<VideoSize, VideoSize>> VideoSize::ParseSizeRange(const std::string str) {
+    size_t ix = str.find_first_of('-');
+    if (ix != std::string::npos) {
+        std::optional<VideoSize> lowerOpt = VideoSize::ParseSize(str.substr(0, ix));
+        std::optional<VideoSize> upperOpt = VideoSize::ParseSize(str.substr(ix + 1));
+        if (!lowerOpt || !upperOpt) {
+            return std::nullopt;
+        }
+        return std::make_optional(
+                std::pair<VideoSize, VideoSize>(lowerOpt.value(), upperOpt.value()));
+    } else {
+        std::optional<VideoSize> opt = VideoSize::ParseSize(str);
+        if (!opt) {
+            return std::nullopt;
+        }
+        return std::make_optional(std::pair<VideoSize, VideoSize>(opt.value(), opt.value()));
+    }
+}
+
+Range<int32_t> VideoSize::GetAllowedDimensionRange() {
+#ifdef __LP64__
+    return Range<int32_t>(1, 32768);
+#else
+    int32_t value = base::GetIntProperty("media.resolution.limit.32bit", (int32_t)4096);
+    return Range<int32_t>(1, value);
+#endif
+}
+
+// Rational
+
+std::optional<Rational> Rational::Parse(std::string str) {
+    if (str.compare("NaN") == 0) {
+        return std::make_optional(NaN);
+    } else if (str.compare("Infinity") == 0) {
+        return std::make_optional(POSITIVE_INFINITY);
+    } else if (str.compare("-Infinity") == 0) {
+        return std::make_optional(NEGATIVE_INFINITY);
+    }
+
+    std::regex regex("([0-9]+)([:/])([0-9]+)");
+    std::smatch match;
+    if (std::regex_match(str, match, regex)) {
+        long int numerator = strtol(match[1].str().c_str(), NULL, 10);
+        long int denominator = strtol(match[3].str().c_str(), NULL, 10);
+        return std::make_optional(Rational(numerator, denominator));
+    } else {
+        ALOGW("could not parse string: %s to Rational", str.c_str());
+        return std::nullopt;
+    }
+}
+
+Rational Rational::scale(int32_t num, int32_t den) {
+    int32_t common = std::gcd(num, den);
+    num /= common;
+    den /= common;
+    return Rational(
+            (int32_t)(mNumerator * (double)num),     // saturate to int
+            (int32_t)(mDenominator * (double)den));  // saturate to int
+}
+
+Range<Rational> Rational::ScaleRange(Range<Rational> range, int32_t num, int32_t den) {
+    if (num == den) {
+        return range;
+    }
+    return Range(
+            range.lower().scale(num, den),
+            range.upper().scale(num, den));
+}
+
+std::optional<Range<Rational>> Rational::ParseRange(const std::string str) {
+    size_t ix = str.find_first_of('-');
+    if (ix != std::string::npos) {
+        std::optional<Rational> lower = Parse(str.substr(0, ix));
+        std::optional<Rational> upper = Parse(str.substr(ix + 1));
+        if (!lower || !upper) {
+            return std::nullopt;
+        }
+        return std::make_optional<Range<Rational>>(lower.value(), upper.value());
+    } else {
+        std::optional<Rational> value = Parse(str);
+        if (!value) {
+            return std::nullopt;
+        }
+        return std::make_optional<Range<Rational>>(value.value(), value.value());
+    }
 }
 
 }  // namespace android
\ No newline at end of file
diff --git a/media/libmedia/EncoderCapabilities.cpp b/media/libmedia/EncoderCapabilities.cpp
new file mode 100644
index 0000000..a840220
--- /dev/null
+++ b/media/libmedia/EncoderCapabilities.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2024, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "EncoderCapabilities"
+
+#include <android-base/strings.h>
+
+#include <media/CodecCapabilities.h>
+#include <media/EncoderCapabilities.h>
+#include <media/stagefright/MediaCodecConstants.h>
+
+namespace android {
+
+const Range<int>& EncoderCapabilities::getQualityRange() {
+    return mQualityRange;
+}
+
+const Range<int>& EncoderCapabilities::getComplexityRange() {
+    return mComplexityRange;
+}
+
+// static
+int EncoderCapabilities::ParseBitrateMode(std::string mode) {
+    for (Feature feat: sBitrateModes) {
+        if (base::EqualsIgnoreCase(feat.mName, mode)) {
+            return feat.mValue;
+        }
+    }
+    return 0;
+}
+
+bool EncoderCapabilities::isBitrateModeSupported(int mode) {
+    for (Feature feat : sBitrateModes) {
+        if (mode == feat.mValue) {
+            return (mBitControl & (1 << mode)) != 0;
+        }
+    }
+    return false;
+}
+
+// static
+std::shared_ptr<EncoderCapabilities> EncoderCapabilities::Create(std::string mediaType,
+        std::vector<ProfileLevel> profLevs, const sp<AMessage> &format) {
+    std::shared_ptr<EncoderCapabilities> caps(new EncoderCapabilities());
+    caps->init(mediaType, profLevs, format);
+    return caps;
+}
+
+void EncoderCapabilities::init(std::string mediaType, std::vector<ProfileLevel> profLevs,
+        const sp<AMessage> &format) {
+    // no support for complexity or quality yet
+    mMediaType = mediaType;
+    mProfileLevels = profLevs;
+    mComplexityRange = Range(0, 0);
+    mQualityRange = Range(0, 0);
+    mBitControl = (1 << BITRATE_MODE_VBR);
+
+    applyLevelLimits();
+    parseFromInfo(format);
+}
+
+void EncoderCapabilities::applyLevelLimits() {
+    if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_FLAC)) {
+        mComplexityRange = Range(0, 8);
+        mBitControl = (1 << BITRATE_MODE_CQ);
+    } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AMR_NB)
+            || base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AMR_WB)
+            || base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_G711_ALAW)
+            || base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_G711_MLAW)
+            || base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_MSGSM)) {
+        mBitControl = (1 << BITRATE_MODE_CBR);
+    }
+}
+
+void EncoderCapabilities::parseFromInfo(const sp<AMessage> &format) {
+    AString complexityRangeAStr;
+    if (format->findString("complexity-range", &complexityRangeAStr)) {
+        std::optional<Range<int>> complexityRangeOpt
+                = Range<int32_t>::Parse(std::string(complexityRangeAStr.c_str()));
+        mComplexityRange = complexityRangeOpt.value_or(mComplexityRange);
+        // TODO should we limit this to level limits?
+    }
+    AString qualityRangeAStr;
+    if (format->findString("quality-range", &qualityRangeAStr)) {
+        std::optional<Range<int>> qualityRangeOpt
+                = Range<int32_t>::Parse(std::string(qualityRangeAStr.c_str()));
+        mQualityRange = qualityRangeOpt.value_or(mQualityRange);
+    }
+    AString bitrateModesAStr;
+    if (format->findString("feature-bitrate-modes", &bitrateModesAStr)) {
+        mBitControl = 0;
+        for (std::string mode: base::Split(std::string(bitrateModesAStr.c_str()), ",")) {
+            mBitControl |= (1 << ParseBitrateMode(mode));
+        }
+    }
+    format->findInt32("complexity-default", &mDefaultComplexity);
+    format->findInt32("quality-default", &mDefaultQuality);
+    AString qualityScaleAStr;
+    if (format->findString("quality-scale", &qualityScaleAStr)) {
+        mQualityScale = std::string(qualityScaleAStr.c_str());
+    }
+}
+
+bool EncoderCapabilities::supports(
+        std::optional<int> complexity, std::optional<int> quality, std::optional<int> profile) {
+    bool ok = true;
+    if (complexity) {
+        ok &= mComplexityRange.contains(complexity.value());
+    }
+    if (quality) {
+        ok &= mQualityRange.contains(quality.value());
+    }
+    if (profile) {
+        ok &= std::any_of(mProfileLevels.begin(), mProfileLevels.end(),
+                [&profile](ProfileLevel pl){ return pl.mProfile == profile.value(); });
+    }
+    return ok;
+}
+
+void EncoderCapabilities::getDefaultFormat(sp<AMessage> &format) {
+    // don't list trivial quality/complexity as default for now
+    if (mQualityRange.upper() != mQualityRange.lower()
+            && mDefaultQuality != 0) {
+        format->setInt32(KEY_QUALITY, mDefaultQuality);
+    }
+    if (mComplexityRange.upper() != mComplexityRange.lower()
+            && mDefaultComplexity != 0) {
+        format->setInt32(KEY_COMPLEXITY, mDefaultComplexity);
+    }
+    // bitrates are listed in order of preference
+    for (Feature feat : sBitrateModes) {
+        if ((mBitControl & (1 << feat.mValue)) != 0) {
+            format->setInt32(KEY_BITRATE_MODE, feat.mValue);
+            break;
+        }
+    }
+}
+
+bool EncoderCapabilities::supportsFormat(const sp<AMessage> &format) {
+    int32_t mode;
+    if (format->findInt32(KEY_BITRATE_MODE, &mode) && !isBitrateModeSupported(mode)) {
+        return false;
+    }
+
+    int tmp;
+    std::optional<int> complexity = std::nullopt;
+    if (format->findInt32(KEY_COMPLEXITY, &tmp)) {
+        complexity = tmp;
+    }
+
+    if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_FLAC)) {
+        int flacComplexity;
+        if (format->findInt32(KEY_FLAC_COMPRESSION_LEVEL, &flacComplexity)) {
+            if (!complexity) {
+                complexity = flacComplexity;
+            } else if (flacComplexity != complexity.value()) {
+                ALOGE("Conflicting values for complexity and flac-compression-level,"
+                        " which are %d and %d", complexity.value(), flacComplexity);
+                return false;
+            }
+        }
+    }
+
+    // other audio parameters
+    std::optional<int> profile = std::nullopt;
+    if (format->findInt32(KEY_PROFILE, &tmp)) {
+        profile = tmp;
+    }
+
+    if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AAC)) {
+        int aacProfile;
+        if (format->findInt32(KEY_AAC_PROFILE, &aacProfile)) {
+            if (!profile) {
+                profile = aacProfile;
+            } else if (aacProfile != profile.value()) {
+                ALOGE("Conflicting values for profile and aac-profile, which are %d and %d",
+                        profile.value(), aacProfile);
+                return false;
+            }
+        }
+    }
+
+    std::optional<int> quality = std::nullopt;
+    if (format->findInt32(KEY_QUALITY, &tmp)) {
+        quality = tmp;
+    }
+
+    return supports(complexity, quality, profile);
+}
+
+}  // namespace android
\ No newline at end of file
diff --git a/media/libmedia/MediaCodecInfo.cpp b/media/libmedia/MediaCodecInfo.cpp
index d5d1a09..3834278 100644
--- a/media/libmedia/MediaCodecInfo.cpp
+++ b/media/libmedia/MediaCodecInfo.cpp
@@ -26,6 +26,9 @@
 
 namespace android {
 
+// initialize max supported instances with default value.
+int32_t MediaCodecInfo::sMaxSupportedInstances = 0;
+
 /** This redundant redeclaration is needed for C++ pre 14 */
 constexpr char MediaCodecInfo::Capabilities::FEATURE_ADAPTIVE_PLAYBACK[];
 constexpr char MediaCodecInfo::Capabilities::FEATURE_DYNAMIC_TIMESTAMP[];
@@ -169,6 +172,15 @@
     return NULL;
 }
 
+const std::shared_ptr<CodecCapabilities> MediaCodecInfo::getCodecCapsFor(
+        const char *mediaType) const {
+    ssize_t ix = getCodecCapIndex(mediaType);
+    if (ix >= 0) {
+        return mCodecCaps.valueAt(ix);
+    }
+    return nullptr;
+}
+
 const char *MediaCodecInfo::getCodecName() const {
     return mName.c_str();
 }
@@ -179,6 +191,7 @@
 
 // static
 sp<MediaCodecInfo> MediaCodecInfo::FromParcel(const Parcel &parcel) {
+    sMaxSupportedInstances = parcel.readInt32();
     AString name = AString::FromParcel(parcel);
     AString owner = AString::FromParcel(parcel);
     Attributes attributes = static_cast<Attributes>(parcel.readInt32());
@@ -201,12 +214,17 @@
             return NULL;
         if (info != NULL) {
             info->mCaps.add(mediaType, caps);
+            std::shared_ptr<CodecCapabilities> codecCaps
+                    = MediaCodecInfoWriter::BuildCodecCapabilities(
+                            mediaType.c_str(), caps, info->isEncoder());
+            info->mCodecCaps.add(mediaType, codecCaps);
         }
     }
     return info;
 }
 
 status_t MediaCodecInfo::writeToParcel(Parcel *parcel) const {
+    parcel->writeInt32(sMaxSupportedInstances);
     mName.writeToParcel(parcel);
     mOwner.writeToParcel(parcel);
     parcel->writeInt32(mAttributes);
@@ -234,6 +252,25 @@
     return -1;
 }
 
+ssize_t MediaCodecInfo::getCodecCapIndex(const char *mediaType) const {
+    if (mediaType == nullptr) {
+        return -1;
+    }
+
+    if (mCodecCaps.size() != mCaps.size()) {
+        ALOGE("Size of mCodecCaps and mCaps do not match, which are %zu and %zu",
+                mCodecCaps.size(), mCaps.size());
+    }
+
+    for (size_t ix = 0; ix < mCodecCaps.size(); ix++) {
+        if (mCodecCaps.keyAt(ix).equalsIgnoreCase(mediaType)) {
+            return ix;
+        }
+    }
+
+    return -1;
+}
+
 MediaCodecInfo::MediaCodecInfo()
     : mAttributes((MediaCodecInfo::Attributes)0),
       mRank(0x100) {
@@ -283,6 +320,52 @@
     return false;
 }
 
+void MediaCodecInfoWriter::createCodecCaps() {
+    mInfo->mCodecCaps.clear();
+    for (size_t ix = 0; ix < mInfo->mCaps.size(); ix++) {
+        AString mediaType = mInfo->mCaps.keyAt(ix);
+        sp<MediaCodecInfo::Capabilities> caps = mInfo->mCaps.valueAt(ix);
+        mInfo->mCodecCaps.add(mediaType,
+                BuildCodecCapabilities(mediaType.c_str(), caps, mInfo->isEncoder(),
+                MediaCodecInfo::sMaxSupportedInstances));
+    }
+}
+
+// static
+std::shared_ptr<CodecCapabilities> MediaCodecInfoWriter::BuildCodecCapabilities(
+        const char *mediaType, sp<MediaCodecInfo::Capabilities> caps, bool isEncoder,
+        int32_t maxSupportedInstances) {
+    Vector<ProfileLevel> profileLevels_;
+    Vector<uint32_t> colorFormats_;
+    caps->getSupportedProfileLevels(&profileLevels_);
+    caps->getSupportedColorFormats(&colorFormats_);
+
+    std::vector<ProfileLevel> profileLevels;
+    std::vector<uint32_t> colorFormats;
+    for (ProfileLevel pl : profileLevels_) {
+        profileLevels.push_back(pl);
+    }
+    for (uint32_t cf : colorFormats_) {
+        colorFormats.push_back(cf);
+    }
+
+    sp<AMessage> defaultFormat = new AMessage();
+    defaultFormat->setString("mime", mediaType);
+
+    sp<AMessage> capabilitiesInfo = caps->getDetails();
+
+    std::shared_ptr<CodecCapabilities> codecCaps = std::make_shared<CodecCapabilities>();
+    codecCaps->init(profileLevels, colorFormats, isEncoder, defaultFormat,
+            capabilitiesInfo, maxSupportedInstances);
+
+    return codecCaps;
+}
+
+// static
+void MediaCodecInfoWriter::SetMaxSupportedInstances(int32_t maxSupportedInstances) {
+    MediaCodecInfo::sMaxSupportedInstances = maxSupportedInstances;
+}
+
 MediaCodecInfoWriter::MediaCodecInfoWriter(MediaCodecInfo* info) :
     mInfo(info) {
 }
diff --git a/media/libmedia/VideoCapabilities.cpp b/media/libmedia/VideoCapabilities.cpp
new file mode 100644
index 0000000..bd26b8c
--- /dev/null
+++ b/media/libmedia/VideoCapabilities.cpp
@@ -0,0 +1,1702 @@
+/*
+ * Copyright 2024, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "VideoCapabilities"
+
+#include <android-base/strings.h>
+
+#include <media/CodecCapabilities.h>
+#include <media/VideoCapabilities.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaCodecConstants.h>
+
+#include <utils/Errors.h>
+
+namespace android {
+
+static const Range<int64_t> POSITIVE_INT64 = Range((int64_t)1, INT64_MAX);
+static const Range<int32_t> BITRATE_RANGE = Range<int32_t>(0, 500000000);
+static const Range<int32_t> FRAME_RATE_RANGE = Range<int32_t>(0, 960);
+static const Range<Rational> POSITIVE_RATIONALS =
+            Range<Rational>(Rational((int32_t)1, INT32_MAX), Rational(INT32_MAX, (int32_t)1));
+
+const Range<int32_t>& VideoCapabilities::getBitrateRange() const {
+    return mBitrateRange;
+}
+
+const Range<int32_t>& VideoCapabilities::getSupportedWidths() const {
+    return mWidthRange;
+}
+
+const Range<int32_t>& VideoCapabilities::getSupportedHeights() const {
+    return mHeightRange;
+}
+
+int32_t VideoCapabilities::getWidthAlignment() const {
+    return mWidthAlignment;
+}
+
+int32_t VideoCapabilities::getHeightAlignment() const {
+    return mHeightAlignment;
+}
+
+int32_t VideoCapabilities::getSmallerDimensionUpperLimit() const {
+    return mSmallerDimensionUpperLimit;
+}
+
+const Range<int32_t>& VideoCapabilities::getSupportedFrameRates() const {
+    return mFrameRateRange;
+}
+
+std::optional<Range<int32_t>> VideoCapabilities::getSupportedWidthsFor(int32_t height) const {
+    Range<int32_t> range = mWidthRange;
+    if (!mHeightRange.contains(height)
+            || (height % mHeightAlignment) != 0) {
+        ALOGE("unsupported height");
+        return std::nullopt;
+    }
+    const int32_t heightInBlocks = divUp(height, mBlockHeight);
+
+    // constrain by block count and by block aspect ratio
+    const int32_t minWidthInBlocks = std::max(
+            divUp(mBlockCountRange.lower(), heightInBlocks),
+            (int32_t)std::ceil(mBlockAspectRatioRange.lower().asDouble()
+                    * heightInBlocks));
+    const int32_t maxWidthInBlocks = std::min(
+            mBlockCountRange.upper() / heightInBlocks,
+            (int32_t)(mBlockAspectRatioRange.upper().asDouble()
+                    * heightInBlocks));
+    range = range.intersect(
+            (minWidthInBlocks - 1) * mBlockWidth + mWidthAlignment,
+            maxWidthInBlocks * mBlockWidth);
+
+    // constrain by smaller dimension limit
+    if (height > mSmallerDimensionUpperLimit) {
+        range = range.intersect(1, mSmallerDimensionUpperLimit);
+    }
+
+    // constrain by aspect ratio
+    range = range.intersect(
+            (int32_t)std::ceil(mAspectRatioRange.lower().asDouble()
+                    * height),
+            (int32_t)(mAspectRatioRange.upper().asDouble() * height));
+    return range;
+}
+
+std::optional<Range<int32_t>> VideoCapabilities::getSupportedHeightsFor(int32_t width) const {
+    Range<int32_t> range = mHeightRange;
+    if (!mWidthRange.contains(width)
+            || (width % mWidthAlignment) != 0) {
+        ALOGE("unsupported width");
+        return std::nullopt;
+    }
+    const int32_t widthInBlocks = divUp(width, mBlockWidth);
+
+    // constrain by block count and by block aspect ratio
+    const int32_t minHeightInBlocks = std::max(
+            divUp(mBlockCountRange.lower(), widthInBlocks),
+            (int32_t)std::ceil(widthInBlocks /
+                    mBlockAspectRatioRange.upper().asDouble()));
+    const int32_t maxHeightInBlocks = std::min(
+            mBlockCountRange.upper() / widthInBlocks,
+            (int32_t)(widthInBlocks /
+                    mBlockAspectRatioRange.lower().asDouble()));
+    range = range.intersect(
+            (minHeightInBlocks - 1) * mBlockHeight + mHeightAlignment,
+            maxHeightInBlocks * mBlockHeight);
+
+    // constrain by smaller dimension limit
+    if (width > mSmallerDimensionUpperLimit) {
+        range = range.intersect(1, mSmallerDimensionUpperLimit);
+    }
+
+    // constrain by aspect ratio
+    range = range.intersect(
+            (int32_t)std::ceil(width /
+                    mAspectRatioRange.upper().asDouble()),
+            (int32_t)(width / mAspectRatioRange.lower().asDouble()));
+    return range;
+}
+
+std::optional<Range<double>> VideoCapabilities::getSupportedFrameRatesFor(
+        int32_t width, int32_t height) const {
+    if (!supports(std::make_optional<int32_t>(width), std::make_optional<int32_t>(height),
+            std::nullopt /* rate */)) {
+        ALOGE("Unsupported size. width: %d, height: %d", width, height);
+        return std::nullopt;
+    }
+
+    const int32_t blockCount =
+            divUp(width, mBlockWidth) * divUp(height, mBlockHeight);
+
+    return std::make_optional(Range(
+            std::max(mBlocksPerSecondRange.lower() / (double) blockCount,
+                (double) mFrameRateRange.lower()),
+            std::min(mBlocksPerSecondRange.upper() / (double) blockCount,
+                (double) mFrameRateRange.upper())));
+}
+
+int32_t VideoCapabilities::getBlockCount(int32_t width, int32_t height) const {
+    return divUp(width, mBlockWidth) * divUp(height, mBlockHeight);
+}
+
+std::optional<VideoSize> VideoCapabilities::findClosestSize(
+        int32_t width, int32_t height) const {
+    int32_t targetBlockCount = getBlockCount(width, height);
+    std::optional<VideoSize> closestSize;
+    int32_t minDiff = INT32_MAX;
+    for (const auto &[size, range] : mMeasuredFrameRates) {
+        int32_t diff = std::abs(targetBlockCount -
+                getBlockCount(size.getWidth(), size.getHeight()));
+        if (diff < minDiff) {
+            minDiff = diff;
+            closestSize = size;
+        }
+    }
+    return closestSize;
+}
+
+std::optional<Range<double>> VideoCapabilities::estimateFrameRatesFor(
+        int32_t width, int32_t height) const {
+    std::optional<VideoSize> size = findClosestSize(width, height);
+    if (!size) {
+        return std::nullopt;
+    }
+    auto rangeItr = mMeasuredFrameRates.find(size.value());
+    if (rangeItr == mMeasuredFrameRates.end()) {
+        return std::nullopt;
+    }
+    Range<int64_t> range = rangeItr->second;
+    double ratio = getBlockCount(size.value().getWidth(), size.value().getHeight())
+            / (double)std::max(getBlockCount(width, height), 1);
+    return std::make_optional(Range(range.lower() * ratio, range.upper() * ratio));
+}
+
+std::optional<Range<double>> VideoCapabilities::getAchievableFrameRatesFor(
+        int32_t width, int32_t height) const {
+    if (!supports(std::make_optional<int32_t>(width), std::make_optional<int32_t>(height),
+            std::nullopt /* rate */)) {
+        ALOGE("Unsupported size. width: %d, height: %d", width, height);
+        return std::nullopt;
+    }
+
+    if (mMeasuredFrameRates.empty()) {
+        ALOGW("Codec did not publish any measurement data.");
+        return std::nullopt;
+    }
+
+    return estimateFrameRatesFor(width, height);
+}
+
+// VideoCapabilities::PerformancePoint
+
+int32_t VideoCapabilities::PerformancePoint::getMaxMacroBlocks() const {
+    return saturateInt64ToInt32(mWidth * (int64_t)mHeight);
+}
+
+int32_t VideoCapabilities::PerformancePoint::getWidth() const {
+    return mWidth;
+}
+
+int32_t VideoCapabilities::PerformancePoint::getHeight() const {
+    return mHeight;
+}
+
+int32_t VideoCapabilities::PerformancePoint::getMaxFrameRate() const {
+    return mMaxFrameRate;
+}
+
+int64_t VideoCapabilities::PerformancePoint::getMaxMacroBlockRate() const {
+    return mMaxMacroBlockRate;
+}
+
+VideoSize VideoCapabilities::PerformancePoint::getBlockSize() const {
+    return mBlockSize;
+}
+
+std::string VideoCapabilities::PerformancePoint::toString() const {
+    int64_t blockWidth = 16 * (int64_t)mBlockSize.getWidth();
+    int64_t blockHeight = 16 * (int64_t)mBlockSize.getHeight();
+    int32_t origRate = (int32_t)divUp(mMaxMacroBlockRate, (int64_t)getMaxMacroBlocks());
+    std::string info = std::to_string(mWidth * (int64_t)16) + "x"
+            + std::to_string(mHeight * (int64_t)16) + "@" + std::to_string(origRate);
+    if (origRate < mMaxFrameRate) {
+        info += ", max " + std::to_string(mMaxFrameRate) + "fps";
+    }
+    if (blockWidth > 16 || blockHeight > 16) {
+        info += ", " + std::to_string(blockWidth) + "x"
+                + std::to_string(blockHeight) + " blocks";
+    }
+    return "PerformancePoint(" + info + ")";
+}
+
+void VideoCapabilities::PerformancePoint::init(int32_t width, int32_t height,
+        int32_t frameRate, int32_t maxFrameRate, VideoSize blockSize) {
+    mBlockSize = VideoSize(divUp(blockSize.getWidth(), (int32_t)16),
+                            divUp(blockSize.getHeight(), (int32_t)16));
+    // Use  IsPowerOfTwoStrict as we do not want width and height to be 0;
+    if (!IsPowerOfTwoStrict(blockSize.getWidth()) || !IsPowerOfTwoStrict(blockSize.getHeight())) {
+        ALOGE("The width and height of a PerformancePoint must be the power of two and not zero."
+                " width: %d, height: %d", blockSize.getWidth(), blockSize.getHeight());
+    }
+
+    // these are guaranteed not to overflow as we decimate by 16
+    mWidth = (int32_t)(divUp(std::max(width, 1),
+                            std::max(blockSize.getWidth(), 16))
+                        * mBlockSize.getWidth());
+    mHeight = (int32_t)(divUp(std::max(height, 1),
+                            std::max(blockSize.getHeight(), 16))
+                        * mBlockSize.getHeight());
+    mMaxFrameRate = std::max(std::max(frameRate, maxFrameRate), 1);
+    mMaxMacroBlockRate = std::max(frameRate, 1) * (int64_t)getMaxMacroBlocks();
+}
+
+VideoCapabilities::PerformancePoint::PerformancePoint(int32_t width, int32_t height,
+        int32_t frameRate, int32_t maxFrameRate, VideoSize blockSize) {
+    init(width, height, frameRate, maxFrameRate, blockSize);
+}
+
+VideoCapabilities::PerformancePoint::PerformancePoint(VideoSize blockSize, int32_t width,
+        int32_t height, int32_t maxFrameRate, int64_t maxMacroBlockRate) :
+        mBlockSize(blockSize), mWidth(width), mHeight(height), mMaxFrameRate(maxFrameRate),
+        mMaxMacroBlockRate(maxMacroBlockRate) {}
+
+VideoCapabilities::PerformancePoint::PerformancePoint(
+        const PerformancePoint &pp, VideoSize newBlockSize) {
+    init(16 * pp.mWidth, 16 * pp.mHeight,
+            // guaranteed not to overflow as these were multiplied at construction
+            (int32_t)divUp(pp.mMaxMacroBlockRate, (int64_t)pp.getMaxMacroBlocks()),
+            pp.mMaxFrameRate,
+            VideoSize(std::max(newBlockSize.getWidth(), 16 * pp.mBlockSize.getWidth()),
+                 std::max(newBlockSize.getHeight(), 16 * pp.mBlockSize.getHeight())));
+}
+
+VideoCapabilities::PerformancePoint::PerformancePoint(
+        int32_t width, int32_t height, int32_t frameRate) {
+    init(width, height, frameRate, frameRate /* maxFrameRate */, VideoSize(16, 16));
+}
+
+int32_t VideoCapabilities::PerformancePoint::saturateInt64ToInt32(int64_t value) const {
+    if (value < INT32_MIN) {
+        return INT32_MIN;
+    } else if (value > INT32_MAX) {
+        return INT32_MAX;
+    } else {
+        return (int32_t)value;
+    }
+}
+
+/* This method may overflow */
+int32_t VideoCapabilities::PerformancePoint::align(
+        int32_t value, int32_t alignment) const {
+    return divUp(value, alignment) * alignment;
+}
+
+bool VideoCapabilities::PerformancePoint::covers(
+        const sp<AMessage> &format) const {
+    int32_t width, height;
+    format->findInt32(KEY_WIDTH, &width);
+    format->findInt32(KEY_HEIGHT, &height);
+    double frameRate;
+    format->findDouble(KEY_FRAME_RATE, &frameRate);
+    PerformancePoint other = PerformancePoint(
+            width, height,
+            // safely convert ceil(double) to int through float cast and std::round
+            std::round((float)(std::ceil(frameRate)))
+    );
+    return covers(other);
+}
+
+bool VideoCapabilities::PerformancePoint::covers(
+        const PerformancePoint &other) const {
+    // convert performance points to common block size
+    VideoSize commonSize = getCommonBlockSize(other);
+    PerformancePoint aligned = PerformancePoint(*this, commonSize);
+    PerformancePoint otherAligned = PerformancePoint(other, commonSize);
+
+    return (aligned.getMaxMacroBlocks() >= otherAligned.getMaxMacroBlocks()
+            && aligned.mMaxFrameRate >= otherAligned.mMaxFrameRate
+            && aligned.mMaxMacroBlockRate >= otherAligned.mMaxMacroBlockRate);
+}
+
+VideoSize VideoCapabilities::PerformancePoint::getCommonBlockSize(
+        const PerformancePoint &other) const {
+    return VideoSize(
+            16 * std::max(mBlockSize.getWidth(), other.mBlockSize.getWidth()),
+            16 * std::max(mBlockSize.getHeight(), other.mBlockSize.getHeight()));
+}
+
+bool VideoCapabilities::PerformancePoint::equals(
+        const PerformancePoint &other) const {
+    // convert performance points to common block size
+    VideoSize commonSize = getCommonBlockSize(other);
+    PerformancePoint aligned = PerformancePoint(*this, commonSize);
+    PerformancePoint otherAligned = PerformancePoint(other, commonSize);
+
+    return (aligned.getMaxMacroBlocks() == otherAligned.getMaxMacroBlocks()
+            && aligned.mMaxFrameRate == otherAligned.mMaxFrameRate
+            && aligned.mMaxMacroBlockRate == otherAligned.mMaxMacroBlockRate);
+}
+
+// VideoCapabilities
+
+const std::vector<VideoCapabilities::PerformancePoint>&
+        VideoCapabilities::getSupportedPerformancePoints() const {
+    return mPerformancePoints;
+}
+
+bool VideoCapabilities::areSizeAndRateSupported(
+        int32_t width, int32_t height, double frameRate) const {
+    return supports(std::make_optional<int32_t>(width), std::make_optional<int32_t>(height),
+            std::make_optional<double>(frameRate));
+}
+
+bool VideoCapabilities::isSizeSupported(int32_t width, int32_t height) const {
+    return supports(std::make_optional<int32_t>(width), std::make_optional<int32_t>(height),
+            std::nullopt /* rate */);
+}
+
+bool VideoCapabilities::supports(std::optional<int32_t> width, std::optional<int32_t> height,
+        std::optional<double> rate) const {
+    bool ok = true;
+
+    if (width) {
+        ok &= mWidthRange.contains(width.value())
+                && (width.value() % mWidthAlignment == 0);
+    }
+    if (height) {
+        ok &= mHeightRange.contains(height.value())
+                && (height.value() % mHeightAlignment == 0);
+    }
+    if (rate) {
+        ok &= mFrameRateRange.contains(Range<int32_t>::RangeFor(rate.value()));
+    }
+    if (height && width) {
+        ok &= std::min(height.value(), width.value()) <= mSmallerDimensionUpperLimit;
+
+        const int32_t widthInBlocks = divUp(width.value(), mBlockWidth);
+        const int32_t heightInBlocks = divUp(height.value(), mBlockHeight);
+        const int32_t blockCount = widthInBlocks * heightInBlocks;
+        ok &= mBlockCountRange.contains(blockCount)
+                && mBlockAspectRatioRange.contains(
+                        Rational(widthInBlocks, heightInBlocks))
+                && mAspectRatioRange.contains(Rational(width.value(), height.value()));
+        if (rate) {
+            double blocksPerSec = blockCount * rate.value();
+            ok &= mBlocksPerSecondRange.contains(
+                    Range<int64_t>::RangeFor(blocksPerSec));
+        }
+    }
+    return ok;
+}
+
+bool VideoCapabilities::supportsFormat(const sp<AMessage> &format) const {
+    int32_t widthVal, heightVal;
+    std::optional<int32_t> width = format->findInt32(KEY_WIDTH, &widthVal)
+            ? std::make_optional<int32_t>(widthVal) : std::nullopt;
+    std::optional<int32_t> height = format->findInt32(KEY_HEIGHT, &heightVal)
+            ? std::make_optional<int32_t>(heightVal) : std::nullopt;
+    double rateVal;
+    std::optional<double> rate = format->findDouble(KEY_FRAME_RATE, &rateVal)
+            ? std::make_optional<double>(rateVal) : std::nullopt;
+
+    if (!supports(width, height, rate)) {
+        return false;
+    }
+
+    if (!CodecCapabilities::SupportsBitrate(mBitrateRange, format)) {
+        return false;
+    }
+
+    // we ignore color-format for now as it is not reliably reported by codec
+    return true;
+}
+
+// static
+std::shared_ptr<VideoCapabilities> VideoCapabilities::Create(std::string mediaType,
+        std::vector<ProfileLevel> profLevs, const sp<AMessage> &format) {
+    std::shared_ptr<VideoCapabilities> caps(new VideoCapabilities());
+    caps->init(mediaType, profLevs, format);
+    return caps;
+}
+
+void VideoCapabilities::init(std::string mediaType, std::vector<ProfileLevel> profLevs,
+        const sp<AMessage> &format) {
+    mMediaType = mediaType;
+    mProfileLevels = profLevs;
+    mError = 0;
+
+    initWithPlatformLimits();
+    applyLevelLimits();
+    parseFromInfo(format);
+    updateLimits();
+}
+
+VideoSize VideoCapabilities::getBlockSize() const {
+    return VideoSize(mBlockWidth, mBlockHeight);
+}
+
+const Range<int32_t>& VideoCapabilities::getBlockCountRange() const {
+    return mBlockCountRange;
+}
+
+const Range<int64_t>& VideoCapabilities::getBlocksPerSecondRange() const {
+    return mBlocksPerSecondRange;
+}
+
+Range<Rational> VideoCapabilities::getAspectRatioRange(bool blocks) const {
+    return blocks ? mBlockAspectRatioRange : mAspectRatioRange;
+}
+
+void VideoCapabilities::initWithPlatformLimits() {
+    mBitrateRange = BITRATE_RANGE;
+
+    mWidthRange  = VideoSize::GetAllowedDimensionRange();
+    mHeightRange = VideoSize::GetAllowedDimensionRange();
+    mFrameRateRange = FRAME_RATE_RANGE;
+
+    mHorizontalBlockRange = VideoSize::GetAllowedDimensionRange();
+    mVerticalBlockRange   = VideoSize::GetAllowedDimensionRange();
+
+    // full positive ranges are supported as these get calculated
+    mBlockCountRange      = POSITIVE_INT32;
+    mBlocksPerSecondRange = POSITIVE_INT64;
+
+    mBlockAspectRatioRange = POSITIVE_RATIONALS;
+    mAspectRatioRange      = POSITIVE_RATIONALS;
+
+    // YUV 4:2:0 requires 2:2 alignment
+    mWidthAlignment = 2;
+    mHeightAlignment = 2;
+    mBlockWidth = 2;
+    mBlockHeight = 2;
+    mSmallerDimensionUpperLimit = VideoSize::GetAllowedDimensionRange().upper();
+}
+
+std::vector<VideoCapabilities::PerformancePoint>
+        VideoCapabilities::getPerformancePoints(
+        const sp<AMessage> &format) const {
+    std::vector<PerformancePoint> ret;
+    AMessage::Type type;
+    for (int i = 0; i < format->countEntries(); i++) {
+        const char *name = format->getEntryNameAt(i, &type);
+        AString rangeStr;
+        if (!format->findString(name, &rangeStr)) {
+            continue;
+        }
+
+        const std::string key = std::string(name);
+        // looking for: performance-point-WIDTHxHEIGHT-range
+
+        // check none performance point
+        if (key == "performance-point-none" && ret.size() == 0) {
+            // This means that component knowingly did not publish performance points.
+            // This is different from when the component forgot to publish performance
+            // points.
+            return ret;
+        }
+
+        // parse size from key
+        std::regex sizeRegex("performance-point-(.+)-range");
+        std::smatch sizeMatch;
+        if (!std::regex_match(key, sizeMatch, sizeRegex)) {
+            continue;
+        }
+        std::optional<VideoSize> size = VideoSize::ParseSize(sizeMatch[1].str());
+        if (!size || size.value().getWidth() * size.value().getHeight() <= 0) {
+            continue;
+        }
+
+        // parse range from value
+        std::optional<Range<int64_t>> range = Range<int64_t>::Parse(std::string(rangeStr.c_str()));
+        if (!range || range.value().lower() < 0 || range.value().upper() < 0) {
+            continue;
+        }
+
+        PerformancePoint given = PerformancePoint(
+                size.value().getWidth(), size.value().getHeight(), (int32_t)range.value().lower(),
+                (int32_t)range.value().upper(), VideoSize(mBlockWidth, mBlockHeight));
+        PerformancePoint rotated = PerformancePoint(
+                size.value().getHeight(), size.value().getWidth(), (int32_t)range.value().lower(),
+                (int32_t)range.value().upper(), VideoSize(mBlockWidth, mBlockHeight));
+        ret.push_back(given);
+        if (!given.covers(rotated)) {
+            ret.push_back(rotated);
+        }
+    }
+
+    // check if the component specified no performance point indication
+    if (ret.size() == 0) {
+        return ret;
+    }
+
+    // sort reversed by area first, then by frame rate
+    std::sort(ret.begin(), ret.end(), [](const PerformancePoint &a, const PerformancePoint &b) {
+        return -((a.getMaxMacroBlocks() != b.getMaxMacroBlocks()) ?
+                        (a.getMaxMacroBlocks() < b.getMaxMacroBlocks() ? -1 : 1) :
+                (a.getMaxMacroBlockRate() != b.getMaxMacroBlockRate()) ?
+                        (a.getMaxMacroBlockRate() < b.getMaxMacroBlockRate() ? -1 : 1) :
+                (a.getMaxFrameRate() != b.getMaxFrameRate()) ?
+                        (a.getMaxFrameRate() < b.getMaxFrameRate() ? -1 : 1) : 0);
+    });
+
+    return ret;
+}
+
+std::map<VideoSize, Range<int64_t>, VideoSizeCompare> VideoCapabilities
+        ::getMeasuredFrameRates(const sp<AMessage> &format) const {
+    std::map<VideoSize, Range<int64_t>, VideoSizeCompare> ret;
+    AMessage::Type type;
+    for (int i = 0; i < format->countEntries(); i++) {
+        const char *name = format->getEntryNameAt(i, &type);
+        AString rangeStr;
+        if (!format->findString(name, &rangeStr)) {
+            continue;
+        }
+
+        const std::string key = std::string(name);
+        // looking for: measured-frame-rate-WIDTHxHEIGHT-range
+
+        std::regex sizeRegex("measured-frame-rate-(.+)-range");
+        std::smatch sizeMatch;
+        if (!std::regex_match(key, sizeMatch, sizeRegex)) {
+            continue;
+        }
+
+        std::optional<VideoSize> size = VideoSize::ParseSize(sizeMatch[1].str());
+        if (!size || size.value().getWidth() * size.value().getHeight() <= 0) {
+            continue;
+        }
+
+        std::optional<Range<int64_t>> range = Range<int64_t>::Parse(std::string(rangeStr.c_str()));
+        if (!range || range.value().lower() < 0 || range.value().upper() < 0) {
+            continue;
+        }
+
+        ret.emplace(size.value(), range.value());
+    }
+    return ret;
+}
+
+// static
+std::optional<std::pair<Range<int32_t>, Range<int32_t>>> VideoCapabilities
+        ::ParseWidthHeightRanges(const std::string &str) {
+    std::optional<std::pair<VideoSize, VideoSize>> range = VideoSize::ParseSizeRange(str);
+    if (!range) {
+        ALOGW("could not parse size range: %s", str.c_str());
+        return std::nullopt;
+    }
+
+    return std::make_optional(std::pair(
+            Range(range.value().first.getWidth(), range.value().second.getWidth()),
+            Range(range.value().first.getHeight(), range.value().second.getHeight())));
+}
+
+// static
+int32_t VideoCapabilities::EquivalentVP9Level(const sp<AMessage> &format) {
+    int32_t blockSizeWidth = 8;
+    int32_t blockSizeHeight = 8;
+    // VideoSize *blockSizePtr = &VideoSize(8, 8);
+    AString blockSizeStr;
+    if (format->findString("block-size", &blockSizeStr)) {
+        std::optional<VideoSize> parsedBlockSize
+                = VideoSize::ParseSize(std::string(blockSizeStr.c_str()));
+        if (parsedBlockSize) {
+            // blockSize = parsedBlockSize.value();
+            blockSizeWidth = parsedBlockSize.value().getWidth();
+            blockSizeHeight = parsedBlockSize.value().getHeight();
+        }
+    }
+    int32_t BS = blockSizeWidth * blockSizeHeight;
+
+    int32_t FS = 0;
+    AString blockCountRangeStr;
+    if (format->findString("block-count-range", &blockCountRangeStr)) {
+        std::optional<Range<int>> counts = Range<int32_t>::Parse(
+                std::string(blockCountRangeStr.c_str()));
+        if (counts) {
+            FS = BS * counts.value().upper();
+        }
+    }
+
+    int64_t SR = 0;
+    AString blockRatesStr;
+    if (format->findString("blocks-per-second-range", &blockRatesStr)) {
+        std::optional<Range<int64_t>> blockRates
+                = Range<int64_t>::Parse(std::string(blockRatesStr.c_str()));
+        if (blockRates) {
+            // ToDo: Catch the potential overflow issue.
+            SR = BS * blockRates.value().upper();
+        }
+    }
+
+    int32_t D = 0;
+    AString dimensionRangesStr;
+    if (format->findString("size-range", &dimensionRangesStr)) {
+        std::optional<std::pair<Range<int>, Range<int>>> dimensionRanges =
+                ParseWidthHeightRanges(std::string(dimensionRangesStr.c_str()));
+        if (dimensionRanges) {
+            D = std::max(dimensionRanges.value().first.upper(),
+                    dimensionRanges.value().second.upper());
+        }
+    }
+
+    int32_t BR = 0;
+    AString bitrateRangeStr;
+    if (format->findString("bitrate-range", &bitrateRangeStr)) {
+        std::optional<Range<int>> bitRates = Range<int32_t>::Parse(
+                std::string(bitrateRangeStr.c_str()));
+        if (bitRates) {
+            BR = divUp(bitRates.value().upper(), 1000);
+        }
+    }
+
+    if (SR <=      829440 && FS <=    36864 && BR <=    200 && D <=   512)
+        return VP9Level1;
+    if (SR <=     2764800 && FS <=    73728 && BR <=    800 && D <=   768)
+        return VP9Level11;
+    if (SR <=     4608000 && FS <=   122880 && BR <=   1800 && D <=   960)
+        return VP9Level2;
+    if (SR <=     9216000 && FS <=   245760 && BR <=   3600 && D <=  1344)
+        return VP9Level21;
+    if (SR <=    20736000 && FS <=   552960 && BR <=   7200 && D <=  2048)
+        return VP9Level3;
+    if (SR <=    36864000 && FS <=   983040 && BR <=  12000 && D <=  2752)
+        return VP9Level31;
+    if (SR <=    83558400 && FS <=  2228224 && BR <=  18000 && D <=  4160)
+        return VP9Level4;
+    if (SR <=   160432128 && FS <=  2228224 && BR <=  30000 && D <=  4160)
+        return VP9Level41;
+    if (SR <=   311951360 && FS <=  8912896 && BR <=  60000 && D <=  8384)
+        return VP9Level5;
+    if (SR <=   588251136 && FS <=  8912896 && BR <= 120000 && D <=  8384)
+        return VP9Level51;
+    if (SR <=  1176502272 && FS <=  8912896 && BR <= 180000 && D <=  8384)
+        return VP9Level52;
+    if (SR <=  1176502272 && FS <= 35651584 && BR <= 180000 && D <= 16832)
+        return VP9Level6;
+    if (SR <= 2353004544L && FS <= 35651584 && BR <= 240000 && D <= 16832)
+        return VP9Level61;
+    if (SR <= 4706009088L && FS <= 35651584 && BR <= 480000 && D <= 16832)
+        return VP9Level62;
+    // returning largest level
+    return VP9Level62;
+}
+
+void VideoCapabilities::parseFromInfo(const sp<AMessage> &format) {
+    VideoSize blockSize = VideoSize(mBlockWidth, mBlockHeight);
+    VideoSize alignment = VideoSize(mWidthAlignment, mHeightAlignment);
+    std::optional<Range<int32_t>> counts, widths, heights;
+    std::optional<Range<int32_t>> frameRates, bitRates;
+    std::optional<Range<int64_t>> blockRates;
+    std::optional<Range<Rational>> ratios, blockRatios;
+
+    AString blockSizeStr;
+    if (format->findString("block-size", &blockSizeStr)) {
+        std::optional<VideoSize> parsedBlockSize
+                = VideoSize::ParseSize(std::string(blockSizeStr.c_str()));
+        blockSize = parsedBlockSize.value_or(blockSize);
+    }
+    AString alignmentStr;
+    if (format->findString("alignment", &alignmentStr)) {
+        std::optional<VideoSize> parsedAlignment
+            = VideoSize::ParseSize(std::string(alignmentStr.c_str()));
+        alignment = parsedAlignment.value_or(alignment);
+    }
+    AString blockCountRangeStr;
+    if (format->findString("block-count-range", &blockCountRangeStr)) {
+        std::optional<Range<int>> parsedBlockCountRange =
+                Range<int32_t>::Parse(std::string(blockCountRangeStr.c_str()));
+        if (parsedBlockCountRange) {
+            counts = parsedBlockCountRange.value();
+        }
+    }
+    AString blockRatesStr;
+    if (format->findString("blocks-per-second-range", &blockRatesStr)) {
+        blockRates = Range<int64_t>::Parse(std::string(blockRatesStr.c_str()));
+    }
+    mMeasuredFrameRates = getMeasuredFrameRates(format);
+    mPerformancePoints = getPerformancePoints(format);
+    AString sizeRangesStr;
+    if (format->findString("size-range", &sizeRangesStr)) {
+        std::optional<std::pair<Range<int>, Range<int>>> sizeRanges =
+            ParseWidthHeightRanges(std::string(sizeRangesStr.c_str()));
+        if (sizeRanges) {
+            widths = sizeRanges.value().first;
+            heights = sizeRanges.value().second;
+        }
+    }
+    // for now this just means using the smaller max size as 2nd
+    // upper limit.
+    // for now we are keeping the profile specific "width/height
+    // in macroblocks" limits.
+    if (format->contains("feature-can-swap-width-height")) {
+        if (widths && heights) {
+            mSmallerDimensionUpperLimit =
+                std::min(widths.value().upper(), heights.value().upper());
+            widths = heights = widths.value().extend(heights.value());
+        } else {
+            ALOGW("feature can-swap-width-height is best used with size-range");
+            mSmallerDimensionUpperLimit =
+                std::min(mWidthRange.upper(), mHeightRange.upper());
+            mWidthRange = mHeightRange = mWidthRange.extend(mHeightRange);
+        }
+    }
+
+    AString ratioStr;
+    if (format->findString("block-aspect-ratio-range", &ratioStr)) {
+        ratios = Rational::ParseRange(std::string(ratioStr.c_str()));
+    }
+    AString blockRatiosStr;
+    if (format->findString("pixel-aspect-ratio-range", &blockRatiosStr)) {
+        blockRatios = Rational::ParseRange(std::string(blockRatiosStr.c_str()));
+    }
+    AString frameRatesStr;
+    if (format->findString("frame-rate-range", &frameRatesStr)) {
+        frameRates = Range<int32_t>::Parse(std::string(frameRatesStr.c_str()));
+        if (frameRates) {
+            frameRates = frameRates.value().intersect(FRAME_RATE_RANGE);
+            if (frameRates.value().empty()) {
+                ALOGW("frame rate range is out of limits");
+                frameRates = std::nullopt;
+            }
+        }
+    }
+    AString bitRatesStr;
+    if (format->findString("bitrate-range", &bitRatesStr)) {
+        bitRates = Range<int32_t>::Parse(std::string(bitRatesStr.c_str()));
+        if (bitRates) {
+            bitRates = bitRates.value().intersect(BITRATE_RANGE);
+            if (bitRates.value().empty()) {
+                ALOGW("bitrate range is out of limits");
+                bitRates = std::nullopt;
+            }
+        }
+    }
+
+    if (!IsPowerOfTwo(blockSize.getWidth()) || !IsPowerOfTwo(blockSize.getHeight())
+            || !IsPowerOfTwo(alignment.getWidth()) || !IsPowerOfTwo(alignment.getHeight())) {
+        ALOGE("The widths and heights of blockSizes and alignments must be the power of two."
+                " blockSize width: %d; blockSize height: %d;"
+                " alignment width: %d; alignment height: %d.",
+                blockSize.getWidth(), blockSize.getHeight(),
+                alignment.getWidth(), alignment.getHeight());
+        mError |= ERROR_CAPABILITIES_UNRECOGNIZED;
+        return;
+    }
+
+    // update block-size and alignment
+    applyMacroBlockLimits(
+            INT32_MAX, INT32_MAX, INT32_MAX, INT64_MAX,
+            blockSize.getWidth(), blockSize.getHeight(),
+            alignment.getWidth(), alignment.getHeight());
+
+    if ((mError & ERROR_CAPABILITIES_UNSUPPORTED) != 0 || mAllowMbOverride) {
+        // codec supports profiles that we don't know.
+        // Use supplied values clipped to platform limits
+        if (widths) {
+            mWidthRange = VideoSize::GetAllowedDimensionRange().intersect(widths.value());
+        }
+        if (heights) {
+            mHeightRange = VideoSize::GetAllowedDimensionRange().intersect(heights.value());
+        }
+        if (counts) {
+            mBlockCountRange = POSITIVE_INT32.intersect(
+                    counts.value().factor(mBlockWidth * mBlockHeight
+                            / blockSize.getWidth() / blockSize.getHeight()));
+        }
+        if (blockRates) {
+            mBlocksPerSecondRange = POSITIVE_INT64.intersect(
+                    blockRates.value().factor(mBlockWidth * mBlockHeight
+                            / blockSize.getWidth() / blockSize.getHeight()));
+        }
+        if (blockRatios) {
+            mBlockAspectRatioRange = POSITIVE_RATIONALS.intersect(
+                    Rational::ScaleRange(blockRatios.value(),
+                            mBlockHeight / blockSize.getHeight(),
+                            mBlockWidth / blockSize.getWidth()));
+        }
+        if (ratios) {
+            mAspectRatioRange = POSITIVE_RATIONALS.intersect(ratios.value());
+        }
+        if (frameRates) {
+            mFrameRateRange = FRAME_RATE_RANGE.intersect(frameRates.value());
+        }
+        if (bitRates) {
+            // only allow bitrate override if unsupported profiles were encountered
+            if ((mError & ERROR_CAPABILITIES_UNSUPPORTED) != 0) {
+                mBitrateRange = BITRATE_RANGE.intersect(bitRates.value());
+            } else {
+                mBitrateRange = mBitrateRange.intersect(bitRates.value());
+            }
+        }
+    } else {
+        // no unsupported profile/levels, so restrict values to known limits
+        if (widths) {
+            mWidthRange = mWidthRange.intersect(widths.value());
+        }
+        if (heights) {
+            mHeightRange = mHeightRange.intersect(heights.value());
+        }
+        if (counts) {
+            mBlockCountRange = mBlockCountRange.intersect(
+                    counts.value().factor(mBlockWidth * mBlockHeight
+                            / blockSize.getWidth() / blockSize.getHeight()));
+        }
+        if (blockRates) {
+            mBlocksPerSecondRange = mBlocksPerSecondRange.intersect(
+                    blockRates.value().factor(mBlockWidth * mBlockHeight
+                            / blockSize.getWidth() / blockSize.getHeight()));
+        }
+        if (blockRatios) {
+            mBlockAspectRatioRange = mBlockAspectRatioRange.intersect(
+                    Rational::ScaleRange(blockRatios.value(),
+                            mBlockHeight / blockSize.getHeight(),
+                            mBlockWidth / blockSize.getWidth()));
+        }
+        if (ratios) {
+            mAspectRatioRange = mAspectRatioRange.intersect(ratios.value());
+        }
+        if (frameRates) {
+            mFrameRateRange = mFrameRateRange.intersect(frameRates.value());
+        }
+        if (bitRates) {
+            mBitrateRange = mBitrateRange.intersect(bitRates.value());
+        }
+    }
+    updateLimits();
+}
+
+void VideoCapabilities::applyBlockLimits(
+        int32_t blockWidth, int32_t blockHeight,
+        Range<int32_t> counts, Range<int64_t> rates, Range<Rational> ratios) {
+
+    if (!IsPowerOfTwo(blockWidth) || !IsPowerOfTwo(blockHeight)) {
+        ALOGE("blockWidth and blockHeight must be the power of two."
+                " blockWidth: %d; blockHeight: %d", blockWidth, blockHeight);
+        mError |= ERROR_CAPABILITIES_UNRECOGNIZED;
+        return;
+    }
+
+    const int32_t newBlockWidth = std::max(blockWidth, mBlockWidth);
+    const int32_t newBlockHeight = std::max(blockHeight, mBlockHeight);
+
+    // factor will always be a power-of-2
+    int32_t factor =
+        newBlockWidth * newBlockHeight / mBlockWidth / mBlockHeight;
+    if (factor != 1) {
+        mBlockCountRange = mBlockCountRange.factor(factor);
+        mBlocksPerSecondRange = mBlocksPerSecondRange.factor(factor);
+        mBlockAspectRatioRange = Rational::ScaleRange(
+                mBlockAspectRatioRange,
+                newBlockHeight / mBlockHeight,
+                newBlockWidth / mBlockWidth);
+        mHorizontalBlockRange = mHorizontalBlockRange.factor(newBlockWidth / mBlockWidth);
+        mVerticalBlockRange = mVerticalBlockRange.factor(newBlockHeight / mBlockHeight);
+    }
+    factor = newBlockWidth * newBlockHeight / blockWidth / blockHeight;
+    if (factor != 1) {
+        counts = counts.factor(factor);
+        rates = rates.factor((int64_t)factor);
+        ratios = Rational::ScaleRange(
+                ratios, newBlockHeight / blockHeight,
+                newBlockWidth / blockWidth);
+    }
+    mBlockCountRange = mBlockCountRange.intersect(counts);
+    mBlocksPerSecondRange = mBlocksPerSecondRange.intersect(rates);
+    mBlockAspectRatioRange = mBlockAspectRatioRange.intersect(ratios);
+    mBlockWidth = newBlockWidth;
+    mBlockHeight = newBlockHeight;
+}
+
+void VideoCapabilities::applyAlignment(
+        int32_t widthAlignment, int32_t heightAlignment) {
+    if (!IsPowerOfTwo(widthAlignment) || !IsPowerOfTwo(heightAlignment)) {
+        ALOGE("width and height alignments must be the power of two."
+                " widthAlignment: %d; heightAlignment: %d", widthAlignment, heightAlignment);
+        mError |= ERROR_CAPABILITIES_UNRECOGNIZED;
+        return;
+    }
+
+    if (widthAlignment > mBlockWidth || heightAlignment > mBlockHeight) {
+        // maintain assumption that 0 < alignment <= block-size
+        applyBlockLimits(
+                std::max(widthAlignment, mBlockWidth),
+                std::max(heightAlignment, mBlockHeight),
+                POSITIVE_INT32, POSITIVE_INT64, POSITIVE_RATIONALS);
+    }
+
+    mWidthAlignment = std::max(widthAlignment, mWidthAlignment);
+    mHeightAlignment = std::max(heightAlignment, mHeightAlignment);
+
+    mWidthRange = mWidthRange.align(mWidthAlignment);
+    mHeightRange = mHeightRange.align(mHeightAlignment);
+}
+
+void VideoCapabilities::updateLimits() {
+    // pixels -> blocks <- counts
+    mHorizontalBlockRange = mHorizontalBlockRange.intersect(
+            mWidthRange.factor(mBlockWidth));
+    mHorizontalBlockRange = mHorizontalBlockRange.intersect(
+            Range(  mBlockCountRange.lower() / mVerticalBlockRange.upper(),
+                    mBlockCountRange.upper() / mVerticalBlockRange.lower()));
+    mVerticalBlockRange = mVerticalBlockRange.intersect(
+            mHeightRange.factor(mBlockHeight));
+    mVerticalBlockRange = mVerticalBlockRange.intersect(
+            Range(  mBlockCountRange.lower() / mHorizontalBlockRange.upper(),
+                    mBlockCountRange.upper() / mHorizontalBlockRange.lower()));
+    mBlockCountRange = mBlockCountRange.intersect(
+            Range(  mHorizontalBlockRange.lower()
+                            * mVerticalBlockRange.lower(),
+                    mHorizontalBlockRange.upper()
+                            * mVerticalBlockRange.upper()));
+    mBlockAspectRatioRange = mBlockAspectRatioRange.intersect(
+            Rational(mHorizontalBlockRange.lower(), mVerticalBlockRange.upper()),
+            Rational(mHorizontalBlockRange.upper(), mVerticalBlockRange.lower()));
+
+    // blocks -> pixels
+    mWidthRange = mWidthRange.intersect(
+            (mHorizontalBlockRange.lower() - 1) * mBlockWidth + mWidthAlignment,
+            mHorizontalBlockRange.upper() * mBlockWidth);
+    mHeightRange = mHeightRange.intersect(
+            (mVerticalBlockRange.lower() - 1) * mBlockHeight + mHeightAlignment,
+            mVerticalBlockRange.upper() * mBlockHeight);
+    mAspectRatioRange = mAspectRatioRange.intersect(
+            Rational(mWidthRange.lower(), mHeightRange.upper()),
+            Rational(mWidthRange.upper(), mHeightRange.lower()));
+
+    mSmallerDimensionUpperLimit = std::min(
+            mSmallerDimensionUpperLimit,
+            std::min(mWidthRange.upper(), mHeightRange.upper()));
+
+    // blocks -> rate
+    mBlocksPerSecondRange = mBlocksPerSecondRange.intersect(
+            mBlockCountRange.lower() * (int64_t)mFrameRateRange.lower(),
+            mBlockCountRange.upper() * (int64_t)mFrameRateRange.upper());
+    mFrameRateRange = mFrameRateRange.intersect(
+            (int32_t)(mBlocksPerSecondRange.lower()
+                    / mBlockCountRange.upper()),
+            (int32_t)(mBlocksPerSecondRange.upper()
+                    / (double)mBlockCountRange.lower()));
+}
+
+void VideoCapabilities::applyMacroBlockLimits(
+        int32_t maxHorizontalBlocks, int32_t maxVerticalBlocks,
+        int32_t maxBlocks, int64_t maxBlocksPerSecond,
+        int32_t blockWidth, int32_t blockHeight,
+        int32_t widthAlignment, int32_t heightAlignment) {
+    applyMacroBlockLimits(
+            1 /* minHorizontalBlocks */, 1 /* minVerticalBlocks */,
+            maxHorizontalBlocks, maxVerticalBlocks,
+            maxBlocks, maxBlocksPerSecond,
+            blockWidth, blockHeight, widthAlignment, heightAlignment);
+}
+
+void VideoCapabilities::applyMacroBlockLimits(
+        int32_t minHorizontalBlocks, int32_t minVerticalBlocks,
+        int32_t maxHorizontalBlocks, int32_t maxVerticalBlocks,
+        int32_t maxBlocks, int64_t maxBlocksPerSecond,
+        int32_t blockWidth, int32_t blockHeight,
+        int32_t widthAlignment, int32_t heightAlignment) {
+    applyAlignment(widthAlignment, heightAlignment);
+    applyBlockLimits(
+            blockWidth, blockHeight, Range((int32_t)1, maxBlocks),
+            Range((int64_t)1, maxBlocksPerSecond),
+            Range(Rational(1, maxVerticalBlocks), Rational(maxHorizontalBlocks, 1)));
+    mHorizontalBlockRange =
+            mHorizontalBlockRange.intersect(
+                    divUp(minHorizontalBlocks, (mBlockWidth / blockWidth)),
+                    maxHorizontalBlocks / (mBlockWidth / blockWidth));
+    mVerticalBlockRange =
+            mVerticalBlockRange.intersect(
+                    divUp(minVerticalBlocks, (mBlockHeight / blockHeight)),
+                    maxVerticalBlocks / (mBlockHeight / blockHeight));
+}
+
+void VideoCapabilities::applyLevelLimits() {
+    int64_t maxBlocksPerSecond = 0;
+    int32_t maxBlocks = 0;
+    int32_t maxBps = 0;
+    int32_t maxDPBBlocks = 0;
+
+    int errors = ERROR_CAPABILITIES_NONE_SUPPORTED;
+    const char *mediaType = mMediaType.c_str();
+    if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_AVC)) {
+        maxBlocks = 99;
+        maxBlocksPerSecond = 1485;
+        maxBps = 64000;
+        maxDPBBlocks = 396;
+        for (ProfileLevel profileLevel: mProfileLevels) {
+            int32_t MBPS = 0, FS = 0, BR = 0, DPB = 0;
+            bool supported = true;
+            switch (profileLevel.mLevel) {
+                case AVCLevel1:
+                    MBPS =     1485; FS =     99; BR =     64; DPB =    396; break;
+                case AVCLevel1b:
+                    MBPS =     1485; FS =     99; BR =    128; DPB =    396; break;
+                case AVCLevel11:
+                    MBPS =     3000; FS =    396; BR =    192; DPB =    900; break;
+                case AVCLevel12:
+                    MBPS =     6000; FS =    396; BR =    384; DPB =   2376; break;
+                case AVCLevel13:
+                    MBPS =    11880; FS =    396; BR =    768; DPB =   2376; break;
+                case AVCLevel2:
+                    MBPS =    11880; FS =    396; BR =   2000; DPB =   2376; break;
+                case AVCLevel21:
+                    MBPS =    19800; FS =    792; BR =   4000; DPB =   4752; break;
+                case AVCLevel22:
+                    MBPS =    20250; FS =   1620; BR =   4000; DPB =   8100; break;
+                case AVCLevel3:
+                    MBPS =    40500; FS =   1620; BR =  10000; DPB =   8100; break;
+                case AVCLevel31:
+                    MBPS =   108000; FS =   3600; BR =  14000; DPB =  18000; break;
+                case AVCLevel32:
+                    MBPS =   216000; FS =   5120; BR =  20000; DPB =  20480; break;
+                case AVCLevel4:
+                    MBPS =   245760; FS =   8192; BR =  20000; DPB =  32768; break;
+                case AVCLevel41:
+                    MBPS =   245760; FS =   8192; BR =  50000; DPB =  32768; break;
+                case AVCLevel42:
+                    MBPS =   522240; FS =   8704; BR =  50000; DPB =  34816; break;
+                case AVCLevel5:
+                    MBPS =   589824; FS =  22080; BR = 135000; DPB = 110400; break;
+                case AVCLevel51:
+                    MBPS =   983040; FS =  36864; BR = 240000; DPB = 184320; break;
+                case AVCLevel52:
+                    MBPS =  2073600; FS =  36864; BR = 240000; DPB = 184320; break;
+                case AVCLevel6:
+                    MBPS =  4177920; FS = 139264; BR = 240000; DPB = 696320; break;
+                case AVCLevel61:
+                    MBPS =  8355840; FS = 139264; BR = 480000; DPB = 696320; break;
+                case AVCLevel62:
+                    MBPS = 16711680; FS = 139264; BR = 800000; DPB = 696320; break;
+                default:
+                    ALOGW("Unrecognized level %d for %s", profileLevel.mLevel, mediaType);
+                    errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+            }
+            switch (profileLevel.mProfile) {
+                case AVCProfileConstrainedHigh:
+                case AVCProfileHigh:
+                    BR *= 1250; break;
+                case AVCProfileHigh10:
+                    BR *= 3000; break;
+                case AVCProfileExtended:
+                case AVCProfileHigh422:
+                case AVCProfileHigh444:
+                    ALOGW("Unsupported profile %d for %s", profileLevel.mProfile, mediaType);
+                    errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+                    supported = false;
+                    FALLTHROUGH_INTENDED;
+                    // fall through - treat as base profile
+                case AVCProfileConstrainedBaseline:
+                    FALLTHROUGH_INTENDED;
+                case AVCProfileBaseline:
+                    FALLTHROUGH_INTENDED;
+                case AVCProfileMain:
+                    BR *= 1000; break;
+                default:
+                    ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile, mediaType);
+                    errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+                    BR *= 1000;
+            }
+            if (supported) {
+                errors &= ~ERROR_CAPABILITIES_NONE_SUPPORTED;
+            }
+            maxBlocksPerSecond = std::max((int64_t)MBPS, maxBlocksPerSecond);
+            maxBlocks = std::max(FS, maxBlocks);
+            maxBps = std::max(BR, maxBps);
+            maxDPBBlocks = std::max(maxDPBBlocks, DPB);
+        }
+
+        int32_t maxLengthInBlocks = (int32_t)(std::sqrt(8 * maxBlocks));
+        applyMacroBlockLimits(
+                maxLengthInBlocks, maxLengthInBlocks,
+                maxBlocks, maxBlocksPerSecond,
+                16 /* blockWidth */, 16 /* blockHeight */,
+                1 /* widthAlignment */, 1 /* heightAlignment */);
+    } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_MPEG2)) {
+        int32_t maxWidth = 11, maxHeight = 9, maxRate = 15;
+        maxBlocks = 99;
+        maxBlocksPerSecond = 1485;
+        maxBps = 64000;
+        for (ProfileLevel profileLevel: mProfileLevels) {
+            int32_t MBPS = 0, FS = 0, BR = 0, FR = 0, W = 0, H = 0;
+            bool supported = true;
+            switch (profileLevel.mProfile) {
+                case MPEG2ProfileSimple:
+                    switch (profileLevel.mLevel) {
+                        case MPEG2LevelML:
+                            FR = 30; W = 45; H =  36; MBPS =  40500; FS =  1620; BR =  15000; break;
+                        default:
+                            ALOGW("Unrecognized profile/level %d/%d for %s",
+                                    profileLevel.mProfile, profileLevel.mLevel, mediaType);
+                            errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+                    }
+                    break;
+                case MPEG2ProfileMain:
+                    switch (profileLevel.mLevel) {
+                        case MPEG2LevelLL:
+                            FR = 30; W = 22; H =  18; MBPS =  11880; FS =   396; BR =  4000; break;
+                        case MPEG2LevelML:
+                            FR = 30; W = 45; H =  36; MBPS =  40500; FS =  1620; BR = 15000; break;
+                        case MPEG2LevelH14:
+                            FR = 60; W = 90; H =  68; MBPS = 183600; FS =  6120; BR = 60000; break;
+                        case MPEG2LevelHL:
+                            FR = 60; W = 120; H = 68; MBPS = 244800; FS =  8160; BR = 80000; break;
+                        case MPEG2LevelHP:
+                            FR = 60; W = 120; H = 68; MBPS = 489600; FS =  8160; BR = 80000; break;
+                        default:
+                            ALOGW("Unrecognized profile/level %d / %d for %s",
+                                    profileLevel.mProfile, profileLevel.mLevel, mediaType);
+                            errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+                    }
+                    break;
+                case MPEG2Profile422:
+                case MPEG2ProfileSNR:
+                case MPEG2ProfileSpatial:
+                case MPEG2ProfileHigh:
+                    ALOGW("Unsupported profile %d for %s", profileLevel.mProfile, mediaType);
+                    errors |= ERROR_CAPABILITIES_UNSUPPORTED;
+                    supported = false;
+                    break;
+                default:
+                    ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile, mediaType);
+                    errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+            }
+            if (supported) {
+                errors &= ~ERROR_CAPABILITIES_NONE_SUPPORTED;
+            }
+            maxBlocksPerSecond = std::max((int64_t)MBPS, maxBlocksPerSecond);
+            maxBlocks = std::max(FS, maxBlocks);
+            maxBps = std::max(BR * 1000, maxBps);
+            maxWidth = std::max(W, maxWidth);
+            maxHeight = std::max(H, maxHeight);
+            maxRate = std::max(FR, maxRate);
+        }
+        applyMacroBlockLimits(maxWidth, maxHeight,
+                maxBlocks, maxBlocksPerSecond,
+                16 /* blockWidth */, 16 /* blockHeight */,
+                1 /* widthAlignment */, 1 /* heightAlignment */);
+        mFrameRateRange = mFrameRateRange.intersect(12, maxRate);
+    } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_MPEG4)) {
+        int32_t maxWidth = 11, maxHeight = 9, maxRate = 15;
+        maxBlocks = 99;
+        maxBlocksPerSecond = 1485;
+        maxBps = 64000;
+        for (ProfileLevel profileLevel: mProfileLevels) {
+            int32_t MBPS = 0, FS = 0, BR = 0, FR = 0, W = 0, H = 0;
+            bool strict = false; // true: W, H and FR are individual max limits
+            bool supported = true;
+            switch (profileLevel.mProfile) {
+                case MPEG4ProfileSimple:
+                    switch (profileLevel.mLevel) {
+                        case MPEG4Level0:
+                            strict = true;
+                            FR = 15; W = 11; H =  9; MBPS =  1485; FS =  99; BR =  64; break;
+                        case MPEG4Level1:
+                            FR = 30; W = 11; H =  9; MBPS =  1485; FS =  99; BR =  64; break;
+                        case MPEG4Level0b:
+                            strict = true;
+                            FR = 15; W = 11; H =  9; MBPS =  1485; FS =  99; BR = 128; break;
+                        case MPEG4Level2:
+                            FR = 30; W = 22; H = 18; MBPS =  5940; FS = 396; BR = 128; break;
+                        case MPEG4Level3:
+                            FR = 30; W = 22; H = 18; MBPS = 11880; FS = 396; BR = 384; break;
+                        case MPEG4Level4a:
+                            FR = 30; W = 40; H = 30; MBPS = 36000; FS = 1200; BR = 4000; break;
+                        case MPEG4Level5:
+                            FR = 30; W = 45; H = 36; MBPS = 40500; FS = 1620; BR = 8000; break;
+                        case MPEG4Level6:
+                            FR = 30; W = 80; H = 45; MBPS = 108000; FS = 3600; BR = 12000; break;
+                        default:
+                            ALOGW("Unrecognized profile/level %d/%d for %s",
+                                    profileLevel.mProfile, profileLevel.mLevel, mediaType);
+                            errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+                    }
+                    break;
+                case MPEG4ProfileAdvancedSimple:
+                    switch (profileLevel.mLevel) {
+                        case MPEG4Level0:
+                        case MPEG4Level1:
+                            FR = 30; W = 11; H =  9; MBPS =  2970; FS =   99; BR =  128; break;
+                        case MPEG4Level2:
+                            FR = 30; W = 22; H = 18; MBPS =  5940; FS =  396; BR =  384; break;
+                        case MPEG4Level3:
+                            FR = 30; W = 22; H = 18; MBPS = 11880; FS =  396; BR =  768; break;
+                        case MPEG4Level3b:
+                            FR = 30; W = 22; H = 18; MBPS = 11880; FS =  396; BR = 1500; break;
+                        case MPEG4Level4:
+                            FR = 30; W = 44; H = 36; MBPS = 23760; FS =  792; BR = 3000; break;
+                        case MPEG4Level5:
+                            FR = 30; W = 45; H = 36; MBPS = 48600; FS = 1620; BR = 8000; break;
+                        default:
+                            ALOGW("Unrecognized profile/level %d/%d for %s",
+                                    profileLevel.mProfile, profileLevel.mLevel, mediaType);
+                            errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+                    }
+                    break;
+                case MPEG4ProfileMain:             // 2-4
+                case MPEG4ProfileNbit:             // 2
+                case MPEG4ProfileAdvancedRealTime: // 1-4
+                case MPEG4ProfileCoreScalable:     // 1-3
+                case MPEG4ProfileAdvancedCoding:   // 1-4
+                case MPEG4ProfileCore:             // 1-2
+                case MPEG4ProfileAdvancedCore:     // 1-4
+                case MPEG4ProfileSimpleScalable:   // 0-2
+                case MPEG4ProfileHybrid:           // 1-2
+
+                // Studio profiles are not supported by our codecs.
+
+                // Only profiles that can decode simple object types are considered.
+                // The following profiles are not able to.
+                case MPEG4ProfileBasicAnimated:    // 1-2
+                case MPEG4ProfileScalableTexture:  // 1
+                case MPEG4ProfileSimpleFace:       // 1-2
+                case MPEG4ProfileAdvancedScalable: // 1-3
+                case MPEG4ProfileSimpleFBA:        // 1-2
+                    ALOGV("Unsupported profile %d for %s", profileLevel.mProfile, mediaType);
+                    errors |= ERROR_CAPABILITIES_UNSUPPORTED;
+                    supported = false;
+                    break;
+                default:
+                    ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile, mediaType);
+                    errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+            }
+            if (supported) {
+                errors &= ~ERROR_CAPABILITIES_NONE_SUPPORTED;
+            }
+            maxBlocksPerSecond = std::max((int64_t)MBPS, maxBlocksPerSecond);
+            maxBlocks = std::max(FS, maxBlocks);
+            maxBps = std::max(BR * 1000, maxBps);
+            if (strict) {
+                maxWidth = std::max(W, maxWidth);
+                maxHeight = std::max(H, maxHeight);
+                maxRate = std::max(FR, maxRate);
+            } else {
+                // assuming max 60 fps frame rate and 1:2 aspect ratio
+                int32_t maxDim = (int32_t)std::sqrt(2 * FS);
+                maxWidth = std::max(maxDim, maxWidth);
+                maxHeight = std::max(maxDim, maxHeight);
+                maxRate = std::max(std::max(FR, 60), maxRate);
+            }
+        }
+        applyMacroBlockLimits(maxWidth, maxHeight,
+                maxBlocks, maxBlocksPerSecond,
+                16 /* blockWidth */, 16 /* blockHeight */,
+                1 /* widthAlignment */, 1 /* heightAlignment */);
+        mFrameRateRange = mFrameRateRange.intersect(12, maxRate);
+    } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_H263)) {
+        int32_t maxWidth = 11, maxHeight = 9, maxRate = 15;
+        int32_t minWidth = maxWidth, minHeight = maxHeight;
+        int32_t minAlignment = 16;
+        maxBlocks = 99;
+        maxBlocksPerSecond = 1485;
+        maxBps = 64000;
+        for (ProfileLevel profileLevel: mProfileLevels) {
+            int32_t MBPS = 0, BR = 0, FR = 0, W = 0, H = 0, minW = minWidth, minH = minHeight;
+            bool strict = false; // true: support only sQCIF, QCIF (maybe CIF)
+            switch (profileLevel.mLevel) {
+                case H263Level10:
+                    strict = true; // only supports sQCIF & QCIF
+                    FR = 15; W = 11; H =  9; BR =   1; MBPS =  W * H * FR; break;
+                case H263Level20:
+                    strict = true; // only supports sQCIF, QCIF & CIF
+                    FR = 30; W = 22; H = 18; BR =   2; MBPS =  W * H * 15; break;
+                case H263Level30:
+                    strict = true; // only supports sQCIF, QCIF & CIF
+                    FR = 30; W = 22; H = 18; BR =   6; MBPS =  W * H * FR; break;
+                case H263Level40:
+                    strict = true; // only supports sQCIF, QCIF & CIF
+                    FR = 30; W = 22; H = 18; BR =  32; MBPS =  W * H * FR; break;
+                case H263Level45:
+                    // only implies level 10 support
+                    strict = profileLevel.mProfile == H263ProfileBaseline
+                            || profileLevel.mProfile ==
+                                    H263ProfileBackwardCompatible;
+                    if (!strict) {
+                        minW = 1; minH = 1; minAlignment = 4;
+                    }
+                    FR = 15; W = 11; H =  9; BR =   2; MBPS =  W * H * FR; break;
+                case H263Level50:
+                    // only supports 50fps for H > 15
+                    minW = 1; minH = 1; minAlignment = 4;
+                    FR = 60; W = 22; H = 18; BR =  64; MBPS =  W * H * 50; break;
+                case H263Level60:
+                    // only supports 50fps for H > 15
+                    minW = 1; minH = 1; minAlignment = 4;
+                    FR = 60; W = 45; H = 18; BR = 128; MBPS =  W * H * 50; break;
+                case H263Level70:
+                    // only supports 50fps for H > 30
+                    minW = 1; minH = 1; minAlignment = 4;
+                    FR = 60; W = 45; H = 36; BR = 256; MBPS =  W * H * 50; break;
+                default:
+                    ALOGW("Unrecognized profile/level %d/%d for %s",
+                            profileLevel.mProfile, profileLevel.mLevel, mediaType);
+                    errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+            }
+            switch (profileLevel.mProfile) {
+                case H263ProfileBackwardCompatible:
+                case H263ProfileBaseline:
+                case H263ProfileH320Coding:
+                case H263ProfileHighCompression:
+                case H263ProfileHighLatency:
+                case H263ProfileInterlace:
+                case H263ProfileInternet:
+                case H263ProfileISWV2:
+                case H263ProfileISWV3:
+                    break;
+                default:
+                    ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile, mediaType);
+                    errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+            }
+            if (strict) {
+                // Strict levels define sub-QCIF min size and enumerated sizes. We cannot
+                // express support for "only sQCIF & QCIF (& CIF)" using VideoCapabilities
+                // but we can express "only QCIF (& CIF)", so set minimume size at QCIF.
+                // minW = 8; minH = 6;
+                minW = 11; minH = 9;
+            } else {
+                // any support for non-strict levels (including unrecognized profiles or
+                // levels) allow custom frame size support beyond supported limits
+                // (other than bitrate)
+                mAllowMbOverride = true;
+            }
+            errors &= ~ERROR_CAPABILITIES_NONE_SUPPORTED;
+            maxBlocksPerSecond = std::max((int64_t)MBPS, maxBlocksPerSecond);
+            maxBlocks = std::max(W * H, maxBlocks);
+            maxBps = std::max(BR * 64000, maxBps);
+            maxWidth = std::max(W, maxWidth);
+            maxHeight = std::max(H, maxHeight);
+            maxRate = std::max(FR, maxRate);
+            minWidth = std::min(minW, minWidth);
+            minHeight = std::min(minH, minHeight);
+        }
+        // unless we encountered custom frame size support, limit size to QCIF and CIF
+        // using aspect ratio.
+        if (!mAllowMbOverride) {
+            mBlockAspectRatioRange =
+                Range(Rational(11, 9), Rational(11, 9));
+        }
+        applyMacroBlockLimits(
+                minWidth, minHeight,
+                maxWidth, maxHeight,
+                maxBlocks, maxBlocksPerSecond,
+                16 /* blockWidth */, 16 /* blockHeight */,
+                minAlignment /* widthAlignment */, minAlignment /* heightAlignment */);
+        mFrameRateRange = Range(1, maxRate);
+    } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_VP8)) {
+        maxBlocks = INT_MAX;
+        maxBlocksPerSecond = INT_MAX;
+
+        // TODO: set to 100Mbps for now, need a number for VP8
+        maxBps = 100000000;
+
+        // profile levels are not indicative for VPx, but verify
+        // them nonetheless
+        for (ProfileLevel profileLevel: mProfileLevels) {
+            switch (profileLevel.mLevel) {
+                case VP8Level_Version0:
+                case VP8Level_Version1:
+                case VP8Level_Version2:
+                case VP8Level_Version3:
+                    break;
+                default:
+                    ALOGW("Unrecognized level %d for %s", profileLevel.mLevel, mediaType);
+                    errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+            }
+            switch (profileLevel.mProfile) {
+                case VP8ProfileMain:
+                    break;
+                default:
+                    ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile, mediaType);
+                    errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+            }
+            errors &= ~ERROR_CAPABILITIES_NONE_SUPPORTED;
+        }
+
+        const int32_t blockSize = 16;
+        applyMacroBlockLimits(SHRT_MAX, SHRT_MAX,
+                maxBlocks, maxBlocksPerSecond, blockSize, blockSize,
+                1 /* widthAlignment */, 1 /* heightAlignment */);
+    } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_VP9)) {
+        maxBlocksPerSecond = 829440;
+        maxBlocks = 36864;
+        maxBps = 200000;
+        int32_t maxDim = 512;
+
+        for (ProfileLevel profileLevel: mProfileLevels) {
+            int64_t SR = 0; // luma sample rate
+            int32_t FS = 0;  // luma picture size
+            int32_t BR = 0;  // bit rate kbps
+            int32_t D = 0;   // luma dimension
+            switch (profileLevel.mLevel) {
+                case VP9Level1:
+                    SR =      829440; FS =    36864; BR =    200; D =   512; break;
+                case VP9Level11:
+                    SR =     2764800; FS =    73728; BR =    800; D =   768; break;
+                case VP9Level2:
+                    SR =     4608000; FS =   122880; BR =   1800; D =   960; break;
+                case VP9Level21:
+                    SR =     9216000; FS =   245760; BR =   3600; D =  1344; break;
+                case VP9Level3:
+                    SR =    20736000; FS =   552960; BR =   7200; D =  2048; break;
+                case VP9Level31:
+                    SR =    36864000; FS =   983040; BR =  12000; D =  2752; break;
+                case VP9Level4:
+                    SR =    83558400; FS =  2228224; BR =  18000; D =  4160; break;
+                case VP9Level41:
+                    SR =   160432128; FS =  2228224; BR =  30000; D =  4160; break;
+                case VP9Level5:
+                    SR =   311951360; FS =  8912896; BR =  60000; D =  8384; break;
+                case VP9Level51:
+                    SR =   588251136; FS =  8912896; BR = 120000; D =  8384; break;
+                case VP9Level52:
+                    SR =  1176502272; FS =  8912896; BR = 180000; D =  8384; break;
+                case VP9Level6:
+                    SR =  1176502272; FS = 35651584; BR = 180000; D = 16832; break;
+                case VP9Level61:
+                    SR = 2353004544L; FS = 35651584; BR = 240000; D = 16832; break;
+                case VP9Level62:
+                    SR = 4706009088L; FS = 35651584; BR = 480000; D = 16832; break;
+                default:
+                    ALOGW("Unrecognized level %d for %s", profileLevel.mLevel, mediaType);
+                    errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+            }
+            switch (profileLevel.mProfile) {
+                case VP9Profile0:
+                case VP9Profile1:
+                case VP9Profile2:
+                case VP9Profile3:
+                case VP9Profile2HDR:
+                case VP9Profile3HDR:
+                case VP9Profile2HDR10Plus:
+                case VP9Profile3HDR10Plus:
+                    break;
+                default:
+                    ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile, mediaType);
+                    errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+            }
+            errors &= ~ERROR_CAPABILITIES_NONE_SUPPORTED;
+            maxBlocksPerSecond = std::max(SR, maxBlocksPerSecond);
+            maxBlocks = std::max(FS, maxBlocks);
+            maxBps = std::max(BR * 1000, maxBps);
+            maxDim = std::max(D, maxDim);
+        }
+
+        const int32_t blockSize = 8;
+        int32_t maxLengthInBlocks = divUp(maxDim, blockSize);
+        maxBlocks = divUp(maxBlocks, blockSize * blockSize);
+        maxBlocksPerSecond = divUp(maxBlocksPerSecond, blockSize * (int64_t)blockSize);
+
+        applyMacroBlockLimits(
+                maxLengthInBlocks, maxLengthInBlocks,
+                maxBlocks, maxBlocksPerSecond,
+                blockSize, blockSize,
+                1 /* widthAlignment */, 1 /* heightAlignment */);
+    } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_HEVC)) {
+        // CTBs are at least 8x8 so use 8x8 block size
+        maxBlocks = 36864 >> 6; // 192x192 pixels == 576 8x8 blocks
+        maxBlocksPerSecond = maxBlocks * 15;
+        maxBps = 128000;
+        for (ProfileLevel profileLevel: mProfileLevels) {
+            double FR = 0;
+            int32_t FS = 0, BR = 0;
+            switch (profileLevel.mLevel) {
+                /* The HEVC spec talks only in a very convoluted manner about the
+                    existence of levels 1-3.1 for High tier, which could also be
+                    understood as 'decoders and encoders should treat these levels
+                    as if they were Main tier', so we do that. */
+                case HEVCMainTierLevel1:
+                case HEVCHighTierLevel1:
+                    FR =    15; FS =    36864; BR =    128; break;
+                case HEVCMainTierLevel2:
+                case HEVCHighTierLevel2:
+                    FR =    30; FS =   122880; BR =   1500; break;
+                case HEVCMainTierLevel21:
+                case HEVCHighTierLevel21:
+                    FR =    30; FS =   245760; BR =   3000; break;
+                case HEVCMainTierLevel3:
+                case HEVCHighTierLevel3:
+                    FR =    30; FS =   552960; BR =   6000; break;
+                case HEVCMainTierLevel31:
+                case HEVCHighTierLevel31:
+                    FR = 33.75; FS =   983040; BR =  10000; break;
+                case HEVCMainTierLevel4:
+                    FR =    30; FS =  2228224; BR =  12000; break;
+                case HEVCHighTierLevel4:
+                    FR =    30; FS =  2228224; BR =  30000; break;
+                case HEVCMainTierLevel41:
+                    FR =    60; FS =  2228224; BR =  20000; break;
+                case HEVCHighTierLevel41:
+                    FR =    60; FS =  2228224; BR =  50000; break;
+                case HEVCMainTierLevel5:
+                    FR =    30; FS =  8912896; BR =  25000; break;
+                case HEVCHighTierLevel5:
+                    FR =    30; FS =  8912896; BR = 100000; break;
+                case HEVCMainTierLevel51:
+                    FR =    60; FS =  8912896; BR =  40000; break;
+                case HEVCHighTierLevel51:
+                    FR =    60; FS =  8912896; BR = 160000; break;
+                case HEVCMainTierLevel52:
+                    FR =   120; FS =  8912896; BR =  60000; break;
+                case HEVCHighTierLevel52:
+                    FR =   120; FS =  8912896; BR = 240000; break;
+                case HEVCMainTierLevel6:
+                    FR =    30; FS = 35651584; BR =  60000; break;
+                case HEVCHighTierLevel6:
+                    FR =    30; FS = 35651584; BR = 240000; break;
+                case HEVCMainTierLevel61:
+                    FR =    60; FS = 35651584; BR = 120000; break;
+                case HEVCHighTierLevel61:
+                    FR =    60; FS = 35651584; BR = 480000; break;
+                case HEVCMainTierLevel62:
+                    FR =   120; FS = 35651584; BR = 240000; break;
+                case HEVCHighTierLevel62:
+                    FR =   120; FS = 35651584; BR = 800000; break;
+                default:
+                    ALOGW("Unrecognized level %d for %s", profileLevel.mLevel, mediaType);
+                    errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+            }
+            switch (profileLevel.mProfile) {
+                case HEVCProfileMain:
+                case HEVCProfileMain10:
+                case HEVCProfileMainStill:
+                case HEVCProfileMain10HDR10:
+                case HEVCProfileMain10HDR10Plus:
+                    break;
+                default:
+                    ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile, mediaType);
+                    errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+            }
+
+            /* DPB logic:
+            if      (width * height <= FS / 4)    DPB = 16;
+            else if (width * height <= FS / 2)    DPB = 12;
+            else if (width * height <= FS * 0.75) DPB = 8;
+            else                                  DPB = 6;
+            */
+
+            FS >>= 6; // convert pixels to blocks
+            errors &= ~ERROR_CAPABILITIES_NONE_SUPPORTED;
+            maxBlocksPerSecond = std::max((int64_t)(FR * FS), maxBlocksPerSecond);
+            maxBlocks = std::max(FS, maxBlocks);
+            maxBps = std::max(1000 * BR, maxBps);
+        }
+
+        int32_t maxLengthInBlocks = (int32_t)(std::sqrt(8 * maxBlocks));
+        applyMacroBlockLimits(
+                maxLengthInBlocks, maxLengthInBlocks,
+                maxBlocks, maxBlocksPerSecond,
+                8 /* blockWidth */, 8 /* blockHeight */,
+                1 /* widthAlignment */, 1 /* heightAlignment */);
+    } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_AV1)) {
+        maxBlocksPerSecond = 829440;
+        maxBlocks = 36864;
+        maxBps = 200000;
+        int32_t maxDim = 512;
+
+        // Sample rate, Picture Size, Bit rate and luma dimension for AV1 Codec,
+        // corresponding to the definitions in
+        // "AV1 Bitstream & Decoding Process Specification", Annex A
+        // found at https://aomedia.org/av1-bitstream-and-decoding-process-specification/
+        for (ProfileLevel profileLevel: mProfileLevels) {
+            int64_t SR = 0; // luma sample rate
+            int32_t FS = 0;  // luma picture size
+            int32_t BR = 0;  // bit rate kbps
+            int32_t D = 0;   // luma D
+            switch (profileLevel.mLevel) {
+                case AV1Level2:
+                    SR =     5529600; FS =   147456; BR =   1500; D =  2048; break;
+                case AV1Level21:
+                case AV1Level22:
+                case AV1Level23:
+                    SR =    10454400; FS =   278784; BR =   3000; D =  2816; break;
+
+                case AV1Level3:
+                    SR =    24969600; FS =   665856; BR =   6000; D =  4352; break;
+                case AV1Level31:
+                case AV1Level32:
+                case AV1Level33:
+                    SR =    39938400; FS =  1065024; BR =  10000; D =  5504; break;
+
+                case AV1Level4:
+                    SR =    77856768; FS =  2359296; BR =  12000; D =  6144; break;
+                case AV1Level41:
+                case AV1Level42:
+                case AV1Level43:
+                    SR =   155713536; FS =  2359296; BR =  20000; D =  6144; break;
+
+                case AV1Level5:
+                    SR =   273715200; FS =  8912896; BR =  30000; D =  8192; break;
+                case AV1Level51:
+                    SR =   547430400; FS =  8912896; BR =  40000; D =  8192; break;
+                case AV1Level52:
+                    SR =  1094860800; FS =  8912896; BR =  60000; D =  8192; break;
+                case AV1Level53:
+                    SR =  1176502272; FS =  8912896; BR =  60000; D =  8192; break;
+
+                case AV1Level6:
+                    SR =  1176502272; FS = 35651584; BR =  60000; D = 16384; break;
+                case AV1Level61:
+                    SR = 2189721600L; FS = 35651584; BR = 100000; D = 16384; break;
+                case AV1Level62:
+                    SR = 4379443200L; FS = 35651584; BR = 160000; D = 16384; break;
+                case AV1Level63:
+                    SR = 4706009088L; FS = 35651584; BR = 160000; D = 16384; break;
+
+                default:
+                    ALOGW("Unrecognized level %d for %s", profileLevel.mLevel, mediaType);
+                    errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+            }
+            switch (profileLevel.mProfile) {
+                case AV1ProfileMain8:
+                case AV1ProfileMain10:
+                case AV1ProfileMain10HDR10:
+                case AV1ProfileMain10HDR10Plus:
+                    break;
+                default:
+                    ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile, mediaType);
+                    errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+            }
+            errors &= ~ERROR_CAPABILITIES_NONE_SUPPORTED;
+            maxBlocksPerSecond = std::max(SR, maxBlocksPerSecond);
+            maxBlocks = std::max(FS, maxBlocks);
+            maxBps = std::max(BR * 1000, maxBps);
+            maxDim = std::max(D, maxDim);
+        }
+
+        const int32_t blockSize = 8;
+        int32_t maxLengthInBlocks = divUp(maxDim, blockSize);
+        maxBlocks = divUp(maxBlocks, blockSize * blockSize);
+        maxBlocksPerSecond = divUp(maxBlocksPerSecond, blockSize * (int64_t)blockSize);
+        applyMacroBlockLimits(
+                maxLengthInBlocks, maxLengthInBlocks,
+                maxBlocks, maxBlocksPerSecond,
+                blockSize, blockSize,
+                1 /* widthAlignment */, 1 /* heightAlignment */);
+    } else {
+        ALOGW("Unsupported mime %s", mediaType);
+        // using minimal bitrate here.  should be overridden by
+        // info from media_codecs.xml
+        maxBps = 64000;
+        errors |= ERROR_CAPABILITIES_UNSUPPORTED;
+    }
+    mBitrateRange = Range(1, maxBps);
+    mError |= errors;
+}
+
+}  // namespace android
\ No newline at end of file
diff --git a/media/libmedia/include/media/AudioCapabilities.h b/media/libmedia/include/media/AudioCapabilities.h
index 2cb957e..3e1c9df 100644
--- a/media/libmedia/include/media/AudioCapabilities.h
+++ b/media/libmedia/include/media/AudioCapabilities.h
@@ -37,21 +37,21 @@
     /**
      * Returns the range of supported bitrates in bits/second.
      */
-    const Range<int>& getBitrateRange() const;
+    const Range<int32_t>& getBitrateRange() const;
 
     /**
      * Returns the array of supported sample rates if the codec
      * supports only discrete values. Otherwise, it returns an empty array.
      * The array is sorted in ascending order.
      */
-    const std::vector<int>& getSupportedSampleRates() const;
+    const std::vector<int32_t>& getSupportedSampleRates() const;
 
     /**
      * Returns the array of supported sample rate ranges.  The
      * array is sorted in ascending order, and the ranges are
      * distinct.
      */
-    const std::vector<Range<int>>& getSupportedSampleRateRanges() const;
+    const std::vector<Range<int32_t>>& getSupportedSampleRateRanges() const;
 
     /**
      * Returns the maximum number of input channels supported.
@@ -68,7 +68,7 @@
      * The {@link #getMaxInputChannelCount} method will return the highest value
      * in the ranges returned by {@link #getInputChannelCountRanges}
      */
-    int getMaxInputChannelCount() const;
+    int32_t getMaxInputChannelCount() const;
 
     /**
      * Returns the minimum number of input channels supported.
@@ -77,7 +77,7 @@
      * This returns the lowest channel count in the ranges returned by
      * {@link #getInputChannelCountRanges}.
      */
-    int getMinInputChannelCount() const;
+    int32_t getMinInputChannelCount() const;
 
     /**
      * Returns an array of ranges representing the number of input channels supported.
@@ -89,12 +89,12 @@
      *
      * The returned array cannot be empty.
      */
-    const std::vector<Range<int>>& getInputChannelCountRanges() const;
+    const std::vector<Range<int32_t>>& getInputChannelCountRanges() const;
 
     /**
      * Query whether the sample rate is supported by the codec.
      */
-    bool isSampleRateSupported(int sampleRate);
+    bool isSampleRateSupported(int32_t sampleRate);
 
     /* For internal use only. Not exposed as a public API */
     void getDefaultFormat(sp<AMessage> &format);
@@ -103,31 +103,31 @@
     bool supportsFormat(const sp<AMessage> &format);
 
 private:
-    static constexpr int MAX_INPUT_CHANNEL_COUNT = 30;
+    static constexpr int32_t MAX_INPUT_CHANNEL_COUNT = 30;
     static constexpr uint32_t MAX_NUM_CHANNELS = FCC_LIMIT;
 
     int mError;
     std::string mMediaType;
     std::vector<ProfileLevel> mProfileLevels;
 
-    Range<int> mBitrateRange;
+    Range<int32_t> mBitrateRange;
 
-    std::vector<int> mSampleRates;
-    std::vector<Range<int>> mSampleRateRanges;
-    std::vector<Range<int>> mInputChannelRanges;
+    std::vector<int32_t> mSampleRates;
+    std::vector<Range<int32_t>> mSampleRateRanges;
+    std::vector<Range<int32_t>> mInputChannelRanges;
 
     /* no public constructor */
     AudioCapabilities() {}
     void init(std::string mediaType, std::vector<ProfileLevel> profLevs,
             const sp<AMessage> &format);
     void initWithPlatformLimits();
-    bool supports(std::optional<int> sampleRate, std::optional<int> inputChannels);
-    void limitSampleRates(std::vector<int> rates);
+    bool supports(std::optional<int32_t> sampleRate, std::optional<int32_t> inputChannels);
+    void limitSampleRates(std::vector<int32_t> rates);
     void createDiscreteSampleRates();
-    void limitSampleRates(std::vector<Range<int>> rateRanges);
+    void limitSampleRates(std::vector<Range<int32_t>> rateRanges);
     void applyLevelLimits();
-    void applyLimits(const std::vector<Range<int>> &inputChannels,
-            const std::optional<Range<int>> &bitRates);
+    void applyLimits(const std::vector<Range<int32_t>> &inputChannels,
+            const std::optional<Range<int32_t>> &bitRates);
     void parseFromInfo(const sp<AMessage> &format);
 
     friend struct CodecCapabilities;
diff --git a/media/libmedia/include/media/CodecCapabilities.h b/media/libmedia/include/media/CodecCapabilities.h
index 9d1c4ea..0611d8c 100644
--- a/media/libmedia/include/media/CodecCapabilities.h
+++ b/media/libmedia/include/media/CodecCapabilities.h
@@ -20,6 +20,8 @@
 
 #include <media/AudioCapabilities.h>
 #include <media/CodecCapabilitiesUtils.h>
+#include <media/EncoderCapabilities.h>
+#include <media/VideoCapabilities.h>
 #include <media/stagefright/foundation/ABase.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/foundation/AString.h>
@@ -34,10 +36,30 @@
 
 struct CodecCapabilities {
 
-    static bool SupportsBitrate(Range<int> bitrateRange,
+    static bool SupportsBitrate(Range<int32_t> bitrateRange,
             const sp<AMessage> &format);
 
     /**
+     * Retrieve the codec capabilities for a certain {@code mime type}, {@code
+     * profile} and {@code level}.  If the type, or profile-level combination
+     * is not understood by the framework, it returns null.
+     * <p class=note> In {@link android.os.Build.VERSION_CODES#M}, calling this
+     * method without calling any method of the {@link MediaCodecList} class beforehand
+     * results in a {@link NullPointerException}.</p>
+     */
+    static std::shared_ptr<CodecCapabilities> CreateFromProfileLevel(std::string mediaType,
+                int32_t profile, int32_t level, int32_t maxConcurrentInstances = -1);
+
+    CodecCapabilities() {};
+
+    /**
+     * Init CodecCapabilities with settings.
+     */
+    void init(std::vector<ProfileLevel> profLevs, std::vector<uint32_t> colFmts, bool encoder,
+            sp<AMessage> &defaultFormat, sp<AMessage> &capabilitiesInfo,
+            int32_t maxConcurrentInstances = 0);
+
+    /**
      * Returns the media type for which this codec-capability object was created.
      */
     const std::string& getMediaType();
@@ -47,11 +69,209 @@
      */
     const std::vector<ProfileLevel>& getProfileLevels();
 
+    /**
+     * Returns the supported color formats.
+     */
+    std::vector<uint32_t> getColorFormats() const;
+
+    /**
+     * Returns a media format with default values for configurations that have defaults.
+     */
+    sp<AMessage> getDefaultFormat() const;
+
+    /**
+     * Returns the max number of the supported concurrent codec instances.
+     * <p>
+     * This is a hint for an upper bound. Applications should not expect to successfully
+     * operate more instances than the returned value, but the actual number of
+     * concurrently operable instances may be less as it depends on the available
+     * resources at time of use.
+     */
+    int32_t getMaxSupportedInstances() const;
+
+    /**
+     * Returns the audio capabilities or {@code null} if this is not an audio codec.
+     */
+    std::shared_ptr<AudioCapabilities> getAudioCapabilities() const;
+
+    /**
+     * Returns the video capabilities or {@code null} if this is not a video codec.
+     */
+    std::shared_ptr<VideoCapabilities> getVideoCapabilities() const;
+
+    /**
+     * Returns the encoding capabilities or {@code null} if this is not an encoder.
+     */
+    std::shared_ptr<EncoderCapabilities> getEncoderCapabilities() const;
+
+    std::vector<std::string> validFeatures() const;
+
+    /**
+     * Query codec feature capabilities.
+     * <p>
+     * These features are supported to be used by the codec.  These
+     * include optional features that can be turned on, as well as
+     * features that are always on.
+     */
+    bool isFeatureSupported(const std::string &name) const;
+
+    /**
+     * Query codec feature requirements.
+     * <p>
+     * These features are required to be used by the codec, and as such,
+     * they are always turned on.
+     */
+    bool isFeatureRequired(const std::string &name) const;
+
+    bool isRegular() const;
+
+    /**
+    * Query whether codec supports a given {@link MediaFormat}.
+    *
+    * <p class=note>
+    * <strong>Note:</strong> On {@link android.os.Build.VERSION_CODES#LOLLIPOP},
+    * {@code format} must not contain a {@linkplain MediaFormat#KEY_FRAME_RATE
+    * frame rate}. Use
+    * <code class=prettyprint>format.setString(MediaFormat.KEY_FRAME_RATE, null)</code>
+    * to clear any existing frame rate setting in the format.
+    * <p>
+    *
+    * The following table summarizes the format keys considered by this method.
+    * This is especially important to consider when targeting a higher SDK version than the
+    * minimum SDK version, as this method will disregard some keys on devices below the target
+    * SDK version.
+    *
+    * <table style="width: 0%">
+    *  <thead>
+    *   <tr>
+    *    <th rowspan=3>OS Version(s)</th>
+    *    <td colspan=3>{@code MediaFormat} keys considered for</th>
+    *   </tr><tr>
+    *    <th>Audio Codecs</th>
+    *    <th>Video Codecs</th>
+    *    <th>Encoders</th>
+    *   </tr>
+    *  </thead>
+    *  <tbody>
+    *   <tr>
+    *    <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP}</td>
+    *    <td rowspan=3>{@link MediaFormat#KEY_MIME}<sup>*</sup>,<br>
+    *        {@link MediaFormat#KEY_SAMPLE_RATE},<br>
+    *        {@link MediaFormat#KEY_CHANNEL_COUNT},</td>
+    *    <td>{@link MediaFormat#KEY_MIME}<sup>*</sup>,<br>
+    *        {@link CodecCapabilities#FEATURE_AdaptivePlayback}<sup>D</sup>,<br>
+    *        {@link CodecCapabilities#FEATURE_SecurePlayback}<sup>D</sup>,<br>
+    *        {@link CodecCapabilities#FEATURE_TunneledPlayback}<sup>D</sup>,<br>
+    *        {@link MediaFormat#KEY_WIDTH},<br>
+    *        {@link MediaFormat#KEY_HEIGHT},<br>
+    *        <strong>no</strong> {@code KEY_FRAME_RATE}</td>
+    *    <td rowspan=10>as to the left, plus<br>
+    *        {@link MediaFormat#KEY_BITRATE_MODE},<br>
+    *        {@link MediaFormat#KEY_PROFILE}
+    *        (and/or {@link MediaFormat#KEY_AAC_PROFILE}<sup>~</sup>),<br>
+    *        <!-- {link MediaFormat#KEY_QUALITY},<br> -->
+    *        {@link MediaFormat#KEY_COMPLEXITY}
+    *        (and/or {@link MediaFormat#KEY_FLAC_COMPRESSION_LEVEL}<sup>~</sup>)</td>
+    *   </tr><tr>
+    *    <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}</td>
+    *    <td rowspan=2>as above, plus<br>
+    *        {@link MediaFormat#KEY_FRAME_RATE}</td>
+    *   </tr><tr>
+    *    <td>{@link android.os.Build.VERSION_CODES#M}</td>
+    *   </tr><tr>
+    *    <td>{@link android.os.Build.VERSION_CODES#N}</td>
+    *    <td rowspan=2>as above, plus<br>
+    *        {@link MediaFormat#KEY_PROFILE},<br>
+    *        <!-- {link MediaFormat#KEY_MAX_BIT_RATE},<br> -->
+    *        {@link MediaFormat#KEY_BIT_RATE}</td>
+    *    <td rowspan=2>as above, plus<br>
+    *        {@link MediaFormat#KEY_PROFILE},<br>
+    *        {@link MediaFormat#KEY_LEVEL}<sup>+</sup>,<br>
+    *        <!-- {link MediaFormat#KEY_MAX_BIT_RATE},<br> -->
+    *        {@link MediaFormat#KEY_BIT_RATE},<br>
+    *        {@link CodecCapabilities#FEATURE_IntraRefresh}<sup>E</sup></td>
+    *   </tr><tr>
+    *    <td>{@link android.os.Build.VERSION_CODES#N_MR1}</td>
+    *   </tr><tr>
+    *    <td>{@link android.os.Build.VERSION_CODES#O}</td>
+    *    <td rowspan=3 colspan=2>as above, plus<br>
+    *        {@link CodecCapabilities#FEATURE_PartialFrame}<sup>D</sup></td>
+    *   </tr><tr>
+    *    <td>{@link android.os.Build.VERSION_CODES#O_MR1}</td>
+    *   </tr><tr>
+    *    <td>{@link android.os.Build.VERSION_CODES#P}</td>
+    *   </tr><tr>
+    *    <td>{@link android.os.Build.VERSION_CODES#Q}</td>
+    *    <td colspan=2>as above, plus<br>
+    *        {@link CodecCapabilities#FEATURE_FrameParsing}<sup>D</sup>,<br>
+    *        {@link CodecCapabilities#FEATURE_MultipleFrames},<br>
+    *        {@link CodecCapabilities#FEATURE_DynamicTimestamp}</td>
+    *   </tr><tr>
+    *    <td>{@link android.os.Build.VERSION_CODES#R}</td>
+    *    <td colspan=2>as above, plus<br>
+    *        {@link CodecCapabilities#FEATURE_LowLatency}<sup>D</sup></td>
+    *   </tr>
+    *   <tr>
+    *    <td colspan=4>
+    *     <p class=note><strong>Notes:</strong><br>
+    *      *: must be specified; otherwise, method returns {@code false}.<br>
+    *      +: method does not verify that the format parameters are supported
+    *      by the specified level.<br>
+    *      D: decoders only<br>
+    *      E: encoders only<br>
+    *      ~: if both keys are provided values must match
+    *    </td>
+    *   </tr>
+    *  </tbody>
+    * </table>
+    *
+    * @param format media format with optional feature directives.
+    * @return whether the codec capabilities support the given format
+    *         and feature requests.
+    */
+    bool isFormatSupported(const sp<AMessage> &format) const;
+
+    /**
+     * If the CodecCapabilities contains an AudioCapabilities.
+     *
+     * Not a public API to users.
+     */
+    bool isAudio() const;
+
+    /**
+     * If the CodecCapabilities contains a VideoCapabilities.
+     *
+     * Not a public API to users.
+     */
+    bool isVideo() const;
+
+    /**
+     * If the CodecCapabilities contains an EncoderCapabilities.
+     *
+     * Not a public API to users.
+     */
+    bool isEncoder() const;
+
 private:
     std::string mMediaType;
     std::vector<ProfileLevel> mProfileLevels;
+    std::vector<uint32_t> mColorFormats;
+    int32_t mMaxSupportedInstances;
+
+    sp<AMessage> mDefaultFormat;
+    sp<AMessage> mCapabilitiesInfo;
+
+    // Features
+    std::set<std::string> mFeaturesSupported;
+    std::set<std::string> mFeaturesRequired;
 
     std::shared_ptr<AudioCapabilities> mAudioCaps;
+    std::shared_ptr<VideoCapabilities> mVideoCaps;
+    std::shared_ptr<EncoderCapabilities> mEncoderCaps;
+
+    bool supportsProfileLevel(int32_t profile, int32_t level) const;
+    std::vector<Feature> getValidFeatures() const;
+    int32_t getErrors() const;
 };
 
 }  // namespace android
diff --git a/media/libmedia/include/media/CodecCapabilitiesUtils.h b/media/libmedia/include/media/CodecCapabilitiesUtils.h
index 2bf822a..eb62bf9 100644
--- a/media/libmedia/include/media/CodecCapabilitiesUtils.h
+++ b/media/libmedia/include/media/CodecCapabilitiesUtils.h
@@ -19,8 +19,12 @@
 #define CODEC_CAPABILITIES__UTILS_H_
 
 #include <algorithm>
+#include <cerrno>
 #include <cmath>
+#include <cstdlib>
+#include <numeric>
 #include <optional>
+#include <regex>
 #include <string>
 #include <vector>
 
@@ -38,6 +42,21 @@
     }
 };
 
+struct Feature {
+    std::string mName;
+    int mValue;
+    bool mDefault;
+    bool mInternal;
+    Feature(std::string name, int value, bool def, bool internal) {
+        mName = name;
+        mValue = value;
+        mDefault = def;
+        mInternal = internal;
+    }
+    Feature(std::string name, int value, bool def) :
+        Feature(name, value, def, false /* internal */) {}
+};
+
 /**
  * Immutable class for describing the range of two numeric values.
  *
@@ -113,19 +132,87 @@
         return Range(std::max(lower_, lower), std::min(upper_, upper));
     }
 
+    /**
+     * Returns the smallest range that includes this range and
+     * another range.
+     *
+     * E.g. if a < b < c < d, the
+     * extension of [a, c] and [b, d] ranges is [a, d].
+     * As the endpoints are object references, there is no guarantee
+     * which specific endpoint reference is used from the input ranges:
+     *
+     * E.g. if a == a' < b < c, the
+     * extension of [a, b] and [a', c] ranges could be either
+     * [a, c] or ['a, c], where ['a, c] could be either the exact
+     * input range, or a newly created range with the same endpoints.
+     *
+     * @param range a non-null Range<T> reference
+     * @return the extension of this range and the other range.
+     */
+    Range<T> extend(Range<T> range) {
+        return Range<T>(std::min(lower_, range.lower_), std::max(upper_, range.upper_));
+    }
+
+    Range<T> align(T align) {
+        return this->intersect(
+                divUp(lower_, align) * align, (upper_ / align) * align);
+    }
+
+    Range<T> factor(T factor) {
+        if (factor == 1) {
+            return *this;
+        }
+        return Range(divUp(this->lower(), factor), this->upper() / factor);
+    }
+
+    // parse a string into a range
+    static std::optional<Range<T>> Parse(const std::string &str) {
+        if (str.empty()) {
+            ALOGW("could not parse empty integer range");
+            return std::nullopt;
+        }
+        long long lower, upper;
+        std::regex regex("^([0-9]+)-([0-9]+)$");
+        std::smatch match;
+        errno = 0;
+        if (std::regex_match(str, match, regex)) {
+            lower = std::strtoll(match[1].str().c_str(), NULL, 10);
+            upper = std::strtoll(match[2].str().c_str(), NULL, 10);
+        } else {
+            char *end;
+            lower = upper = std::strtoll(str.c_str(), &end, 10);
+            if (*end != '\0') {
+                ALOGW("could not parse integer range: %s", str.c_str());
+                return std::nullopt;
+            }
+        }
+
+        if (errno == ERANGE || lower < std::numeric_limits<T>::min()
+                || std::numeric_limits<T>::max() < upper || upper < lower) {
+            ALOGW("could not parse integer range: %s", str.c_str());
+            return std::nullopt;
+        }
+
+        return std::make_optional<Range<T>>((T)lower, (T)upper);
+    }
+
+    static Range<T> RangeFor(double v) {
+        return Range((T)v, (T)ceil(v));
+    }
+
 private:
     T lower_;
     T upper_;
 };
 
-static const Range<int> POSITIVE_INTEGERS = Range<int>(1, INT_MAX);
+static const Range<int32_t> POSITIVE_INT32 = Range<int32_t>(1, INT32_MAX);
 
 // found stuff that is not supported by framework (=> this should not happen)
 constexpr int ERROR_CAPABILITIES_UNRECOGNIZED   = (1 << 0);
 // found profile/level for which we don't have capability estimates
 constexpr int ERROR_CAPABILITIES_UNSUPPORTED    = (1 << 1);
 // have not found any profile/level for which we don't have capability estimate
-// constexpr int ERROR_NONE_SUPPORTED = (1 << 2);
+constexpr int ERROR_CAPABILITIES_NONE_SUPPORTED = (1 << 2);
 
 /**
  * Sorts distinct (non-intersecting) range array in ascending order.
@@ -176,8 +263,464 @@
     return result;
 }
 
-// parse string into int range
-std::optional<Range<int>> ParseIntRange(const std::string &str);
+/**
+ * Immutable class for describing width and height dimensions in pixels.
+ */
+struct VideoSize {
+    /**
+     * Create a new immutable VideoSize instance.
+     *
+     * @param width The width of the size, in pixels
+     * @param height The height of the size, in pixels
+     */
+    VideoSize(int32_t width, int32_t height);
+
+    // default constructor
+    VideoSize();
+
+    /**
+     * Get the width of the size (in pixels).
+     * @return width
+     */
+    int32_t getWidth() const;
+
+    /**
+     * Get the height of the size (in pixels).
+     * @return height
+     */
+    int32_t getHeight() const;
+
+    /**
+     * Check if this size is equal to another size.
+     *
+     * Two sizes are equal if and only if both their widths and heights are
+     * equal.
+     *
+     * A size object is never equal to any other type of object.
+     *
+     * @return true if the objects were equal, false otherwise
+     */
+    bool equals(VideoSize other) const;
+
+    bool empty() const;
+
+    std::string toString() const;
+
+    /**
+     * Parses the specified string as a size value.
+     *
+     * The ASCII characters {@code \}{@code u002a} ('*') and
+     * {@code \}{@code u0078} ('x') are recognized as separators between
+     * the width and height.
+     *
+     * For any {@code VideoSize s}: {@code VideoSize::ParseSize(s.toString()).equals(s)}.
+     * However, the method also handles sizes expressed in the
+     * following forms:
+     *
+     * "<i>width</i>{@code x}<i>height</i>" or
+     * "<i>width</i>{@code *}<i>height</i>" {@code => new VideoSize(width, height)},
+     * where <i>width</i> and <i>height</i> are string integers potentially
+     * containing a sign, such as "-10", "+7" or "5".
+     *
+     * <pre>{@code
+     * VideoSize::ParseSize("3*+6").equals(new VideoSize(3, 6)) == true
+     * VideoSize::ParseSize("-3x-6").equals(new VideoSize(-3, -6)) == true
+     * VideoSize::ParseSize("4 by 3") => throws NumberFormatException
+     * }</pre>
+     *
+     * @param string the string representation of a size value.
+     * @return the size value represented by {@code string}.
+     */
+    static std::optional<VideoSize> ParseSize(std::string str);
+
+    static std::optional<std::pair<VideoSize, VideoSize>> ParseSizeRange(const std::string str);
+
+    static Range<int32_t> GetAllowedDimensionRange();
+
+private:
+    int32_t mWidth;
+    int32_t mHeight;
+};
+
+// This is used for the std::map<VideoSize> in VideoCapabilities
+struct VideoSizeCompare {
+    bool operator() (const VideoSize& lhs, const VideoSize& rhs) const {
+        if (lhs.getWidth() == rhs.getWidth()) {
+            return lhs.getHeight() < rhs.getHeight();
+        } else {
+            return lhs.getWidth() < rhs.getWidth();
+        }
+    }
+};
+
+/**
+ * An immutable data type representation a rational number.
+ *
+ * Contains a pair of ints representing the numerator and denominator of a
+ * Rational number.
+ */
+struct Rational {
+    /**
+     * <p>Create a {@code Rational} with a given numerator and denominator.</p>
+     *
+     * <p>The signs of the numerator and the denominator may be flipped such that the denominator
+     * is always positive. Both the numerator and denominator will be converted to their reduced
+     * forms (see {@link #equals} for more details).</p>
+     *
+     * <p>For example,
+     * <ul>
+     * <li>a rational of {@code 2/4} will be reduced to {@code 1/2}.
+     * <li>a rational of {@code 1/-1} will be flipped to {@code -1/1}
+     * <li>a rational of {@code 5/0} will be reduced to {@code 1/0}
+     * <li>a rational of {@code 0/5} will be reduced to {@code 0/1}
+     * </ul>
+     * </p>
+     *
+     * @param numerator the numerator of the rational
+     * @param denominator the denominator of the rational
+     *
+     * @see #equals
+     */
+    Rational(int32_t numerator, int32_t denominator) {
+        if (denominator < 0) {
+            numerator = -numerator;
+            denominator = -denominator;
+        }
+
+        // Convert to reduced form
+        if (denominator == 0 && numerator > 0) {
+            mNumerator = 1; // +Inf
+            mDenominator = 0;
+        } else if (denominator == 0 && numerator < 0) {
+            mNumerator = -1; // -Inf
+            mDenominator = 0;
+        } else if (denominator == 0 && numerator == 0) {
+            mNumerator = 0; // NaN
+            mDenominator = 0;
+        } else if (numerator == 0) {
+            mNumerator = 0;
+            mDenominator = 1;
+        } else {
+            int gcd = std::gcd(numerator, denominator);
+
+            mNumerator = numerator / gcd;
+            mDenominator = denominator / gcd;
+        }
+    }
+
+    // default constructor;
+    Rational() {
+        Rational(0, 0);
+    }
+
+    /**
+     * Gets the numerator of the rational.
+     *
+     * <p>The numerator will always return {@code 1} if this rational represents
+     * infinity (that is, the denominator is {@code 0}).</p>
+     */
+    int32_t getNumerator() const {
+        return mNumerator;
+    }
+
+    /**
+     * Gets the denominator of the rational
+     *
+     * <p>The denominator may return {@code 0}, in which case the rational may represent
+     * positive infinity (if the numerator was positive), negative infinity (if the numerator
+     * was negative), or {@code NaN} (if the numerator was {@code 0}).</p>
+     *
+     * <p>The denominator will always return {@code 1} if the numerator is {@code 0}.
+     */
+    int32_t getDenominator() const {
+        return mDenominator;
+    }
+
+    /**
+     * Indicates whether this rational is a <em>Not-a-Number (NaN)</em> value.
+     *
+     * <p>A {@code NaN} value occurs when both the numerator and the denominator are {@code 0}.</p>
+     *
+     * @return {@code true} if this rational is a <em>Not-a-Number (NaN)</em> value;
+     *         {@code false} if this is a (potentially infinite) number value
+     */
+    bool isNaN() const {
+        return mDenominator == 0 && mNumerator == 0;
+    }
+
+    /**
+     * Indicates whether this rational represents an infinite value.
+     *
+     * <p>An infinite value occurs when the denominator is {@code 0} (but the numerator is not).</p>
+     *
+     * @return {@code true} if this rational is a (positive or negative) infinite value;
+     *         {@code false} if this is a finite number value (or {@code NaN})
+     */
+    bool isInfinite() const {
+        return mNumerator != 0 && mDenominator == 0;
+    }
+
+    /**
+     * Indicates whether this rational represents a finite value.
+     *
+     * <p>A finite value occurs when the denominator is not {@code 0}; in other words
+     * the rational is neither infinity or {@code NaN}.</p>
+     *
+     * @return {@code true} if this rational is a (positive or negative) infinite value;
+     *         {@code false} if this is a finite number value (or {@code NaN})
+     */
+    bool isFinite() const {
+        return mDenominator != 0;
+    }
+
+    /**
+     * Indicates whether this rational represents a zero value.
+     *
+     * <p>A zero value is a {@link #isFinite finite} rational with a numerator of {@code 0}.</p>
+     *
+     * @return {@code true} if this rational is finite zero value;
+     *         {@code false} otherwise
+     */
+    bool isZero() const {
+        return isFinite() && mNumerator == 0;
+    }
+
+    /**
+     * Return a string representation of this rational, e.g. {@code "1/2"}.
+     *
+     * <p>The following rules of conversion apply:
+     * <ul>
+     * <li>{@code NaN} values will return {@code "NaN"}
+     * <li>Positive infinity values will return {@code "Infinity"}
+     * <li>Negative infinity values will return {@code "-Infinity"}
+     * <li>All other values will return {@code "numerator/denominator"} where {@code numerator}
+     * and {@code denominator} are substituted with the appropriate numerator and denominator
+     * values.
+     * </ul></p>
+     */
+    std::string toString() const {
+        if (isNaN()) {
+            return "NaN";
+        } else if (isPosInf()) {
+            return "Infinity";
+        } else if (isNegInf()) {
+            return "-Infinity";
+        } else {
+            return std::to_string(mNumerator) + "/" + std::to_string(mDenominator);
+        }
+    }
+
+    /**
+     * Returns the value of the specified number as a {@code double}.
+     *
+     * <p>The {@code double} is calculated by converting both the numerator and denominator
+     * to a {@code double}; then returning the result of dividing the numerator by the
+     * denominator.</p>
+     *
+     * @return the divided value of the numerator and denominator as a {@code double}.
+     */
+    double asDouble() const {
+        double num = mNumerator;
+        double den = mDenominator;
+
+        return num / den;
+    }
+
+    /**
+     * Returns the value of the specified number as a {@code float}.
+     *
+     * <p>The {@code float} is calculated by converting both the numerator and denominator
+     * to a {@code float}; then returning the result of dividing the numerator by the
+     * denominator.</p>
+     *
+     * @return the divided value of the numerator and denominator as a {@code float}.
+     */
+    float asfloat() const {
+        float num = mNumerator;
+        float den = mDenominator;
+
+        return num / den;
+    }
+
+    /**
+     * Returns the value of the specified number as a {@code int}.
+     *
+     * <p>{@link #isInfinite Finite} rationals are converted to an {@code int} value
+     * by dividing the numerator by the denominator; conversion for non-finite values happens
+     * identically to casting a floating point value to an {@code int}, in particular:
+     *
+     * @return the divided value of the numerator and denominator as a {@code int}.
+     */
+    int32_t asInt32() const {
+        // Mimic float to int conversion rules from JLS 5.1.3
+
+        if (isPosInf()) {
+            return INT32_MAX;
+        } else if (isNegInf()) {
+            return INT32_MIN;
+        } else if (isNaN()) {
+            return 0;
+        } else { // finite
+            return mNumerator / mDenominator;
+        }
+    }
+
+    /**
+     * Returns the value of the specified number as a {@code long}.
+     *
+     * <p>{@link #isInfinite Finite} rationals are converted to an {@code long} value
+     * by dividing the numerator by the denominator; conversion for non-finite values happens
+     * identically to casting a floating point value to a {@code long}, in particular:
+     *
+     * @return the divided value of the numerator and denominator as a {@code long}.
+     */
+    int64_t asInt64() const {
+        // Mimic float to long conversion rules from JLS 5.1.3
+
+        if (isPosInf()) {
+            return INT64_MAX;
+        } else if (isNegInf()) {
+            return INT64_MIN;
+        } else if (isNaN()) {
+            return 0;
+        } else { // finite
+            return mNumerator / mDenominator;
+        }
+    }
+
+    /**
+     * Returns the value of the specified number as a {@code short}.
+     *
+     * <p>{@link #isInfinite Finite} rationals are converted to a {@code short} value
+     * identically to {@link #intValue}; the {@code int} result is then truncated to a
+     * {@code short} before returning the value.</p>
+     *
+     * @return the divided value of the numerator and denominator as a {@code short}.
+     */
+    int16_t asInt16() const {
+        return (int16_t) asInt32();
+    }
+
+    /**
+     * Compare this rational to the specified rational to determine their natural order.
+     *
+     * Nan is considered to be equal to itself and greater than all other
+     * Rational values. Otherwise, if the objects are not equal, then
+     * the following rules apply:
+     *
+     * Positive infinity is greater than any other finite number (or negative infinity)
+     * Negative infinity is less than any other finite number (or positive infinity)
+     * The finite number represented by this rational is checked numerically
+     * against the other finite number by converting both rationals to a common denominator multiple
+     * and comparing their numerators.
+     *
+     * @param another the rational to be compared
+     *
+     * @return a negative integer, zero, or a positive integer as this object is less than,
+     *         equal to, or greater than the specified rational.
+     */
+    // bool operator> (const Rational& another) {
+    int compareTo(Rational another) const {
+        if (equals(another)) {
+            return 0;
+        } else if (isNaN()) { // NaN is greater than the other non-NaN value
+            return 1;
+        } else if (another.isNaN()) { // the other NaN is greater than this non-NaN value
+            return -1;
+        } else if (isPosInf() || another.isNegInf()) {
+            return 1; // positive infinity is greater than any non-NaN/non-posInf value
+        } else if (isNegInf() || another.isPosInf()) {
+            return -1; // negative infinity is less than any non-NaN/non-negInf value
+        }
+
+        // else both this and another are finite numbers
+
+        // make the denominators the same, then compare numerators. int64_t to avoid overflow
+        int64_t thisNumerator = ((int64_t)mNumerator) * another.mDenominator;
+        int64_t otherNumerator = ((int64_t)another.mNumerator) * mDenominator;
+
+        // avoid underflow from subtraction by doing comparisons
+        if (thisNumerator < otherNumerator) {
+            return -1;
+        } else if (thisNumerator > otherNumerator) {
+            return 1;
+        } else {
+            // This should be covered by #equals, but have this code path just in case
+            return 0;
+        }
+    }
+
+    bool operator > (const Rational& another) const {
+        return compareTo(another) > 0;
+    }
+
+    bool operator >= (const Rational& another) const {
+        return compareTo(another) >= 0;
+    }
+
+    bool operator < (const Rational& another) const {
+        return compareTo(another) < 0;
+    }
+
+    bool operator <= (const Rational& another) const {
+        return compareTo(another) <= 0;
+    }
+
+    bool operator == (const Rational& another) const {
+        return equals(another);
+    }
+
+    static std::optional<Range<Rational>> ParseRange(const std::string str);
+
+    static Range<Rational> ScaleRange(Range<Rational> range, int32_t num, int32_t den);
+
+private:
+    int32_t mNumerator;
+    int32_t mDenominator;
+
+    bool isPosInf() const {
+        return mDenominator == 0 && mNumerator > 0;
+    }
+
+    bool isNegInf() const {
+        return mDenominator == 0 && mNumerator < 0;
+    }
+
+    bool equals(Rational other) const {
+        return (mNumerator == other.mNumerator && mDenominator == other.mDenominator);
+    }
+
+    Rational scale(int32_t num, int32_t den);
+
+    /**
+     * Parses the specified string as a rational value.
+     * The ASCII characters {@code \}{@code u003a} (':') and
+     * {@code \}{@code u002f} ('/') are recognized as separators between
+     * the numerator and denominator.
+     *
+     * For any {@code Rational r}: {@code Rational::parseRational(r.toString()).equals(r)}.
+     * However, the method also handles rational numbers expressed in the
+     * following forms:
+     *
+     * "<i>num</i>{@code /}<i>den</i>" or
+     * "<i>num</i>{@code :}<i>den</i>" {@code => new Rational(num, den);},
+     * where <i>num</i> and <i>den</i> are string integers potentially
+     * containing a sign, such as "-10", "+7" or "5".
+     *
+     * Rational::Parse("3:+6").equals(new Rational(1, 2)) == true
+     * Rational::Parse("-3/-6").equals(new Rational(1, 2)) == true
+     * Rational::Parse("4.56") => return std::nullopt
+     *
+     * @param str the string representation of a rational value.
+     * @return the rational value wrapped by std::optional represented by str.
+     */
+    static std::optional<Rational> Parse(std::string str);
+};
+
+static const Rational NaN = Rational(0, 0);
+static const Rational POSITIVE_INFINITY = Rational(1, 0);
+static const Rational NEGATIVE_INFINITY = Rational(-1, 0);
+static const Rational ZERO = Rational(0, 1);
 
 }  // namespace android
 
diff --git a/media/libmedia/include/media/EncoderCapabilities.h b/media/libmedia/include/media/EncoderCapabilities.h
new file mode 100644
index 0000000..a9654bb
--- /dev/null
+++ b/media/libmedia/include/media/EncoderCapabilities.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2024, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ENCODER_CAPABILITIES_H_
+
+#define ENCODER_CAPABILITIES_H_
+
+#include <media/CodecCapabilitiesUtils.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+/**
+ * A class that supports querying the encoding capabilities of a codec.
+ */
+struct EncoderCapabilities {
+    /**
+    * Returns the supported range of quality values.
+    *
+    * Quality is implementation-specific. As a general rule, a higher quality
+    * setting results in a better image quality and a lower compression ratio.
+    */
+    const Range<int>& getQualityRange();
+
+    /**
+     * Returns the supported range of encoder complexity values.
+     * <p>
+     * Some codecs may support multiple complexity levels, where higher
+     * complexity values use more encoder tools (e.g. perform more
+     * intensive calculations) to improve the quality or the compression
+     * ratio.  Use a lower value to save power and/or time.
+     */
+    const Range<int>& getComplexityRange();
+
+    /** Constant quality mode */
+    inline static constexpr int BITRATE_MODE_CQ = 0;
+    /** Variable bitrate mode */
+    inline static constexpr int BITRATE_MODE_VBR = 1;
+    /** Constant bitrate mode */
+    inline static constexpr int BITRATE_MODE_CBR = 2;
+    /** Constant bitrate mode with frame drops */
+    inline static constexpr int BITRATE_MODE_CBR_FD =  3;
+
+    /**
+     * Query whether a bitrate mode is supported.
+     */
+    bool isBitrateModeSupported(int mode);
+
+    /** @hide */
+    static std::shared_ptr<EncoderCapabilities> Create(std::string mediaType,
+            std::vector<ProfileLevel> profLevs, const sp<AMessage> &format);
+
+    /** @hide */
+    void getDefaultFormat(sp<AMessage> &format);
+
+    /** @hide */
+    bool supportsFormat(const sp<AMessage> &format);
+
+private:
+    inline static const Feature sBitrateModes[] = {
+        Feature("VBR", BITRATE_MODE_VBR, true),
+        Feature("CBR", BITRATE_MODE_CBR, false),
+        Feature("CQ",  BITRATE_MODE_CQ,  false),
+        Feature("CBR-FD", BITRATE_MODE_CBR_FD, false)
+    };
+    static int ParseBitrateMode(std::string mode);
+
+    std::string mMediaType;
+    std::vector<ProfileLevel> mProfileLevels;
+
+    Range<int> mQualityRange;
+    Range<int> mComplexityRange;
+    int mBitControl;
+    int mDefaultComplexity;
+    int mDefaultQuality;
+    std::string mQualityScale;
+
+    /* no public constructor */
+    EncoderCapabilities() {}
+    void init(std::string mediaType, std::vector<ProfileLevel> profLevs,
+            const sp<AMessage> &format);
+    void applyLevelLimits();
+    void parseFromInfo(const sp<AMessage> &format);
+    bool supports(std::optional<int> complexity, std::optional<int> quality,
+            std::optional<int> profile);
+};
+
+}  // namespace android
+
+#endif // ENCODER_CAPABILITIES_H_
\ No newline at end of file
diff --git a/media/libmedia/include/media/MediaCodecInfo.h b/media/libmedia/include/media/MediaCodecInfo.h
index 72aca98..60e383a 100644
--- a/media/libmedia/include/media/MediaCodecInfo.h
+++ b/media/libmedia/include/media/MediaCodecInfo.h
@@ -192,6 +192,7 @@
     Attributes getAttributes() const;
     void getSupportedMediaTypes(Vector<AString> *mediaTypes) const;
     const sp<Capabilities> getCapabilitiesFor(const char *mediaType) const;
+    const std::shared_ptr<CodecCapabilities> getCodecCapsFor(const char *mediaType) const;
     const char *getCodecName() const;
 
     /**
@@ -229,14 +230,21 @@
     status_t writeToParcel(Parcel *parcel) const;
 
 private:
+    /**
+     * Max supported instances setting from MediaCodecList global setting.
+     */
+    static int32_t sMaxSupportedInstances;
+
     AString mName;
     AString mOwner;
     Attributes mAttributes;
     KeyedVector<AString, sp<Capabilities> > mCaps;
+    KeyedVector<AString, std::shared_ptr<CodecCapabilities>> mCodecCaps;
     Vector<AString> mAliases;
     uint32_t mRank;
 
     ssize_t getCapabilityIndex(const char *mediaType) const;
+    ssize_t getCodecCapIndex(const char *mediaType) const;
 
     /**
      * Construct an `MediaCodecInfo` object. After the construction, its
@@ -264,6 +272,15 @@
  */
 struct MediaCodecInfoWriter {
     /**
+     * Get CodecCapabilities from Capabilities.
+     */
+    static std::shared_ptr<CodecCapabilities> BuildCodecCapabilities(const char *mediaType,
+            sp<MediaCodecInfo::Capabilities> caps, bool isEncoder, int maxSupportedInstances = 0);
+    /**
+     * Set the max supported instances global setting from MediaCodecList.
+     */
+    static void SetMaxSupportedInstances(int32_t maxSupportedInstances);
+    /**
      * Set the name of the codec.
      *
      * @param name The new name.
@@ -319,6 +336,10 @@
      * @param rank The rank of the component.
      */
     void setRank(uint32_t rank);
+    /**
+     * Create CodecCapabilities map from Capabilities.
+     */
+    void createCodecCaps();
 private:
     /**
      * The associated `MediaCodecInfo`.
diff --git a/media/libmedia/include/media/VideoCapabilities.h b/media/libmedia/include/media/VideoCapabilities.h
new file mode 100644
index 0000000..5671375
--- /dev/null
+++ b/media/libmedia/include/media/VideoCapabilities.h
@@ -0,0 +1,457 @@
+/*
+ * Copyright 2024, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VIDEO_CAPABILITIES_H_
+
+#define VIDEO_CAPABILITIES_H_
+
+#include <media/CodecCapabilitiesUtils.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+struct VideoCapabilities {
+    struct PerformancePoint {
+        /**
+         * Maximum number of macroblocks in the frame.
+         *
+         * Video frames are conceptually divided into 16-by-16 pixel blocks called macroblocks.
+         * Most coding standards operate on these 16-by-16 pixel blocks; thus, codec performance
+         * is characterized using such blocks.
+         *
+         * Test API
+         */
+        int32_t getMaxMacroBlocks() const;
+
+        /**
+         * Return the width.
+         *
+         * For internal use only.
+         */
+        int32_t getWidth() const;
+
+        /**
+         * Return the height.
+         *
+         * For internal use only.
+         */
+        int32_t getHeight() const;
+
+        /**
+         * Maximum frame rate in frames per second.
+         *
+         * Test API
+         */
+        int32_t getMaxFrameRate() const;
+
+        /**
+         * Maximum number of macroblocks processed per second.
+         *
+         * Test API
+         */
+        int64_t getMaxMacroBlockRate() const;
+
+        /**
+         * Return the block size.
+         *
+         * For internal use only.
+         */
+        VideoSize getBlockSize() const;
+
+        /**
+         * convert to a debug string
+         *
+         * Be careful about the serializable compatibility across API revisions.
+         */
+        std::string toString() const;
+
+        /**
+         * Create a detailed performance point with custom max frame rate and macroblock size.
+         *
+         * @param width  frame width in pixels
+         * @param height frame height in pixels
+         * @param frameRate frames per second for frame width and height
+         * @param maxFrameRate maximum frames per second for any frame size
+         * @param blockSize block size for codec implementation. Must be powers of two in both
+         *        width and height.
+         *
+         * Test API
+         */
+        PerformancePoint(int32_t width, int32_t height, int32_t frameRate, int32_t maxFrameRate,
+                VideoSize blockSize);
+
+        /**
+         * Create a detailed performance point with custom max frame rate and macroblock size.
+         *
+         * @param width  frame width in pixels
+         * @param height frame height in pixels
+         * @param maxFrameRate maximum frames per second for any frame size
+         * @param maxMacroBlockRate maximum number of macroblocks processed per second.
+         * @param blockSize block size for codec implementation. Must be powers of two in both
+         *        width and height.
+         *
+         * Test API
+         */
+        PerformancePoint(VideoSize blockSize, int32_t width, int32_t height, int maxFrameRate,
+                int64_t maxMacroBlockRate);
+
+        /**
+         * Convert a performance point to a larger blocksize.
+         *
+         * @param pp performance point. NonNull
+         * @param blockSize block size for codec implementation. NonNull.
+         *
+         * Test API
+         */
+        PerformancePoint(const PerformancePoint &pp, VideoSize newBlockSize);
+
+        /**
+         * Create a performance point for a given frame size and frame rate.
+         *
+         * @param width width of the frame in pixels
+         * @param height height of the frame in pixels
+         * @param frameRate frame rate in frames per second
+         */
+        PerformancePoint(int32_t width, int32_t height, int32_t frameRate);
+
+        /**
+         * Checks whether the performance point covers a media format.
+         *
+         * @param format Stream format considered
+         *
+         * @return {@code true} if the performance point covers the format.
+         */
+        bool covers(const sp<AMessage> &format) const;
+
+        /**
+         * Checks whether the performance point covers another performance point. Use this
+         * method to determine if a performance point advertised by a codec covers the
+         * performance point required. This method can also be used for loose ordering as this
+         * method is transitive.
+         *
+         * @param other other performance point considered
+         *
+         * @return {@code true} if the performance point covers the other.
+         */
+        bool covers(const PerformancePoint &other) const;
+
+        /**
+         * Check if two PerformancePoint instances are equal.
+         *
+         * @param other other PerformancePoint instance for comparison.
+         *
+         * @return true if two PerformancePoint are equal.
+         */
+        bool equals(const PerformancePoint &other) const;
+
+    private:
+        VideoSize mBlockSize; // codec block size in macroblocks
+        int32_t mWidth; // width in macroblocks
+        int32_t mHeight; // height in macroblocks
+        int32_t mMaxFrameRate; // max frames per second
+        int64_t mMaxMacroBlockRate; // max macro block rate
+
+        void init(int32_t width, int32_t height, int32_t frameRate, int32_t maxFrameRate,
+                VideoSize blockSize);
+
+        /** Saturates a 64 bit integer value to a 32 bit integer */
+        int32_t saturateInt64ToInt32(int64_t value) const;
+
+        /** This method may overflow */
+        int32_t align(int32_t value, int32_t alignment) const;
+
+        /** @return NonNull */
+        VideoSize getCommonBlockSize(const PerformancePoint &other) const;
+    };
+
+    /**
+     * Find the equivalent VP9 profile level.
+     *
+     * Not a public API to developers.
+     */
+    static int32_t EquivalentVP9Level(const sp<AMessage> &format);
+
+    /**
+     * Returns the range of supported bitrates in bits/second.
+     */
+    const Range<int32_t>& getBitrateRange() const;
+
+    /**
+     * Returns the range of supported video widths.
+     * 32-bit processes will not support resolutions larger than 4096x4096 due to
+     * the limited address space.
+     */
+    const Range<int32_t>& getSupportedWidths() const;
+
+    /**
+     * Returns the range of supported video heights.
+     * 32-bit processes will not support resolutions larger than 4096x4096 due to
+     * the limited address space.
+     */
+    const Range<int32_t>& getSupportedHeights() const;
+
+    /**
+     * Returns the alignment requirement for video width (in pixels).
+     *
+     * This is a power-of-2 value that video width must be a
+     * multiple of.
+     */
+    int32_t getWidthAlignment() const;
+
+    /**
+     * Returns the alignment requirement for video height (in pixels).
+     *
+     * This is a power-of-2 value that video height must be a
+     * multiple of.
+     */
+    int32_t getHeightAlignment() const;
+
+    /**
+     * Return the upper limit on the smaller dimension of width or height.
+     *
+     * Some codecs have a limit on the smaller dimension, whether it be
+     * the width or the height.  E.g. a codec may only be able to handle
+     * up to 1920x1080 both in landscape and portrait mode (1080x1920).
+     * In this case the maximum width and height are both 1920, but the
+     * smaller dimension limit will be 1080. For other codecs, this is
+     * {@code Math.min(getSupportedWidths().getUpper(),
+     * getSupportedHeights().getUpper())}.
+     */
+    int32_t getSmallerDimensionUpperLimit() const;
+
+    /**
+     * Returns the range of supported frame rates.
+     *
+     * This is not a performance indicator.  Rather, it expresses the
+     * limits specified in the coding standard, based on the complexities
+     * of encoding material for later playback at a certain frame rate,
+     * or the decoding of such material in non-realtime.
+     */
+    const Range<int32_t>& getSupportedFrameRates() const;
+
+    /**
+     * Returns the range of supported video widths for a video height.
+     * @param height the height of the video
+     */
+    std::optional<Range<int32_t>> getSupportedWidthsFor(int32_t height) const;
+
+    /**
+     * Returns the range of supported video heights for a video width
+     * @param width the width of the video
+     */
+    std::optional<Range<int32_t>> getSupportedHeightsFor(int32_t width) const;
+
+    /**
+     * Returns the range of supported video frame rates for a video size.
+     *
+     * This is not a performance indicator.  Rather, it expresses the limits specified in
+     * the coding standard, based on the complexities of encoding material of a given
+     * size for later playback at a certain frame rate, or the decoding of such material
+     * in non-realtime.
+
+     * @param width the width of the video
+     * @param height the height of the video
+     */
+    std::optional<Range<double>> getSupportedFrameRatesFor(int32_t width, int32_t height) const;
+
+    /**
+     * Returns the range of achievable video frame rates for a video size.
+     * May return {@code null}, if the codec did not publish any measurement
+     * data.
+     * <p>
+     * This is a performance estimate provided by the device manufacturer based on statistical
+     * sampling of full-speed decoding and encoding measurements in various configurations
+     * of common video sizes supported by the codec. As such it should only be used to
+     * compare individual codecs on the device. The value is not suitable for comparing
+     * different devices or even different android releases for the same device.
+     * <p>
+     * <em>On {@link android.os.Build.VERSION_CODES#M} release</em> the returned range
+     * corresponds to the fastest frame rates achieved in the tested configurations. As
+     * such, it should not be used to gauge guaranteed or even average codec performance
+     * on the device.
+     * <p>
+     * <em>On {@link android.os.Build.VERSION_CODES#N} release</em> the returned range
+     * corresponds closer to sustained performance <em>in tested configurations</em>.
+     * One can expect to achieve sustained performance higher than the lower limit more than
+     * 50% of the time, and higher than half of the lower limit at least 90% of the time
+     * <em>in tested configurations</em>.
+     * Conversely, one can expect performance lower than twice the upper limit at least
+     * 90% of the time.
+     * <p class=note>
+     * Tested configurations use a single active codec. For use cases where multiple
+     * codecs are active, applications can expect lower and in most cases significantly lower
+     * performance.
+     * <p class=note>
+     * The returned range value is interpolated from the nearest frame size(s) tested.
+     * Codec performance is severely impacted by other activity on the device as well
+     * as environmental factors (such as battery level, temperature or power source), and can
+     * vary significantly even in a steady environment.
+     * <p class=note>
+     * Use this method in cases where only codec performance matters, e.g. to evaluate if
+     * a codec has any chance of meeting a performance target. Codecs are listed
+     * in {@link MediaCodecList} in the preferred order as defined by the device
+     * manufacturer. As such, applications should use the first suitable codec in the
+     * list to achieve the best balance between power use and performance.
+     *
+     * @param width the width of the video
+     * @param height the height of the video
+     */
+    std::optional<Range<double>> getAchievableFrameRatesFor(int32_t width, int32_t height) const;
+
+    /**
+     * Returns the supported performance points. May return {@code null} if the codec did not
+     * publish any performance point information (e.g. the vendor codecs have not been updated
+     * to the latest android release). May return an empty list if the codec published that
+     * if does not guarantee any performance points.
+     * <p>
+     * This is a performance guarantee provided by the device manufacturer for hardware codecs
+     * based on hardware capabilities of the device.
+     * <p>
+     * The returned list is sorted first by decreasing number of pixels, then by decreasing
+     * width, and finally by decreasing frame rate.
+     * Performance points assume a single active codec. For use cases where multiple
+     * codecs are active, should use that highest pixel count, and add the frame rates of
+     * each individual codec.
+     * <p class=note>
+     * 32-bit processes will not support resolutions larger than 4096x4096 due to
+     * the limited address space, but performance points will be presented as is.
+     * In other words, even though a component publishes a performance point for
+     * a resolution higher than 4096x4096, it does not mean that the resolution is supported
+     * for 32-bit processes.
+     */
+    const std::vector<PerformancePoint>& getSupportedPerformancePoints() const;
+
+    /**
+     * Returns whether a given video size ({@code width} and
+     * {@code height}) and {@code frameRate} combination is supported.
+     */
+    bool areSizeAndRateSupported(int32_t width, int32_t height, double frameRate) const;
+
+    /**
+     * Returns whether a given video size ({@code width} and
+     * {@code height}) is supported.
+     */
+    bool isSizeSupported(int32_t width, int32_t height) const;
+
+    /**
+     * Returns if a media format is supported.
+     *
+     * Not exposed to public
+     */
+    bool supportsFormat(const sp<AMessage> &format) const;
+
+    /**
+     * Create VideoCapabilities.
+     */
+    static std::shared_ptr<VideoCapabilities> Create(std::string mediaType,
+            std::vector<ProfileLevel> profLevs, const sp<AMessage> &format);
+
+    /**
+     * Get the block size.
+     *
+     * Not a public API to developers
+     */
+    VideoSize getBlockSize() const;
+
+    /**
+     * Get the block count range.
+     *
+     * Not a public API to developers
+     */
+    const Range<int32_t>& getBlockCountRange() const;
+
+    /**
+     * Get the blocks per second range.
+     *
+     * Not a public API to developers
+     */
+    const Range<int64_t>& getBlocksPerSecondRange() const;
+
+    /**
+     * Get the aspect ratio range.
+     *
+     * Not a public API to developers
+     */
+    Range<Rational> getAspectRatioRange(bool blocks) const;
+
+private:
+    std::string mMediaType;
+    std::vector<ProfileLevel> mProfileLevels;
+    int mError;
+
+    Range<int32_t> mBitrateRange;
+    Range<int32_t> mHeightRange;
+    Range<int32_t> mWidthRange;
+    Range<int32_t> mBlockCountRange;
+    Range<int32_t> mHorizontalBlockRange;
+    Range<int32_t> mVerticalBlockRange;
+    Range<Rational> mAspectRatioRange;
+    Range<Rational> mBlockAspectRatioRange;
+    Range<int64_t> mBlocksPerSecondRange;
+    std::map<VideoSize, Range<int64_t>, VideoSizeCompare> mMeasuredFrameRates;
+    std::vector<PerformancePoint> mPerformancePoints;
+    Range<int32_t> mFrameRateRange;
+
+    int32_t mBlockWidth;
+    int32_t mBlockHeight;
+    int32_t mWidthAlignment;
+    int32_t mHeightAlignment;
+    int mSmallerDimensionUpperLimit;
+
+    bool mAllowMbOverride; // allow XML to override calculated limits
+
+    int32_t getBlockCount(int32_t width, int32_t height) const;
+    std::optional<VideoSize> findClosestSize(int32_t width, int32_t height) const;
+    std::optional<Range<double>> estimateFrameRatesFor(int32_t width, int32_t height) const;
+    bool supports(std::optional<int32_t> width, std::optional<int32_t> height,
+                std::optional<double> rate) const;
+    /* no public constructor */
+    VideoCapabilities() {}
+    void init(std::string mediaType, std::vector<ProfileLevel> profLevs,
+            const sp<AMessage> &format);
+    void initWithPlatformLimits();
+    std::vector<PerformancePoint> getPerformancePoints(const sp<AMessage> &format) const;
+    std::map<VideoSize, Range<int64_t>, VideoSizeCompare>
+            getMeasuredFrameRates(const sp<AMessage> &format) const;
+
+    static std::optional<std::pair<Range<int32_t>, Range<int32_t>>> ParseWidthHeightRanges(
+            const std::string &str);
+    void parseFromInfo(const sp<AMessage> &format);
+    void applyBlockLimits(int32_t blockWidth, int32_t blockHeight,
+            Range<int32_t> counts, Range<int64_t> rates, Range<Rational> ratios);
+    void applyAlignment(int32_t widthAlignment, int32_t heightAlignment);
+    void updateLimits();
+    void applyMacroBlockLimits(
+            int32_t maxHorizontalBlocks, int32_t maxVerticalBlocks,
+            int32_t maxBlocks, int64_t maxBlocksPerSecond,
+            int32_t blockWidth, int32_t blockHeight,
+            int32_t widthAlignment, int32_t heightAlignment);
+    void applyMacroBlockLimits(
+            int32_t minHorizontalBlocks, int32_t minVerticalBlocks,
+            int32_t maxHorizontalBlocks, int32_t maxVerticalBlocks,
+            int32_t maxBlocks, int64_t maxBlocksPerSecond,
+            int32_t blockWidth, int32_t blockHeight,
+            int32_t widthAlignment, int32_t heightAlignment);
+    void applyLevelLimits();
+
+    friend struct CodecCapabilities;
+};
+
+}  // namespace android
+
+#endif // VIDEO_CAPABILITIES_H_
\ No newline at end of file
diff --git a/media/libmedia/tests/codeccapabilities/CodecCapabilitiesTest.cpp b/media/libmedia/tests/codeccapabilities/CodecCapabilitiesTest.cpp
index 89c9739..e59d4d6 100644
--- a/media/libmedia/tests/codeccapabilities/CodecCapabilitiesTest.cpp
+++ b/media/libmedia/tests/codeccapabilities/CodecCapabilitiesTest.cpp
@@ -64,7 +64,7 @@
 };
 
 TEST_F(AudioCapsAacTest, AudioCaps_Aac_Bitrate) {
-    const Range<int>& bitrateRange = audioCaps->getBitrateRange();
+    const Range<int32_t>& bitrateRange = audioCaps->getBitrateRange();
     EXPECT_EQ(bitrateRange.lower(), 8000) << "bitrate range1 does not match. lower: "
             << bitrateRange.lower();
     EXPECT_EQ(bitrateRange.upper(), 510000) << "bitrate range1 does not match. upper: "
@@ -72,15 +72,15 @@
 }
 
 TEST_F(AudioCapsAacTest, AudioCaps_Aac_InputChannelCount) {
-    int maxInputChannelCount = audioCaps->getMaxInputChannelCount();
+    int32_t maxInputChannelCount = audioCaps->getMaxInputChannelCount();
     EXPECT_EQ(maxInputChannelCount, 8);
-    int minInputChannelCount = audioCaps->getMinInputChannelCount();
+    int32_t minInputChannelCount = audioCaps->getMinInputChannelCount();
     EXPECT_EQ(minInputChannelCount, 1);
 }
 
 TEST_F(AudioCapsAacTest, AudioCaps_Aac_SupportedSampleRates) {
-    const std::vector<int>& sampleRates = audioCaps->getSupportedSampleRates();
-    EXPECT_EQ(sampleRates, std::vector<int>({7350, 8000, 11025, 12000, 16000, 22050,
+    const std::vector<int32_t>& sampleRates = audioCaps->getSupportedSampleRates();
+    EXPECT_EQ(sampleRates, std::vector<int32_t>({7350, 8000, 11025, 12000, 16000, 22050,
             24000, 32000, 44100, 48000}));
 
     EXPECT_FALSE(audioCaps->isSampleRateSupported(6000))
@@ -114,22 +114,22 @@
 };
 
 TEST_F(AudioCapsRawTest, AudioCaps_Raw_Bitrate) {
-    const Range<int>& bitrateRange = audioCaps->getBitrateRange();
+    const Range<int32_t>& bitrateRange = audioCaps->getBitrateRange();
     EXPECT_EQ(bitrateRange.lower(), 1);
     EXPECT_EQ(bitrateRange.upper(), 10000000);
 }
 
 TEST_F(AudioCapsRawTest, AudioCaps_Raw_InputChannelCount) {
-    int maxInputChannelCount = audioCaps->getMaxInputChannelCount();
+    int32_t maxInputChannelCount = audioCaps->getMaxInputChannelCount();
     EXPECT_EQ(maxInputChannelCount, 12);
-    int minInputChannelCount = audioCaps->getMinInputChannelCount();
+    int32_t minInputChannelCount = audioCaps->getMinInputChannelCount();
     EXPECT_EQ(minInputChannelCount, 1);
 }
 
 TEST_F(AudioCapsRawTest, AudioCaps_Raw_InputChannelCountRanges) {
-    const std::vector<Range<int>>& inputChannelCountRanges
+    const std::vector<Range<int32_t>>& inputChannelCountRanges
             = audioCaps->getInputChannelCountRanges();
-    std::vector<Range<int>> expectedOutput({{1,1}, {2,2}, {3,3}, {4,4}, {5,5},
+    std::vector<Range<int32_t>> expectedOutput({{1,1}, {2,2}, {3,3}, {4,4}, {5,5},
             {6,6}, {7,7}, {8,8}, {9,9}, {10,10}, {11,11}, {12,12}});
     ASSERT_EQ(inputChannelCountRanges.size(), expectedOutput.size());
     for (int i = 0; i < inputChannelCountRanges.size(); i++) {
@@ -139,7 +139,7 @@
 }
 
 TEST_F(AudioCapsRawTest, AudioCaps_Raw_SupportedSampleRates) {
-    const std::vector<Range<int>>& sampleRateRanges = audioCaps->getSupportedSampleRateRanges();
+    const std::vector<Range<int32_t>>& sampleRateRanges = audioCaps->getSupportedSampleRateRanges();
     EXPECT_EQ(sampleRateRanges.size(), 1);
     EXPECT_EQ(sampleRateRanges.at(0).lower(), 8000);
     EXPECT_EQ(sampleRateRanges.at(0).upper(), 192000);
@@ -148,3 +148,226 @@
     EXPECT_EQ(audioCaps->isSampleRateSupported(10000), true);
     EXPECT_EQ(audioCaps->isSampleRateSupported(193000), false);
 }
+
+class VideoCapsHevcTest : public testing::Test {
+protected:
+    VideoCapsHevcTest() {
+        std::string mediaType = MIMETYPE_VIDEO_HEVC;
+
+        sp<AMessage> details = new AMessage;
+        details->setString("alignment", "2x2");
+        details->setString("bitrate-range", "1-120000000");
+        details->setString("block-count-range", "1-32640");
+        details->setString("block-size", "16x16");
+        details->setString("blocks-per-second-range", "1-3916800");
+        details->setInt32("feature-adaptive-playback", 0);
+        details->setInt32("feature-can-swap-width-height", 1);
+        details->setString("max-concurrent-instances", "16");
+        details->setString("measured-frame-rate-1280x720-range", "547-553");
+        details->setString("measured-frame-rate-1920x1080-range", "569-572");
+        details->setString("measured-frame-rate-352x288-range", "1150-1250");
+        details->setString("measured-frame-rate-3840x2160-range", "159-159");
+        details->setString("measured-frame-rate-640x360-range", "528-529");
+        details->setString("measured-frame-rate-720x480-range", "546-548");
+        details->setString("performance-point-1280x720-range", "240");
+        details->setString("performance-point-3840x2160-range", "120");
+        details->setString("size-range", "64x64-3840x2176");
+
+        std::vector<ProfileLevel> profileLevel{
+            ProfileLevel(1, 8388608),
+            ProfileLevel(2, 8388608),
+            ProfileLevel(4096, 8388608),
+            ProfileLevel(8192, 8388608),
+        };
+
+        videoCaps = VideoCapabilities::Create(mediaType, profileLevel, details);
+    }
+
+    std::shared_ptr<VideoCapabilities> videoCaps;
+};
+
+TEST_F(VideoCapsHevcTest, VideoCaps_HEVC_Alignment) {
+    int32_t widthAlignment = videoCaps->getWidthAlignment();
+    EXPECT_EQ(widthAlignment, 2);
+    int32_t heightAlignment = videoCaps->getHeightAlignment();
+    EXPECT_EQ(heightAlignment, 2);
+}
+
+TEST_F(VideoCapsHevcTest, VideoCaps_HEVC_BitrateRange) {
+    const Range<int32_t>& bitrateRange = videoCaps->getBitrateRange();
+    EXPECT_EQ(bitrateRange.lower(), 1);
+    EXPECT_EQ(bitrateRange.upper(), 120000000);
+}
+
+TEST_F(VideoCapsHevcTest, VideoCaps_HEVC_SupportedWidthsAndHeights) {
+    const Range<int32_t>& supportedWidths = videoCaps->getSupportedWidths();
+    EXPECT_EQ(supportedWidths.upper(), 3840);
+    const Range<int32_t>& supportedHeights = videoCaps->getSupportedHeights();
+    EXPECT_EQ(supportedHeights.upper(), 3840);
+}
+
+TEST_F(VideoCapsHevcTest, VideoCaps_HEVC_SupportedFrameRates) {
+    const Range<int32_t>& supportedFrameRates = videoCaps->getSupportedFrameRates();
+    EXPECT_EQ(supportedFrameRates.lower(), 0);
+    EXPECT_EQ(supportedFrameRates.upper(), 960);
+
+    std::optional<Range<double>> supportedFR720p = videoCaps->getSupportedFrameRatesFor(1280, 720);
+    EXPECT_EQ(supportedFR720p.value().upper(), 960.0);
+    std::optional<Range<double>> supportedFR1080p
+            = videoCaps->getSupportedFrameRatesFor(1920, 1080);
+    EXPECT_EQ(supportedFR1080p.value().upper(), 480.0);
+    std::optional<Range<double>> supportedFR4k = videoCaps->getSupportedFrameRatesFor(3840, 2160);
+    EXPECT_EQ(std::round(supportedFR4k.value().upper()), 121);
+}
+
+TEST_F(VideoCapsHevcTest, VideoCaps_HEVC_AchievableFrameRates) {
+    std::optional<Range<double>> achievableFR1080p
+            = videoCaps->getAchievableFrameRatesFor(1920, 1080);
+    ASSERT_NE(achievableFR1080p, std::nullopt) << "resolution not supported";
+    EXPECT_EQ(achievableFR1080p.value().lower(), 569);
+    EXPECT_EQ(achievableFR1080p.value().upper(), 572);
+}
+
+class EncoderCapsAacTest : public testing::Test {
+protected:
+    EncoderCapsAacTest() {
+        std::string mediaType = MIMETYPE_AUDIO_AAC;
+
+        sp<AMessage> details = new AMessage;
+        details->setString("bitrate-range", "8000-960000");
+        details->setString("max-channel-count", "6");
+        details->setString("sample-rate-ranges",
+                "8000,11025,12000,16000,22050,24000,32000,44100,48000");
+
+        std::vector<ProfileLevel> profileLevel{
+            ProfileLevel(2, 0),
+            ProfileLevel(5, 0),
+            ProfileLevel(29, 0),
+            ProfileLevel(23, 0),
+            ProfileLevel(39, 0),
+        };
+
+        encoderCaps = EncoderCapabilities::Create(mediaType, profileLevel, details);
+    }
+
+    std::shared_ptr<EncoderCapabilities> encoderCaps;
+};
+
+
+TEST_F(EncoderCapsAacTest, EncoderCaps_AAC_ComplexityRange) {
+    const Range<int>& complexityRange = encoderCaps->getComplexityRange();
+    EXPECT_EQ(complexityRange.lower(), 0);
+    EXPECT_EQ(complexityRange.upper(), 0);
+}
+
+TEST_F(EncoderCapsAacTest, EncoderCaps_AAC_QualityRange) {
+    const Range<int>& qualityRange = encoderCaps->getQualityRange();
+    EXPECT_EQ(qualityRange.lower(), 0);
+    EXPECT_EQ(qualityRange.upper(), 0);
+}
+
+TEST_F(EncoderCapsAacTest, EncoderCaps_AAC_SupportedBitrateMode) {
+    EXPECT_FALSE(encoderCaps->isBitrateModeSupported(BITRATE_MODE_CBR));
+    EXPECT_TRUE(encoderCaps->isBitrateModeSupported(BITRATE_MODE_VBR));
+    EXPECT_FALSE(encoderCaps->isBitrateModeSupported(BITRATE_MODE_CQ));
+    EXPECT_FALSE(encoderCaps->isBitrateModeSupported(BITRATE_MODE_CBR_FD));
+}
+
+class EncoderCapsFlacTest : public testing::Test {
+protected:
+    EncoderCapsFlacTest() {
+        std::string mediaType = MIMETYPE_AUDIO_FLAC;
+
+        sp<AMessage> details = new AMessage;
+        details->setString("bitrate-range", "1-21000000");
+        details->setString("complexity-default", "5");
+        details->setString("complexity-range", "0-8");
+        details->setString("feature-bitrate-modes", "CQ");
+        details->setString("max-channel-count", "2");
+        details->setString("sample-rate-ranges", "1-655350");
+
+        std::vector<ProfileLevel> profileLevel;
+
+        encoderCaps = EncoderCapabilities::Create(mediaType, profileLevel, details);
+    }
+
+    std::shared_ptr<EncoderCapabilities> encoderCaps;
+};
+
+TEST_F(EncoderCapsFlacTest, EncoderCaps_FLAC_ComplexityRange) {
+    const Range<int>& complexityRange = encoderCaps->getComplexityRange();
+    EXPECT_EQ(complexityRange.lower(), 0);
+    EXPECT_EQ(complexityRange.upper(), 8);
+}
+
+TEST_F(EncoderCapsFlacTest, EncoderCaps_FLAC_QualityRange) {
+    const Range<int>& qualityRange = encoderCaps->getQualityRange();
+    EXPECT_EQ(qualityRange.lower(), 0);
+    EXPECT_EQ(qualityRange.upper(), 0);
+}
+
+TEST_F(EncoderCapsFlacTest, EncoderCaps_FLAC_SupportedBitrateMode) {
+    EXPECT_FALSE(encoderCaps->isBitrateModeSupported(BITRATE_MODE_CBR));
+    EXPECT_FALSE(encoderCaps->isBitrateModeSupported(BITRATE_MODE_VBR));
+    EXPECT_TRUE(encoderCaps->isBitrateModeSupported(BITRATE_MODE_CQ));
+    EXPECT_FALSE(encoderCaps->isBitrateModeSupported(BITRATE_MODE_CBR_FD));
+}
+
+class EncoderCapsHevcTest : public testing::Test {
+protected:
+    EncoderCapsHevcTest() {
+        std::string mediaType = MIMETYPE_VIDEO_HEVC;
+
+        sp<AMessage> details = new AMessage;
+        details->setString("alignment", "2x2");
+        details->setString("bitrate-range", "1-120000000");
+        details->setString("block-count-range", "1-8160");
+        details->setString("block-size", "32x32");
+        details->setString("blocks-per-second-range", "1-979200");
+        details->setString("feature-bitrate-modes", "VBR,CBR,CQ,CBR-FD");
+        details->setInt32("feature-can-swap-width-height", 1);
+        details->setInt32("feature-qp-bounds", 0);
+        details->setInt32("feature-vq-minimum-quality", 0);
+        details->setString("max-concurrent-instances", "16");
+        details->setString("measured-frame-rate-1280x720-range", "154-198");
+        details->setString("measured-frame-rate-1920x1080-range", "46-97");
+        details->setString("measured-frame-rate-320x240-range", "371-553");
+        details->setString("measured-frame-rate-720x480-range", "214-305");
+        details->setString("performance-point-1280x720-range", "240");
+        details->setString("performance-point-3840x2160-range", "120");
+        details->setString("quality-default", "57");
+        details->setString("quality-range", "0-100");
+        details->setString("quality-scale", "linear");
+        details->setString("size-range", "64x64-3840x2176");
+
+        std::vector<ProfileLevel> profileLevel{
+            ProfileLevel(1, 2097152),
+            ProfileLevel(2, 2097152),
+            ProfileLevel(4096, 2097152),
+            ProfileLevel(8192, 2097152),
+        };
+
+        encoderCaps = EncoderCapabilities::Create(mediaType, profileLevel, details);
+    }
+
+    std::shared_ptr<EncoderCapabilities> encoderCaps;
+};
+
+TEST_F(EncoderCapsHevcTest, EncoderCaps_HEVC_ComplexityRange) {
+    const Range<int>& complexityRange = encoderCaps->getComplexityRange();
+    EXPECT_EQ(complexityRange.lower(), 0);
+    EXPECT_EQ(complexityRange.upper(), 0);
+}
+
+TEST_F(EncoderCapsHevcTest, EncoderCaps_HEVC_QualityRange) {
+    const Range<int>& qualityRange = encoderCaps->getQualityRange();
+    EXPECT_EQ(qualityRange.lower(), 0);
+    EXPECT_EQ(qualityRange.upper(), 100);
+}
+
+TEST_F(EncoderCapsHevcTest, EncoderCaps_HEVC_SupportedBitrateMode) {
+    EXPECT_TRUE(encoderCaps->isBitrateModeSupported(BITRATE_MODE_CBR));
+    EXPECT_TRUE(encoderCaps->isBitrateModeSupported(BITRATE_MODE_VBR));
+    EXPECT_TRUE(encoderCaps->isBitrateModeSupported(BITRATE_MODE_CQ));
+    EXPECT_TRUE(encoderCaps->isBitrateModeSupported(BITRATE_MODE_CBR_FD));
+}
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 761137e..b267c08 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1816,8 +1816,6 @@
         const sp<AudioSystem::AudioDeviceCallback>& deviceCallback)
     : mCachedPlayerIId(PLAYER_PIID_INVALID),
       mCallback(NULL),
-      mCallbackCookie(NULL),
-      mCallbackData(NULL),
       mStreamType(AUDIO_STREAM_MUSIC),
       mLeftVolume(1.0),
       mRightVolume(1.0),
@@ -2085,7 +2083,7 @@
 status_t MediaPlayerService::AudioOutput::open(
         uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
         audio_format_t format, int bufferCount,
-        AudioCallback cb, void *cookie,
+        AudioCallback cb, const wp<RefBase>& cookie,
         audio_output_flags_t flags,
         const audio_offload_info_t *offloadInfo,
         bool doNotReconnect,
@@ -2714,7 +2712,7 @@
         return 0;
     }
     size_t actualSize = (*me->mCallback)(
-            me.get(), buffer.data(), buffer.size(), me->mCallbackCookie,
+            me, buffer.data(), buffer.size(), me->mCallbackCookie,
             CB_EVENT_FILL_BUFFER);
 
     // Log when no data is returned from the callback.
@@ -2739,7 +2737,7 @@
         return;
     }
     ALOGV("callbackwrapper: deliver EVENT_STREAM_END");
-    (*me->mCallback)(me.get(), NULL /* buffer */, 0 /* size */,
+    (*me->mCallback)(me, nullptr /* buffer */, 0 /* size */,
             me->mCallbackCookie, CB_EVENT_STREAM_END);
     unlock();
 }
@@ -2753,7 +2751,7 @@
         return;
     }
     ALOGV("callbackwrapper: deliver EVENT_TEAR_DOWN");
-    (*me->mCallback)(me.get(),  NULL /* buffer */, 0 /* size */,
+    (*me->mCallback)(me, nullptr /* buffer */, 0 /* size */,
             me->mCallbackCookie, CB_EVENT_TEAR_DOWN);
     unlock();
 }
@@ -2803,7 +2801,7 @@
 struct CallbackThread : public Thread {
     CallbackThread(const wp<MediaPlayerBase::AudioSink> &sink,
                    MediaPlayerBase::AudioSink::AudioCallback cb,
-                   void *cookie);
+                   const wp<RefBase>& cookie);
 
 protected:
     virtual ~CallbackThread();
@@ -2813,7 +2811,7 @@
 private:
     wp<MediaPlayerBase::AudioSink> mSink;
     MediaPlayerBase::AudioSink::AudioCallback mCallback;
-    void *mCookie;
+    wp<RefBase> mCookie;
     void *mBuffer;
     size_t mBufferSize;
 
@@ -2824,7 +2822,7 @@
 CallbackThread::CallbackThread(
         const wp<MediaPlayerBase::AudioSink> &sink,
         MediaPlayerBase::AudioSink::AudioCallback cb,
-        void *cookie)
+        const wp<RefBase>& cookie)
     : mSink(sink),
       mCallback(cb),
       mCookie(cookie),
@@ -2851,7 +2849,7 @@
     }
 
     size_t actualSize =
-        (*mCallback)(sink.get(), mBuffer, mBufferSize, mCookie,
+        (*mCallback)(sink, mBuffer, mBufferSize, mCookie,
                 MediaPlayerBase::AudioSink::CB_EVENT_FILL_BUFFER);
 
     if (actualSize > 0) {
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index cb544bd..76b7bcf 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -104,14 +104,14 @@
         virtual int64_t         getBufferDurationInUs() const;
         virtual audio_output_flags_t getFlags() const { return mFlags; }
 
-        virtual status_t        open(
+        status_t open(
                 uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
                 audio_format_t format, int bufferCount,
-                AudioCallback cb, void *cookie,
+                AudioCallback cb, const wp<RefBase>& cookie,
                 audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
                 const audio_offload_info_t *offloadInfo = NULL,
                 bool doNotReconnect = false,
-                uint32_t suggestedFrameCount = 0);
+                uint32_t suggestedFrameCount = 0) override;
 
         virtual void            setPlayerIId(int32_t playerIId);
 
@@ -164,7 +164,7 @@
         sp<AudioOutput>         mNextOutput;
         int                     mCachedPlayerIId;
         AudioCallback           mCallback;
-        void *                  mCallbackCookie;
+        wp<RefBase>             mCallbackCookie;
         sp<CallbackData>        mCallbackData;
         audio_stream_type_t     mStreamType;
         audio_attributes_t *    mAttributes;
diff --git a/media/libmediaplayerservice/include/MediaPlayerInterface.h b/media/libmediaplayerservice/include/MediaPlayerInterface.h
index be1aa00..495cf00 100644
--- a/media/libmediaplayerservice/include/MediaPlayerInterface.h
+++ b/media/libmediaplayerservice/include/MediaPlayerInterface.h
@@ -76,6 +76,33 @@
         virtual ~Listener() {}
     };
 
+    // For the AudioCallback, we provide a WeakWrapper class
+    // to wrap a virtual RefBase derived object to pass into the AudioCallback.
+    // This is not used for NuPlayer::Renderer, only for legacy AudioPlayer implementation.
+    template <typename T>
+    class WeakWrapper : public RefBase {
+    public:
+        explicit WeakWrapper(const sp<T>& object)
+                : mObject(object) {}
+
+        sp<T> promote() const {
+            if (mObject == nullptr) return {};
+            return mObject.promote();
+        }
+
+        static sp<T> promoteFromRefBase(const wp<RefBase>& weakWrapper) {
+            if (weakWrapper == nullptr) return {};
+            const auto refBase = weakWrapper.promote();
+            if (!refBase) return {};
+            const auto wrapper = sp<WeakWrapper<T>>::fromExisting(
+                    static_cast<WeakWrapper<T>*>(refBase.get()));
+            return wrapper->promote();
+        }
+
+    private:
+        const wp<T> mObject;
+    };
+
     // AudioSink: abstraction layer for audio output
     class AudioSink : public RefBase {
     public:
@@ -89,8 +116,8 @@
 
         // Callback returns the number of bytes actually written to the buffer.
         typedef size_t (*AudioCallback)(
-                AudioSink *audioSink, void *buffer, size_t size, void *cookie,
-                        cb_event_t event);
+                const sp<AudioSink>& audioSink, void *buffer, size_t size,
+                const wp<RefBase>& cookie, cb_event_t event);
 
         virtual             ~AudioSink() {}
         virtual bool        ready() const = 0; // audio output is open and ready
@@ -117,7 +144,7 @@
                 audio_format_t format=AUDIO_FORMAT_PCM_16_BIT,
                 int bufferCount=DEFAULT_AUDIOSINK_BUFFERCOUNT,
                 AudioCallback cb = NULL,
-                void *cookie = NULL,
+                const wp<RefBase>& cookie = {},
                 audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
                 const audio_offload_info_t *offloadInfo = NULL,
                 bool doNotReconnect = false,
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 3d4e955..225cfdd 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -98,20 +98,6 @@
 // static
 const int64_t NuPlayer::Renderer::kMinPositionUpdateDelayUs = 100000ll;
 
-static audio_format_t constexpr audioFormatFromEncoding(int32_t pcmEncoding) {
-    switch (pcmEncoding) {
-    case kAudioEncodingPcmFloat:
-        return AUDIO_FORMAT_PCM_FLOAT;
-    case kAudioEncodingPcm16bit:
-        return AUDIO_FORMAT_PCM_16_BIT;
-    case kAudioEncodingPcm8bit:
-        return AUDIO_FORMAT_PCM_8_BIT; // TODO: do we want to support this?
-    default:
-        ALOGE("%s: Invalid encoding: %d", __func__, pcmEncoding);
-        return AUDIO_FORMAT_INVALID;
-    }
-}
-
 NuPlayer::Renderer::Renderer(
         const sp<MediaPlayerBase::AudioSink> &sink,
         const sp<MediaClock> &mediaClock,
@@ -904,12 +890,15 @@
 
 // static
 size_t NuPlayer::Renderer::AudioSinkCallback(
-        MediaPlayerBase::AudioSink * /* audioSink */,
+        const sp<MediaPlayerBase::AudioSink>& /* audioSink */,
         void *buffer,
         size_t size,
-        void *cookie,
+        const wp<RefBase>& cookie,
         MediaPlayerBase::AudioSink::cb_event_t event) {
-    NuPlayer::Renderer *me = (NuPlayer::Renderer *)cookie;
+    if (cookie == nullptr) return 0;
+    const auto ref = cookie.promote();
+    if (!ref) return 0;
+    const auto me = static_cast<NuPlayer::Renderer*>(ref.get()); // we already hold a sp.
 
     switch (event) {
         case MediaPlayerBase::AudioSink::CB_EVENT_FILL_BUFFER:
@@ -2025,7 +2014,12 @@
     if (offloadingAudio()) {
         AString mime;
         CHECK(format->findString("mime", &mime));
-        status_t err = mapMimeToAudioFormat(audioFormat, mime.c_str());
+        status_t err = OK;
+        if (audioFormat == AUDIO_FORMAT_PCM_16_BIT) {
+            // If there is probably no pcm-encoding in the format message, try to get the format by
+            // its mimetype.
+            err = mapMimeToAudioFormat(audioFormat, mime.c_str());
+        }
 
         if (err != OK) {
             ALOGE("Couldn't map mime \"%s\" to a valid "
diff --git a/media/libmediaplayerservice/nuplayer/include/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/include/nuplayer/NuPlayerRenderer.h
index 574ad3d..cfa742e 100644
--- a/media/libmediaplayerservice/nuplayer/include/nuplayer/NuPlayerRenderer.h
+++ b/media/libmediaplayerservice/nuplayer/include/nuplayer/NuPlayerRenderer.h
@@ -43,8 +43,8 @@
              uint32_t flags = 0);
 
     static size_t AudioSinkCallback(
-            MediaPlayerBase::AudioSink *audioSink,
-            void *data, size_t size, void *me,
+            const sp<MediaPlayerBase::AudioSink>& audioSink,
+            void *data, size_t size, const wp<RefBase>& me,
             MediaPlayerBase::AudioSink::cb_event_t event);
 
     void queueBuffer(
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index d084f10..9ed5343 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -99,6 +99,7 @@
 
     static_libs: [
         "libstagefright_esds",
+        "android.media.extractor.flags-aconfig-cc",
     ],
 
     export_include_dirs: [
@@ -321,6 +322,7 @@
 
     static_libs: [
         "android.media.codec-aconfig-cc",
+        "android.media.extractor.flags-aconfig-cc",
         "com.android.media.flags.editing-aconfig-cc",
         "libstagefright_esds",
         "libstagefright_color_conversion",
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 3aa0107..cb3c185 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -54,6 +54,7 @@
 #include "include/HevcUtils.h"
 
 #include <com_android_media_editing_flags.h>
+namespace editing_flags = com::android::media::editing::flags;
 
 #ifndef __predict_false
 #define __predict_false(exp) __builtin_expect((exp) != 0, 0)
@@ -160,6 +161,7 @@
     bool isAvc() const { return mIsAvc; }
     bool isHevc() const { return mIsHevc; }
     bool isAv1() const { return mIsAv1; }
+    bool isApv() const { return mIsApv; }
     bool isHeic() const { return mIsHeic; }
     bool isAvif() const { return mIsAvif; }
     bool isHeif() const { return mIsHeif; }
@@ -328,6 +330,7 @@
     bool mIsAvc;
     bool mIsHevc;
     bool mIsAv1;
+    bool mIsApv;
     bool mIsDovi;
     bool mIsAudio;
     bool mIsVideo;
@@ -479,6 +482,7 @@
     void writeAvccBox();
     void writeHvccBox();
     void writeAv1cBox();
+    void writeApvcBox();
     void writeDoviConfigBox();
     void writeUrlBox();
     void writeDrefBox();
@@ -680,6 +684,9 @@
             return "hvc1";
         } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AV1, mime)) {
             return "av01";
+        } else if (editing_flags::muxer_mp4_enable_apv() &&
+                   !strcasecmp(MEDIA_MIMETYPE_VIDEO_APV, mime)) {
+            return "apv1";
         }
     } else if (!strncasecmp(mime, "application/", 12)) {
         return "mett";
@@ -2266,6 +2273,7 @@
     mIsAvc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
     mIsHevc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC);
     mIsAv1 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1);
+    mIsApv = editing_flags::muxer_mp4_enable_apv() && !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_APV);
     mIsDovi = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION);
     mIsAudio = !strncasecmp(mime, "audio/", 6);
     mIsVideo = !strncasecmp(mime, "video/", 6);
@@ -2708,6 +2716,9 @@
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1) ||
                !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_AVIF)) {
         mMeta->findData(kKeyAV1C, &type, &data, &size);
+    } else if (editing_flags::muxer_mp4_enable_apv() &&
+               !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_APV)) {
+        mMeta->findData(kKeyAPVC, &type, &data, &size);
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) {
         getDolbyVisionProfile();
         if (!mMeta->findData(kKeyAVCC, &type, &data, &size) &&
@@ -3609,7 +3620,7 @@
                             (const uint8_t *)buffer->data()
                                 + buffer->range_offset(),
                             buffer->range_length());
-                } else if (mIsMPEG4 || mIsAv1) {
+                } else if (mIsMPEG4 || mIsAv1 || mIsApv) {
                     err = copyCodecSpecificData((const uint8_t *)buffer->data() + buffer->range_offset(),
                             buffer->range_length());
                 }
@@ -4338,6 +4349,7 @@
         !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime) ||
         !strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime) ||
         !strcasecmp(MEDIA_MIMETYPE_VIDEO_AV1, mime) ||
+        (editing_flags::muxer_mp4_enable_apv() && !strcasecmp(MEDIA_MIMETYPE_VIDEO_APV, mime)) ||
         !strcasecmp(MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, mime) ||
         !strcasecmp(MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC, mime) ||
         !strcasecmp(MEDIA_MIMETYPE_IMAGE_AVIF, mime)) {
@@ -4512,6 +4524,9 @@
         writeHvccBox();
     } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AV1, mime)) {
         writeAv1cBox();
+    } else if (editing_flags::muxer_mp4_enable_apv() &&
+               !strcasecmp(MEDIA_MIMETYPE_VIDEO_APV, mime)) {
+        writeApvcBox();
     } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, mime)) {
         if (mDoviProfile <= DolbyVisionProfileDvheSt) {
             writeHvccBox();
@@ -5103,6 +5118,15 @@
     mOwner->endBox();  // av1C
 }
 
+void MPEG4Writer::Track::writeApvcBox() {
+    CHECK(mCodecSpecificData);
+    CHECK_GE(mCodecSpecificDataSize, 4u);
+
+    mOwner->beginBox("apvC");
+    mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
+    mOwner->endBox();  // apvC
+}
+
 void MPEG4Writer::Track::writeDoviConfigBox() {
     CHECK_NE(mDoviProfile, 0u);
 
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 7d47837..a374bf4 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -1189,6 +1189,19 @@
     return new PersistentSurface(bufferProducer, bufferSource);
 }
 
+//static
+status_t MediaCodec::getGloballyAvailableResources(std::vector<GlobalResourceInfo>& resources) {
+    resources.clear();
+    // Make sure codec availability feature is on.
+    if (!android::media::codec::codec_availability()) {
+        return ERROR_UNSUPPORTED;
+    }
+    // TODO: For now this is just an empty function.
+    // The actual implementation should use component store to query the
+    // available resources from hal, and fill in resources with the same.
+    return ERROR_UNSUPPORTED;
+}
+
 // GenerateCodecId generates a 64bit Random ID for each codec that is created.
 // The Codec ID is generated as:
 //   - A process-unique random high 32bits
@@ -1295,7 +1308,12 @@
     CHECK_EQ(mState, UNINITIALIZED);
     mResourceManagerProxy->removeClient();
 
-    flushMediametrics();
+    flushMediametrics();  // this deletes mMetricsHandle
+    // don't keep the last metrics handle around
+    if (mLastMetricsHandle != 0) {
+        mediametrics_delete(mLastMetricsHandle);
+        mLastMetricsHandle = 0;
+    }
 
     // clean any saved metrics info we stored as part of configure()
     if (mConfigureMsg != nullptr) {
@@ -1306,7 +1324,7 @@
     }
 }
 
-// except for in constructor, called from the looper thread (and therefore mutexed)
+// except for in constructor, called from the looper thread (and therefore not mutexed)
 void MediaCodec::initMediametrics() {
     if (mMetricsHandle == 0) {
         mMetricsHandle = mediametrics_create(kCodecKeyName);
@@ -1332,6 +1350,7 @@
         mInputBufferCounter = 0;
     }
 
+    mSubsessionCount = 0;
     mLifetimeStartNs = systemTime(SYSTEM_TIME_MONOTONIC);
     resetMetricsFields();
 }
@@ -1343,6 +1362,17 @@
     mReliabilityContextMetrics = ReliabilityContextMetrics();
 }
 
+// always called from the looper thread (and therefore not mutexed)
+void MediaCodec::resetSubsessionMetricsFields() {
+    mBytesEncoded = 0;
+    mFramesEncoded = 0;
+    mFramesInput = 0;
+    mBytesInput = 0;
+    mEarliestEncodedPtsUs = INT64_MAX;
+    mLatestEncodedPtsUs = INT64_MIN;
+}
+
+// always called from the looper thread
 void MediaCodec::updateMediametrics() {
     if (mMetricsHandle == 0) {
         ALOGV("no metrics handle found");
@@ -1707,6 +1737,7 @@
     }
 }
 
+// except for in destructor, called from the looper thread
 void MediaCodec::flushMediametrics() {
     ALOGV("flushMediametrics");
 
@@ -1720,7 +1751,14 @@
         if (mMetricsToUpload && mediametrics_count(mMetricsHandle) > 0) {
             mediametrics_selfRecord(mMetricsHandle);
         }
-        mediametrics_delete(mMetricsHandle);
+        // keep previous metrics handle for subsequent getMetrics() calls.
+        // NOTE: There could be multiple error events, each flushing the metrics.
+        // We keep the last non-empty metrics handle, so getMetrics() in the
+        // next call will get the latest metrics prior to the errors.
+        if (mLastMetricsHandle != 0) {
+            mediametrics_delete(mLastMetricsHandle);
+        }
+        mLastMetricsHandle = mMetricsHandle;
         mMetricsHandle = 0;
     }
     // we no longer have anything pending upload
@@ -1885,7 +1923,10 @@
         });
     }
 
-    if (mDomain == DOMAIN_VIDEO && (mFlags & kFlagIsEncoder)) {
+    // NOTE: these were erroneously restricted to video encoders, but we want them for all
+    // codecs.
+    if (android::media::codec::provider_->subsession_metrics()
+            || (mDomain == DOMAIN_VIDEO && (mFlags & kFlagIsEncoder))) {
         mBytesInput += buffer->size();
         mFramesInput++;
     }
@@ -1907,12 +1948,15 @@
     ++mInputBufferCounter;
 }
 
-// when we get a buffer back from the codec
+// when we get a buffer back from the codec, always called from the looper thread
 void MediaCodec::statsBufferReceived(int64_t presentationUs, const sp<MediaCodecBuffer> &buffer) {
 
     CHECK_NE(mState, UNINITIALIZED);
 
-    if (mDomain == DOMAIN_VIDEO && (mFlags & kFlagIsEncoder)) {
+    // NOTE: these were erroneously restricted to video encoders, but we want them for all
+    // codecs.
+    if (android::media::codec::provider_->subsession_metrics()
+            || (mDomain == DOMAIN_VIDEO && (mFlags & kFlagIsEncoder))) {
         int32_t flags = 0;
         (void) buffer->meta()->findInt32("flags", &flags);
 
@@ -2460,12 +2504,8 @@
             mediametrics_setInt32(nextMetricsHandle, kCodecCrypto, 1);
         }
     } else if (mFlags & kFlagIsSecure) {
-        if (android::media::codec::provider_->secure_codecs_require_crypto()) {
-            mErrorLog.log(LOG_TAG, "Crypto or descrambler must be given for secure codec");
-            return INVALID_OPERATION;
-        } else {
-            ALOGW("Crypto or descrambler should be given for secure codec");
-        }
+        // We'll catch this later when we process the buffers.
+        ALOGW("Crypto or descrambler should be given for secure codec");
     }
 
     if (mConfigureMsg != nullptr) {
@@ -2526,6 +2566,31 @@
     return err;
 }
 
+status_t MediaCodec::getRequiredResources(std::vector<InstanceResourceInfo>& resources) {
+    resources.clear();
+    // Make sure codec availability feature is on.
+    if (!android::media::codec::codec_availability()) {
+        return ERROR_UNSUPPORTED;
+    }
+    // Make sure that the codec was configured already.
+    if (mState != CONFIGURED && mState != STARTING && mState != STARTED &&
+        mState != FLUSHING && mState != FLUSHED) {
+        ALOGE("Codec wasn't configured yet!");
+        return INVALID_OPERATION;
+    }
+
+    if (!mRequiredResourceInfo.empty()) {
+        resources = mRequiredResourceInfo;
+        return OK;
+    }
+
+    // TODO: For now this is just an empty function.
+    // The actual implementation should use component interface
+    // (for example, through mCodec->getRequiredDeviceResources) to query the
+    // the required resources for this configuration, and fill in resources with the same.
+    return ERROR_UNSUPPORTED;
+}
+
 // Media Format Shaping support
 //
 
@@ -3614,6 +3679,10 @@
         updateMediametrics();
         results = mediametrics_dup(mMetricsHandle);
         updateEphemeralMediametrics(results);
+    } else if (mLastMetricsHandle != 0) {
+        // After error, mMetricsHandle is cleared, but we keep the last
+        // metrics around so that it can be queried by getMetrics().
+        results = mediametrics_dup(mLastMetricsHandle);
     } else {
         results = mediametrics_dup(mMetricsHandle);
     }
@@ -3883,6 +3952,7 @@
     return true;
 }
 
+// always called from the looper thread
 MediaCodec::DequeueOutputResult MediaCodec::handleDequeueOutputBuffer(
         const sp<AReplyToken> &replyID, bool newRequest) {
     if (!isExecuting()) {
@@ -3938,6 +4008,9 @@
 
         response->setInt32("flags", flags);
 
+        // NOTE: we must account the stats for an output buffer only after we
+        // already handled a potential output format change that could have
+        // started a new subsession.
         statsBufferReceived(timeUs, buffer);
 
         response->postReply(replyID);
@@ -5842,6 +5915,7 @@
     }
 }
 
+// always called from the looper thread
 void MediaCodec::handleOutputFormatChangeIfNeeded(const sp<MediaCodecBuffer> &buffer) {
     sp<AMessage> format = buffer->format();
     if (mOutputFormat == format) {
@@ -5925,6 +5999,24 @@
             }
         }
     }
+
+    // Update the width and the height.
+    int32_t left = 0, top = 0, right = 0, bottom = 0, width = 0, height = 0;
+    bool newSubsession = false;
+    if (android::media::codec::provider_->subsession_metrics()
+            && mOutputFormat->findInt32("width", &width)
+            && mOutputFormat->findInt32("height", &height)
+            && (width != mWidth || height != mHeight)) {
+        // consider a new subsession if the width or height changes.
+        newSubsession = true;
+    }
+    // TODO: properly detect new audio subsession
+
+    // Only consider a new subsession if we already have output (from a previous subsession).
+    if (newSubsession && mMetricsToUpload && mBytesEncoded > 0) {
+        handleStartingANewSubsession();
+    }
+
     if (mFlags & kFlagIsAsync) {
         onOutputFormatChanged();
     } else {
@@ -5932,8 +6024,6 @@
         postActivityNotificationIfPossible();
     }
 
-    // Update the width and the height.
-    int32_t left = 0, top = 0, right = 0, bottom = 0, width = 0, height = 0;
     bool resolutionChanged = false;
     if (mOutputFormat->findRect("crop", &left, &top, &right, &bottom)) {
         mWidth = right - left + 1;
@@ -5960,6 +6050,35 @@
     updateHdrMetrics(false /* isConfig */);
 }
 
+// always called from the looper thread (and therefore not mutexed)
+void MediaCodec::handleStartingANewSubsession() {
+    // create a new metrics item for the subsession with the new resolution.
+    // TODO: properly account input counts for the previous and the new
+    // subsessions. We only find out that a new subsession started from the
+    // output format, but by that time we already accounted the input counts
+    // to the previous subsession.
+    flushMediametrics(); // this deletes mMetricsHandle, but stores it in mLastMetricsHandle
+
+    // hence mLastMetricsHandle has the metrics item for the previous subsession.
+    if ((mFlags & kFlagIsAsync) && mCallback != nullptr) {
+        sp<AMessage> msg = mCallback->dup();
+        msg->setInt32("callbackID", CB_METRICS_FLUSHED);
+        std::unique_ptr<mediametrics::Item> flushedMetrics(
+                mediametrics::Item::convert(mediametrics_dup(mLastMetricsHandle)));
+        msg->setObject("metrics", new WrapperObject<std::unique_ptr<mediametrics::Item>>(
+                std::move(flushedMetrics)));
+        msg->post();
+    }
+
+    // reuse/continue old metrics item for the new subsession.
+    mMetricsHandle = mediametrics_dup(mLastMetricsHandle);
+    mMetricsToUpload = true;
+    // TODO: configured width/height for the new subsession should be the
+    // previous width/height.
+    mSubsessionCount++;
+    resetSubsessionMetricsFields();
+}
+
 void MediaCodec::extractCSD(const sp<AMessage> &format) {
     mCSD.clear();
 
@@ -5971,9 +6090,9 @@
         }
         if (csd->size() == 0) {
             ALOGW("csd-%zu size is 0", i);
+        } else {
+            mCSD.push_back(csd);
         }
-
-        mCSD.push_back(csd);
         ++i;
     }
 
@@ -6210,6 +6329,12 @@
     CryptoPlugin::SubSample ss;
     CryptoPlugin::Pattern pattern;
 
+    if (android::media::codec::provider_->secure_codecs_require_crypto()
+            && (mFlags & kFlagIsSecure) && !hasCryptoOrDescrambler()) {
+        mErrorLog.log(LOG_TAG, "Crypto or descrambler must be given for secure codec");
+        return INVALID_OPERATION;
+    }
+
     if (msg->findSize("size", &size)) {
         if (hasCryptoOrDescrambler()) {
             ss.mNumBytesOfClearData = size;
@@ -6954,6 +7079,18 @@
     }
 }
 
+void MediaCodec::onRequiredResourcesChanged(
+        const std::vector<InstanceResourceInfo>& resourceInfo) {
+    mRequiredResourceInfo = resourceInfo;
+    // Make sure codec availability feature is on.
+    if (mCallback != nullptr && android::media::codec::codec_availability()) {
+        // Post the callback
+        sp<AMessage> msg = mCallback->dup();
+        msg->setInt32("callbackID", CB_REQUIRED_RESOURCES_CHANGED);
+        msg->post();
+    }
+}
+
 void MediaCodec::postActivityNotificationIfPossible() {
     if (mActivityNotify == NULL) {
         return;
diff --git a/media/libstagefright/OmxInfoBuilder.cpp b/media/libstagefright/OmxInfoBuilder.cpp
index 79ffdeb..1cb8f14 100644
--- a/media/libstagefright/OmxInfoBuilder.cpp
+++ b/media/libstagefright/OmxInfoBuilder.cpp
@@ -21,6 +21,8 @@
 #define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
 #endif
 
+#include <cstdlib>
+
 #include <android-base/properties.h>
 #include <utils/Log.h>
 
@@ -131,6 +133,10 @@
     for (const auto& p : serviceAttributes) {
         writer->addGlobalSetting(
                 p.key.c_str(), p.value.c_str());
+        if (p.key == "max-concurrent-instances") {
+            MediaCodecInfoWriter::SetMaxSupportedInstances(
+                    (int32_t)strtol(p.value.c_str(), NULL, 10));
+        }
     }
 
     // Convert roles to lists of codecs
@@ -217,6 +223,8 @@
                 ALOGW("Fail to add media type %s to codec %s",
                         typeName.c_str(), nodeName.c_str());
                 info->removeMediaType(typeName.c_str());
+            } else {
+                info->createCodecCaps();
             }
         }
     }
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 86741a6..46606de 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -44,6 +44,8 @@
 #include <media/AudioParameter.h>
 #include <system/audio.h>
 
+#include <com_android_media_extractor_flags.h>
+
 // TODO : Remove the defines once mainline media is built against NDK >= 31.
 // The mp4 extractor is part of mainline and builds against NDK 29 as of
 // writing. These keys are available only from NDK 31:
@@ -1443,6 +1445,17 @@
         buffer->meta()->setInt64("timeUs", 0);
         msg->setBuffer("csd-0", buffer);
         parseAV1ProfileLevelFromCsd(buffer, msg);
+    } else if (com::android::media::extractor::flags::extractor_mp4_enable_apv() &&
+               meta->findData(kKeyAPVC, &type, &data, &size)) {
+        sp<ABuffer> buffer = new (std::nothrow) ABuffer(size);
+        if (buffer.get() == NULL || buffer->base() == NULL) {
+            return NO_MEMORY;
+        }
+        memcpy(buffer->data(), data, size);
+
+        buffer->meta()->setInt32("csd", true);
+        buffer->meta()->setInt64("timeUs", 0);
+        msg->setBuffer("csd-0", buffer);
     } else if (meta->findData(kKeyESDS, &type, &data, &size)) {
         ESDS esds((const char *)data, size);
         if (esds.InitCheck() != (status_t)OK) {
@@ -2091,6 +2104,9 @@
         } else if (mime == MEDIA_MIMETYPE_VIDEO_AV1 ||
                    mime == MEDIA_MIMETYPE_IMAGE_AVIF) {
             meta->setData(kKeyAV1C, 0, csd0->data(), csd0->size());
+        } else if (com::android::media::extractor::flags::extractor_mp4_enable_apv() &&
+                   mime == MEDIA_MIMETYPE_VIDEO_APV) {
+            meta->setData(kKeyAPVC, 0, csd0->data(), csd0->size());
         } else if (mime == MEDIA_MIMETYPE_VIDEO_DOLBY_VISION) {
             int32_t profile = -1;
             uint8_t blCompatibilityId = -1;
@@ -2406,6 +2422,24 @@
     return;
 }
 
+audio_format_t audioFormatFromEncoding(int32_t pcmEncoding) {
+    switch (pcmEncoding) {
+    case kAudioEncodingPcmFloat:
+        return AUDIO_FORMAT_PCM_FLOAT;
+    case kAudioEncodingPcm32bit:
+        return AUDIO_FORMAT_PCM_32_BIT;
+    case kAudioEncodingPcm24bitPacked:
+        return AUDIO_FORMAT_PCM_24_BIT_PACKED;
+    case kAudioEncodingPcm16bit:
+        return AUDIO_FORMAT_PCM_16_BIT;
+    case kAudioEncodingPcm8bit:
+        return AUDIO_FORMAT_PCM_8_BIT; // TODO: do we want to support this?
+    default:
+        ALOGE("%s: Invalid encoding: %d", __func__, pcmEncoding);
+        return AUDIO_FORMAT_INVALID;
+    }
+}
+
 status_t getAudioOffloadInfo(const sp<MetaData>& meta, bool hasVideo,
         bool isStreaming, audio_stream_type_t streamType, audio_offload_info_t *info)
 {
@@ -2425,6 +2459,12 @@
         ALOGV("Mime type \"%s\" mapped to audio_format %d", mime, info->format);
     }
 
+    int32_t pcmEncoding;
+    if (meta->findInt32(kKeyPcmEncoding, &pcmEncoding)) {
+        info->format = audioFormatFromEncoding(pcmEncoding);
+        ALOGV("audio_format use kKeyPcmEncoding value %d first", info->format);
+    }
+
     if (AUDIO_FORMAT_INVALID == info->format) {
         // can't offload if we don't know what the source format is
         ALOGE("mime type \"%s\" not a known audio format", mime);
diff --git a/media/libstagefright/data/media_codecs_google_c2_video.xml b/media/libstagefright/data/media_codecs_google_c2_video.xml
index 137b282..2fb2d59 100644
--- a/media/libstagefright/data/media_codecs_google_c2_video.xml
+++ b/media/libstagefright/data/media_codecs_google_c2_video.xml
@@ -95,6 +95,15 @@
             <Feature name="adaptive-playback" />
             <Attribute name="software-codec" />
         </MediaCodec>
+        <MediaCodec name="c2.android.apv.decoder" type="video/apv" enabled="false" minsdk="36" variant="!slow-cpu">
+            <Limit name="size" min="16x16" max="1920x1920"/>
+            <Limit name="alignment" value="2x2"/>
+            <Limit name="bitrate" range="1-240000000"/>
+            <Limit name="block-size" value="16x16" />
+            <Limit name="block-count" range="1-32768" /> <!-- max 4096x2048 equivalent -->
+            <Feature name="dynamic-color-aspects" />
+            <Attribute name="software-codec"/>
+         </MediaCodec>
     </Decoders>
 
     <Encoders>
@@ -160,5 +169,13 @@
             <Limit name="bitrate" range="1-40000000" />
             <Feature name="bitrate-modes" value="VBR,CBR" />
         </MediaCodec>
+        <MediaCodec name="c2.android.apv.encoder" type="video/apv" enabled="false" minsdk="36" variant="!slow-cpu">
+            <Limit name="size" min="2x2" max="1920x1920" />
+            <Limit name="alignment" value="2x2" />
+            <Limit name="block-size" value="16x16" />
+            <Limit name="block-count" range="1-32768" /> <!-- max 4096x2048 equivalent -->
+            <Limit name="bitrate" range="1-240000000" />
+            <Feature name="bitrate-modes" value="VBR,CBR" />
+        </MediaCodec>
     </Encoders>
 </Included>
diff --git a/media/libstagefright/data/media_codecs_sw.xml b/media/libstagefright/data/media_codecs_sw.xml
index c18ab94..c79ac5c 100644
--- a/media/libstagefright/data/media_codecs_sw.xml
+++ b/media/libstagefright/data/media_codecs_sw.xml
@@ -255,6 +255,16 @@
             <Feature name="adaptive-playback" />
             <Attribute name="software-codec" />
         </MediaCodec>
+        <MediaCodec name="c2.android.apv.decoder" type="video/apv" enabled="false" minsdk="36" variant="!slow-cpu">
+            <Limit name="size" min="16x16" max="1920x1920"/>
+            <Limit name="alignment" value="2x2"/>
+            <Limit name="bitrate" range="1-240000000"/>
+            <Limit name="block-size" value="16x16" />
+            <Limit name="block-count" range="1-32768" /> <!-- max 4096x2048 equivalent -->
+            <Feature name="adaptive-playback" />
+            <Feature name="dynamic-color-aspects" />
+            <Attribute name="software-codec"/>
+        </MediaCodec>
     </Decoders>
     <Encoders>
         <MediaCodec name="c2.android.aac.encoder" type="audio/mp4a-latm">
@@ -409,5 +419,14 @@
             <Feature name="qp-bounds" />
             <Attribute name="software-codec" />
         </MediaCodec>
+        <MediaCodec name="c2.android.apv.encoder" type="video/apv" enabled="false" minsdk="36" variant="!slow-cpu">
+            <Limit name="size" min="2x2" max="1920x1920" />
+            <Limit name="alignment" value="2x2" />
+            <Limit name="block-size" value="16x16" />
+            <Limit name="block-count" range="1-32768" /> <!-- max 4096x2048 equivalent -->
+            <Limit name="bitrate" range="1-240000000" />
+            <Feature name="bitrate-modes" value="VBR,CBR" />
+            <Attribute name="software-codec" />
+        </MediaCodec>
     </Encoders>
 </MediaCodecs>
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 7169b1e..df1ebd7 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -123,6 +123,18 @@
         CB_RESOURCE_RECLAIMED = 5,
         CB_CRYPTO_ERROR = 6,
         CB_LARGE_FRAME_OUTPUT_AVAILABLE = 7,
+
+        /** Callback ID for when the metrics for this codec have been flushed
+         * due to the start of a new subsession. The associated AMessage will
+         * contain an sp<WrapperObject<std::unique_ptr<mediametrics::Item>>>
+         * Object at the "metrics" key.
+         */
+        CB_METRICS_FLUSHED = 8,
+
+        /** Callback ID to notify the change in resource requirement
+         * for the codec component.
+         */
+        CB_REQUIRED_RESOURCES_CHANGED = 9,
     };
 
     static const pid_t kNoPid = -1;
@@ -142,6 +154,73 @@
 
     static sp<PersistentSurface> CreatePersistentInputSurface();
 
+    /**
+     * Abstraction for the Global Codec resources.
+     * This encapsulates all the available codec resources on the device.
+     */
+    struct GlobalResourceInfo {
+        /**
+         * Name of the Resource type.
+         */
+        std::string mName;
+        /**
+         * Total count/capacity of resources of this type.
+         */
+        int mCapacity;
+        /**
+         * Available count of this resource type.
+         */
+        int mAvailable;
+
+        GlobalResourceInfo(const std::string& name, int capacity, int available) :
+                mName(name),
+                mCapacity(capacity),
+                mAvailable(available) {}
+
+        GlobalResourceInfo(const GlobalResourceInfo& info) :
+                mName(info.mName),
+                mCapacity(info.mCapacity),
+                mAvailable(info.mAvailable) {}
+    };
+
+    /**
+     * Abstraction for the resources associated with a codec instance.
+     * This encapsulates the required codec resources for a configured codec instance.
+     */
+    struct InstanceResourceInfo {
+        /**
+         * Name of the Resource type.
+         */
+        std::string mName;
+        /**
+         * Required resource count of this type.
+         */
+        int mStaticCount;
+        /**
+         * Per frame resource requirement of this resource type.
+         */
+        int mPerFrameCount;
+
+        InstanceResourceInfo(const std::string& name, int staticCount, int perFrameCount) :
+                mName(name),
+                mStaticCount(staticCount),
+                mPerFrameCount(perFrameCount) {}
+
+        InstanceResourceInfo(const InstanceResourceInfo& info) :
+                mName(info.mName),
+                mStaticCount(info.mStaticCount),
+                mPerFrameCount(info.mPerFrameCount) {}
+    };
+
+    /**
+     * Get a list of Globally available device codec resources.
+     *
+     * It will return INVALID_OPERATION if:
+     *  - HAL does not implement codec availability API
+     *  - codec_availability feature flag isn't defined.
+     */
+    static status_t getGloballyAvailableResources(std::vector<GlobalResourceInfo>& resources);
+
     status_t configure(
             const sp<AMessage> &format,
             const sp<Surface> &nativeWindow,
@@ -155,6 +234,19 @@
             const sp<IDescrambler> &descrambler,
             uint32_t flags);
 
+    /**
+     * Get a list of required codec resources.
+     *
+     * This may only be called after configuring the codec.
+     *
+     * Calling this while the codec wasn't configured, will result in
+     * returning INVALID_OPERATION error code.
+     * It will also return INVALID_OPERATION if:
+     *  - HAL does not implement codec availability API
+     *  - codec_availability feature flag isn't defined.
+     */
+    status_t getRequiredResources(std::vector<InstanceResourceInfo>& resources);
+
     status_t releaseCrypto();
 
     status_t setCallback(const sp<AMessage> &callback);
@@ -484,12 +576,21 @@
 
     Mutex mMetricsLock;
     mediametrics_handle_t mMetricsHandle = 0;
+    mediametrics_handle_t mLastMetricsHandle = 0; // only accessed from the looper or destructor
     bool mMetricsToUpload = false;
     nsecs_t mLifetimeStartNs = 0;
     void initMediametrics();
     void updateMediametrics();
     void flushMediametrics();
     void resetMetricsFields();
+
+    // Reset the metrics fields for a new subsession.
+    void resetSubsessionMetricsFields();
+
+    // Start a new subsession (for metrics). This includes flushing the current
+    // metrics, notifying the client and resetting the session fields.
+    void handleStartingANewSubsession();
+
     void updateEphemeralMediametrics(mediametrics_handle_t item);
     void updateLowLatency(const sp<AMessage> &msg);
     void updateCodecImportance(const sp<AMessage>& msg);
@@ -551,6 +652,7 @@
         int32_t setOutputSurfaceCount;
         int32_t resolutionChangeCount;
     } mReliabilityContextMetrics;
+    int32_t mSubsessionCount;
 
     // initial create parameters
     AString mInitName;
@@ -671,6 +773,7 @@
     void onCryptoError(const sp<AMessage> &msg);
     void onError(status_t err, int32_t actionCode, const char *detail = NULL);
     void onOutputFormatChanged();
+    void onRequiredResourcesChanged(const std::vector<InstanceResourceInfo>& resourceInfo);
 
     status_t onSetParameters(const sp<AMessage> &params);
 
@@ -770,6 +873,8 @@
     friend class MediaTestHelper;
 
     CodecErrorLog mErrorLog;
+    // Required resource info for this codec.
+    std::vector<InstanceResourceInfo> mRequiredResourceInfo;
 
     DISALLOW_EVIL_CONSTRUCTORS(MediaCodec);
 };
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
index b1cf665..b0f671d 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
@@ -586,6 +586,139 @@
     }
 }
 
+// APV ProfileLevel
+inline constexpr int32_t APVProfile422_10           = 0x01;
+inline constexpr int32_t APVProfile422_10HDR10      = 0x1000;
+inline constexpr int32_t APVProfile422_10HDR10Plus  = 0x2000;
+
+inline static const char *asString_APVProfile(int32_t i, const char *def = "??") {
+    switch (i) {
+        case APVProfile422_10:           return "APVProfile422_10";
+        case APVProfile422_10HDR10:      return "APVProfile422_10HDR10";
+        case APVProfile422_10HDR10Plus:  return "APVProfile422_10HDR10Plus";
+        default:                        return def;
+    }
+}
+
+inline constexpr int32_t APVLevel1Band0 = 0x101;
+inline constexpr int32_t APVLevel1Band1 = 0x102;
+inline constexpr int32_t APVLevel1Band2 = 0x104;
+inline constexpr int32_t APVLevel1Band3 = 0x108;
+inline constexpr int32_t APVLevel11Band0 = 0x201;
+inline constexpr int32_t APVLevel11Band1 = 0x202;
+inline constexpr int32_t APVLevel11Band2 = 0x204;
+inline constexpr int32_t APVLevel11Band3 = 0x208;
+inline constexpr int32_t APVLevel2Band0 = 0x401;
+inline constexpr int32_t APVLevel2Band1 = 0x402;
+inline constexpr int32_t APVLevel2Band2 = 0x404;
+inline constexpr int32_t APVLevel2Band3 = 0x408;
+inline constexpr int32_t APVLevel21Band0 = 0x801;
+inline constexpr int32_t APVLevel21Band1 = 0x802;
+inline constexpr int32_t APVLevel21Band2 = 0x804;
+inline constexpr int32_t APVLevel21Band3 = 0x808;
+inline constexpr int32_t APVLevel3Band0 = 0x1001;
+inline constexpr int32_t APVLevel3Band1 = 0x1002;
+inline constexpr int32_t APVLevel3Band2 = 0x1004;
+inline constexpr int32_t APVLevel3Band3 = 0x1008;
+inline constexpr int32_t APVLevel31Band0 = 0x2001;
+inline constexpr int32_t APVLevel31Band1 = 0x2002;
+inline constexpr int32_t APVLevel31Band2 = 0x2004;
+inline constexpr int32_t APVLevel31Band3 = 0x2008;
+inline constexpr int32_t APVLevel4Band0 = 0x4001;
+inline constexpr int32_t APVLevel4Band1 = 0x4002;
+inline constexpr int32_t APVLevel4Band2 = 0x4004;
+inline constexpr int32_t APVLevel4Band3 = 0x4008;
+inline constexpr int32_t APVLevel41Band0 = 0x8001;
+inline constexpr int32_t APVLevel41Band1 = 0x8002;
+inline constexpr int32_t APVLevel41Band2 = 0x8004;
+inline constexpr int32_t APVLevel41Band3 = 0x8008;
+inline constexpr int32_t APVLevel5Band0 = 0x10001;
+inline constexpr int32_t APVLevel5Band1 = 0x10002;
+inline constexpr int32_t APVLevel5Band2 = 0x10004;
+inline constexpr int32_t APVLevel5Band3 = 0x10008;
+inline constexpr int32_t APVLevel51Band0 = 0x20001;
+inline constexpr int32_t APVLevel51Band1 = 0x20002;
+inline constexpr int32_t APVLevel51Band2 = 0x20004;
+inline constexpr int32_t APVLevel51Band3 = 0x20008;
+inline constexpr int32_t APVLevel6Band0 = 0x40001;
+inline constexpr int32_t APVLevel6Band1 = 0x40002;
+inline constexpr int32_t APVLevel6Band2 = 0x40004;
+inline constexpr int32_t APVLevel6Band3 = 0x40008;
+inline constexpr int32_t APVLevel61Band0 = 0x80001;
+inline constexpr int32_t APVLevel61Band1 = 0x80002;
+inline constexpr int32_t APVLevel61Band2 = 0x80004;
+inline constexpr int32_t APVLevel61Band3 = 0x80008;
+inline constexpr int32_t APVLevel7Band0 = 0x100001;
+inline constexpr int32_t APVLevel7Band1 = 0x100002;
+inline constexpr int32_t APVLevel7Band2 = 0x100004;
+inline constexpr int32_t APVLevel7Band3 = 0x100008;
+inline constexpr int32_t APVLevel71Band0 = 0x200001;
+inline constexpr int32_t APVLevel71Band1 = 0x200002;
+inline constexpr int32_t APVLevel71Band2 = 0x200004;
+inline constexpr int32_t APVLevel71Band3 = 0x200008;
+
+inline static const char *asString_APVBandLevel(int32_t i, const char *def = "??") {
+    switch (i) {
+        case APVLevel1Band0:     return "Level 1, Band 0";
+        case APVLevel1Band1:     return "Level 1, Band 1";
+        case APVLevel1Band2:     return "Level 1, Band 2";
+        case APVLevel1Band3:     return "Level 1, Band 3";
+        case APVLevel11Band0:     return "Level 1.1, Band 0";
+        case APVLevel11Band1:     return "Level 1.1, Band 1";
+        case APVLevel11Band2:     return "Level 1.1, Band 2";
+        case APVLevel11Band3:     return "Level 1.1, Band 3";
+        case APVLevel2Band0:     return "Level 2, Band 0";
+        case APVLevel2Band1:     return "Level 2, Band 1";
+        case APVLevel2Band2:     return "Level 2, Band 2";
+        case APVLevel2Band3:     return "Level 2, Band 3";
+        case APVLevel21Band0:     return "Level 2.1, Band 0";
+        case APVLevel21Band1:     return "Level 2.1, Band 1";
+        case APVLevel21Band2:     return "Level 2.1, Band 2";
+        case APVLevel21Band3:     return "Level 2.1, Band 3";
+        case APVLevel3Band0:     return "Level 3, Band 0";
+        case APVLevel3Band1:     return "Level 3, Band 1";
+        case APVLevel3Band2:     return "Level 3, Band 2";
+        case APVLevel3Band3:     return "Level 3, Band 3";
+        case APVLevel31Band0:     return "Level 3.1, Band 0";
+        case APVLevel31Band1:     return "Level 3.1, Band 1";
+        case APVLevel31Band2:     return "Level 3.1, Band 2";
+        case APVLevel31Band3:     return "Level 3.1, Band 3";
+        case APVLevel4Band0:     return "Level 4, Band 0";
+        case APVLevel4Band1:     return "Level 4, Band 1";
+        case APVLevel4Band2:     return "Level 4, Band 2";
+        case APVLevel4Band3:     return "Level 4, Band 3";
+        case APVLevel41Band0:     return "Level 4.1, Band 0";
+        case APVLevel41Band1:     return "Level 4.1, Band 1";
+        case APVLevel41Band2:     return "Level 4.1, Band 2";
+        case APVLevel41Band3:     return "Level 4.1, Band 3";
+        case APVLevel5Band0:     return "Level 5, Band 0";
+        case APVLevel5Band1:     return "Level 5, Band 1";
+        case APVLevel5Band2:     return "Level 5, Band 2";
+        case APVLevel5Band3:     return "Level 5, Band 3";
+        case APVLevel51Band0:     return "Level 5.1, Band 0";
+        case APVLevel51Band1:     return "Level 5.1, Band 1";
+        case APVLevel51Band2:     return "Level 5.1, Band 2";
+        case APVLevel51Band3:     return "Level 5.1, Band 3";
+        case APVLevel6Band0:     return "Level 6, Band 0";
+        case APVLevel6Band1:     return "Level 6, Band 1";
+        case APVLevel6Band2:     return "Level 6, Band 2";
+        case APVLevel6Band3:     return "Level 6, Band 3";
+        case APVLevel61Band0:     return "Level 6.1, Band 0";
+        case APVLevel61Band1:     return "Level 6.1, Band 1";
+        case APVLevel61Band2:     return "Level 6.1, Band 2";
+        case APVLevel61Band3:     return "Level 6.1, Band 3";
+        case APVLevel7Band0:     return "Level 7, Band 0";
+        case APVLevel7Band1:     return "Level 7, Band 1";
+        case APVLevel7Band2:     return "Level 7, Band 2";
+        case APVLevel7Band3:     return "Level 7, Band 3";
+        case APVLevel71Band0:     return "Level 7.1, Band 0";
+        case APVLevel71Band1:     return "Level 7.1, Band 1";
+        case APVLevel71Band2:     return "Level 7.1, Band 2";
+        case APVLevel71Band3:     return "Level 7.1, Band 3";
+        default:                return def;
+    }
+}
+
 inline constexpr int32_t BITRATE_MODE_CBR = 2;
 inline constexpr int32_t BITRATE_MODE_CBR_FD = 3;
 inline constexpr int32_t BITRATE_MODE_CQ = 0;
@@ -654,6 +787,7 @@
 inline constexpr int32_t COLOR_FormatYUV444Flexible          = 0x7F444888;
 inline constexpr int32_t COLOR_FormatYUV444Interleaved       = 29;
 inline constexpr int32_t COLOR_FormatYUVP010                 = 54;
+inline constexpr int32_t COLOR_FormatYUVP210                 = 60;
 inline constexpr int32_t COLOR_QCOM_FormatYUV420SemiPlanar   = 0x7fa30c00;
 inline constexpr int32_t COLOR_TI_FormatYUV420PackedSemiPlanar = 0x7f000100;
 
@@ -712,6 +846,7 @@
         case COLOR_FormatYUV444Flexible:            return "YUV444Flexible";
         case COLOR_FormatYUV444Interleaved:         return "YUV444Interleaved";
         case COLOR_FormatYUVP010:                   return "YUVP010";
+        case COLOR_FormatYUVP210:                   return "YUVP210";
         case COLOR_QCOM_FormatYUV420SemiPlanar:     return "QCOM_YUV420SemiPlanar";
         case COLOR_TI_FormatYUV420PackedSemiPlanar: return "TI_YUV420PackedSemiPlanar";
         default:                                    return def;
@@ -719,18 +854,24 @@
 }
 
 inline constexpr char FEATURE_AdaptivePlayback[]       = "adaptive-playback";
+inline constexpr char FEATURE_DynamicTimestamp[]       = "dynamic-timestamp";
 inline constexpr char FEATURE_EncodingStatistics[]     = "encoding-statistics";
+inline constexpr char FEATURE_FrameParsing[]           = "frame-parsing";
+inline constexpr char FEATURE_HdrEditing[]             = "hdr-editing";
 inline constexpr char FEATURE_IntraRefresh[] = "intra-refresh";
+inline constexpr char FEATURE_LowLatency[]             = "low-latency";
 inline constexpr char FEATURE_MultipleFrames[] = "multiple-frames";
 inline constexpr char FEATURE_PartialFrame[] = "partial-frame";
 inline constexpr char FEATURE_QpBounds[] = "qp-bounds";
 inline constexpr char FEATURE_SecurePlayback[]         = "secure-playback";
+inline constexpr char FEATURE_SpecialCodec[]           = "special-codec";
 inline constexpr char FEATURE_TunneledPlayback[]       = "tunneled-playback";
 
 // from MediaFormat.java
 inline constexpr char MIMETYPE_VIDEO_VP8[] = "video/x-vnd.on2.vp8";
 inline constexpr char MIMETYPE_VIDEO_VP9[] = "video/x-vnd.on2.vp9";
 inline constexpr char MIMETYPE_VIDEO_AV1[] = "video/av01";
+inline constexpr char MIMETYPE_VIDEO_APV[] = "video/apv";
 inline constexpr char MIMETYPE_VIDEO_AVC[] = "video/avc";
 inline constexpr char MIMETYPE_VIDEO_HEVC[] = "video/hevc";
 inline constexpr char MIMETYPE_VIDEO_MPEG4[] = "video/mp4v-es";
@@ -847,6 +988,7 @@
 inline constexpr char KEY_MAX_PTS_GAP_TO_ENCODER[] = "max-pts-gap-to-encoder";
 inline constexpr char KEY_MAX_WIDTH[] = "max-width";
 inline constexpr char KEY_MIME[] = "mime";
+inline constexpr char KEY_NUM_SLOTS[] = "num-slots";
 inline constexpr char KEY_OPERATING_RATE[] = "operating-rate";
 inline constexpr char KEY_OUTPUT_REORDER_DEPTH[] = "output-reorder-depth";
 inline constexpr char KEY_PCM_ENCODING[] = "pcm-encoding";
diff --git a/media/libstagefright/include/media/stagefright/MetaDataBase.h b/media/libstagefright/include/media/stagefright/MetaDataBase.h
index a7d2eb9..9dce55b 100644
--- a/media/libstagefright/include/media/stagefright/MetaDataBase.h
+++ b/media/libstagefright/include/media/stagefright/MetaDataBase.h
@@ -63,6 +63,7 @@
     kKeyDVVC              = 'dvvc',  // raw data
     kKeyDVWC              = 'dvwc',  // raw data
     kKeyAV1C              = 'av1c',  // raw data
+    kKeyAPVC              = 'apvc',  // raw data
     kKeyThumbnailHVCC     = 'thvc',  // raw data
     kKeyThumbnailAV1C     = 'tav1',  // raw data
     kKeyD263              = 'd263',  // raw data
diff --git a/media/libstagefright/include/media/stagefright/Utils.h b/media/libstagefright/include/media/stagefright/Utils.h
index 1673120..e190374 100644
--- a/media/libstagefright/include/media/stagefright/Utils.h
+++ b/media/libstagefright/include/media/stagefright/Utils.h
@@ -44,6 +44,9 @@
 // Convert a MIME type to a AudioSystem::audio_format
 status_t mapMimeToAudioFormat(audio_format_t& format, const char* mime);
 
+// Convert a pcm-encoding to a AudioSystem::audio_format
+audio_format_t audioFormatFromEncoding(int32_t pcmEncoding);
+
 // Convert a aac profile to a AudioSystem::audio_format
 void mapAACProfileToAudioFormat(audio_format_t& format, uint64_t eAacProfile);
 
diff --git a/media/module/aidlpersistentsurface/Android.bp b/media/module/aidlpersistentsurface/Android.bp
index 5c1a010..8b273f3 100644
--- a/media/module/aidlpersistentsurface/Android.bp
+++ b/media/module/aidlpersistentsurface/Android.bp
@@ -3,6 +3,9 @@
     unstable: true,
     local_include_dir: "aidl",
     min_sdk_version: "29",
+    defaults: [
+        "android.hardware.graphics.common-latest"
+    ],
     srcs: [
         "aidl/android/media/AidlColorAspects.aidl",
         "aidl/android/media/IAidlGraphicBufferSource.aidl",
@@ -12,9 +15,6 @@
     headers: [
         "HardwareBuffer_aidl",
     ],
-    imports: [
-        "android.hardware.graphics.common-V5",
-    ],
     include_dirs: [
         "frameworks/native/aidl/gui",
     ],
@@ -41,6 +41,9 @@
 cc_library_shared {
     name: "libstagefright_graphicbuffersource_aidl",
     min_sdk_version: "29",
+    defaults: [
+        "android.hardware.graphics.common-ndk_shared",
+    ],
     srcs: [
         "AidlGraphicBufferSource.cpp",
         "wrapper/WAidlGraphicBufferSource.cpp",
@@ -56,7 +59,6 @@
         "media_plugin_headers",
     ],
     shared_libs: [
-        "android.hardware.graphics.common-V5-ndk",
         "graphicbuffersource-aidl-ndk",
         "libbinder_ndk",
         "libcutils",
diff --git a/media/module/codecs/amrnb/common/Android.bp b/media/module/codecs/amrnb/common/Android.bp
index 0bc6ed2..35937cb 100644
--- a/media/module/codecs/amrnb/common/Android.bp
+++ b/media/module/codecs/amrnb/common/Android.bp
@@ -1,4 +1,5 @@
 package {
+    default_team: "trendy_team_media_codec_framework",
     default_applicable_licenses: [
         "frameworks_av_media_codecs_amrnb_common_license",
     ],
@@ -42,8 +43,8 @@
         "src/gains_tbl.cpp",
         "src/gc_pred.cpp",
         "src/gmed_n.cpp",
-        "src/grid_tbl.cpp",
         "src/gray_tbl.cpp",
+        "src/grid_tbl.cpp",
         "src/int_lpc.cpp",
         "src/inv_sqrt.cpp",
         "src/inv_sqrt_tbl.cpp",
@@ -91,9 +92,9 @@
     export_include_dirs: ["include"],
 
     cflags: [
-        "-DOSCL_UNUSED_ARG(x)=(void)(x)",
-        "-DOSCL_IMPORT_REF=",
         "-DOSCL_EXPORT_REF=",
+        "-DOSCL_IMPORT_REF=",
+        "-DOSCL_UNUSED_ARG(x)=(void)(x)",
 
         "-Werror",
     ],
diff --git a/media/module/codecs/amrnb/dec/Android.bp b/media/module/codecs/amrnb/dec/Android.bp
index 70741d2..a28500a 100644
--- a/media/module/codecs/amrnb/dec/Android.bp
+++ b/media/module/codecs/amrnb/dec/Android.bp
@@ -1,4 +1,5 @@
 package {
+    default_team: "trendy_team_media_codec_framework",
     default_applicable_licenses: [
         "frameworks_av_media_codecs_amrnb_dec_license",
     ],
@@ -47,12 +48,12 @@
         "src/b_cn_cod.cpp",
         "src/bgnscd.cpp",
         "src/c_g_aver.cpp",
-        "src/d1035pf.cpp",
-        "src/d2_11pf.cpp",
         "src/d2_9pf.cpp",
+        "src/d2_11pf.cpp",
         "src/d3_14pf.cpp",
         "src/d4_17pf.cpp",
         "src/d8_31pf.cpp",
+        "src/d1035pf.cpp",
         "src/d_gain_c.cpp",
         "src/d_gain_p.cpp",
         "src/d_plsf.cpp",
@@ -81,8 +82,8 @@
     export_include_dirs: ["src"],
 
     cflags: [
-        "-DOSCL_UNUSED_ARG(x)=(void)(x)",
         "-DOSCL_IMPORT_REF=",
+        "-DOSCL_UNUSED_ARG(x)=(void)(x)",
 
         "-Werror",
     ],
@@ -94,8 +95,8 @@
     //},
 
     shared_libs: [
-        "libstagefright_amrnb_common",
         "liblog",
+        "libstagefright_amrnb_common",
     ],
 
     target: {
@@ -113,19 +114,22 @@
 
     srcs: ["test/amrnbdec_test.cpp"],
 
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 
     local_include_dirs: ["src"],
 
     static_libs: [
-        "libstagefright_amrnbdec",
         "libsndfile",
+        "libstagefright_amrnbdec",
     ],
 
     shared_libs: [
-        "libstagefright_amrnb_common",
         "libaudioutils",
         "liblog",
+        "libstagefright_amrnb_common",
     ],
 
     target: {
diff --git a/media/module/codecs/amrnb/enc/Android.bp b/media/module/codecs/amrnb/enc/Android.bp
index 3c6566e..13bb29c 100644
--- a/media/module/codecs/amrnb/enc/Android.bp
+++ b/media/module/codecs/amrnb/enc/Android.bp
@@ -1,4 +1,5 @@
 package {
+    default_team: "trendy_team_media_codec_framework",
     default_applicable_licenses: [
         "frameworks_av_media_codecs_amrnb_enc_license",
     ],
@@ -42,12 +43,12 @@
     srcs: [
         "src/amrencode.cpp",
         "src/autocorr.cpp",
-        "src/c1035pf.cpp",
-        "src/c2_11pf.cpp",
         "src/c2_9pf.cpp",
+        "src/c2_11pf.cpp",
         "src/c3_14pf.cpp",
         "src/c4_17pf.cpp",
         "src/c8_31pf.cpp",
+        "src/c1035pf.cpp",
         "src/calc_cor.cpp",
         "src/calc_en.cpp",
         "src/cbsearch.cpp",
@@ -132,7 +133,10 @@
 
     srcs: ["test/amrnb_enc_test.cpp"],
 
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 
     local_include_dirs: ["src"],
 
diff --git a/media/module/codecs/amrnb/enc/fuzzer/Android.bp b/media/module/codecs/amrnb/enc/fuzzer/Android.bp
index bcbcee2..1b2ec87 100644
--- a/media/module/codecs/amrnb/enc/fuzzer/Android.bp
+++ b/media/module/codecs/amrnb/enc/fuzzer/Android.bp
@@ -19,6 +19,7 @@
  */
 
 package {
+    default_team: "trendy_team_media_codec_framework",
     // See: http://go/android-license-faq
     // A large-scale-change added 'default_applicable_licenses' to import
     // all of the 'license_kinds' from "frameworks_av_media_codecs_amrnb_enc_license"
@@ -39,8 +40,8 @@
 
     static_libs: [
         "liblog",
-        "libstagefright_amrnbenc",
         "libstagefright_amrnb_common",
+        "libstagefright_amrnbenc",
     ],
 
     fuzz_config: {
diff --git a/media/module/codecs/amrnb/fuzzer/Android.bp b/media/module/codecs/amrnb/fuzzer/Android.bp
index 3f29267..c5cbbe2 100644
--- a/media/module/codecs/amrnb/fuzzer/Android.bp
+++ b/media/module/codecs/amrnb/fuzzer/Android.bp
@@ -19,6 +19,7 @@
  */
 
 package {
+    default_team: "trendy_team_media_codec_framework",
     // See: http://go/android-license-faq
     // A large-scale-change added 'default_applicable_licenses' to import
     // all of the 'license_kinds' from "frameworks_av_license"
@@ -34,9 +35,9 @@
         "amrnb_dec_fuzzer.cpp",
     ],
     static_libs: [
-        "libstagefright_amrnbdec",
-        "libstagefright_amrnb_common",
         "liblog",
+        "libstagefright_amrnb_common",
+        "libstagefright_amrnbdec",
     ],
     target: {
         darwin: {
diff --git a/media/module/codecserviceregistrant/Android.bp b/media/module/codecserviceregistrant/Android.bp
index 56cd8b8..1d1dd71 100644
--- a/media/module/codecserviceregistrant/Android.bp
+++ b/media/module/codecserviceregistrant/Android.bp
@@ -43,6 +43,8 @@
         "libcodec2_soft_flacdec",
         "libcodec2_soft_flacenc",
         "libcodec2_soft_gsmdec",
+        "libcodec2_soft_apvenc",
+        "libcodec2_soft_apvdec",
     ],
 }
 
diff --git a/media/module/extractors/Android.bp b/media/module/extractors/Android.bp
index e29d3e6..cbaabe3 100644
--- a/media/module/extractors/Android.bp
+++ b/media/module/extractors/Android.bp
@@ -81,6 +81,12 @@
     srcs: ["extractor.aconfig"],
 }
 
+java_aconfig_library {
+    name: "android.media.extractor.flags-aconfig-java",
+    aconfig_declarations: "android.media.extractor.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
 cc_aconfig_library {
     name: "android.media.extractor.flags-aconfig-cc",
     aconfig_declarations: "android.media.extractor.flags-aconfig",
diff --git a/media/module/extractors/extractor.aconfig b/media/module/extractors/extractor.aconfig
index c9bf694..a7d3397 100644
--- a/media/module/extractors/extractor.aconfig
+++ b/media/module/extractors/extractor.aconfig
@@ -8,7 +8,16 @@
     name: "extractor_sniff_midi_optimizations"
     is_exported: true
     is_fixed_read_only: true
-    namespace: "media_extractor"
+    namespace: "media_solutions"
     description: "Enable SniffMidi optimizations."
     bug: "359920208"
 }
+
+flag {
+    name: "extractor_mp4_enable_apv"
+    is_exported: true
+    is_fixed_read_only: true
+    namespace: "media_solutions"
+    description: "Enable APV support in mp4 extractor."
+    bug: "370061501"
+}
diff --git a/media/module/extractors/fuzzers/Android.bp b/media/module/extractors/fuzzers/Android.bp
index 3da1589..f3da389 100644
--- a/media/module/extractors/fuzzers/Android.bp
+++ b/media/module/extractors/fuzzers/Android.bp
@@ -129,12 +129,18 @@
     ],
 
     static_libs: [
+        "android.media.extractor.flags-aconfig-cc",
+        "libaconfig_storage_read_api_cc",
         "libstagefright_id3",
         "libstagefright_esds",
         "libmp4extractor",
         "libstagefright_metadatautils",
     ],
 
+    shared_libs: [
+        "server_configurable_flags",
+    ],
+
     dictionary: "mp4_extractor_fuzzer.dict",
 
     corpus: ["corpus_mp4/*"],
diff --git a/media/module/extractors/mp4/Android.bp b/media/module/extractors/mp4/Android.bp
index 8072002..effd24a 100644
--- a/media/module/extractors/mp4/Android.bp
+++ b/media/module/extractors/mp4/Android.bp
@@ -42,12 +42,18 @@
     ],
 
     static_libs: [
+        "android.media.extractor.flags-aconfig-cc",
+        "libaconfig_storage_read_api_cc",
         "libstagefright_esds",
         "libstagefright_foundation",
         "libstagefright_id3",
         "libutils",
     ],
 
+    shared_libs: [
+        "server_configurable_flags",
+    ],
+
     host_supported: true,
 
     target: {
diff --git a/media/module/extractors/mp4/MPEG4Extractor.cpp b/media/module/extractors/mp4/MPEG4Extractor.cpp
index 12c0aaf..f062491 100644
--- a/media/module/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/module/extractors/mp4/MPEG4Extractor.cpp
@@ -33,6 +33,7 @@
 #include "SampleTable.h"
 #include "ItemTable.h"
 
+#include <com_android_media_extractor_flags.h>
 #include <media/esds/ESDS.h>
 #include <ID3.h>
 #include <media/stagefright/DataSourceBase.h>
@@ -147,6 +148,7 @@
 
     bool mIsAVC;
     bool mIsHEVC;
+    bool mIsAPV;
     bool mIsDolbyVision;
     bool mIsAC4;
     bool mIsMpegH = false;
@@ -366,6 +368,13 @@
         case FOURCC("hev1"):
             return MEDIA_MIMETYPE_VIDEO_HEVC;
 
+        case FOURCC("apv1"):
+            if (!com::android::media::extractor::flags::extractor_mp4_enable_apv()) {
+                ALOGV("APV support not enabled");
+                return "application/octet-stream";
+            }
+            return MEDIA_MIMETYPE_VIDEO_APV;
+
         case FOURCC("dvav"):
         case FOURCC("dva1"):
         case FOURCC("dvhe"):
@@ -2106,6 +2115,7 @@
         case FOURCC("dav1"):
         case FOURCC("av01"):
         case FOURCC("vp09"):
+        case FOURCC("apv1"):
         {
             uint8_t buffer[78];
             if (chunk_data_size < (ssize_t)sizeof(buffer)) {
@@ -2623,8 +2633,16 @@
             break;
         }
 
+        case FOURCC("apvC"):
         case FOURCC("av1C"):
         {
+            if (!com::android::media::extractor::flags::extractor_mp4_enable_apv() &&
+                chunk_type == FOURCC("apvC")) {
+                ALOGV("APV support not enabled");
+                *offset += chunk_size;
+                break;
+            }
+
             auto buffer = heapbuffer<uint8_t>(chunk_data_size);
 
             if (buffer.get() == NULL) {
@@ -5145,6 +5163,7 @@
       mCurrentSampleInfoOffsets(NULL),
       mIsAVC(false),
       mIsHEVC(false),
+      mIsAPV(false),
       mIsDolbyVision(false),
       mIsAC4(false),
       mIsPcm(false),
@@ -5187,6 +5206,8 @@
     mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
     mIsHEVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC) ||
               !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
+    mIsAPV = com::android::media::extractor::flags::extractor_mp4_enable_apv() &&
+             !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_APV);
     mIsAC4 = !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AC4);
     mIsDolbyVision = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION);
     mIsHeif = !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC) && mItemTable != NULL;
diff --git a/media/module/extractors/mpeg2/Android.bp b/media/module/extractors/mpeg2/Android.bp
index aa59a0c..63dbcda 100644
--- a/media/module/extractors/mpeg2/Android.bp
+++ b/media/module/extractors/mpeg2/Android.bp
@@ -44,7 +44,6 @@
 
     shared_libs: [
         "libbase",
-        "libcgrouprc#29",
     ],
 
     header_libs: [
diff --git a/media/module/extractors/tests/Android.bp b/media/module/extractors/tests/Android.bp
index d6e79c7..5f0f4fa 100644
--- a/media/module/extractors/tests/Android.bp
+++ b/media/module/extractors/tests/Android.bp
@@ -21,6 +21,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_av_license"],
+    default_team: "trendy_team_android_media_solutions_playback",
 }
 
 cc_test {
@@ -31,6 +32,8 @@
     srcs: ["ExtractorUnitTest.cpp"],
 
     static_libs: [
+        "android.media.extractor.flags-aconfig-cc",
+        "libaconfig_storage_read_api_cc",
         "libaacextractor",
         "libamrextractor",
         "libmp3extractor",
@@ -77,6 +80,7 @@
         "libhidlmemory",
         "libhidlbase",
         "libbase",
+        "server_configurable_flags",
     ],
 
     compile_multilib: "first",
diff --git a/media/module/foundation/MediaDefs.cpp b/media/module/foundation/MediaDefs.cpp
index 7abab63..a890696 100644
--- a/media/module/foundation/MediaDefs.cpp
+++ b/media/module/foundation/MediaDefs.cpp
@@ -25,6 +25,7 @@
 const char *MEDIA_MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
 const char *MEDIA_MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
 const char *MEDIA_MIMETYPE_VIDEO_AV1 = "video/av01";
+const char *MEDIA_MIMETYPE_VIDEO_APV = "video/apv";
 const char *MEDIA_MIMETYPE_VIDEO_AVC = "video/avc";
 const char *MEDIA_MIMETYPE_VIDEO_HEVC = "video/hevc";
 const char *MEDIA_MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es";
diff --git a/media/module/foundation/include/media/stagefright/foundation/AUtils.h b/media/module/foundation/include/media/stagefright/foundation/AUtils.h
index 3b646dc..eb605a7 100644
--- a/media/module/foundation/include/media/stagefright/foundation/AUtils.h
+++ b/media/module/foundation/include/media/stagefright/foundation/AUtils.h
@@ -92,4 +92,13 @@
     return (err < (period / 2)) ? err : (period - err);
 }
 
+inline static bool IsPowerOfTwo(int32_t value) {
+    return (value & (value - 1)) == 0;
+}
+
+/** Checks if the value is a power of two and not zero. */
+inline static bool IsPowerOfTwoStrict(int32_t value) {
+    return value != 0 && (value & (value - 1)) == 0;
+}
+
 #endif  // A_UTILS_H_
diff --git a/media/module/foundation/include/media/stagefright/foundation/MediaDefs.h b/media/module/foundation/include/media/stagefright/foundation/MediaDefs.h
index 05ee7fc..2b3f446 100644
--- a/media/module/foundation/include/media/stagefright/foundation/MediaDefs.h
+++ b/media/module/foundation/include/media/stagefright/foundation/MediaDefs.h
@@ -27,6 +27,7 @@
 extern const char *MEDIA_MIMETYPE_VIDEO_VP8;
 extern const char *MEDIA_MIMETYPE_VIDEO_VP9;
 extern const char *MEDIA_MIMETYPE_VIDEO_AV1;
+extern const char *MEDIA_MIMETYPE_VIDEO_APV;
 extern const char *MEDIA_MIMETYPE_VIDEO_AVC;
 extern const char *MEDIA_MIMETYPE_VIDEO_HEVC;
 extern const char *MEDIA_MIMETYPE_VIDEO_MPEG4;
diff --git a/media/module/libapexcodecs/Android.bp b/media/module/libapexcodecs/Android.bp
new file mode 100644
index 0000000..dbda81b
--- /dev/null
+++ b/media/module/libapexcodecs/Android.bp
@@ -0,0 +1,80 @@
+//
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+    default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_defaults {
+    name: "libapexcodecs-defaults",
+    header_libs: [
+        "libbase_headers",
+    ],
+
+    srcs: ["ApexCodecs.cpp"],
+
+    shared_libs: [
+        "libbase",
+        "libnativewindow",
+    ],
+
+    export_include_dirs: ["include"],
+
+    export_shared_lib_headers: [
+        "libbase",
+        "libnativewindow",
+    ],
+
+}
+
+cc_library_headers {
+    name: "libapexcodecs-header",
+    visibility: [
+        "//frameworks/av/apex:__subpackages__",
+        "//frameworks/av/media/codec2/hal/client",
+    ],
+    export_include_dirs: ["include"],
+}
+
+cc_library {
+    name: "libapexcodecs-testing",
+    defaults: ["libapexcodecs-defaults"],
+
+    visibility: [
+        ":__subpackages__",
+    ],
+}
+
+cc_library {
+    name: "libapexcodecs",
+    defaults: ["libapexcodecs-defaults"],
+
+    visibility: [
+        "//frameworks/av/apex:__subpackages__",
+        "//frameworks/av/media/codec2/hal/client",
+    ],
+
+    min_sdk_version: "apex_inherit",
+    version_script: "libapexcodecs.map.txt",
+    stubs: {
+        symbol_file: "libapexcodecs.map.txt",
+        versions: ["36"],
+    },
+
+    apex_available: [
+        "com.android.media.swcodec",
+    ],
+}
diff --git a/media/module/libapexcodecs/ApexCodecs.cpp b/media/module/libapexcodecs/ApexCodecs.cpp
new file mode 100644
index 0000000..7101677
--- /dev/null
+++ b/media/module/libapexcodecs/ApexCodecs.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <new>
+
+#include <android-base/no_destructor.h>
+#include <apex/ApexCodecs.h>
+
+// TODO: remove when we have real implementations
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
+
+struct ApexCodec_ComponentStore {
+    ApexCodec_ComponentStore() = default;
+};
+
+ApexCodec_ComponentStore *ApexCodec_GetComponentStore() {
+    ::android::base::NoDestructor<ApexCodec_ComponentStore> store;
+    return store.get();
+}
+
+ApexCodec_ComponentTraits *ApexCodec_Traits_get(
+        ApexCodec_ComponentStore *store, size_t index) {
+    return nullptr;
+}
+
+ApexCodec_Status ApexCodec_Component_create(
+        ApexCodec_ComponentStore *store, const char *name, ApexCodec_Component **comp) {
+    *comp = nullptr;
+    return APEXCODEC_STATUS_NOT_FOUND;
+}
+
+void ApexCodec_Component_destroy(ApexCodec_Component *comp) {}
+
+ApexCodec_Status ApexCodec_Component_start(ApexCodec_Component *comp) {
+    return APEXCODEC_STATUS_OMITTED;
+}
+
+ApexCodec_Status ApexCodec_Component_flush(ApexCodec_Component *comp) {
+    return APEXCODEC_STATUS_OMITTED;
+}
+
+ApexCodec_Status ApexCodec_Component_reset(ApexCodec_Component *comp) {
+    return APEXCODEC_STATUS_OMITTED;
+}
+
+ApexCodec_Configurable *ApexCodec_Component_getConfigurable(
+        ApexCodec_Component *comp) {
+    return nullptr;
+}
+
+ApexCodec_Status ApexCodec_SupportedValues_getTypeAndValues(
+        ApexCodec_SupportedValues *supportedValues,
+        ApexCodec_SupportedValuesType *type,
+        ApexCodec_SupportedValuesNumberType *numberType,
+        ApexCodec_Value **values,
+        uint32_t *numValues) {
+    return APEXCODEC_STATUS_OMITTED;
+}
+
+void ApexCodec_SupportedValues_release(ApexCodec_SupportedValues *values) {}
+
+ApexCodec_Status ApexCodec_SettingResults_getResultAtIndex(
+        ApexCodec_SettingResults *results,
+        size_t index,
+        ApexCodec_SettingResultFailure *failure,
+        ApexCodec_ParamFieldValues *field,
+        ApexCodec_ParamFieldValues **conflicts,
+        size_t *numConflicts) {
+    return APEXCODEC_STATUS_OMITTED;
+}
+
+void ApexCodec_SettingResults_release(ApexCodec_SettingResults *results) {}
+
+ApexCodec_Status ApexCodec_Component_process(
+        ApexCodec_Component *comp,
+        const ApexCodec_Buffer *input,
+        ApexCodec_Buffer *output,
+        size_t *consumed,
+        size_t *produced) {
+    return APEXCODEC_STATUS_OMITTED;
+}
+
+ApexCodec_Status ApexCodec_Configurable_config(
+        ApexCodec_Configurable *comp,
+        ApexCodec_LinearBuffer *config,
+        ApexCodec_SettingResults **results) {
+    return APEXCODEC_STATUS_OMITTED;
+}
+
+ApexCodec_Status ApexCodec_Configurable_query(
+        ApexCodec_Configurable *comp,
+        uint32_t indices[],
+        size_t numIndices,
+        ApexCodec_LinearBuffer *config,
+        size_t *written) {
+    return APEXCODEC_STATUS_OMITTED;
+}
+
+ApexCodec_Status ApexCodec_ParamDescriptors_getIndices(
+        ApexCodec_ParamDescriptors *descriptors,
+        uint32_t **indices,
+        size_t *numIndices) {
+    return APEXCODEC_STATUS_OMITTED;
+}
+
+ApexCodec_Status ApexCodec_ParamDescriptors_getDescriptor(
+        ApexCodec_ParamDescriptors *descriptors,
+        uint32_t index,
+        ApexCodec_ParamAttribute *attr,
+        const char **name,
+        uint32_t **dependencies,
+        size_t *numDependencies) {
+    return APEXCODEC_STATUS_OMITTED;
+}
+
+ApexCodec_Status ApexCodec_ParamDescriptors_release(
+        ApexCodec_ParamDescriptors *descriptors) {
+    return APEXCODEC_STATUS_OMITTED;
+}
+
+ApexCodec_Status ApexCodec_Configurable_querySupportedParams(
+        ApexCodec_Configurable *comp,
+        ApexCodec_ParamDescriptors **descriptors) {
+    return APEXCODEC_STATUS_OMITTED;
+}
+
+ApexCodec_Status ApexCodec_Configurable_querySupportedValues(
+        ApexCodec_Configurable *comp,
+        ApexCodec_SupportedValuesQuery *queries,
+        size_t numQueries) {
+    return APEXCODEC_STATUS_OMITTED;
+}
+
+#pragma clang diagnostic pop
\ No newline at end of file
diff --git a/media/module/libapexcodecs/include/apex/ApexCodecs.h b/media/module/libapexcodecs/include/apex/ApexCodecs.h
new file mode 100644
index 0000000..b9f2e83
--- /dev/null
+++ b/media/module/libapexcodecs/include/apex/ApexCodecs.h
@@ -0,0 +1,768 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/cdefs.h>
+#include <errno.h>
+#include <stdint.h>
+
+#include <android/api-level.h>
+#include <android/hardware_buffer.h>
+#include <android/versioning.h>
+
+__BEGIN_DECLS
+
+/**
+ * An API to access and operate codecs implemented within an APEX module,
+ * used only by the OS when using the codecs within a client process
+ * (instead of via a HAL).
+ *
+ * NOTE: Many of the constants and types mirror the ones in the Codec 2.0 API.
+ */
+
+/**
+ * Error code for ApexCodec APIs.
+ *
+ * Introduced in API 36.
+ */
+typedef enum ApexCodec_Status : int32_t {
+    APEXCODEC_STATUS_OK        = 0,
+
+    /* bad input */
+    APEXCODEC_STATUS_BAD_VALUE = EINVAL,
+    APEXCODEC_STATUS_BAD_INDEX = ENXIO,
+    APEXCODEC_STATUS_CANNOT_DO = ENOTSUP,
+
+    /* bad sequencing of events */
+    APEXCODEC_STATUS_DUPLICATE = EEXIST,
+    APEXCODEC_STATUS_NOT_FOUND = ENOENT,
+    APEXCODEC_STATUS_BAD_STATE = EPERM,
+    APEXCODEC_STATUS_BLOCKING  = EWOULDBLOCK,
+    APEXCODEC_STATUS_CANCELED  = EINTR,
+
+    /* bad environment */
+    APEXCODEC_STATUS_NO_MEMORY = ENOMEM,
+    APEXCODEC_STATUS_REFUSED   = EACCES,
+
+    APEXCODEC_STATUS_TIMED_OUT = ETIMEDOUT,
+
+    /* bad versioning */
+    APEXCODEC_STATUS_OMITTED   = ENOSYS,
+
+    /* unknown fatal */
+    APEXCODEC_STATUS_CORRUPTED = EFAULT,
+    APEXCODEC_STATUS_NO_INIT   = ENODEV,
+} ApexCodec_Status;
+
+/**
+ * Enum that represents the kind of component
+ *
+ * Introduced in API 36.
+ */
+typedef enum ApexCodec_Kind : uint32_t {
+    /**
+     * The component is of a kind that is not listed below.
+     */
+    APEXCODEC_KIND_OTHER = 0x0,
+    /**
+     * The component is a decoder, which decodes coded bitstream
+     * into raw buffers.
+     *
+     * Introduced in API 36.
+     */
+    APEXCODEC_KIND_DECODER = 0x1,
+    /**
+     * The component is an encoder, which encodes raw buffers
+     * into coded bitstream.
+     *
+     * Introduced in API 36.
+     */
+    APEXCODEC_KIND_ENCODER = 0x2,
+} ApexCodec_Kind;
+
+typedef enum ApexCodec_Domain : uint32_t {
+    /**
+     * A component domain that is not listed below.
+     *
+     * Introduced in API 36.
+     */
+    APEXCODEC_DOMAIN_OTHER = 0x0,
+    /**
+     * A component domain that operates on video.
+     *
+     * Introduced in API 36.
+     */
+    APEXCODEC_DOMAIN_VIDEO = 0x1,
+    /**
+     * A component domain that operates on audio.
+     *
+     * Introduced in API 36.
+     */
+    APEXCODEC_DOMAIN_AUDIO = 0x2,
+    /**
+     * A component domain that operates on image.
+     *
+     * Introduced in API 36.
+     */
+    APEXCODEC_DOMAIN_IMAGE = 0x3,
+} ApexCodec_Domain;
+
+/**
+ * Handle for component traits such as name, media type, kind (decoder/encoder),
+ * domain (audio/video/image), etc.
+ *
+ * Introduced in API 36.
+ */
+typedef struct ApexCodec_ComponentTraits {
+    /**
+     * The name of the component.
+     */
+    const char *name;
+    /**
+     * The supported media type of the component.
+     */
+    const char *mediaType;
+    /**
+     * The kind of the component.
+     */
+    ApexCodec_Kind kind;
+    /**
+     * The domain on which the component operates.
+     */
+    ApexCodec_Domain domain;
+} ApexCodec_ComponentTraits;
+
+/**
+ * An opaque struct that represents a component store.
+ *
+ * Introduced in API 36.
+ */
+typedef struct ApexCodec_ComponentStore ApexCodec_ComponentStore;
+
+/**
+ * Get the component store object. This function never fails.
+ *
+ * \return component store object.
+ */
+ApexCodec_ComponentStore *ApexCodec_GetComponentStore()
+        __INTRODUCED_IN(36);
+
+/**
+ * Get the traits object of a component at given index. ApexCodecs_Traits_*
+ * functions are used to extract information from the traits object.
+ *
+ * Returns nullptr if index is out of bounds. The returned object is owned by
+ * ApexCodec_ComponentStore object and the client should not delete it.
+ *
+ * The client can iterate through the traits objects by calling this function
+ * with an index incrementing from 0 until it gets a nullptr.
+ *
+ * \param index index of the traits object to query
+ * \return traits object at the index, or nullptr if the index is out of bounds.
+ */
+ApexCodec_ComponentTraits *ApexCodec_Traits_get(
+        ApexCodec_ComponentStore *store, size_t index) __INTRODUCED_IN(36);
+
+/**
+ * An opaque struct that represents a codec.
+ */
+typedef struct ApexCodec_Component ApexCodec_Component;
+
+/**
+ * Create a component by the name.
+ *
+ * \param store the component store
+ * \param name the name of the component
+ * \param component out-param to be filled with the component; must not be null
+ * \return  APEXCODEC_STATUS_OK         if successful
+ *          APEXCODEC_STATUS_NOT_FOUND  if the name is not found
+ */
+ApexCodec_Status ApexCodec_Component_create(
+        ApexCodec_ComponentStore *store, const char *name, ApexCodec_Component **comp)
+        __INTRODUCED_IN(36);
+
+/**
+ * Destroy the component by the handle. It is invalid to call component methods on the handle
+ * after calling this method. It is no-op to call this method with |comp| == nullptr.
+ *
+ * \param comp the handle for the component
+ */
+void ApexCodec_Component_destroy(ApexCodec_Component *comp) __INTRODUCED_IN(36);
+
+/**
+ * Start the component. The component is ready to process buffers after this call.
+ *
+ * \param comp the handle for the component
+ */
+ApexCodec_Status ApexCodec_Component_start(
+        ApexCodec_Component *comp) __INTRODUCED_IN(36);
+
+/**
+ * Flush the component's internal states. This operation preserves the existing configurations.
+ *
+ * \param comp the handle for the component
+ */
+ApexCodec_Status ApexCodec_Component_flush(
+        ApexCodec_Component *comp) __INTRODUCED_IN(36);
+
+/**
+ * Resets the component to the initial state, right after creation. Note that the configuration
+ * will also revert to the initial state, so if there are configurations required those should be
+ * set again to use the component.
+ *
+ * \param comp the handle for the component
+ */
+ApexCodec_Status ApexCodec_Component_reset(
+        ApexCodec_Component *comp) __INTRODUCED_IN(36);
+
+/**
+ * An opaque struct that represents a configurable part of the component.
+ *
+ * Introduced in API 36.
+ */
+typedef struct ApexCodec_Configurable ApexCodec_Configurable;
+
+/**
+ * Return the configurable object for the given ApexCodec_Component.
+ * The returned object has the same lifecycle as |comp|.
+ *
+ * \param comp the handle for the component
+ * \return the configurable object handle
+ */
+ApexCodec_Configurable *ApexCodec_Component_getConfigurable(
+        ApexCodec_Component *comp) __INTRODUCED_IN(36);
+
+/**
+ * Enum that represents the flags for ApexCodec_Buffer.
+ *
+ * Introduced in API 36.
+ */
+typedef enum ApexCodec_BufferFlags : uint32_t {
+    APEXCODEC_FLAG_DROP_FRAME    = (1 << 0),
+    APEXCODEC_FLAG_END_OF_STREAM = (1 << 1),
+    APEXCODEC_FLAG_DISCARD_FRAME = (1 << 2),
+    APEXCODEC_FLAG_INCOMPLETE    = (1 << 3),
+    APEXCODEC_FLAG_CORRECTED     = (1 << 4),
+    APEXCODEC_FLAG_CORRUPT       = (1 << 5),
+    APEXCODEC_FLAG_CODEC_CONFIG  = (1u << 31),
+} ApexCodec_BufferFlags;
+
+/**
+ * Enum that represents the type of buffer.
+ *
+ * Introduced in API 36.
+ */
+typedef enum ApexCodec_BufferType : uint32_t {
+    APEXCODEC_BUFFER_TYPE_INVALID,
+    APEXCODEC_BUFFER_TYPE_LINEAR,
+    APEXCODEC_BUFFER_TYPE_LINEAR_CHUNKS,
+    APEXCODEC_BUFFER_TYPE_GRAPHIC,
+    APEXCODEC_BUFFER_TYPE_GRAPHIC_CHUNKS,
+} ApexCodec_BufferType;
+
+/**
+ * Struct that represents the memory for ApexCodec_Buffer.
+ *
+ * All memory regions have the simple 1D representation.
+ *
+ * Introduced in API 36.
+ */
+typedef struct ApexCodec_LinearBuffer {
+    /**
+     * A pointer to the start of the buffer. This is not aligned.
+     */
+    uint8_t *data;
+    /**
+     * Size of the buffer. The memory region between |data| (inclusive) and
+     * |data + size| (exclusive) is assumed to be valid for read/write.
+     */
+    size_t size;
+} ApexCodec_LinearBuffer;
+
+/**
+ * Struct that represents a buffer for ApexCodec_Component.
+ *
+ * Introduced in API 36.
+ */
+typedef struct ApexCodec_Buffer {
+    /**
+     * Flags associated with the buffer.
+     */
+    ApexCodec_BufferFlags flags;
+    /**
+     * For input buffers client assign a unique sequential index for each buffer. For output buffers
+     * it is the same as the associated input buffer's frame index.
+     */
+    uint64_t frameIndex;
+    /**
+     * A timestamp associated with the buffer in microseconds.
+     */
+    uint64_t timestampUs;
+    /**
+     * The type of the buffer. The component may reject request to process a buffer with the wrong
+     * type. For example, a video decoder will reject an input buffer with type BUFFER_TYPE_GRAPHIC,
+     * or an output buffer with type BUFFER_TYPE_LINEAR.
+     */
+    ApexCodec_BufferType type;
+    /**
+     * The actual memory for the buffer.
+     */
+    union {
+        ApexCodec_LinearBuffer linear;
+        AHardwareBuffer *graphic;
+    } memory;
+    /**
+     * Config updates associated with the buffer. For input buffers these are sent to the component
+     * at the specific input frame. For output buffers these are config updates as a result of
+     * processing the buffer.
+     */
+    ApexCodec_LinearBuffer configUpdates;
+} ApexCodec_Buffer;
+
+/**
+ * Enum that represents the query type for the supported values.
+ *
+ * Introduced in API 36.
+ */
+typedef enum ApexCodec_SupportedValuesQueryType : uint32_t {
+    /** Query all possible supported values regardless of current configuration */
+    APEXCODEC_SUPPORTED_VALUES_QUERY_POSSIBLE,
+    /** Query supported values at current configuration */
+    APEXCODEC_SUPPORTED_VALUES_QUERY_CURRENT,
+} ApexCodec_SupportedValuesQueryType;
+
+/**
+ * Enum that represents the type of the supported values.
+ *
+ * Introduced in API 36.
+ */
+typedef enum ApexCodec_SupportedValuesType : uint32_t {
+    /** The supported values are empty. */
+    APEXCODEC_SUPPORTED_VALUES_EMPTY,
+    /**
+     * The supported values are represented by a range defined with {min, max, step, num, den}.
+     *
+     * If step is 0 and num and denom are both 1, the supported values are any value, for which
+     * min <= value <= max.
+     *
+     * Otherwise, the range represents a geometric/arithmetic/multiply-accumulate series, where
+     * successive supported values can be derived from previous values (starting at min), using the
+     * following formula:
+     *  v[0] = min
+     *  v[i] = v[i-1] * num / denom + step for i >= 1, while min < v[i] <= max.
+     */
+    APEXCODEC_SUPPORTED_VALUES_RANGE,
+    /** The supported values are represented by a list of values. */
+    APEXCODEC_SUPPORTED_VALUES_VALUES,
+    /** The supported values are represented by a list of flags. */
+    APEXCODEC_SUPPORTED_VALUES_FLAGS,
+} ApexCodec_SupportedValuesType;
+
+/**
+ * Enum that represents numeric types of the supported values.
+ *
+ * Introduced in API 36.
+ */
+typedef enum ApexCodec_SupportedValuesNumberType : uint32_t {
+    APEXCODEC_SUPPORTED_VALUES_TYPE_NONE   = 0,
+    APEXCODEC_SUPPORTED_VALUES_TYPE_INT32  = 1,
+    APEXCODEC_SUPPORTED_VALUES_TYPE_UINT32 = 2,
+    // RESERVED                            = 3,
+    APEXCODEC_SUPPORTED_VALUES_TYPE_INT64  = 4,
+    APEXCODEC_SUPPORTED_VALUES_TYPE_UINT64 = 5,
+    // RESERVED                            = 6,
+    APEXCODEC_SUPPORTED_VALUES_TYPE_FLOAT  = 7,
+} ApexCodec_SupportedValuesNumberType;
+
+/**
+ * Union of primitive types.
+ *
+ * Introduced in API 36.
+ */
+typedef union {
+    int32_t i32;
+    uint32_t u32;
+    int64_t i64;
+    uint64_t u64;
+    float f;
+} ApexCodec_Value;
+
+/**
+ * An opaque struct that represents the supported values of a parameter.
+ *
+ * Introduced in API 36.
+ */
+typedef struct ApexCodec_SupportedValues ApexCodec_SupportedValues;
+
+/**
+ * Extract information from ApexCodec_SupportedValues object.
+ *
+ * \param [in] supportedValues the supported values object
+ * \param [out] type        pointer to be filled with the type of the supported values
+ * \param [out] numberType  pointer to be filled with the numeric type of the supported values
+ * \param [out] values      pointer to be filled with the array of the actual supported values.
+ *                          if type == APEXCODEC_SUPPORTED_VALUES_EMPTY: nullptr
+ *                          if type == APEXCODEC_SUPPORTED_VALUES_RANGE: {min, max, step, num, den}
+ *                          if type == APEXCODEC_SUPPORTED_VALUES_VALUES/_FLAGS:
+ *                              the array of supported values/flags
+ *                          the array is owned by the |supportedValues| object and the client
+ *                          should not free it.
+ * \param [out] numValues   pointer to be filled with the number of values.
+ *                          if type == APEXCODEC_SUPPORTED_VALUES_EMPTY: 0
+ *                          if type == APEXCODEC_SUPPORTED_VALUES_RANGE: 5
+ *                          if type == APEXCODEC_SUPPORTED_VALUES_VALUES/_FLAGS: varies
+ */
+ApexCodec_Status ApexCodec_SupportedValues_getTypeAndValues(
+        ApexCodec_SupportedValues *supportedValues,
+        ApexCodec_SupportedValuesType *type,
+        ApexCodec_SupportedValuesNumberType *numberType,
+        ApexCodec_Value **values,
+        uint32_t *numValues) __INTRODUCED_IN(36);
+
+/**
+ * Release the supported values object.
+ *
+ * \param values the supported values object
+ */
+void ApexCodec_SupportedValues_release(
+        ApexCodec_SupportedValues *values) __INTRODUCED_IN(36);
+
+/**
+ * Struct that represents the result of ApexCodec_Configurable_config.
+ *
+ * Introduced in API 36.
+ */
+typedef struct ApexCodec_SettingResults ApexCodec_SettingResults;
+
+/**
+ * Enum that represents the failure code of ApexCodec_SettingResults.
+ *
+ * Introduced in API 36.
+ */
+typedef enum ApexCodec_SettingResultFailure : uint32_t {
+    /** parameter type is not supported */
+    APEXCODEC_SETTING_RESULT_BAD_TYPE,
+    /** parameter is not supported on the specific port */
+    APEXCODEC_SETTING_RESULT_BAD_PORT,
+    /** parameter is not supported on the specific stream */
+    APEXCODEC_SETTING_RESULT_BAD_INDEX,
+    /** parameter is read-only */
+    APEXCODEC_SETTING_RESULT_READ_ONLY,
+    /** parameter mismatches input data */
+    APEXCODEC_SETTING_RESULT_MISMATCH,
+    /** strict parameter does not accept value for the field at all */
+    APEXCODEC_SETTING_RESULT_BAD_VALUE,
+    /** strict parameter field value conflicts with another settings */
+    APEXCODEC_SETTING_RESULT_CONFLICT,
+    /** strict parameter field is out of range due to other settings */
+    APEXCODEC_SETTING_RESULT_UNSUPPORTED,
+    /**
+     * field does not accept the requested parameter value at all. It has been corrected to
+     * the closest supported value. This failure mode is provided to give guidance as to what
+     * are the currently supported values for this field (which may be a subset of the at-all-
+     * potential values)
+     */
+    APEXCODEC_SETTING_RESULT_INFO_BAD_VALUE,
+    /**
+     * requested parameter value is in conflict with an/other setting(s)
+     * and has been corrected to the closest supported value. This failure
+     * mode is given to provide guidance as to what are the currently supported values as well
+     * as to optionally provide suggestion to the client as to how to enable the requested
+     * parameter value.
+     */
+    APEXCODEC_SETTING_RESULT_INFO_CONFLICT,
+} ApexCodec_SettingResultFailure;
+
+/**
+ * Struct that represents a field and its supported values of a parameter.
+ *
+ * The offset and size of the field are where the field is located in the blob representation of
+ * the parameter, as used in the ApexCodec_Configurable_query() and ApexCodec_Configurable_config(),
+ * for example.
+ *
+ * Introduced in API 36.
+ */
+typedef struct ApexCodec_ParamFieldValues {
+    /** index of the param */
+    uint32_t index;
+    /** offset of the param field */
+    uint32_t offset;
+    /** size of the param field */
+    uint32_t size;
+    /** currently supported values of the param field */
+    ApexCodec_SupportedValues *values;
+} ApexCodec_ParamFieldValues;
+
+/**
+ * Extract the result of ApexCodec_Configurable_config.
+ * The client can iterate through the results with index starting from 0 until this function returns
+ * APEXCODEC_STATUS_NOT_FOUND.
+ *
+ * \param [in]  result  the result object
+ * \param [in]  index   the index of the result to extract, starts from 0.
+ * \param [out] failure pointer to be filled with the failure code
+ * \param [out] field   pointer to be filled with the field that failed.
+ *                      |field->value| is owned by the |result| object and the client should not
+ *                      free it.
+ * \param [out] conflicts   pointer to be filled with the array of conflicts.
+ *                          nullptr if |numConflicts| is 0.
+ *                          the array and its content is owned by the |result| object and the client
+ *                          should not free it.
+ * \param [out] numConflicts pointer to be filled with the number of conflicts
+ *                          may be 0 if there are no conflicts
+ * \return APEXCODEC_STATUS_OK         if successful
+ * \return APEXCODEC_STATUS_NOT_FOUND  if index is out of range
+ */
+ApexCodec_Status ApexCodec_SettingResults_getResultAtIndex(
+        ApexCodec_SettingResults *results,
+        size_t index,
+        ApexCodec_SettingResultFailure *failure,
+        ApexCodec_ParamFieldValues *field,
+        ApexCodec_ParamFieldValues **conflicts,
+        size_t *numConflicts) __INTRODUCED_IN(36);
+
+/**
+ * Release the setting result object.
+ *
+ * \param result the setting result object
+ */
+void ApexCodec_SettingResults_release(
+        ApexCodec_SettingResults *results) __INTRODUCED_IN(36);
+
+/**
+ * Process one frame from |input|, and produce one frame to |output| if possible.
+ * When successfully filled, |output->memory.linear| has the size adjusted to the produced
+ * output size, in case of linear buffers. |input->configUpdates| is applied with the input
+ * buffer; |output->configUpdates| contains config updates as a result of processing the frame.
+ *
+ * \param comp      the component to process the buffers
+ * \param input     the input buffer; when nullptr, the component should fill |output| if there are
+ *                  any pending output buffers.
+ * \param output    the output buffer, should not be nullptr.
+ * \param consumed  the number of consumed bytes from the input buffer
+ *                  set to 0 if no input buffer has been consumed, including |input| is nullptr.
+ *                  for graphic buffers, any non-zero value means that the input buffer is consumed.
+ * \param produced  the number of bytes produced on the output buffer
+ *                  set to 0 if no output buffer has been produced.
+ *                  for graphic buffers, any non-zero value means that the output buffer is filled.
+ * \return APEXCODEC_STATUS_OK         if successful
+ * \return APEXCODEC_STATUS_NO_MEMORY  if the output buffer is not suitable to hold the output frame
+ *                                     the client should retry with a new output buffer;
+ *                                     configUpdates should have the information to update
+ *                                     the buffer size.
+ * \return APEXCODEC_STATUS_BAD_VALUE  if the parameters are bad
+ * \return APEXCODEC_STATUS_BAD_STATE  if the component is not in the right state
+ *                                     to process the frame
+ * \return APEXCODEC_STATUS_CORRUPTED  if unexpected error has occurred
+ */
+ApexCodec_Status ApexCodec_Component_process(
+        ApexCodec_Component *comp,
+        const ApexCodec_Buffer *input,
+        ApexCodec_Buffer *output,
+        size_t *consumed,
+        size_t *produced) __INTRODUCED_IN(36);
+
+/**
+ * Configure the component with the given config.
+ *
+ * Configurations are Codec 2.0 configs in binary blobs,
+ * concatenated if there are multiple configs.
+ *
+ * frameworks/av/media/codec2/core/include/C2Param.h contains more details about the configuration
+ * blob layout.
+ *
+ * The component may correct the configured parameters to the closest supported values, and could
+ * fail in case there are no values that the component can auto-correct to. |result| contains the
+ * information about the failures. See ApexCodec_SettingResultFailure and ApexCodec_SettingResults
+ * for more details.
+ *
+ * \param [in]    comp   the handle for the component
+ * \param [inout] config the config blob; after the call, the config blob is updated to the actual
+ *                       config by the component.
+ * \param [out]   result the result of the configuration.
+ *                       the client should call ApexCodec_SettingResult_getResultAtIndex()
+ *                       to extract the result. The result object is owned by the client and should
+ *                       be released with ApexCodec_SettingResult_release().
+ *                       |result| may be nullptr if empty.
+ * \return APEXCODEC_STATUS_OK         if successful
+ * \return APEXCODEC_STATUS_BAD_VALUE  if the config is invalid
+ * \return APEXCODEC_STATUS_BAD_STATE  if the component is not in the right state to be configured
+ * \return APEXCODEC_STATUS_CORRUPTED  if unexpected error has occurred
+ */
+ApexCodec_Status ApexCodec_Configurable_config(
+        ApexCodec_Configurable *comp,
+        ApexCodec_LinearBuffer *config,
+        ApexCodec_SettingResults **results) __INTRODUCED_IN(36);
+
+/**
+ * Query the component for the given indices.
+ *
+ * Parameter indices are defined in frameworks/av/media/codec2/core/include/C2Config.h.
+ *
+ * \param [in] comp         the handle for the component
+ * \param [in] indices      the array of indices to query
+ * \param [in] numIndices   the size of the indices array
+ * \param [inout] config    the output buffer for the config blob, allocated by the client.
+ *                          if the |config->size| was insufficient, it is set to the required size
+ *                          and |config->data| remains unchanged.
+ * \param [out] written     the number of bytes written to |config|.
+ * \return APEXCODEC_STATUS_OK          if successful
+ * \return APEXCODEC_STATUS_NO_MEMORY   if |config.size| is too small; |config.size| is updated to the
+ *                                      requested buffer size.
+ * \return APEXCODEC_STATUS_BAD_VALUE   if the parameters are bad. e.g. |indices|, |config|,
+ *                                      |config->data| or |written| is nullptr.
+ */
+ApexCodec_Status ApexCodec_Configurable_query(
+        ApexCodec_Configurable *comp,
+        uint32_t indices[],
+        size_t numIndices,
+        ApexCodec_LinearBuffer *config,
+        size_t *written) __INTRODUCED_IN(36);
+
+/**
+ * Struct that represents a parameter descriptor.
+ *
+ * Introduced in API 36.
+ */
+typedef struct ApexCodec_ParamDescriptors ApexCodec_ParamDescriptors;
+
+/**
+ * Enum that represents the attributes of a parameter.
+ *
+ * Introduced in API 36.
+ */
+typedef enum ApexCodec_ParamAttribute : uint32_t {
+    /** parameter is required to be specified */
+    APEXCODEC_PARAM_IS_REQUIRED   = 1u << 0,
+    /** parameter retains its value */
+    APEXCODEC_PARAM_IS_PERSISTENT = 1u << 1,
+    /** parameter is strict */
+    APEXCODEC_PARAM_IS_STRICT     = 1u << 2,
+    /** parameter is read-only */
+    APEXCODEC_PARAM_IS_READ_ONLY  = 1u << 3,
+    /** parameter shall not be visible to clients */
+    APEXCODEC_PARAM_IS_HIDDEN     = 1u << 4,
+    /** parameter shall not be used by framework (other than testing) */
+    APEXCODEC_PARAM_IS_INTERNAL   = 1u << 5,
+    /** parameter is publicly const (hence read-only) */
+    APEXCODEC_PARAM_IS_CONST      = 1u << 6 | APEXCODEC_PARAM_IS_READ_ONLY,
+} ApexCodec_ParamAttribute;
+
+/**
+ * Get the parameter indices of the param descriptors.
+ *
+ * \param [in] descriptors the param descriptors object
+ * \param [out] indices the pointer to be filled with the array of the indices;
+ *                      the array is owned by |descriptors| and should not be freed by the client.
+ * \param [out] numIndices the size of the indices array
+ * \return APEXCODEC_STATUS_OK          if successful
+ * \return APEXCODEC_STATUS_BAD_VALUE   if parameters are bad. e.g. |descriptors|, |indices| or
+ *                                  |numIndices| is nullptr.
+ */
+ApexCodec_Status ApexCodec_ParamDescriptors_getIndices(
+        ApexCodec_ParamDescriptors *descriptors,
+        uint32_t **indices,
+        size_t *numIndices) __INTRODUCED_IN(36);
+
+/**
+ * Get the descriptor of the param.
+ *
+ * \param [in] descriptors the param descriptors object
+ * \param [in] index the index of the param
+ * \param [out] attr the attribute of the param
+ * \param [out] name    the pointer to be filled with the name of the param
+ *                      the string is owned by |descriptors| and should not be freed by the client.
+ * \param [out] dependencies the pointer to be filled with an array of the parameter indices
+ *                        that the parameter with |index| depends on.
+ *                        may be null if empty.
+ *                        the array is owned by |descriptors| and should not be freed by the client.
+ * \param [out] numDependencies the number of dependencies
+ * \return APEXCODEC_STATUS_OK          if successful
+ * \return APEXCODEC_STATUS_BAD_VALUE   if parameters are bad. e.g. |descriptors|, |attr|, |name|,
+ *                                  |dependencies| or |numDependencies| is nullptr.
+ * \return APEXCODEC_STATUS_BAD_INDEX   if the index is not included in the param descriptors.
+ */
+ApexCodec_Status ApexCodec_ParamDescriptors_getDescriptor(
+        ApexCodec_ParamDescriptors *descriptors,
+        uint32_t index,
+        ApexCodec_ParamAttribute *attr,
+        const char **name,
+        uint32_t **dependencies,
+        size_t *numDependencies) __INTRODUCED_IN(36);
+
+/**
+ * Release the param descriptors object.
+ *
+ * \param descriptors the param descriptors object
+ */
+ApexCodec_Status ApexCodec_ParamDescriptors_release(
+        ApexCodec_ParamDescriptors *descriptors) __INTRODUCED_IN(36);
+
+/**
+ * Query the component for the supported parameters.
+ *
+ * \param comp the handle for the component
+ * \param descriptors   the pointer to be filled with the param descriptors object
+ *                      the object should be released with ApexCodec_ParamDescriptors_release().
+ * \return APEXCODEC_STATUS_OK          if successful
+ * \return APEXCODEC_STATUS_BAD_VALUE   if parameters are bad. e.g. |descriptors| is nullptr.
+ */
+ApexCodec_Status ApexCodec_Configurable_querySupportedParams(
+        ApexCodec_Configurable *comp,
+        ApexCodec_ParamDescriptors **descriptors) __INTRODUCED_IN(36);
+
+/**
+ * Struct that represents the query for the supported values of a parameter.
+ *
+ * The offset of the field can be found in the layout of the parameter blob.
+ *
+ * Introduced in API 36.
+ */
+typedef struct ApexCodec_SupportedValuesQuery {
+    /* in-params */
+
+    /** index of the param */
+    uint32_t index;
+    /** offset to the param field */
+    size_t offset;
+    /** query type */
+    ApexCodec_SupportedValuesQueryType type;
+
+    /* out-params */
+
+    /** status of the query */
+    ApexCodec_Status status;
+
+    /** supported values. must be released with ApexCodec_SupportedValues_release(). */
+    ApexCodec_SupportedValues *values;
+} ApexCodec_SupportedValuesQuery;
+
+/**
+ * Query the component for the supported values of the given indices.
+ *
+ * \param comp the handle for the component
+ * \param queries the array of queries
+ * \param numQueries the size of the queries array
+ * \return  APEXCODEC_STATUS_OK         if successful
+ *          APEXCODEC_STATUS_CORRUPTED  if unexpected error has occurred
+ */
+ApexCodec_Status ApexCodec_Configurable_querySupportedValues(
+        ApexCodec_Configurable *comp,
+        ApexCodec_SupportedValuesQuery *queries,
+        size_t numQueries) __INTRODUCED_IN(36);
+
+__END_DECLS
\ No newline at end of file
diff --git a/media/module/libapexcodecs/libapexcodecs.map.txt b/media/module/libapexcodecs/libapexcodecs.map.txt
new file mode 100644
index 0000000..672cf89
--- /dev/null
+++ b/media/module/libapexcodecs/libapexcodecs.map.txt
@@ -0,0 +1,26 @@
+LIBAPEXCODECS_36 { # introduced=36
+  global:
+    ApexCodec_Component_create; # apex
+    ApexCodec_Component_destroy; # apex
+    ApexCodec_Component_flush; # apex
+    ApexCodec_Component_getConfigurable; # apex
+    ApexCodec_Component_process; # apex
+    ApexCodec_Component_start; # apex
+    ApexCodec_Component_reset; # apex
+    ApexCodec_Configurable_config; # apex
+    ApexCodec_Configurable_query; # apex
+    ApexCodec_Configurable_querySupportedParams; # apex
+    ApexCodec_Configurable_querySupportedValues; # apex
+    ApexCodec_GetComponentStore; # apex
+    ApexCodec_ParamDescriptors_getDescriptor; # apex
+    ApexCodec_ParamDescriptors_getIndices; # apex
+    ApexCodec_ParamDescriptors_release; # apex
+    ApexCodec_SettingResults_getResultAtIndex; # apex
+    ApexCodec_SettingResults_release; # apex
+    ApexCodec_SupportedValues_getTypeAndValues; # apex
+    ApexCodec_SupportedValues_release; # apex
+    ApexCodec_Traits_get; # apex
+
+  local:
+    *;
+};
\ No newline at end of file
diff --git a/media/module/libapexcodecs/tests/Android.bp b/media/module/libapexcodecs/tests/Android.bp
new file mode 100644
index 0000000..162d12c
--- /dev/null
+++ b/media/module/libapexcodecs/tests/Android.bp
@@ -0,0 +1,30 @@
+//
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+    default_team: "trendy_team_android_media_codec_framework",
+    default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_test {
+    name: "libapexcodecs_tests",
+    shared_libs: [
+        "libapexcodecs-testing",
+        "libcodec2",
+    ],
+
+    srcs: ["ApexCodecsTest.cpp"],
+}
diff --git a/media/module/libapexcodecs/tests/ApexCodecsTest.cpp b/media/module/libapexcodecs/tests/ApexCodecsTest.cpp
new file mode 100644
index 0000000..cd5ebba
--- /dev/null
+++ b/media/module/libapexcodecs/tests/ApexCodecsTest.cpp
@@ -0,0 +1,100 @@
+#include <C2.h>
+#include <C2Component.h>
+
+#include <apex/ApexCodecs.h>
+
+// static_asserts for enum values match
+static_assert((uint32_t)APEXCODEC_STATUS_OK        == (uint32_t)C2_OK);
+static_assert((uint32_t)APEXCODEC_STATUS_BAD_VALUE == (uint32_t)C2_BAD_VALUE);
+static_assert((uint32_t)APEXCODEC_STATUS_BAD_INDEX == (uint32_t)C2_BAD_INDEX);
+static_assert((uint32_t)APEXCODEC_STATUS_CANNOT_DO == (uint32_t)C2_CANNOT_DO);
+static_assert((uint32_t)APEXCODEC_STATUS_DUPLICATE == (uint32_t)C2_DUPLICATE);
+static_assert((uint32_t)APEXCODEC_STATUS_NOT_FOUND == (uint32_t)C2_NOT_FOUND);
+static_assert((uint32_t)APEXCODEC_STATUS_BAD_STATE == (uint32_t)C2_BAD_STATE);
+static_assert((uint32_t)APEXCODEC_STATUS_BLOCKING  == (uint32_t)C2_BLOCKING);
+static_assert((uint32_t)APEXCODEC_STATUS_CANCELED  == (uint32_t)C2_CANCELED);
+static_assert((uint32_t)APEXCODEC_STATUS_NO_MEMORY == (uint32_t)C2_NO_MEMORY);
+static_assert((uint32_t)APEXCODEC_STATUS_REFUSED   == (uint32_t)C2_REFUSED);
+static_assert((uint32_t)APEXCODEC_STATUS_TIMED_OUT == (uint32_t)C2_TIMED_OUT);
+static_assert((uint32_t)APEXCODEC_STATUS_OMITTED   == (uint32_t)C2_OMITTED);
+static_assert((uint32_t)APEXCODEC_STATUS_CORRUPTED == (uint32_t)C2_CORRUPTED);
+static_assert((uint32_t)APEXCODEC_STATUS_NO_INIT   == (uint32_t)C2_NO_INIT);
+
+static_assert((uint32_t)APEXCODEC_KIND_OTHER   == (uint32_t)C2Component::KIND_OTHER);
+static_assert((uint32_t)APEXCODEC_KIND_DECODER == (uint32_t)C2Component::KIND_DECODER);
+static_assert((uint32_t)APEXCODEC_KIND_ENCODER == (uint32_t)C2Component::KIND_ENCODER);
+
+static_assert((uint32_t)APEXCODEC_DOMAIN_OTHER == (uint32_t)C2Component::DOMAIN_OTHER);
+static_assert((uint32_t)APEXCODEC_DOMAIN_VIDEO == (uint32_t)C2Component::DOMAIN_VIDEO);
+static_assert((uint32_t)APEXCODEC_DOMAIN_AUDIO == (uint32_t)C2Component::DOMAIN_AUDIO);
+static_assert((uint32_t)APEXCODEC_DOMAIN_IMAGE == (uint32_t)C2Component::DOMAIN_IMAGE);
+
+static_assert((uint32_t)APEXCODEC_FLAG_DROP_FRAME    == (uint32_t)C2FrameData::FLAG_DROP_FRAME);
+static_assert((uint32_t)APEXCODEC_FLAG_END_OF_STREAM == (uint32_t)C2FrameData::FLAG_END_OF_STREAM);
+static_assert((uint32_t)APEXCODEC_FLAG_DISCARD_FRAME == (uint32_t)C2FrameData::FLAG_DISCARD_FRAME);
+static_assert((uint32_t)APEXCODEC_FLAG_INCOMPLETE    == (uint32_t)C2FrameData::FLAG_INCOMPLETE);
+static_assert((uint32_t)APEXCODEC_FLAG_CORRECTED     == (uint32_t)C2FrameData::FLAG_CORRECTED);
+static_assert((uint32_t)APEXCODEC_FLAG_CORRUPT       == (uint32_t)C2FrameData::FLAG_CORRUPT);
+static_assert((uint32_t)APEXCODEC_FLAG_CODEC_CONFIG  == (uint32_t)C2FrameData::FLAG_CODEC_CONFIG);
+
+static_assert((uint32_t)APEXCODEC_BUFFER_TYPE_INVALID        ==
+              (uint32_t)C2BufferData::INVALID);
+static_assert((uint32_t)APEXCODEC_BUFFER_TYPE_LINEAR         ==
+              (uint32_t)C2BufferData::LINEAR);
+static_assert((uint32_t)APEXCODEC_BUFFER_TYPE_LINEAR_CHUNKS  ==
+              (uint32_t)C2BufferData::LINEAR_CHUNKS);
+static_assert((uint32_t)APEXCODEC_BUFFER_TYPE_GRAPHIC        ==
+              (uint32_t)C2BufferData::GRAPHIC);
+static_assert((uint32_t)APEXCODEC_BUFFER_TYPE_GRAPHIC_CHUNKS ==
+              (uint32_t)C2BufferData::GRAPHIC_CHUNKS);
+
+static_assert((uint32_t)APEXCODEC_SUPPORTED_VALUES_QUERY_CURRENT  ==
+              (uint32_t)C2FieldSupportedValuesQuery::CURRENT);
+static_assert((uint32_t)APEXCODEC_SUPPORTED_VALUES_QUERY_POSSIBLE ==
+              (uint32_t)C2FieldSupportedValuesQuery::POSSIBLE);
+
+static_assert((uint32_t)APEXCODEC_SUPPORTED_VALUES_EMPTY  ==
+              (uint32_t)C2FieldSupportedValues::EMPTY);
+static_assert((uint32_t)APEXCODEC_SUPPORTED_VALUES_RANGE  ==
+              (uint32_t)C2FieldSupportedValues::RANGE);
+static_assert((uint32_t)APEXCODEC_SUPPORTED_VALUES_VALUES ==
+              (uint32_t)C2FieldSupportedValues::VALUES);
+static_assert((uint32_t)APEXCODEC_SUPPORTED_VALUES_FLAGS  ==
+              (uint32_t)C2FieldSupportedValues::FLAGS);
+
+static_assert((uint32_t)APEXCODEC_SUPPORTED_VALUES_TYPE_NONE   == (uint32_t)C2Value::NO_INIT);
+static_assert((uint32_t)APEXCODEC_SUPPORTED_VALUES_TYPE_INT32  == (uint32_t)C2Value::INT32);
+static_assert((uint32_t)APEXCODEC_SUPPORTED_VALUES_TYPE_UINT32 == (uint32_t)C2Value::UINT32);
+static_assert((uint32_t)APEXCODEC_SUPPORTED_VALUES_TYPE_INT64  == (uint32_t)C2Value::INT64);
+static_assert((uint32_t)APEXCODEC_SUPPORTED_VALUES_TYPE_UINT64 == (uint32_t)C2Value::UINT64);
+static_assert((uint32_t)APEXCODEC_SUPPORTED_VALUES_TYPE_FLOAT  == (uint32_t)C2Value::FLOAT);
+
+static_assert((uint32_t)APEXCODEC_SETTING_RESULT_BAD_TYPE       ==
+              (uint32_t)C2SettingResult::BAD_TYPE);
+static_assert((uint32_t)APEXCODEC_SETTING_RESULT_BAD_PORT       ==
+              (uint32_t)C2SettingResult::BAD_PORT);
+static_assert((uint32_t)APEXCODEC_SETTING_RESULT_BAD_INDEX      ==
+              (uint32_t)C2SettingResult::BAD_INDEX);
+static_assert((uint32_t)APEXCODEC_SETTING_RESULT_READ_ONLY      ==
+              (uint32_t)C2SettingResult::READ_ONLY);
+static_assert((uint32_t)APEXCODEC_SETTING_RESULT_MISMATCH       ==
+              (uint32_t)C2SettingResult::MISMATCH);
+static_assert((uint32_t)APEXCODEC_SETTING_RESULT_BAD_VALUE      ==
+              (uint32_t)C2SettingResult::BAD_VALUE);
+static_assert((uint32_t)APEXCODEC_SETTING_RESULT_CONFLICT       ==
+              (uint32_t)C2SettingResult::CONFLICT);
+static_assert((uint32_t)APEXCODEC_SETTING_RESULT_UNSUPPORTED    ==
+              (uint32_t)C2SettingResult::UNSUPPORTED);
+static_assert((uint32_t)APEXCODEC_SETTING_RESULT_INFO_BAD_VALUE ==
+              (uint32_t)C2SettingResult::INFO_BAD_VALUE);
+static_assert((uint32_t)APEXCODEC_SETTING_RESULT_INFO_CONFLICT  ==
+              (uint32_t)C2SettingResult::INFO_CONFLICT);
+
+static_assert((uint32_t)APEXCODEC_PARAM_IS_REQUIRED   == (uint32_t)C2ParamDescriptor::IS_REQUIRED);
+static_assert((uint32_t)APEXCODEC_PARAM_IS_PERSISTENT ==
+              (uint32_t)C2ParamDescriptor::IS_PERSISTENT);
+static_assert((uint32_t)APEXCODEC_PARAM_IS_STRICT     == (uint32_t)C2ParamDescriptor::IS_STRICT);
+static_assert((uint32_t)APEXCODEC_PARAM_IS_READ_ONLY  == (uint32_t)C2ParamDescriptor::IS_READ_ONLY);
+static_assert((uint32_t)APEXCODEC_PARAM_IS_HIDDEN     == (uint32_t)C2ParamDescriptor::IS_HIDDEN);
+static_assert((uint32_t)APEXCODEC_PARAM_IS_INTERNAL   == (uint32_t)C2ParamDescriptor::IS_INTERNAL);
+static_assert((uint32_t)APEXCODEC_PARAM_IS_CONST      == (uint32_t)C2ParamDescriptor::IS_CONST);
\ No newline at end of file
diff --git a/media/module/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp b/media/module/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
index d21908f..7905e4f 100644
--- a/media/module/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
+++ b/media/module/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
@@ -458,6 +458,12 @@
 
     if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
         LOG(DEBUG) << "EOS from decoder.";
+        // NOTE - b/360057459
+        // There is a synchronization problem between feeding the frame to the encoder input surface
+        // and signaling end of stream.
+        // Waiting before signaling end of stream so that input surface has time to feed remaining
+        // frames to the encoder.
+        std::this_thread::sleep_for(std::chrono::milliseconds(10));
         media_status_t status = AMediaCodec_signalEndOfInputStream(mEncoder->getCodec());
         if (status != AMEDIA_OK) {
             LOG(ERROR) << "SignalEOS on encoder returned error: " << status;
diff --git a/media/mtp/OWNERS b/media/mtp/OWNERS
index 6b5336e..bdb6cdb 100644
--- a/media/mtp/OWNERS
+++ b/media/mtp/OWNERS
@@ -1,10 +1,9 @@
 set noparent
 
-aprasath@google.com
 anothermark@google.com
-kumarashishg@google.com
-sarup@google.com
+febinthattil@google.com
+aprasath@google.com
 jsharkey@android.com
 jameswei@google.com
 rmojumder@google.com
-
+kumarashishg@google.com
\ No newline at end of file
diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp
index b250a03..e7fc106 100644
--- a/media/ndk/Android.bp
+++ b/media/ndk/Android.bp
@@ -93,6 +93,8 @@
     srcs: [
         "NdkJavaVMHelper.cpp",
         "NdkMediaCodec.cpp",
+        "NdkMediaCodecInfo.cpp",
+        "NdkMediaCodecStore.cpp",
         "NdkMediaCrypto.cpp",
         "NdkMediaDataSource.cpp",
         "NdkMediaExtractor.cpp",
@@ -131,6 +133,8 @@
         "libbase",
         "libdatasource",
         "libmedia",
+        "libmedia_codeclist",
+        "libmedia_codeclist_capabilities",
         "libmediadrm",
         "libmedia_omx",
         "libmedia_jni_utils",
diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp
index 7b19ac0..7797841 100644
--- a/media/ndk/NdkImageReader.cpp
+++ b/media/ndk/NdkImageReader.cpp
@@ -655,6 +655,28 @@
     }
 }
 
+media_status_t
+AImageReader::setUsage(uint64_t usage) {
+    Mutex::Autolock _l(mLock);
+    if (!mIsOpen || mBufferItemConsumer == nullptr) {
+        ALOGE("not ready to perform setUsage()");
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+    if (mUsage == usage) {
+        return AMEDIA_OK;
+    }
+
+    uint64_t halUsage = AHardwareBuffer_convertToGrallocUsageBits(mUsage);
+    status_t ret = mBufferItemConsumer->setConsumerUsageBits(halUsage);
+    if (ret != OK) {
+        ALOGE("setConsumerUsageBits() failed %d", ret);
+        return AMEDIA_ERROR_UNKNOWN;
+    }
+    mUsage = usage;
+    mHalUsage = halUsage;
+    return AMEDIA_OK;
+}
+
 static
 media_status_t validateParameters(int32_t width, int32_t height, int32_t format,
                                   uint64_t usage, int32_t maxImages,
@@ -912,3 +934,14 @@
     reader->setBufferRemovedListener(listener);
     return AMEDIA_OK;
 }
+
+EXPORT
+media_status_t AImageReader_setUsage(
+    AImageReader *reader, uint64_t usage) {
+    ALOGV("%s", __FUNCTION__);
+    if (reader == nullptr) {
+        ALOGE("%s: invalid argument! reader %p", __FUNCTION__, reader);
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+    return reader->setUsage(usage);
+}
diff --git a/media/ndk/NdkImageReaderPriv.h b/media/ndk/NdkImageReaderPriv.h
index 0199616..1c50d83 100644
--- a/media/ndk/NdkImageReaderPriv.h
+++ b/media/ndk/NdkImageReaderPriv.h
@@ -20,6 +20,7 @@
 #include <inttypes.h>
 
 #include <media/NdkImageReader.h>
+#include <media-vndk/VndkImageReader.h>
 
 #include <utils/List.h>
 #include <utils/Mutex.h>
@@ -67,6 +68,7 @@
 
     media_status_t setImageListener(AImageReader_ImageListener* listener);
     media_status_t setBufferRemovedListener(AImageReader_BufferRemovedListener* listener);
+    media_status_t setUsage(uint64_t usage);
 
     media_status_t acquireNextImage(/*out*/AImage** image, /*out*/int* fenceFd);
     media_status_t acquireLatestImage(/*out*/AImage** image, /*out*/int* fenceFd);
@@ -120,7 +122,7 @@
     const int32_t mWidth;
     const int32_t mHeight;
     int32_t mFormat;
-    const uint64_t mUsage;  // AHARDWAREBUFFER_USAGE_* flags.
+    uint64_t mUsage;  // AHARDWAREBUFFER_USAGE_* flags.
     const int32_t mMaxImages;
 
     // TODO(jwcai) Seems completely unused in AImageReader class.
diff --git a/media/ndk/NdkMediaCodecInfo.cpp b/media/ndk/NdkMediaCodecInfo.cpp
new file mode 100644
index 0000000..82ceb61
--- /dev/null
+++ b/media/ndk/NdkMediaCodecInfo.cpp
@@ -0,0 +1,520 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "NdkMediaCodecInfo"
+
+#include "NdkMediaCodecInfoPriv.h"
+
+#include <media/NdkMediaFormatPriv.h>
+
+using namespace android;
+
+extern "C" {
+
+// Utils
+
+EXPORT
+void AIntRange_delete(AIntRange *range) {
+    free(range);
+}
+
+EXPORT
+void ADoubleRange_delete(ADoubleRange *range) {
+    free(range);
+}
+
+// AMediaCodecInfo
+
+EXPORT
+const char* AMediaCodecInfo_getCanonicalName(const AMediaCodecInfo *info) {
+    if (info == nullptr || info->mInfo == nullptr) {
+        return nullptr;
+    }
+
+    return info->mInfo->getCodecName();
+}
+
+EXPORT
+bool AMediaCodecInfo_isEncoder(const AMediaCodecInfo *info) {
+    return info->mInfo->isEncoder();
+}
+
+EXPORT
+bool AMediaCodecInfo_isVendor(const AMediaCodecInfo *info) {
+    int32_t attributes = info->mInfo->getAttributes();
+    return (attributes & android::MediaCodecInfo::kFlagIsVendor);
+}
+
+EXPORT
+AMediaCodecType AMediaCodecInfo_getMediaCodecInfoType(const AMediaCodecInfo *info) {
+    if (info == nullptr || info->mInfo == nullptr) {
+        return (AMediaCodecType)0;
+    }
+
+    int32_t attributes = info->mInfo->getAttributes();
+
+    if (attributes & android::MediaCodecInfo::kFlagIsSoftwareOnly) {
+        return SOFTWARE_ONLY;
+    }
+    if (attributes & android::MediaCodecInfo::kFlagIsHardwareAccelerated) {
+        return HARDWARE_ACCELERATED;
+    }
+    return SOFTWARE_WITH_DEVICE_ACCESS;
+}
+
+EXPORT
+const char* AMediaCodecInfo_getMediaType(const AMediaCodecInfo *info) {
+    if (info == nullptr || info->mInfo == nullptr) {
+        return nullptr;
+    }
+
+    return info->mMediaType.c_str();
+}
+
+EXPORT
+int32_t AMediaCodecInfo_getMaxSupportedInstances(const AMediaCodecInfo *info) {
+    if (info == nullptr) {
+        return -1;
+    }
+
+    return info->mCodecCaps->getMaxSupportedInstances();
+}
+
+EXPORT
+int32_t AMediaCodecInfo_isFeatureSupported(const AMediaCodecInfo *info, const char *featureName) {
+    if (featureName == nullptr) {
+        return -1;
+    }
+    return info->mCodecCaps->isFeatureSupported(std::string(featureName));
+}
+
+EXPORT
+int32_t AMediaCodecInfo_isFeatureRequired(const AMediaCodecInfo *info, const char *featureName) {
+    if (featureName == nullptr) {
+        return -1;
+    }
+    return info->mCodecCaps->isFeatureRequired(std::string(featureName));
+}
+
+EXPORT
+int32_t AMediaCodecInfo_isFormatSupported(const AMediaCodecInfo *info, const AMediaFormat *format) {
+    if (format == nullptr) {
+        return -1;
+    }
+
+    sp<AMessage> nativeFormat;
+    AMediaFormat_getFormat(format, &nativeFormat);
+
+    return info->mCodecCaps->isFormatSupported(nativeFormat);
+}
+
+EXPORT
+media_status_t AMediaCodecInfo_getAudioCapabilities(const AMediaCodecInfo *info,
+        const ACodecAudioCapabilities **outAudioCaps) {
+    if (info == nullptr || info->mInfo == nullptr) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+
+    *outAudioCaps = info->mAAudioCaps.get();
+
+    if ((*outAudioCaps) == nullptr) {
+        return AMEDIA_ERROR_UNSUPPORTED;
+    }
+
+    return AMEDIA_OK;
+}
+
+EXPORT
+media_status_t AMediaCodecInfo_getVideoCapabilities(const AMediaCodecInfo *info,
+        const ACodecVideoCapabilities **outVideoCaps) {
+    if (info == nullptr || info->mInfo == nullptr) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+
+    *outVideoCaps = info->mAVideoCaps.get();
+
+    if ((*outVideoCaps) == nullptr) {
+        return AMEDIA_ERROR_UNSUPPORTED;
+    }
+
+    return AMEDIA_OK;
+}
+
+EXPORT
+media_status_t AMediaCodecInfo_getEncoderCapabilities(const AMediaCodecInfo *info,
+        const ACodecEncoderCapabilities **outEncoderCaps) {
+    if (info == nullptr || info->mInfo == nullptr) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+
+    *outEncoderCaps = info->mAEncoderCaps.get();
+
+    if ((*outEncoderCaps) == nullptr) {
+        return AMEDIA_ERROR_UNSUPPORTED;
+    }
+
+    return AMEDIA_OK;
+}
+
+// ACodecAudioCapabilities
+
+EXPORT
+media_status_t ACodecAudioCapabilities_getBitrateRange(const ACodecAudioCapabilities *audioCaps,
+        AIntRange *outRange) {
+    if (audioCaps == nullptr || outRange == nullptr) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+
+    const Range<int32_t>& bitrateRange = audioCaps->mAudioCaps->getBitrateRange();
+    outRange->mLower = bitrateRange.lower();
+    outRange->mUpper = bitrateRange.upper();
+
+    return AMEDIA_OK;
+}
+
+EXPORT
+media_status_t ACodecAudioCapabilities_getSupportedSampleRates(
+        const ACodecAudioCapabilities *audioCaps, const int **outArrayPtr, size_t *outCount) {
+    if (audioCaps == nullptr || outArrayPtr == nullptr || outCount == nullptr) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+
+    if (audioCaps->mSampleRates.empty()) {
+        return AMEDIA_ERROR_UNSUPPORTED;
+    }
+
+    *outArrayPtr = audioCaps->mSampleRates.data();
+    *outCount = audioCaps->mSampleRates.size();
+
+    return AMEDIA_OK;
+}
+
+EXPORT
+media_status_t ACodecAudioCapabilities_getSupportedSampleRateRanges(
+        const ACodecAudioCapabilities *audioCaps, const AIntRange **outArrayPtr, size_t *outCount) {
+    if (audioCaps == nullptr || outArrayPtr == nullptr || outCount == nullptr) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+
+    *outArrayPtr = audioCaps->mSampleRateRanges.data();
+    *outCount = audioCaps->mSampleRateRanges.size();
+
+    return AMEDIA_OK;
+}
+
+EXPORT
+int32_t ACodecAudioCapabilities_getMaxInputChannelCount(const ACodecAudioCapabilities *audioCaps) {
+    if (audioCaps == nullptr) {
+        return -1;
+    }
+    return audioCaps->mAudioCaps->getMaxInputChannelCount();
+}
+
+EXPORT
+int32_t ACodecAudioCapabilities_getMinInputChannelCount(const ACodecAudioCapabilities *audioCaps) {
+    if (audioCaps == nullptr) {
+        return -1;
+    }
+    return audioCaps->mAudioCaps->getMinInputChannelCount();
+}
+
+EXPORT
+media_status_t ACodecAudioCapabilities_getInputChannelCountRanges(
+        const ACodecAudioCapabilities *audioCaps, const AIntRange **outArrayPtr, size_t *outCount) {
+    if (audioCaps == nullptr || outArrayPtr == nullptr || outCount == nullptr) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+
+    *outArrayPtr = audioCaps->mInputChannelCountRanges.data();
+    *outCount = audioCaps->mInputChannelCountRanges.size();
+
+    return AMEDIA_OK;
+}
+
+EXPORT
+int32_t ACodecAudioCapabilities_isSampleRateSupported(const ACodecAudioCapabilities *audioCaps,
+        int32_t sampleRate) {
+    if (audioCaps == nullptr) {
+        return -1;
+    }
+    return audioCaps->mAudioCaps->isSampleRateSupported(sampleRate);
+}
+
+// ACodecPerformancePoint
+
+EXPORT
+ACodecPerformancePoint* ACodecPerformancePoint_create(int32_t width, int32_t height,
+        int32_t frameRate) {
+    return new ACodecPerformancePoint(
+            std::make_shared<VideoCapabilities::PerformancePoint>(width, height, frameRate));
+}
+
+EXPORT
+media_status_t ACodecPerformancePoint_delete(ACodecPerformancePoint *performancePoint) {
+    if (performancePoint == nullptr) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+
+    delete performancePoint;
+
+    return AMEDIA_OK;
+}
+
+EXPORT
+bool ACodecPerformancePoint_coversFormat(const ACodecPerformancePoint *performancePoint,
+        const AMediaFormat *format) {
+    sp<AMessage> nativeFormat;
+    AMediaFormat_getFormat(format, &nativeFormat);
+
+    return performancePoint->mPerformancePoint->covers(nativeFormat);
+}
+
+EXPORT
+bool ACodecPerformancePoint_covers(const ACodecPerformancePoint *one,
+        const ACodecPerformancePoint *another) {
+    return one->mPerformancePoint->covers(*(another->mPerformancePoint));
+}
+
+EXPORT
+bool ACodecPerformancePoint_equals(const ACodecPerformancePoint *one,
+        const ACodecPerformancePoint *another) {
+    return one->mPerformancePoint->equals(*(another->mPerformancePoint));
+}
+
+// ACodecVideoCapabilities
+
+EXPORT
+media_status_t ACodecVideoCapabilities_getBitrateRange(const ACodecVideoCapabilities *videoCaps,
+        AIntRange *outRange) {
+    if (videoCaps == nullptr || outRange == nullptr) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+
+    const Range<int32_t>& bitrateRange = videoCaps->mVideoCaps->getBitrateRange();
+    outRange->mLower = bitrateRange.lower();
+    outRange->mUpper = bitrateRange.upper();
+
+    return AMEDIA_OK;
+}
+
+EXPORT
+media_status_t ACodecVideoCapabilities_getSupportedWidths(const ACodecVideoCapabilities *videoCaps,
+        AIntRange *outRange) {
+    if (videoCaps == nullptr || outRange == nullptr) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+
+    const Range<int32_t>& supportedWidths = videoCaps->mVideoCaps->getSupportedWidths();
+    outRange->mLower = supportedWidths.lower();
+    outRange->mUpper = supportedWidths.upper();
+
+    return AMEDIA_OK;
+}
+
+EXPORT
+media_status_t ACodecVideoCapabilities_getSupportedHeights(const ACodecVideoCapabilities *videoCaps,
+        AIntRange *outRange) {
+    if (videoCaps == nullptr || outRange == nullptr) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+
+    const Range<int32_t>& supportedHeights = videoCaps->mVideoCaps->getSupportedHeights();
+    outRange->mLower = supportedHeights.lower();
+    outRange->mUpper = supportedHeights.upper();
+
+    return AMEDIA_OK;
+}
+
+EXPORT
+int32_t ACodecVideoCapabilities_getWidthAlignment(const ACodecVideoCapabilities *videoCaps) {
+    if (videoCaps == nullptr) {
+        return -1;
+    }
+    return videoCaps->mVideoCaps->getWidthAlignment();
+}
+
+EXPORT
+int32_t ACodecVideoCapabilities_getHeightAlignment(const ACodecVideoCapabilities *videoCaps) {
+    if (videoCaps == nullptr) {
+        return -1;
+    }
+    return videoCaps->mVideoCaps->getHeightAlignment();
+}
+
+EXPORT
+media_status_t ACodecVideoCapabilities_getSupportedFrameRates(
+        const ACodecVideoCapabilities *videoCaps, AIntRange *outRange) {
+    if (videoCaps == nullptr || outRange == nullptr) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+
+    const Range<int32_t>& frameRateRange = videoCaps->mVideoCaps->getSupportedFrameRates();
+    outRange->mLower = frameRateRange.lower();
+    outRange->mUpper = frameRateRange.upper();
+
+    return AMEDIA_OK;
+}
+
+EXPORT
+media_status_t ACodecVideoCapabilities_getSupportedWidthsFor(
+        const ACodecVideoCapabilities *videoCaps, int32_t height, AIntRange *outRange) {
+    if (videoCaps == nullptr || outRange == nullptr) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+
+    std::optional<Range<int32_t>> widthRange = videoCaps->mVideoCaps->getSupportedWidthsFor(height);
+    if (!widthRange) {
+        return AMEDIA_ERROR_UNSUPPORTED;
+    }
+
+    outRange->mLower = widthRange.value().lower();
+    outRange->mUpper = widthRange.value().upper();
+
+    return AMEDIA_OK;
+}
+
+EXPORT
+media_status_t ACodecVideoCapabilities_getSupportedHeightsFor(
+        const ACodecVideoCapabilities *videoCaps, int32_t width, AIntRange *outRange) {
+    if (videoCaps == nullptr || outRange == nullptr) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+
+    std::optional<Range<int32_t>> heightRange
+            = videoCaps->mVideoCaps->getSupportedHeightsFor(width);
+    if (!heightRange) {
+        return AMEDIA_ERROR_UNSUPPORTED;
+    }
+
+    outRange->mLower = heightRange.value().lower();
+    outRange->mUpper = heightRange.value().upper();
+
+    return AMEDIA_OK;
+}
+
+EXPORT
+media_status_t ACodecVideoCapabilities_getSupportedFrameRatesFor(
+        const ACodecVideoCapabilities *videoCaps, int32_t width, int32_t height,
+        ADoubleRange *outRange) {
+    if (videoCaps == nullptr || outRange == nullptr) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+
+    std::optional<Range<double>> frameRates
+            = videoCaps->mVideoCaps->getSupportedFrameRatesFor(width, height);
+    if (!frameRates) {
+        return AMEDIA_ERROR_UNSUPPORTED;
+    }
+
+    outRange->mLower = frameRates.value().lower();
+    outRange->mUpper = frameRates.value().upper();
+
+    return AMEDIA_OK;
+}
+
+EXPORT
+media_status_t ACodecVideoCapabilities_getAchievableFrameRatesFor(
+        const ACodecVideoCapabilities *videoCaps, int32_t width, int32_t height,
+        ADoubleRange *outRange) {
+    if (videoCaps == nullptr || outRange == nullptr) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+
+    std::optional<Range<double>> frameRates
+            = videoCaps->mVideoCaps->getAchievableFrameRatesFor(width, height);
+    if (!frameRates) {
+        return AMEDIA_ERROR_UNSUPPORTED;
+    }
+
+    outRange->mLower = frameRates.value().lower();
+    outRange->mUpper = frameRates.value().upper();
+
+    return AMEDIA_OK;
+}
+
+EXPORT
+media_status_t ACodecVideoCapabilities_getSupportedPerformancePoints(
+        const ACodecVideoCapabilities *videoCaps,
+        const ACodecPerformancePoint **outPerformancePointArray, size_t *outCount) {
+    if (videoCaps == nullptr) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+
+    *outPerformancePointArray = videoCaps->mPerformancePoints.data();
+    *outCount = videoCaps->mPerformancePoints.size();
+
+    return AMEDIA_OK;
+}
+
+EXPORT
+int32_t ACodecVideoCapabilities_areSizeAndRateSupported(const ACodecVideoCapabilities *videoCaps,
+        int32_t width, int32_t height, double frameRate) {
+    if (videoCaps == nullptr) {
+        return -1;
+    }
+    return videoCaps->mVideoCaps->areSizeAndRateSupported(width, height, frameRate);
+}
+
+EXPORT
+int32_t ACodecVideoCapabilities_isSizeSupported(const ACodecVideoCapabilities *videoCaps,
+        int32_t width, int32_t height) {
+    if (videoCaps == nullptr) {
+        return -1;
+    }
+    return videoCaps->mVideoCaps->isSizeSupported(width, height);
+}
+
+// ACodecEncoderCapabilities
+
+EXPORT
+media_status_t ACodecEncoderCapabilities_getQualityRange(
+        const ACodecEncoderCapabilities *encoderCaps, AIntRange *outRange) {
+    if (encoderCaps == nullptr || outRange == nullptr) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+
+    const Range<int32_t>& qualityRange = encoderCaps->mEncoderCaps->getQualityRange();
+    outRange->mLower = qualityRange.lower();
+    outRange->mUpper = qualityRange.upper();
+
+    return AMEDIA_OK;
+}
+
+EXPORT
+media_status_t ACodecEncoderCapabilities_getComplexityRange(
+        const ACodecEncoderCapabilities *encoderCaps, AIntRange *outRange) {
+    if (encoderCaps == nullptr || outRange == nullptr) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+
+    const Range<int32_t>& complexityRange = encoderCaps->mEncoderCaps->getComplexityRange();
+    outRange->mLower = complexityRange.lower();
+    outRange->mUpper = complexityRange.upper();
+
+    return AMEDIA_OK;
+}
+
+int32_t ACodecEncoderCapabilities_isBitrateModeSupported(
+        const ACodecEncoderCapabilities *encoderCaps, ABiterateMode mode) {
+    if (encoderCaps == nullptr) {
+        return -1;
+    }
+    return encoderCaps->mEncoderCaps->isBitrateModeSupported(mode);
+}
+
+
+}
\ No newline at end of file
diff --git a/media/ndk/NdkMediaCodecInfoPriv.h b/media/ndk/NdkMediaCodecInfoPriv.h
new file mode 100644
index 0000000..6d9188b
--- /dev/null
+++ b/media/ndk/NdkMediaCodecInfoPriv.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NDK_MEDIA_CODEC_INFO_PRIV_H
+#define _NDK_MEDIA_CODEC_INFO_PRIV_H
+
+#include <media/MediaCodecInfo.h>
+#include <media/NdkMediaCodecInfo.h>
+
+struct ACodecAudioCapabilities {
+    std::shared_ptr<android::AudioCapabilities> mAudioCaps;
+
+    std::vector<int> mSampleRates;
+    std::vector<AIntRange> mSampleRateRanges;
+    std::vector<AIntRange> mInputChannelCountRanges;
+
+    void initSampleRates() {
+        mSampleRates = mAudioCaps->getSupportedSampleRates();
+    }
+
+    void initSampleRateRanges() {
+        const std::vector<android::Range<int>>& sampleRateRanges
+                = mAudioCaps->getSupportedSampleRateRanges();
+        for (auto it = sampleRateRanges.begin(); it != sampleRateRanges.end(); it++) {
+            mSampleRateRanges.emplace_back(it->lower(), it->upper());
+        }
+    }
+
+    void initInputChannelCountRanges() {
+        const std::vector<android::Range<int>>& inputChannels
+                = mAudioCaps->getInputChannelCountRanges();
+        for (auto it = inputChannels.begin(); it != inputChannels.end(); it++) {
+            mInputChannelCountRanges.emplace_back(it->lower(), it->upper());
+        }
+    }
+
+    ACodecAudioCapabilities(std::shared_ptr<android::AudioCapabilities> audioCaps)
+            : mAudioCaps(audioCaps) {
+        initSampleRates();
+        initSampleRateRanges();
+        initInputChannelCountRanges();
+    }
+};
+
+struct ACodecPerformancePoint {
+    std::shared_ptr<const android::VideoCapabilities::PerformancePoint> mPerformancePoint;
+
+    ACodecPerformancePoint(std::shared_ptr<const android::VideoCapabilities::PerformancePoint>
+            performancePoint) : mPerformancePoint(performancePoint) {}
+};
+
+struct ACodecVideoCapabilities {
+    std::shared_ptr<android::VideoCapabilities> mVideoCaps;
+
+    std::vector<ACodecPerformancePoint> mPerformancePoints;
+
+    void initPerformancePoints() {
+        const std::vector<android::VideoCapabilities::PerformancePoint>& performancePoints
+            = mVideoCaps->getSupportedPerformancePoints();
+        for (auto it = performancePoints.begin(); it != performancePoints.end(); it++) {
+            mPerformancePoints.emplace_back(
+                    std::shared_ptr<const android::VideoCapabilities::PerformancePoint>(&(*it)));
+        }
+    }
+
+    ACodecVideoCapabilities(std::shared_ptr<android::VideoCapabilities> videoCaps)
+            : mVideoCaps(videoCaps) {
+        initPerformancePoints();
+    }
+};
+
+struct ACodecEncoderCapabilities {
+    std::shared_ptr<android::EncoderCapabilities> mEncoderCaps;
+
+    ACodecEncoderCapabilities(std::shared_ptr<android::EncoderCapabilities> encoderCaps)
+            : mEncoderCaps(encoderCaps) {}
+};
+
+struct AMediaCodecInfo {
+    std::string mName;
+    android::sp<android::MediaCodecInfo> mInfo;
+    std::string mMediaType;
+    std::shared_ptr<android::CodecCapabilities> mCodecCaps;
+
+    std::shared_ptr<const ACodecAudioCapabilities> mAAudioCaps;
+    std::shared_ptr<const ACodecVideoCapabilities> mAVideoCaps;
+    std::shared_ptr<const ACodecEncoderCapabilities> mAEncoderCaps;
+
+    AMediaCodecInfo(std::string name, android::sp<android::MediaCodecInfo> info,
+            std::shared_ptr<android::CodecCapabilities> codecCaps, std::string mediaType)
+            : mName(name), mInfo(info), mMediaType(mediaType), mCodecCaps(codecCaps) {
+        if (!mName.empty() && mInfo != nullptr && !mMediaType.empty() && mCodecCaps != nullptr) {
+            if (mCodecCaps->getAudioCapabilities() != nullptr) {
+                mAAudioCaps = std::make_shared<const ACodecAudioCapabilities>(
+                        mCodecCaps->getAudioCapabilities());
+            }
+            if (mCodecCaps->getVideoCapabilities() != nullptr) {
+                mAVideoCaps = std::make_shared<const ACodecVideoCapabilities>(
+                        mCodecCaps->getVideoCapabilities());
+            }
+            if (mCodecCaps->getEncoderCapabilities() != nullptr) {
+                mAEncoderCaps = std::make_shared<const ACodecEncoderCapabilities>(
+                    mCodecCaps->getEncoderCapabilities());
+            }
+        }
+    }
+};
+
+#endif //_NDK_MEDIA_CODEC_INFO_PRIV_H
\ No newline at end of file
diff --git a/media/ndk/NdkMediaCodecStore.cpp b/media/ndk/NdkMediaCodecStore.cpp
new file mode 100644
index 0000000..d911593
--- /dev/null
+++ b/media/ndk/NdkMediaCodecStore.cpp
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "NdkMediaCodecStore"
+
+#include "NdkMediaCodecInfoPriv.h"
+
+#include <media/NdkMediaCodecStore.h>
+#include <media/NdkMediaFormatPriv.h>
+
+#include <media/IMediaCodecList.h>
+
+#include <media/MediaCodecInfo.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaCodecConstants.h>
+#include <media/stagefright/MediaCodecList.h>
+
+using namespace android;
+
+static sp<IMediaCodecList> sCodecList;
+static std::vector<AMediaCodecSupportedMediaType> sMediaTypes;
+static std::vector<AMediaCodecInfo> sCodecInfos;
+
+static std::map<std::string, AMediaCodecInfo> sNameToInfoMap;
+static std::map<std::string, std::vector<AMediaCodecInfo>> sTypeToInfoList;
+
+static void initMediaTypes() {
+    if (sCodecList == nullptr) {
+        sCodecList = MediaCodecList::getInstance();
+    }
+
+    std::map<std::string, AMediaCodecSupportedMediaType> typesInfoMap;
+    std::vector<std::string> mediaTypes; // Keep the order of media types appearing in sCodecList.
+    for (size_t idx = 0; idx < sCodecList->countCodecs(); idx++) {
+        sp<MediaCodecInfo> codecInfo = sCodecList->getCodecInfo(idx);
+        if (codecInfo == nullptr) {
+            ALOGW("NULL MediaCodecInfo in MediaCodecList");
+            continue;
+        }
+        Vector<AString> codecMediaTypes;
+        codecInfo->getSupportedMediaTypes(&codecMediaTypes);
+        for (AString codecMediaType : codecMediaTypes) {
+            std::string mediaType = std::string(codecMediaType.c_str());
+
+            // Excludes special codecs from NDK
+            const std::shared_ptr<CodecCapabilities> codecCaps
+                    = codecInfo->getCodecCapsFor(mediaType.c_str());
+            if (codecCaps->isFeatureSupported(FEATURE_SpecialCodec)) {
+                continue;
+            }
+
+            auto it = typesInfoMap.find(mediaType);
+            if (it == typesInfoMap.end()) {
+                AMediaCodecSupportedMediaType supportedType = { mediaType.c_str(), 0 };
+                it = typesInfoMap.emplace(mediaType, supportedType).first;
+                mediaTypes.push_back(mediaType);
+            }
+            uint32_t &mode = it->second.mMode;
+            mode |= (codecInfo->isEncoder() ? AMediaCodecSupportedMediaType::FLAG_ENCODER
+                                            : AMediaCodecSupportedMediaType::FLAG_DECODER);
+        }
+    }
+
+    // sMediaTypes keeps the order of media types appearing in sCodecList.
+    for (std::string &type : mediaTypes) {
+        sMediaTypes.push_back(typesInfoMap.find(type)->second);
+    }
+}
+
+static void initCodecInfoMap() {
+    if (sCodecList == nullptr) {
+        sCodecList = MediaCodecList::getInstance();
+    }
+
+    for (size_t idx = 0; idx < sCodecList->countCodecs(); idx++) {
+        sp<MediaCodecInfo> codecInfo = sCodecList->getCodecInfo(idx);
+        if (codecInfo == nullptr) {
+            ALOGW("NULL MediaCodecInfo in MediaCodecList");
+            continue;
+        }
+
+        Vector<AString> codecMediaTypes;
+        codecInfo->getSupportedMediaTypes(&codecMediaTypes);
+        bool useTypeSuffix = codecMediaTypes.size() > 1;
+        for (AString codecMediaType : codecMediaTypes) {
+            std::string mediaType = std::string(codecMediaType.c_str());
+
+            // Excludes special codecs from NDK
+            const std::shared_ptr<CodecCapabilities> codecCaps
+                    = codecInfo->getCodecCapsFor(mediaType.c_str());
+            if (codecCaps->isFeatureSupported(FEATURE_SpecialCodec)) {
+                continue;
+            }
+
+            // get the type name after the slash. e.g. video/x.on2.vp8
+            size_t slashIx = mediaType.find_last_of('/');
+            if (slashIx == std::string::npos) {
+                slashIx = 0;
+            } else {
+                slashIx++;
+            }
+            std::string ndkBaseName = std::string(codecInfo->getCodecName());
+            if (useTypeSuffix) {
+                // If there are multiple supported media types,
+                // add the type to the end of the name to disambiguate names.
+                ndkBaseName += "." + mediaType.substr(slashIx);
+            }
+
+            int32_t copyIx = 0;
+            std::string ndkName;
+            // if a name is already registered,
+            // add ".1", ".2", ... at the end to disambiguate names.
+            while (true) {
+                ndkName = ndkBaseName;
+                if (copyIx > 0) {
+                    ndkName += "." + std::to_string(copyIx);
+                }
+                if (!sNameToInfoMap.contains(ndkName)) {
+                    break;
+                }
+                copyIx++;
+            }
+
+            AMediaCodecInfo info = AMediaCodecInfo(ndkName, codecInfo, codecCaps, mediaType);
+            sCodecInfos.push_back(info);
+            sNameToInfoMap.emplace(ndkName, info);
+
+            auto it = sTypeToInfoList.find(mediaType);
+            if (it == sTypeToInfoList.end()) {
+                std::vector<AMediaCodecInfo> infoList;
+                infoList.push_back(info);
+                sTypeToInfoList.emplace(mediaType, infoList);
+            } else {
+                it->second.push_back(info);
+            }
+        }
+    }
+}
+
+static bool codecHandlesFormat(const AMediaCodecInfo codecInfo,
+        sp<AMessage> format, bool isEncoder) {
+    return codecInfo.mCodecCaps->isEncoder() == isEncoder
+            && codecInfo.mCodecCaps->isFormatSupported(format);
+}
+
+static media_status_t findNextCodecForFormat(
+        const AMediaFormat *format, bool isEncoder, const AMediaCodecInfo **outCodecInfo) {
+    if (outCodecInfo == nullptr) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+
+    if (sCodecInfos.empty()) {
+        initCodecInfoMap();
+    }
+
+    std::unique_ptr<std::vector<AMediaCodecInfo>> infos;
+    sp<AMessage> nativeFormat;
+    if (format == nullptr) {
+        infos = std::unique_ptr<std::vector<AMediaCodecInfo>>(&sCodecInfos);
+    } else {
+        AMediaFormat_getFormat(format, &nativeFormat);
+        AString mime;
+        if (!nativeFormat->findString(KEY_MIME, &mime)) {
+            return AMEDIA_ERROR_INVALID_PARAMETER;
+        }
+
+        std::string mediaType = std::string(mime.c_str());
+        auto it = sTypeToInfoList.find(mediaType);
+        if (it == sTypeToInfoList.end()) {
+            return AMEDIA_ERROR_UNSUPPORTED;
+        }
+        infos = std::unique_ptr<std::vector<AMediaCodecInfo>>(&(it->second));
+    }
+
+    bool found = *outCodecInfo == nullptr;
+    for (const AMediaCodecInfo &info : *infos) {
+        if (found && (format == nullptr
+                || codecHandlesFormat(info, nativeFormat, isEncoder))) {
+            *outCodecInfo = &info;
+            return AMEDIA_OK;
+        }
+        if (*outCodecInfo == &info) {
+            found = true;
+        }
+
+    }
+    *outCodecInfo = nullptr;
+    return AMEDIA_ERROR_UNSUPPORTED;
+}
+
+extern "C" {
+
+EXPORT
+media_status_t AMediaCodecStore_getSupportedMediaTypes(
+        const AMediaCodecSupportedMediaType **outMediaTypes, size_t *outCount) {
+    if (outMediaTypes == nullptr) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+
+    if (sMediaTypes.empty()) {
+        initMediaTypes();
+    }
+
+    *outCount = sMediaTypes.size();
+    *outMediaTypes = sMediaTypes.data();
+
+    return AMEDIA_OK;
+}
+
+EXPORT
+media_status_t AMediaCodecStore_findNextDecoderForFormat(
+        const AMediaFormat *format, const AMediaCodecInfo **outCodecInfo){
+    return findNextCodecForFormat(format, false, outCodecInfo);
+}
+
+EXPORT
+media_status_t AMediaCodecStore_findNextEncoderForFormat(
+        const AMediaFormat *format, const AMediaCodecInfo **outCodecInfo){
+    return findNextCodecForFormat(format, true, outCodecInfo);
+}
+
+EXPORT
+media_status_t AMediaCodecStore_getCodecInfo(
+        const char *name, const AMediaCodecInfo **outCodecInfo) {
+    if (outCodecInfo == nullptr || name == nullptr) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+
+    auto it = sNameToInfoMap.find(std::string(name));
+    if (it == sNameToInfoMap.end()) {
+        *outCodecInfo = nullptr;
+        return AMEDIA_ERROR_UNSUPPORTED;
+    } else {
+        *outCodecInfo = &(it->second);
+        return AMEDIA_OK;
+    }
+}
+
+}
\ No newline at end of file
diff --git a/media/ndk/include/media-vndk/VndkImageReader.h b/media/ndk/include/media-vndk/VndkImageReader.h
new file mode 100644
index 0000000..c67a38c
--- /dev/null
+++ b/media/ndk/include/media-vndk/VndkImageReader.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _VNDK_IMAGE_READER_H
+#define _VNDK_IMAGE_READER_H
+
+// vndk is a superset of the NDK
+#include <media/NdkImageReader.h>
+
+__BEGIN_DECLS
+
+/**
+ * Set the usage of this image reader.
+ *
+ * <p>Note that calling this method will replace the previously set usage.</p>
+ *
+ * <p>Note: This will trigger re-allocation, could cause producer failures mid-stream
+ * if the new usage combination isn't supported, and thus should be avoided as much as
+ * possible regardless.</p>
+ *
+ * Available since API level 36.
+ *
+ * @param reader The image reader of interest.
+ * @param usage specifies how the consumer will access the AImage.
+ *              See {@link AImageReader_newWithUsage} parameter description for more details.
+ * @return <ul>
+ *         <li>{@link AMEDIA_OK} if the method call succeeds.</li>
+ *         <li>{@link AMEDIA_ERROR_INVALID_PARAMETER} if reader is NULL.</li>
+ *         <li>{@link AMEDIA_ERROR_UNKNOWN} if the method fails for some other reasons.</li></ul>
+ *
+ * @see AImage_getHardwareBuffer
+ */
+media_status_t AImageReader_setUsage(
+        AImageReader* _Nonnull reader, uint64_t usage) __INTRODUCED_IN(36);
+
+__END_DECLS
+
+#endif //_VNDK_IMAGE_READER_H
diff --git a/media/ndk/include/media/NdkMediaCodecInfo.h b/media/ndk/include/media/NdkMediaCodecInfo.h
new file mode 100644
index 0000000..558e82c
--- /dev/null
+++ b/media/ndk/include/media/NdkMediaCodecInfo.h
@@ -0,0 +1,625 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @addtogroup Media
+ * @{
+ */
+
+/**
+ * @file NdkMediaCodecInfo.h
+ */
+
+/*
+ * This file defines an NDK API.
+ * Do not remove methods.
+ * Do not change method signatures.
+ * Do not change the value of constants.
+ * Do not change the size of any of the classes defined in here.
+ * Do not reference types that are not part of the NDK.
+ * Do not #include files that aren't part of the NDK.
+ */
+
+#ifndef _NDK_MEDIA_CODEC_INFO_H
+#define _NDK_MEDIA_CODEC_INFO_H
+
+#include "NdkMediaError.h"
+#include "NdkMediaFormat.h"
+
+__BEGIN_DECLS
+
+struct ACodecAudioCapabilities;
+typedef struct ACodecAudioCapabilities ACodecAudioCapabilities;
+struct ACodecPerformancePoint;
+typedef struct ACodecPerformancePoint ACodecPerformancePoint;
+struct ACodecVideoCapabilities;
+typedef struct ACodecVideoCapabilities ACodecVideoCapabilities;
+struct ACodecEncoderCapabilities;
+typedef struct ACodecEncoderCapabilities ACodecEncoderCapabilities;
+struct AMediaCodecInfo;
+typedef struct AMediaCodecInfo AMediaCodecInfo;
+
+/**
+ * A uitlity structure describing the range of two integer values.
+ */
+typedef struct AIntRange {
+    int32_t mLower;
+    int32_t mUpper;
+} AIntRange;
+
+/**
+ * A uitlity structure describing the range of two double values.
+ */
+typedef struct ADoubleRange {
+    double mLower;
+    double mUpper;
+} ADoubleRange;
+
+// AMediaCodecInfo
+
+/**
+ * Get the canonical name of a codec.
+ *
+ * \return      The char pointer to the canonical name.
+ *              It is owned by the framework. No lifetime management needed for users.
+ *
+ * Return NULL if @param info is invalid.
+ */
+const char* AMediaCodecInfo_getCanonicalName(const AMediaCodecInfo *info) __INTRODUCED_IN(36);
+
+/**
+ * Query if the codec is an encoder.
+ */
+bool AMediaCodecInfo_isEncoder(const AMediaCodecInfo *info) __INTRODUCED_IN(36);
+
+/**
+ * Query if the codec is provided by the Android platform (false) or the device manufacturer (true).
+ */
+bool AMediaCodecInfo_isVendor(const AMediaCodecInfo *info) __INTRODUCED_IN(36);
+
+/**
+ * The type of codecs.
+ */
+typedef enum AMediaCodecType : int32_t {
+    /**
+     * Not a codec type. Used for indicating an invalid operation occurred.
+     */
+    INVALID_CODEC_INFO = 0,
+
+    /**
+     * Software codec.
+     *
+     * Software-only codecs are more secure as they run in a tighter security sandbox.
+     * On the other hand, software-only codecs do not provide any performance guarantees.
+     */
+    SOFTWARE_ONLY = 1,
+
+    /**
+     * Hardware accelerated codec.
+     *
+     * Hardware codecs generally have higher performance or lower power consumption than
+     * software codecs, but since they are specific to each device,
+     * the actual performance details can vary.
+     */
+    HARDWARE_ACCELERATED = 2,
+
+    /**
+     * Software codec but have device access.
+     * Mainly referring to software codecs provided by vendors.
+     */
+    SOFTWARE_WITH_DEVICE_ACCESS = 3,
+} AMediaCodecType;
+
+/**
+ * Query if the codec is SOFTWARE_ONLY, HARDWARE_ACCELERATED or SOFTWARE_WITH_DEVICE_ACCESS.
+ *
+ * Return INVALID_CODEC_INFO if @param info is invalid.
+ */
+AMediaCodecType AMediaCodecInfo_getMediaCodecInfoType(
+        const AMediaCodecInfo *info) __INTRODUCED_IN(36);
+
+/**
+ * Get the supported media type of the codec.
+ *
+ * \return  The char pointer to the media type.
+ *          It is owned by the framework with infinite lifetime.
+ *
+ * Return NULL if @param info is invalid.
+ */
+const char* AMediaCodecInfo_getMediaType(const AMediaCodecInfo *info) __INTRODUCED_IN(36);
+
+/**
+ * Returns the max number of the supported concurrent codec instances.
+ *
+ * This is a hint for an upper bound. Applications should not expect to successfully
+ * operate more instances than the returned value, but the actual number of
+ * concurrently operable instances may be less as it depends on the available
+ * resources at time of use.
+ *
+ * Return -1 if @param info is invalid.
+ */
+int32_t AMediaCodecInfo_getMaxSupportedInstances(const AMediaCodecInfo *info) __INTRODUCED_IN(36);
+
+/**
+ * Query codec feature capabilities.
+ *
+ * These features are supported to be used by the codec.  These
+ * include optional features that can be turned on, as well as
+ * features that are always on.
+ *
+ * Return 1 if the feature is supported;
+ * Return 0 if the feature is unsupported;
+ * Return -1 if @param featureName is invalid.
+ */
+int32_t AMediaCodecInfo_isFeatureSupported(const AMediaCodecInfo *info,
+        const char *featureName) __INTRODUCED_IN(36);
+
+/**
+ * Query codec feature requirements.
+ *
+ * These features are required to be used by the codec, and as such,
+ * they are always turned on.
+ *
+ * Return 1 if the feature is required;
+ * Return 0 if the feature is not required;
+ * Return -1 if @param featureName is invalid.
+ */
+int32_t AMediaCodecInfo_isFeatureRequired(const AMediaCodecInfo *info,
+        const char *featureName) __INTRODUCED_IN(36);
+
+/**
+ * Query whether codec supports a given @param format.
+ *
+ * Return 1 if the format is supported;
+ * Return 0 if the format is unsupported;
+ * Return -1 if @param format is invalid.
+ */
+int32_t AMediaCodecInfo_isFormatSupported(const AMediaCodecInfo *info,
+        const AMediaFormat *format) __INTRODUCED_IN(36);
+
+/**
+ * Get the ACodecAudioCapabilities from the given AMediaCodecInfo.
+ *
+ * @param outAudioCaps        The pointer to the output ACodecAudioCapabilities.
+ *                            It is owned by the framework and has an infinite lifetime.
+ *
+ * Return AMEDIA_OK if successfully got the ACodecAudioCapabilities.
+ * Return AMEDIA_ERROR_UNSUPPORTED if the codec is not an audio codec.
+ * Return AMEDIA_ERROR_INVALID_PARAMETER if @param info is invalid.
+ */
+media_status_t AMediaCodecInfo_getAudioCapabilities(const AMediaCodecInfo *info,
+        const ACodecAudioCapabilities **outAudioCaps) __INTRODUCED_IN(36);
+
+/**
+ * Get the ACodecVideoCapabilities from the given AMediaCodecInfo.
+ *
+ * @param outVideoCaps        The pointer to the output ACodecVideoCapabilities.
+ *                            It is owned by the framework and has an infinite lifetime.
+ *
+ * Return AMEDIA_OK if successfully got the ACodecVideoCapabilities.
+ * Return AMEDIA_ERROR_UNSUPPORTED if the codec is not a video codec.
+ * Return AMEDIA_ERROR_INVALID_PARAMETER if @param info is invalid.
+ */
+media_status_t AMediaCodecInfo_getVideoCapabilities(const AMediaCodecInfo *info,
+        const ACodecVideoCapabilities **outVideoCaps) __INTRODUCED_IN(36);
+
+/**
+ * Get the ACodecEncoderCapabilities from the given AMediaCodecInfo.
+ *
+ * @param outEncoderCaps        The pointer to the output ACodecEncoderCapabilities.
+ *                              It is owned by the framework and has an infinite lifetime.
+ *
+ * Return AMEDIA_OK if successfully got the ACodecEncoderCapabilities.
+ * Return AMEDIA_ERROR_UNSUPPORTED if the codec is not an encoder.
+ * Return AMEDIA_ERROR_INVALID_PARAMETER if @param info is invalid.
+ */
+media_status_t AMediaCodecInfo_getEncoderCapabilities(const AMediaCodecInfo *info,
+        const ACodecEncoderCapabilities **outEncoderCaps) __INTRODUCED_IN(36);
+
+// ACodecAudioCapabilities
+
+/**
+ * Get the range of supported bitrates in bits/second.
+ *
+ * @param outRange  The pointer to the range of supported bitrates.
+ *                  Users are responsible for allocating a valid AIntRange structure and
+ *                  managing the lifetime of it.
+ *
+ * Return AMEDIA_OK if got bitrates successfully.
+ * Return AMEDIA_ERROR_INVALID_PARAMETER if any of @param audioCaps and @param outRange is invalid.
+ */
+media_status_t ACodecAudioCapabilities_getBitrateRange(const ACodecAudioCapabilities *audioCaps,
+        AIntRange *outRange) __INTRODUCED_IN(36);
+
+/**
+ * Get the array of supported sample rates
+ *
+ * The array is sorted in ascending order.
+ *
+ * @param outArrayPtr   The pointer to the output sample rates array.
+ *                      The array is owned by the framework and has an infinite lifetime.
+ * @param outCount      The size of the output array.
+ *
+ * Return AMEDIA_OK if the codec supports only discrete values.
+ * Otherwise, it returns AMEDIA_ERROR_UNSUPPORTED.
+ * Return AMEDIA_ERROR_INVALID_PARAMETER if any of @param audioCaps, @param outArrayPtr
+ * and @param outCount is invalid.
+ */
+media_status_t ACodecAudioCapabilities_getSupportedSampleRates(
+        const ACodecAudioCapabilities *audioCaps, const int **outArrayPtr,
+        size_t *outCount) __INTRODUCED_IN(36);
+
+/**
+ * Get the array of supported sample rate ranges.
+ *
+ * The array is sorted in ascending order, and the ranges are distinct (non-intersecting).
+ *
+ * @param outArrayPtr   The pointer to the out sample rate ranges array.
+ *                      The array is owned by the framework and has an infinite lifetime.
+ * @param outCount      The size of the out array.
+ *
+ * Return AMEDIA_OK if got the sample rate ranges successfully.
+ * Return AMEDIA_ERROR_INVALID_PARAMETER if any of @param audioCaps, @param outArrayPtr
+ * and @param outCount is invalid.
+ */
+media_status_t ACodecAudioCapabilities_getSupportedSampleRateRanges(
+        const ACodecAudioCapabilities *audioCaps,
+        const AIntRange **outArrayPtr, size_t *outCount) __INTRODUCED_IN(36);
+
+/**
+ * Return the maximum number of input channels supported.
+ *
+ * Return -1 if @param audioCaps is invalid.
+ */
+int32_t ACodecAudioCapabilities_getMaxInputChannelCount(
+        const ACodecAudioCapabilities *audioCaps) __INTRODUCED_IN(36);
+
+/**
+ * Returns the minimum number of input channels supported.
+ * This is often 1, but does vary for certain mime types.
+ *
+ * Return -1 if @param audioCaps is invalid.
+ */
+int32_t ACodecAudioCapabilities_getMinInputChannelCount(
+        const ACodecAudioCapabilities *audioCaps) __INTRODUCED_IN(36);
+
+/**
+ * Get an array of ranges representing the number of input channels supported.
+ * The codec supports any number of input channels within this range.
+ * For many codecs, this will be a single range [1..N], for some N.
+ *
+ * The array is sorted in ascending order, and the ranges are distinct (non-intersecting).
+ *
+ * @param outArrayPtr   The pointer to the output array of input-channels ranges.
+ *                      The array is owned by the framework and has an infinite lifetime.
+ * @param outCount      The size of the output array.
+ *
+ * Return AMEDIA_OK if got the input channel array successfully.
+ * Return AMEDIA_ERROR_INVALID_PARAMETER if @param audioCaps is invalid.
+ */
+media_status_t ACodecAudioCapabilities_getInputChannelCountRanges(
+        const ACodecAudioCapabilities *audioCaps,
+        const AIntRange **outArrayPtr, size_t *outCount) __INTRODUCED_IN(36);
+
+/**
+ * Query whether the sample rate is supported by the codec.
+ *
+ * Return 1 if the sample rate is supported.
+ * Return 0 if the sample rate is unsupported
+ * Return -1 if @param audioCaps is invalid.
+ */
+int32_t ACodecAudioCapabilities_isSampleRateSupported(const ACodecAudioCapabilities *audioCaps,
+        int32_t sampleRate) __INTRODUCED_IN(36);
+
+// ACodecPerformancePoint
+
+/**
+ * Create a performance point for a given frame size and frame rate.
+ *
+ * Performance points are defined by number of pixels, pixel rate and frame rate.
+ *
+ * Users are responsible for calling
+ * ACodecPerformancePoint_delete(ACodecPerformancePoint *performancePoint) after use.
+ *
+ * @param width width of the frame in pixels
+ * @param height height of the frame in pixels
+ * @param frameRate frame rate in frames per second
+ */
+ACodecPerformancePoint* ACodecPerformancePoint_create(int32_t width, int32_t height,
+        int32_t frameRate) __INTRODUCED_IN(36);
+
+/**
+ * Delete a created performance point.
+ *
+ * Return AMEDIA_OK if it is successfully deleted.
+ * Return AMEDIA_ERROR_INVALID_PARAMETER if @param performancePoint is invalid.
+ */
+media_status_t ACodecPerformancePoint_delete(
+        ACodecPerformancePoint *performancePoint) __INTRODUCED_IN(36);
+
+/**
+ * Checks whether the performance point covers a media format.
+ *
+ * @param format Stream format considered.
+ * Return true if the performance point covers the format.
+ */
+bool ACodecPerformancePoint_coversFormat(const ACodecPerformancePoint *performancePoint,
+        const AMediaFormat *format) __INTRODUCED_IN(36);
+
+/**
+ * Checks whether a performance point covers another performance point.
+ *
+ * Use this method to determine if a performance point advertised by a codec covers the
+ * performance point required. This method can also be used for loose ordering as this
+ * method is transitive.
+ *
+ * A Performance point represents an upper bound. This means that
+ * it covers all performance points with fewer pixels, pixel rate and frame rate.
+ *
+ * Return true if @param one covers @param another.
+ */
+bool ACodecPerformancePoint_covers(const ACodecPerformancePoint *one,
+        const ACodecPerformancePoint *another) __INTRODUCED_IN(36);
+
+/**
+ * Checks whether two performance points are equal.
+ */
+bool ACodecPerformancePoint_equals(const ACodecPerformancePoint *one,
+        const ACodecPerformancePoint *another) __INTRODUCED_IN(36);
+
+// ACodecVideoCapabilities
+
+/**
+ * Get the range of supported bitrates in bits/second.
+ *
+ * @param outRange  The pointer to the range of output bitrates.
+ *                  Users are responsible for allocating a valid AIntRange structure and
+ *                  managing the lifetime of it.
+ *
+ * Return AMEDIA_OK if got the supported bitrates successfully.
+ * Return AMEDIA_ERROR_INVALID_PARAMETER if any of @param videoCaps and @param outRange is invalid.
+ */
+media_status_t ACodecVideoCapabilities_getBitrateRange(const ACodecVideoCapabilities *videoCaps,
+        AIntRange *outRange) __INTRODUCED_IN(36);
+
+/**
+ * Get the range of supported video widths.
+ *
+ * @param outRange  The pointer to the range of output supported widths.
+ *                  Users are responsible for allocating a valid AIntRange structure and
+ *                  managing the lifetime of it.
+ *
+ * Return AMEDIA_OK if got the supported video widths successfully.
+ * Return AMEDIA_ERROR_INVALID_PARAMETER if any of @param videoCaps and @param outRange is invalid.
+ */
+media_status_t ACodecVideoCapabilities_getSupportedWidths(const ACodecVideoCapabilities *videoCaps,
+        AIntRange *outRange) __INTRODUCED_IN(36);
+
+/**
+ * Get the range of supported video heights.
+ *
+ * @param outRange  The pointer to the range of output supported heights.
+ *                  Users are responsible for allocating a valid AIntRange structure and
+ *                  managing the lifetime of it.
+ *
+ * Return AMEDIA_OK if got the supported video heights successfully.
+ * Return AMEDIA_ERROR_INVALID_PARAMETER if any of @param videoCaps and @param outRange is invalid.
+ */
+media_status_t ACodecVideoCapabilities_getSupportedHeights(const ACodecVideoCapabilities *videoCaps,
+        AIntRange *outRange) __INTRODUCED_IN(36);
+
+/**
+ * Return the alignment requirement for video width (in pixels).
+ *
+ * This is a power-of-2 value that video width must be a multiple of.
+ *
+ * Return -1 if @param videoCaps is invalid.
+ */
+int32_t ACodecVideoCapabilities_getWidthAlignment(
+        const ACodecVideoCapabilities *videoCaps) __INTRODUCED_IN(36);
+
+/**
+ * Return the alignment requirement for video height (in pixels).
+ *
+ * This is a power-of-2 value that video height must be a multiple of.
+ *
+ * Return -1 if @param videoCaps is invalid.
+ */
+int32_t ACodecVideoCapabilities_getHeightAlignment(
+        const ACodecVideoCapabilities *videoCaps) __INTRODUCED_IN(36);
+
+/**
+ * Get the range of supported frame rates.
+ *
+ * This is not a performance indicator. Rather, it expresses the limits specified in the coding
+ * standard, based on the complexities of encoding material for later playback at a certain
+ * frame rate, or the decoding of such material in non-realtime.
+ *
+ * @param outRange  The pointer to the range of output supported frame rates.
+ *                  Users are responsible for allocating a valid AIntRange structure and
+ *                  managing the lifetime of it.
+ *
+ * \return AMEDIA_OK if got the frame rate range successfully.
+ * \return AMEDIA_ERROR_INVALID_PARAMETER if any of @param videoCaps and @param outRange is invalid.
+ */
+media_status_t ACodecVideoCapabilities_getSupportedFrameRates(
+        const ACodecVideoCapabilities *videoCaps, AIntRange *outRange) __INTRODUCED_IN(36);
+
+/**
+ * Get the range of supported video widths for a video height.
+ *
+ * @param outRange      The pointer to the range of supported widths.
+ *                      Users are responsible for allocating a valid AIntRange structure and
+ *                      managing the lifetime of it.
+ *
+ * Return AMEDIA_OK if got the supported video width range successfully.
+ * Return AMEDIA_ERROR_UNSUPPORTED if the height query is not supported.
+ * Return AMEDIA_ERROR_INVALID_PARAMETER if any of @param videoCaps and @param outRange is invalid.
+ */
+media_status_t ACodecVideoCapabilities_getSupportedWidthsFor(
+        const ACodecVideoCapabilities *videoCaps, int32_t height,
+        AIntRange *outRange) __INTRODUCED_IN(36);
+
+/**
+ * Get the range of supported video heights for a video width.
+ *
+ * @param outRange      The pointer to the range of supported heights.
+ *                      Users are responsible for allocating a valid AIntRange structure and
+ *                      managing the lifetime of it.
+ *
+ * Return AMEDIA_OK if got the supported video height range successfully.
+ * Return AMEDIA_ERROR_UNSUPPORTED if the width query is not supported.
+ * Return AMEDIA_ERROR_INVALID_PARAMETER if any of @param videoCaps and @param outRange is invalid.
+ */
+media_status_t ACodecVideoCapabilities_getSupportedHeightsFor(
+        const ACodecVideoCapabilities *videoCaps, int32_t width,
+        AIntRange *outRange) __INTRODUCED_IN(36);
+
+/**
+ * Get the range of supported video frame rates for a video size.
+ *
+ * This is not a performance indicator.  Rather, it expresses the limits specified in the coding
+ * standard, based on the complexities of encoding material of a given size for later playback at
+ * a certain frame rate, or the decoding of such material in non-realtime.
+ *
+ * @param outRange      The pointer to the range of frame rates.
+ *                      Users are responsible for allocating a valid ADoubleRange structure and
+ *                      managing the lifetime of it.
+ *
+ * Return AMEDIA_OK if got the supported video frame rates successfully.
+ * Return AMEDIA_ERROR_UNSUPPORTED if the size query is not supported.
+ * Return AMEDIA_ERROR_INVALID_PARAMETER if any of @param videoCaps and @param outRange is invalid.
+ */
+media_status_t ACodecVideoCapabilities_getSupportedFrameRatesFor(
+        const ACodecVideoCapabilities *videoCaps, int32_t width, int32_t height,
+        ADoubleRange *outRange) __INTRODUCED_IN(36);
+
+/**
+ * Get the range of achievable video frame rates for a video size.
+ *
+ * This is based on manufacturer's performance measurements for this device and codec.
+ * The measurements may not be available for all codecs or devices.
+ *
+ * @param outRange      The pointer to the range of frame rates.
+  *                     Users are responsible for allocating a valid ADoubleRange structure and
+ *                      managing the lifetime of it.
+ *
+ * Return AMEDIA_OK if got the achievable video frame rates successfully.
+ * Return AMEDIA_ERROR_UNSUPPORTED if the codec did not publish any measurement data.
+ * Return AMEDIA_ERROR_INVALID_PARAMETER if any of @param videoCaps and @param outRange is invalid.
+ */
+media_status_t ACodecVideoCapabilities_getAchievableFrameRatesFor(
+        const ACodecVideoCapabilities *videoCaps, int32_t width, int32_t height,
+        ADoubleRange *outRange) __INTRODUCED_IN(36);
+
+/**
+ * Get the supported performance points.
+ *
+ * @param outPerformancePointArray      The pointer to the output performance points array.
+ *                                      The array is owned by the framework and has an infinite
+ *                                      lifetime.
+ * @param outCount                      The size of the output array.
+ *
+ * Return AMEDIA_OK if successfully got the performance points.
+ * Return AMEDIA_ERROR_INVALID_PARAMETER if @param videoCaps is invalid.
+ */
+media_status_t ACodecVideoCapabilities_getSupportedPerformancePoints(
+        const ACodecVideoCapabilities *videoCaps,
+        const ACodecPerformancePoint **outPerformancePointArray,
+        size_t *outCount) __INTRODUCED_IN(36);
+
+/**
+ * Return whether a given video size and frameRate combination is supported.
+ *
+ * Return 1 if the size and rate are supported.
+ * Return 0 if they are not supported.
+ * Return -1 if @param videoCaps is invalid.
+ */
+int32_t ACodecVideoCapabilities_areSizeAndRateSupported(const ACodecVideoCapabilities *videoCaps,
+        int32_t width, int32_t height, double frameRate) __INTRODUCED_IN(36);
+
+/**
+ * Return whether a given video size is supported.
+ *
+ * Return 1 if the size is supported.
+ * Return 0 if the size is not supported.
+ * Return -1 if @param videoCaps is invalid.
+ */
+int32_t ACodecVideoCapabilities_isSizeSupported(const ACodecVideoCapabilities *videoCaps,
+        int32_t width, int32_t height) __INTRODUCED_IN(36);
+
+// ACodecEncoderCapabilities
+
+/**
+ * Get the supported range of quality values.
+ *
+ * Quality is implementation-specific. As a general rule, a higher quality
+ * setting results in a better image quality and a lower compression ratio.
+ *
+ * @param outRange      The pointer to the range of quality values.
+ *                      Users are responsible for allocating a valid AIntRange structure and
+ *                      managing the lifetime of it.
+ *
+ * Return AMEDIA_OK if successfully got the quality range.
+ * Return AMEDIA_ERROR_INVALID_PARAMETER if any of @param videoCaps and @param outRange is invalid.
+ */
+media_status_t ACodecEncoderCapabilities_getQualityRange(
+        const ACodecEncoderCapabilities *encoderCaps,
+        AIntRange *outRange) __INTRODUCED_IN(36);
+
+/**
+ * Get the supported range of encoder complexity values.
+ *
+ * Some codecs may support multiple complexity levels, where higher complexity values use more
+ * encoder tools (e.g. perform more intensive calculations) to improve the quality or the
+ * compression ratio. Use a lower value to save power and/or time.
+ *
+ * @param outRange      The pointer to the range of encoder complexity values.
+ *                      Users are responsible for allocating a valid AIntRange structure and
+ *                      managing the lifetime of it.
+ *
+ * Return AMEDIA_OK if successfully got the complexity range.
+ * Return AMEDIA_ERROR_INVALID_PARAMETER if any of @param videoCaps and @param outRange is invalid.
+ */
+media_status_t ACodecEncoderCapabilities_getComplexityRange(
+        const ACodecEncoderCapabilities *encoderCaps,
+        AIntRange *outRange) __INTRODUCED_IN(36);
+
+/**
+ * Encoder bitrate modes.
+ */
+typedef enum ABiterateMode : int32_t {
+    BITRATE_MODE_CQ = 0,
+    BITRATE_MODE_VBR = 1,
+    BITRATE_MODE_CBR = 2,
+    BITRATE_MODE_CBR_FD = 3
+} ABiterateMode;
+
+/**
+ * Query whether a bitrate mode is supported.
+ *
+ * Return 1 if the bitrate mode is supported.
+ * Return 0 if the bitrate mode is unsupported.
+ * Return -1 if @param encoderCaps is invalid.
+ */
+int32_t ACodecEncoderCapabilities_isBitrateModeSupported(
+        const ACodecEncoderCapabilities *encoderCaps, ABiterateMode mode) __INTRODUCED_IN(36);
+
+__END_DECLS
+
+#endif //_NDK_MEDIA_CODEC_INFO_H
+
+/** @} */
\ No newline at end of file
diff --git a/media/ndk/include/media/NdkMediaCodecStore.h b/media/ndk/include/media/NdkMediaCodecStore.h
new file mode 100644
index 0000000..aab8689
--- /dev/null
+++ b/media/ndk/include/media/NdkMediaCodecStore.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @addtogroup Media
+ * @{
+ */
+
+/**
+ * @file NdkMediaCodecStore.h
+ */
+
+/*
+ * This file defines an NDK API.
+ * Do not remove methods.
+ * Do not change method signatures.
+ * Do not change the value of constants.
+ * Do not change the size of any of the classes defined in here.
+ * Do not reference types that are not part of the NDK.
+ * Do not #include files that aren't part of the NDK.
+ */
+
+#ifndef _NDK_MEDIA_CODEC_STORE_H
+#define _NDK_MEDIA_CODEC_STORE_H
+
+#include <stdint.h>
+
+#include "NdkMediaCodecInfo.h"
+#include "NdkMediaError.h"
+#include "NdkMediaFormat.h"
+
+__BEGIN_DECLS
+
+/**
+ * The media type definition with bitfeids indicating if it is
+ * supported by decoders/ encoders/ both.
+ */
+typedef struct AMediaCodecSupportedMediaType {
+    enum Mode : uint32_t {
+        FLAG_DECODER = 1 << 0,
+        FLAG_ENCODER = 1 << 1,
+    };
+
+    // The media type.
+    const char *mMediaType;
+    // bitfields for modes.
+    uint32_t mMode;
+} AMediaCodecSupportedMediaType;
+
+/**
+ * Get an array of all the supported media types of a device.
+ *
+ * @param outMediaTypes The pointer to the output AMediaCodecSupportedMediaType array.
+ *                      It is owned by the fraework and has an infinite lifetime.
+ *
+ * @param outCount size of the out array.
+ *
+ * Return AMEDIA_OK if successfully made the copy.
+ * Return AMEDIA_ERROR_INVALID_PARAMETER if the @param outMediaTypes is invalid.
+ */
+media_status_t AMediaCodecStore_getSupportedMediaTypes(
+        const AMediaCodecSupportedMediaType **outMediaTypes, size_t *outCount) __INTRODUCED_IN(36);
+
+/**
+ * Get the next decoder info that supports the format.
+ *
+ * @param outCodecInfo  should be set as NULL to start the iteration.
+ *                      Keep the last codecInfo you got from a previous call to get the next one.
+ *                      *outCodecInfo will be set to NULL if reached the end.
+ *                      It is owned by the framework and has an infinite lifetime.
+ *
+ * @param format        If set as NULL, this API will iterate through all available decoders.
+ *                      If NOT NULL, it MUST contain key "mime" implying the media type.
+ *
+ * Return AMEDIA_OK if successfully got the info.
+ * Return AMEDIA_ERROR_INVALID_PARAMETER if @param outCodecInfo or @param format is invalid.
+ * Return AMEDIA_ERROR_UNSUPPORTED if no more decoder supporting the format.
+ *
+ * It is undefined behavior to call this API with a NON NULL @param outCodecInfo
+ * and a different @param format during an iteration.
+ */
+media_status_t AMediaCodecStore_findNextDecoderForFormat(
+        const AMediaFormat *format, const AMediaCodecInfo **outCodecInfo) __INTRODUCED_IN(36);
+
+/**
+ * Get the next encoder info that supports the format.
+ *
+ * @param outCodecInfo  should be set as NULL to start the iteration.
+ *                      Keep the last codecInfo you got from a previous call to get the next one.
+ *                      *outCodecInfo will be set to NULL if reached the end.
+ *                      It is owned by the framework and has an infinite lifetime.
+ *
+ * @param format        If set as NULL, this API will iterate through all available encoders.
+ *                      If NOT NULL, it MUST contain key "mime" implying the media type.
+ *
+ * Return AMEDIA_OK if successfully got the info.
+ * Return AMEDIA_ERROR_INVALID_PARAMETER if @param outCodecInfo is invalid.
+ * Return AMEDIA_ERROR_UNSUPPORTED if no more encoder supporting the format.
+ *
+ * It is undefined behavior to call this API with a NON NULL @param outCodecInfo
+ * and a different @param format during an iteration.
+ *
+ * No secure encoder will show in the output.
+ */
+media_status_t AMediaCodecStore_findNextEncoderForFormat(
+        const AMediaFormat* format, const AMediaCodecInfo **outCodecInfo) __INTRODUCED_IN(36);
+
+/**
+ * Get the codecInfo corresponding to a given codec name.
+ *
+ * @param name          Media codec name.
+ *                      Users can get valid codec names from the AMediaCodecInfo structures
+ *                      returned from findNextDecoder|EncoderForFormat methods.
+ *                      Note that this name may not correspond to the name the same codec used
+ *                      by the SDK API, but will always do for codec names starting with "c2.".
+ *
+ * @param outCodecInfo  Output parameter for the corresponding AMeidaCodecInfo structure.
+ *                      It is owned by the framework and has an infinite lifetime.
+ *
+ * Return AMEDIA_OK if got the codecInfo successfully.
+ * Return AMEDIA_ERROR_UNSUPPORTED if no corresponding codec found.
+ * Return AMEDIA_ERROR_INVALID_PARAMETER if @param outCodecInfo or @param name is invalid.
+ */
+media_status_t AMediaCodecStore_getCodecInfo(
+        const char *name, const AMediaCodecInfo **outCodecInfo) __INTRODUCED_IN(36);
+
+__END_DECLS
+
+#endif //_NDK_MEDIA_CODEC_STORE_H
+
+/** @} */
\ No newline at end of file
diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt
index 262c169..939f151 100644
--- a/media/ndk/libmediandk.map.txt
+++ b/media/ndk/libmediandk.map.txt
@@ -1,5 +1,33 @@
 LIBMEDIANDK {
   global:
+    ACodecAudioCapabilities_getBitrateRange; # introduced=36
+    ACodecAudioCapabilities_getInputChannelCountRanges; # introduced=36
+    ACodecAudioCapabilities_getMaxInputChannelCount; # introduced=36
+    ACodecAudioCapabilities_getMinInputChannelCount; # introduced=36
+    ACodecAudioCapabilities_getSupportedSampleRates; # introduced=36
+    ACodecAudioCapabilities_getSupportedSampleRateRanges; # introduced=36
+    ACodecAudioCapabilities_isSampleRateSupported; # introduced=36
+    ACodecEncoderCapabilities_getComplexityRange; # introduced=36
+    ACodecEncoderCapabilities_getQualityRange; # introduced=36
+    ACodecEncoderCapabilities_isBitrateModeSupported; # introduced=36
+    ACodecPerformancePoint_create; # introduced=36
+    ACodecPerformancePoint_covers; # introduced=36
+    ACodecPerformancePoint_coversFormat; # introduced=36
+    ACodecPerformancePoint_delete; # introduced=36
+    ACodecPerformancePoint_equals; # introduced=36
+    ACodecVideoCapabilities_areSizeAndRateSupported; # introduced=36
+    ACodecVideoCapabilities_getAchievableFrameRatesFor; # introduced=36
+    ACodecVideoCapabilities_getBitrateRange; # introduced=36
+    ACodecVideoCapabilities_getHeightAlignment; # introduced=36
+    ACodecVideoCapabilities_getSupportedFrameRates; # introduced=36
+    ACodecVideoCapabilities_getSupportedFrameRatesFor; # introduced=36
+    ACodecVideoCapabilities_getSupportedHeights; # introduced=36
+    ACodecVideoCapabilities_getSupportedHeightsFor; # introduced=36
+    ACodecVideoCapabilities_getSupportedPerformancePoints; # introduced=36
+    ACodecVideoCapabilities_getSupportedWidths; # introduced=36
+    ACodecVideoCapabilities_getSupportedWidthsFor; # introduced=36
+    ACodecVideoCapabilities_getWidthAlignment; # introduced=36
+    ACodecVideoCapabilities_isSizeSupported; # introduced=36
     AImageReader_acquireLatestImage; # introduced=24
     AImageReader_acquireLatestImageAsync; # introduced=26
     AImageReader_acquireNextImage; # introduced=24
@@ -16,6 +44,7 @@
     AImageReader_newWithDataSpace; # introduced=UpsideDownCake
     AImageReader_setBufferRemovedListener; # introduced=26
     AImageReader_setImageListener; # introduced=24
+    AImageReader_setUsage; # introduced=36 llndk
     AImage_delete; # introduced=24
     AImage_deleteAsync; # introduced=26
     AImage_getCropRect; # introduced=24
@@ -216,6 +245,22 @@
     AMediaCodec_createPersistentInputSurface; # introduced=26
     AMediaCodec_start;
     AMediaCodec_stop;
+    AMediaCodecInfo_getAudioCapabilities; # introduced=36
+    AMediaCodecInfo_getEncoderCapabilities; # introduced=36
+    AMediaCodecInfo_getVideoCapabilities; # introduced=36
+    AMediaCodecInfo_getCanonicalName; # introduced=36
+    AMediaCodecInfo_getMaxSupportedInstances; # introduced=36
+    AMediaCodecInfo_getMediaCodecInfoType; # introduced=36
+    AMediaCodecInfo_getMediaType; # introduced=36
+    AMediaCodecInfo_isEncoder; # introduced=36
+    AMediaCodecInfo_isFeatureRequired; # introduced=36
+    AMediaCodecInfo_isFeatureSupported; # introduced=36
+    AMediaCodecInfo_isFormatSupported; # introduced=36
+    AMediaCodecInfo_isVendor; # introduced=36
+    AMediaCodecStore_getCodecInfo; # introduced=36
+    AMediaCodecStore_getSupportedMediaTypes; # introduced=36
+    AMediaCodecStore_findNextDecoderForFormat; # introduced=36
+    AMediaCodecStore_findNextEncoderForFormat; # introduced=36
     AMediaCrypto_delete;
     AMediaCrypto_isCryptoSchemeSupported;
     AMediaCrypto_new;
diff --git a/media/utils/EventLogTags.logtags b/media/utils/EventLogTags.logtags
index c397f34..5b98b0f 100644
--- a/media/utils/EventLogTags.logtags
+++ b/media/utils/EventLogTags.logtags
@@ -31,7 +31,7 @@
 # 6: Percent
 # Default value for data of type int/long is 2 (bytes).
 #
-# See system/core/logcat/event.logtags for the original definition of the tags.
+# See system/logging/logcat/event.logtags for the original definition of the tags.
 
 # 61000 - 61199 reserved for audioserver
 
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 8215247..282f3fa 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -98,10 +98,6 @@
 
 static constexpr char kAudioServiceName[] = "audio";
 
-// In order to avoid invalidating offloaded tracks each time a Visualizer is turned on and off
-// we define a minimum time during which a global effect is considered enabled.
-static const nsecs_t kMinGlobalEffectEnabletimeNs = seconds(7200);
-
 // Keep a strong reference to media.log service around forever.
 // The service is within our parent process so it can never die in a way that we could observe.
 // These two variables are const after initialization.
@@ -4842,11 +4838,6 @@
 
 bool AudioFlinger::isNonOffloadableGlobalEffectEnabled_l() const
 {
-    if (mGlobalEffectEnableTime != 0 &&
-            ((systemTime() - mGlobalEffectEnableTime) < kMinGlobalEffectEnabletimeNs)) {
-        return true;
-    }
-
     for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
         const auto thread = mPlaybackThreads.valueAt(i);
         audio_utils::lock_guard l(thread->mutex());
@@ -4862,8 +4853,6 @@
 {
     audio_utils::lock_guard _l(mutex());
 
-    mGlobalEffectEnableTime = systemTime();
-
     for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
         const sp<IAfPlaybackThread> t = mPlaybackThreads.valueAt(i);
         if (t->type() == IAfThreadBase::OFFLOAD) {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 6777075..c229e83 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -760,9 +760,6 @@
     std::atomic<size_t> mClientSharedHeapSize = kMinimumClientSharedHeapSizeBytes;
     static constexpr size_t kMinimumClientSharedHeapSizeBytes = 1024 * 1024; // 1MB
 
-    // when a global effect was last enabled
-    nsecs_t mGlobalEffectEnableTime GUARDED_BY(mutex()) = 0;
-
     /* const */ sp<IAfPatchPanel> mPatchPanel;
 
     const sp<EffectsFactoryHalInterface> mEffectsFactoryHal =
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 4aab996..ad48517 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3520,26 +3520,8 @@
 
 void PlaybackThread::checkSilentMode_l()
 {
-    if (!mMasterMute) {
-        char value[PROPERTY_VALUE_MAX];
-        if (mOutDeviceTypeAddrs.empty()) {
-            ALOGD("ro.audio.silent is ignored since no output device is set");
-            return;
-        }
-        if (isSingleDeviceType(outDeviceTypes_l(), AUDIO_DEVICE_OUT_REMOTE_SUBMIX)) {
-            ALOGD("ro.audio.silent will be ignored for threads on AUDIO_DEVICE_OUT_REMOTE_SUBMIX");
-            return;
-        }
-        if (property_get("ro.audio.silent", value, "0") > 0) {
-            char *endptr;
-            unsigned long ul = strtoul(value, &endptr, 0);
-            if (*endptr == '\0' && ul != 0) {
-                ALOGW("%s: mute from ro.audio.silent. Silence is golden", __func__);
-                // The setprop command will not allow a property to be changed after
-                // the first time it is set, so we don't have to worry about un-muting.
-                setMasterMute_l(true);
-            }
-        }
+    if (property_get_bool("ro.audio.silent", false)) {
+        ALOGW("ro.audio.silent is now ignored");
     }
 }
 
@@ -7778,6 +7760,9 @@
         audio_utils::lock_guard l(mutex());
         localTracks = std::move(mOutputTracks);
         mOutputTracks.clear();
+        for (size_t i = 0; i < localTracks.size(); ++i) {
+            localTracks[i]->destroy();
+        }
     }
     localTracks.clear();
     outputTracks.clear();
@@ -8437,7 +8422,6 @@
                     }
                     if (invalidate) {
                         activeTrack->invalidate();
-                        ALOG_ASSERT(fastTrackToRemove == 0);
                         fastTrackToRemove = activeTrack;
                         removeTrack_l(activeTrack);
                         mActiveTracks.remove(activeTrack);
@@ -8474,6 +8458,10 @@
 
             timestampCorrectionEnabled = isTimestampCorrectionEnabled_l();
             lockEffectChains_l(effectChains);
+            // We're exiting locked scope with non empty activeTracks, make sure
+            // that we're not in standby mode which we could have entered if some
+            // tracks were muted/unmuted.
+            mStandby = false;
         }
 
         // thread mutex is now unlocked, mActiveTracks unknown, activeTracks.size() > 0
@@ -11202,18 +11190,8 @@
 
 void MmapPlaybackThread::checkSilentMode_l()
 {
-    if (!mMasterMute) {
-        char value[PROPERTY_VALUE_MAX];
-        if (property_get("ro.audio.silent", value, "0") > 0) {
-            char *endptr;
-            unsigned long ul = strtoul(value, &endptr, 0);
-            if (*endptr == '\0' && ul != 0) {
-                ALOGW("%s: mute from ro.audio.silent. Silence is golden", __func__);
-                // The setprop command will not allow a property to be changed after
-                // the first time it is set, so we don't have to worry about un-muting.
-                setMasterMute_l(true);
-            }
-        }
+    if (property_get_bool("ro.audio.silent", false)) {
+        ALOGW("ro.audio.silent is now ignored");
     }
 }
 
diff --git a/services/audioparameterparser/Android.bp b/services/audioparameterparser/Android.bp
index 1c1c1e1..0b2c1ba 100644
--- a/services/audioparameterparser/Android.bp
+++ b/services/audioparameterparser/Android.bp
@@ -35,10 +35,10 @@
     name: "android.hardware.audio.parameter_parser.example_defaults",
     defaults: [
         "latest_android_hardware_audio_core_ndk_shared",
-        "latest_av_audio_types_aidl_ndk_shared",
     ],
 
     shared_libs: [
+        "av-audio-types-aidl-ndk",
         "libbase",
         "libbinder_ndk",
     ],
diff --git a/services/audiopolicy/common/include/policy.h b/services/audiopolicy/common/include/policy.h
index d499222..0c03900 100644
--- a/services/audiopolicy/common/include/policy.h
+++ b/services/audiopolicy/common/include/policy.h
@@ -22,31 +22,77 @@
 
 #include <media/AudioContainers.h>
 
+#include <string.h>
+
 namespace android {
 
 using StreamTypeVector = std::vector<audio_stream_type_t>;
 
+#define AUDIO_ENUM_QUOTE(x) #x
+#define AUDIO_ENUM_STRINGIFY(x) AUDIO_ENUM_QUOTE(x)
+#define AUDIO_DEFINE_ENUM_SYMBOL_V(symbol, value) symbol = value,
+#define AUDIO_DEFINE_STRINGIFY_CASE_V(symbol, _) case symbol: return AUDIO_ENUM_STRINGIFY(symbol);
+#define AUDIO_DEFINE_PARSE_CASE_V(symbol, _) \
+    if (strcmp(s, AUDIO_ENUM_STRINGIFY(symbol)) == 0) { *t = symbol; return true; } else
+#define AUDIO_DEFINE_MAP_ENTRY_V(symbol, _) { AUDIO_ENUM_STRINGIFY(symbol), symbol },
+
 /**
  * Legacy audio policy product strategies IDs. These strategies are supported by the default
  * policy engine.
  * IMPORTANT NOTE: the order of this enum is important as it determines the priority
- * between active strategies for routing decisions: lower enum value => higher prioriy
+ * between active strategies for routing decisions: lower enum value => higher priority
  */
+#define AUDIO_LEGACY_STRATEGY_LIST_DEF(V)      \
+    V(STRATEGY_NONE, -1)                       \
+    V(STRATEGY_PHONE, 0)                       \
+    V(STRATEGY_SONIFICATION, 1)                \
+    V(STRATEGY_ENFORCED_AUDIBLE, 2)            \
+    V(STRATEGY_ACCESSIBILITY, 3)               \
+    V(STRATEGY_SONIFICATION_RESPECTFUL, 4)     \
+    V(STRATEGY_MEDIA, 5)                       \
+    V(STRATEGY_DTMF, 6)                        \
+    V(STRATEGY_CALL_ASSISTANT, 7)              \
+    V(STRATEGY_TRANSMITTED_THROUGH_SPEAKER, 8) \
+    V(STRATEGY_REROUTING, 9)                   \
+    V(STRATEGY_PATCH, 10)
+
 enum legacy_strategy {
-    STRATEGY_NONE = -1,
-    STRATEGY_PHONE,
-    STRATEGY_SONIFICATION,
-    STRATEGY_ENFORCED_AUDIBLE,
-    STRATEGY_ACCESSIBILITY,
-    STRATEGY_SONIFICATION_RESPECTFUL,
-    STRATEGY_MEDIA,
-    STRATEGY_DTMF,
-    STRATEGY_CALL_ASSISTANT,
-    STRATEGY_TRANSMITTED_THROUGH_SPEAKER,
-    STRATEGY_REROUTING,
-    STRATEGY_PATCH,
+    AUDIO_LEGACY_STRATEGY_LIST_DEF(AUDIO_DEFINE_ENUM_SYMBOL_V)
 };
 
+inline const char* legacy_strategy_to_string(legacy_strategy t) {
+    switch (t) {
+    AUDIO_LEGACY_STRATEGY_LIST_DEF(AUDIO_DEFINE_STRINGIFY_CASE_V)
+    }
+    return "";
+}
+
+inline bool legacy_strategy_from_string(const char* s, legacy_strategy* t) {
+    AUDIO_LEGACY_STRATEGY_LIST_DEF(AUDIO_DEFINE_PARSE_CASE_V)
+    return false;
+}
+
+namespace audio_policy {
+
+struct legacy_strategy_map { const char *name; legacy_strategy id; };
+
+inline std::vector<legacy_strategy_map> getLegacyStrategyMap() {
+    return std::vector<legacy_strategy_map> {
+    AUDIO_LEGACY_STRATEGY_LIST_DEF(AUDIO_DEFINE_MAP_ENTRY_V)
+    };
+}
+
+}  // namespace audio_policy
+
+#undef AUDIO_LEGACY_STRATEGY_LIST_DEF
+
+#undef AUDIO_DEFINE_MAP_ENTRY_V
+#undef AUDIO_DEFINE_PARSE_CASE_V
+#undef AUDIO_DEFINE_STRINGIFY_CASE_V
+#undef AUDIO_DEFINE_ENUM_SYMBOL_V
+#undef AUDIO_ENUM_STRINGIFY
+#undef AUDIO_ENUM_QUOTE
+
 static const audio_attributes_t defaultAttr = AUDIO_ATTRIBUTES_INITIALIZER;
 
 static const std::set<audio_usage_t > gHighPriorityUseCases = {
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
index f066c09..b29033e 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
@@ -147,6 +147,7 @@
 private:
     bool mixMatch(const AudioMix* mix, size_t mixIndex,
                             const audio_attributes_t& attributes,
+                            const audio_output_flags_t outputFlags,
                             const audio_config_base_t& config,
                             uid_t uid,
                             audio_session_t session);
diff --git a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
index f7b9b33..9107e2a 100644
--- a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
@@ -75,7 +75,8 @@
     bool     isEffectEnabled(int id) const;
     uint32_t getMaxEffectsCpuLoad() const;
     uint32_t getMaxEffectsMemory() const;
-    bool isNonOffloadableEffectEnabled() const;
+    bool isNonOffloadableEffectEnabled(
+            const std::optional<const effect_uuid_t>& uuid = std::nullopt) const;
 
     void moveEffects(audio_session_t session,
                      audio_io_handle_t srcOutput,
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index 3430f4b..ea78a5d 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -361,7 +361,7 @@
             continue; // Primary output already found
         }
 
-        if(!mixMatch(policyMix.get(), i, attributes, config, uid, session)) {
+        if(!mixMatch(policyMix.get(), i, attributes, flags, config, uid, session)) {
             ALOGV("%s: Mix %zu: does not match", __func__, i);
             continue; // skip the mix
         }
@@ -422,8 +422,8 @@
 }
 
 bool AudioPolicyMixCollection::mixMatch(const AudioMix* mix, size_t mixIndex,
-    const audio_attributes_t& attributes, const audio_config_base_t& config,
-    uid_t uid, audio_session_t session) {
+    const audio_attributes_t& attributes, const audio_output_flags_t outputFlags,
+    const audio_config_base_t& config, uid_t uid, audio_session_t session) {
 
     if (mix->mMixType == MIX_TYPE_PLAYERS) {
         // Loopback render mixes are created from a public API and thus restricted
@@ -451,12 +451,17 @@
         }
 
         // Permit match only if requested format and mix format are PCM and can be format
-        // adapted by the mixer, or are the same (compressed) format.
+        // adapted by the mixer, or are the same format on direct output.
         if (!is_mix_loopback(mix->mRouteFlags) &&
-            !((audio_is_linear_pcm(config.format) && audio_is_linear_pcm(mix->mFormat.format)) ||
-              (config.format == mix->mFormat.format)) &&
-              config.format != AUDIO_CONFIG_BASE_INITIALIZER.format) {
-            return false;
+                config.format != AUDIO_CONFIG_BASE_INITIALIZER.format) {
+            if (!audio_output_is_mixed_output_flags(outputFlags)) {
+                // Direct output must match format exactly.
+                if (config.format != mix->mFormat.format) return false;
+            } else {
+                // If mixable, both requested and mix format must be linear pcm.
+                if (!audio_is_linear_pcm(config.format) ||
+                          !audio_is_linear_pcm(mix->mFormat.format)) return false;
+            }
         }
 
         // if there is an address match, prioritize that match
diff --git a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
index 090da6c..6d66781 100644
--- a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
@@ -21,6 +21,7 @@
 
 #include "AudioInputDescriptor.h"
 #include "EffectDescriptor.h"
+#include <system/audio_effects/audio_effects_utils.h>
 #include <utils/String8.h>
 
 #include <AudioPolicyInterface.h>
@@ -157,14 +158,18 @@
     return NO_ERROR;
 }
 
-bool EffectDescriptorCollection::isNonOffloadableEffectEnabled() const
+bool EffectDescriptorCollection::isNonOffloadableEffectEnabled(
+        const std::optional<const effect_uuid_t>& uuid) const
 {
+    using namespace android::effect::utils;
     for (size_t i = 0; i < size(); i++) {
         sp<EffectDescriptor> effectDesc = valueAt(i);
-        if (effectDesc->mEnabled && (effectDesc->isMusicEffect()) &&
-                ((effectDesc->mDesc.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) == 0)) {
-            ALOGV("isNonOffloadableEffectEnabled() non offloadable effect %s enabled on session %d",
-                  effectDesc->mDesc.name, effectDesc->mSession);
+        if ((effectDesc->mEnabled && (effectDesc->isMusicEffect()) &&
+             ((effectDesc->mDesc.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) == 0)) &&
+            (uuid == std::nullopt || uuid.value() == effectDesc->mDesc.uuid)) {
+            ALOGE("%s: non offloadable effect %s, uuid %s, enabled on session %d", __func__,
+                  effectDesc->mDesc.name, ToString(effectDesc->mDesc.uuid).c_str(),
+                  effectDesc->mSession);
             return true;
         }
     }
diff --git a/services/audiopolicy/engine/common/include/EngineBase.h b/services/audiopolicy/engine/common/include/EngineBase.h
index edb2e29..4445b66 100644
--- a/services/audiopolicy/engine/common/include/EngineBase.h
+++ b/services/audiopolicy/engine/common/include/EngineBase.h
@@ -117,9 +117,10 @@
             AudioDeviceTypeAddrVector &devices) const override;
 
     engineConfig::ParsingResult loadAudioPolicyEngineConfig(
-            const media::audio::common::AudioHalEngineConfig& aidlConfig);
+            const media::audio::common::AudioHalEngineConfig& aidlConfig, bool);
 
-    engineConfig::ParsingResult loadAudioPolicyEngineConfig(const std::string& xmlFilePath = "");
+    engineConfig::ParsingResult loadAudioPolicyEngineConfig(
+            const std::string& xmlFilePath = "", bool isConfigurable = false);
 
     const ProductStrategyMap &getProductStrategies() const { return mProductStrategies; }
 
diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp
index fb8379e..0799399 100644
--- a/services/audiopolicy/engine/common/src/EngineBase.cpp
+++ b/services/audiopolicy/engine/common/src/EngineBase.cpp
@@ -126,7 +126,7 @@
 }
 
 engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig(
-        const media::audio::common::AudioHalEngineConfig& aidlConfig)
+        const media::audio::common::AudioHalEngineConfig& aidlConfig, bool)
 {
     engineConfig::ParsingResult result = engineConfig::convert(aidlConfig);
     if (result.parsedConfig == nullptr) {
@@ -141,7 +141,8 @@
     return processParsingResult(std::move(result));
 }
 
-engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig(const std::string& xmlFilePath)
+engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig(
+        const std::string& xmlFilePath, bool isConfigurable)
 {
     auto fileExists = [](const char* path) {
         struct stat fileStat;
@@ -150,7 +151,7 @@
     const std::string filePath = xmlFilePath.empty() ? engineConfig::DEFAULT_PATH : xmlFilePath;
     engineConfig::ParsingResult result =
             fileExists(filePath.c_str()) ?
-            engineConfig::parse(filePath.c_str()) : engineConfig::ParsingResult{};
+            engineConfig::parse(filePath.c_str(), isConfigurable) : engineConfig::ParsingResult{};
     if (result.parsedConfig == nullptr) {
         ALOGD("%s: No configuration found, using default matching phone experience.", __FUNCTION__);
         engineConfig::Config config = gDefaultEngineConfig;
diff --git a/services/audiopolicy/engine/config/include/EngineConfig.h b/services/audiopolicy/engine/config/include/EngineConfig.h
index 054bdae..8a4fc88 100644
--- a/services/audiopolicy/engine/config/include/EngineConfig.h
+++ b/services/audiopolicy/engine/config/include/EngineConfig.h
@@ -116,7 +116,7 @@
 /** Parses the provided audio policy usage configuration.
  * @return audio policy usage @see Config
  */
-ParsingResult parse(const char* path = DEFAULT_PATH);
+ParsingResult parse(const char* path = DEFAULT_PATH, bool isConfigurable = false);
 android::status_t parseLegacyVolumes(VolumeGroups &volumeGroups);
 ParsingResult convert(const ::android::media::audio::common::AudioHalEngineConfig& aidlConfig);
 // Exposed for testing.
diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp
index 714ab78..b8d95ee 100644
--- a/services/audiopolicy/engine/config/src/EngineConfig.cpp
+++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp
@@ -52,6 +52,8 @@
 
 namespace {
 
+static bool gIsConfigurableEngine = false;
+
 ConversionResult<std::string> aidl2legacy_AudioHalProductStrategy_ProductStrategyType(int id) {
     using AudioProductStrategyType = media::audio::common::AudioProductStrategyType;
 
@@ -547,9 +549,16 @@
         if (!convertTo(idLiteral, id)) {
             return BAD_VALUE;
         }
-        ALOGV("%s: %s, %s = %d", __FUNCTION__, name.c_str(), Attributes::id, id);
+    } else {
+        legacy_strategy legacyId;
+        if (legacy_strategy_from_string(name.c_str(), &legacyId)) {
+            id = legacyId;
+        } else if (!gIsConfigurableEngine) {
+            return BAD_VALUE;
+        }
+        // With a configurable engine it can be a vendor-provided strategy name.
     }
-    ALOGV("%s: %s = %s", __FUNCTION__, Attributes::name, name.c_str());
+    ALOGV("%s: %s, %s = %d", __FUNCTION__, name.c_str(), Attributes::id, id);
 
     size_t skipped = 0;
     AttributesGroups attrGroups;
@@ -776,7 +785,7 @@
 
 }  // namespace
 
-ParsingResult parse(const char* path) {
+ParsingResult parse(const char* path, bool isConfigurable) {
     XmlErrorHandler errorHandler;
     auto doc = make_xmlUnique(xmlParseFile(path));
     if (doc == NULL) {
@@ -801,6 +810,7 @@
         ALOGE("%s: No version found", __func__);
         return {nullptr, 0};
     }
+    gIsConfigurableEngine = isConfigurable;
     size_t nbSkippedElements = 0;
     auto config = std::make_unique<Config>();
     config->version = std::stof(version);
diff --git a/services/audiopolicy/engineconfigurable/config/example/common/audio_policy_engine_criterion_types_aidl.xml.in b/services/audiopolicy/engineconfigurable/config/example/common/audio_policy_engine_criterion_types_aidl.xml.in
index dc2517b..424c983 100644
--- a/services/audiopolicy/engineconfigurable/config/example/common/audio_policy_engine_criterion_types_aidl.xml.in
+++ b/services/audiopolicy/engineconfigurable/config/example/common/audio_policy_engine_criterion_types_aidl.xml.in
@@ -19,76 +19,77 @@
     <criterion_type name="OutputDevicesAddressesType" type="inclusive">
         <values>
             <!-- legacy remote submix -->
-            <value literal="0" numerical="1"/>
+            <value literal="0"/>
         </values>
     </criterion_type>
     <criterion_type name="InputDevicesAddressesType" type="inclusive">
         <values>
             <!-- legacy remote submix -->
-            <value literal="0" numerical="1"/>
+            <value literal="0"/>
         </values>
     </criterion_type>
     <criterion_type name="AndroidModeType" type="exclusive"/>
     <criterion_type name="ForceUseForCommunicationType" type="exclusive">
         <values>
-            <value literal="NONE" numerical="0"/>
-            <value literal="SPEAKER" numerical="1"/>
-            <value literal="BT_SCO" numerical="3"/>
+            <value literal="NONE"/>
+            <value literal="SPEAKER"/>
+            <value literal="BT_SCO"/>
         </values>
     </criterion_type>
     <criterion_type name="ForceUseForMediaType" type="exclusive">
         <values>
-            <value literal="NONE" numerical="0"/>
-            <value literal="SPEAKER" numerical="1"/>
-            <value literal="HEADPHONES" numerical="2"/>
-            <value literal="BT_A2DP" numerical="4"/>
-            <value literal="WIRED_ACCESSORY" numerical="5"/>
-            <value literal="ANALOG_DOCK" numerical="8"/>
-            <value literal="DIGITAL_DOCK" numerical="9"/>
-            <value literal="NO_BT_A2DP" numerical="10"/>
+            <value literal="NONE"/>
+            <value literal="SPEAKER"/>
+            <value literal="HEADPHONES"/>
+            <value literal="BT_A2DP"/>
+            <value literal="ANALOG_DOCK"/>
+            <value literal="DIGITAL_DOCK"/>
+            <value literal="WIRED_ACCESSORY"/>
+            <value literal="NO_BT_A2DP"/>
         </values>
     </criterion_type>
     <criterion_type name="ForceUseForRecordType" type="exclusive">
         <values>
-            <value literal="NONE" numerical="0"/>
-            <value literal="BT_SCO" numerical="3"/>
-            <value literal="WIRED_ACCESSORY" numerical="5"/>
+            <value literal="NONE"/>
+            <value literal="BT_SCO"/>
+            <value literal="WIRED_ACCESSORY"/>
         </values>
     </criterion_type>
     <criterion_type name="ForceUseForDockType" type="exclusive">
         <values>
-            <value literal="NONE" numerical="0"/>
-            <value literal="WIRED_ACCESSORY" numerical="5"/>
-            <value literal="BT_CAR_DOCK" numerical="6"/>
-            <value literal="BT_DESK_DOCK" numerical="7"/>
-            <value literal="ANALOG_DOCK" numerical="8"/>
-            <value literal="DIGITAL_DOCK" numerical="9"/>
+            <value literal="NONE"/>
+            <value literal="BT_CAR_DOCK"/>
+            <value literal="BT_DESK_DOCK"/>
+            <value literal="ANALOG_DOCK"/>
+            <value literal="DIGITAL_DOCK"/>
+            <value literal="WIRED_ACCESSORY"/>
         </values>
     </criterion_type>
     <criterion_type name="ForceUseForSystemType" type="exclusive" >
         <values>
-            <value literal="NONE" numerical="0"/>
-            <value literal="SYSTEM_ENFORCED" numerical="11"/>
+            <value literal="NONE"/>
+            <value literal="SYSTEM_ENFORCED"/>
         </values>
     </criterion_type>
     <criterion_type name="ForceUseForHdmiSystemAudioType" type="exclusive">
         <values>
-            <value literal="NONE" numerical="0"/>
-            <value literal="HDMI_SYSTEM_AUDIO_ENFORCED" numerical="12"/>
+            <value literal="NONE"/>
+            <value literal="HDMI_SYSTEM_AUDIO_ENFORCED"/>
         </values>
     </criterion_type>
     <criterion_type name="ForceUseForEncodedSurroundType" type="exclusive">
         <values>
-            <value literal="NONE" numerical="0"/>
-            <value literal="ENCODED_SURROUND_NEVER" numerical="13"/>
-            <value literal="ENCODED_SURROUND_ALWAYS" numerical="14"/>
-            <value literal="ENCODED_SURROUND_MANUAL" numerical="15"/>
+            <value literal="UNSPECIFIED"/>
+            <value literal="NEVER"/>
+            <value literal="ALWAYS"/>
+            <value literal="MANUAL"/>
         </values>
     </criterion_type>
     <criterion_type name="ForceUseForVibrateRingingType" type="exclusive">
         <values>
-            <value literal="NONE" numerical="0"/>
-            <value literal="BT_SCO" numerical="3"/>
+            <value literal="NONE"/>
+            <value literal="BT_SCO"/>
+            <value literal="BT_BLE"/>
         </values>
     </criterion_type>
 </criterion_types>
diff --git a/services/audiopolicy/engineconfigurable/config/src/CapEngineConfig.cpp b/services/audiopolicy/engineconfigurable/config/src/CapEngineConfig.cpp
index a1b4470..b89fba0 100644
--- a/services/audiopolicy/engineconfigurable/config/src/CapEngineConfig.cpp
+++ b/services/audiopolicy/engineconfigurable/config/src/CapEngineConfig.cpp
@@ -41,21 +41,23 @@
 
 namespace android {
 
-using utilities::convertTo;
+using base::unexpected;
 using media::audio::common::AudioDeviceAddress;
 using media::audio::common::AudioDeviceDescription;
 using media::audio::common::AudioHalCapCriterion;
+using media::audio::common::AudioHalCapCriterionV2;
 using media::audio::common::AudioHalCapParameter;
 using media::audio::common::AudioHalCapRule;
+using media::audio::common::AudioPolicyForceUse;
 using media::audio::common::AudioSource;
 using media::audio::common::AudioStreamType;
-using media::audio::common::AudioHalCapCriterionV2;
-using ::android::base::unexpected;
+using utilities::convertTo;
 
 namespace capEngineConfig {
 
 static constexpr const char *gLegacyOutputDevicePrefix = "AUDIO_DEVICE_OUT_";
 static constexpr const char *gLegacyInputDevicePrefix = "AUDIO_DEVICE_IN_";
+static constexpr const char *gLegacyForcePrefix = "AUDIO_POLICY_FORCE_";
 static constexpr const char *gLegacyStreamPrefix = "AUDIO_STREAM_";
 static constexpr const char *gLegacySourcePrefix = "AUDIO_SOURCE_";
 static constexpr const char *gPolicyParamPrefix = "/Policy/policy/";
@@ -83,6 +85,134 @@
     return capName;
 }
 
+ConversionResult<audio_policy_forced_cfg_t>
+        aidl2legacy_AudioPolicyForceUseCommunicationDeviceCategory_audio_policy_forced_cfg_t(
+        const AudioPolicyForceUse::CommunicationDeviceCategory aidl) {
+    switch (aidl) {
+        case AudioPolicyForceUse::CommunicationDeviceCategory::NONE:
+            return AUDIO_POLICY_FORCE_NONE;
+        case AudioPolicyForceUse::CommunicationDeviceCategory::SPEAKER:
+            return AUDIO_POLICY_FORCE_SPEAKER;
+        case AudioPolicyForceUse::CommunicationDeviceCategory::BT_SCO:
+            return AUDIO_POLICY_FORCE_BT_SCO;
+        case AudioPolicyForceUse::CommunicationDeviceCategory::BT_BLE:
+            return AUDIO_POLICY_FORCE_BT_BLE;
+        case AudioPolicyForceUse::CommunicationDeviceCategory::WIRED_ACCESSORY:
+            return AUDIO_POLICY_FORCE_WIRED_ACCESSORY;
+    }
+    return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_policy_forced_cfg_t>
+        aidl2legacy_AudioPolicyForceUseMediaDeviceCategory_audio_policy_forced_cfg_t(
+        const AudioPolicyForceUse::MediaDeviceCategory aidl) {
+    switch (aidl) {
+        case AudioPolicyForceUse::MediaDeviceCategory::NONE:
+            return AUDIO_POLICY_FORCE_NONE;
+        case AudioPolicyForceUse::MediaDeviceCategory::SPEAKER:
+            return AUDIO_POLICY_FORCE_SPEAKER;
+        case AudioPolicyForceUse::MediaDeviceCategory::HEADPHONES:
+            return AUDIO_POLICY_FORCE_HEADPHONES;
+        case AudioPolicyForceUse::MediaDeviceCategory::BT_A2DP:
+            return AUDIO_POLICY_FORCE_BT_A2DP;
+        case AudioPolicyForceUse::MediaDeviceCategory::ANALOG_DOCK:
+            return AUDIO_POLICY_FORCE_ANALOG_DOCK;
+        case AudioPolicyForceUse::MediaDeviceCategory::DIGITAL_DOCK:
+            return AUDIO_POLICY_FORCE_DIGITAL_DOCK;
+        case AudioPolicyForceUse::MediaDeviceCategory::WIRED_ACCESSORY:
+            return AUDIO_POLICY_FORCE_WIRED_ACCESSORY;
+        case AudioPolicyForceUse::MediaDeviceCategory::NO_BT_A2DP:
+            return AUDIO_POLICY_FORCE_NO_BT_A2DP;
+    }
+    return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_policy_forced_cfg_t>
+        aidl2legacy_AudioPolicyForceUseDockType_audio_policy_forced_cfg_t(
+        const AudioPolicyForceUse::DockType aidl) {
+    switch (aidl) {
+        case AudioPolicyForceUse::DockType::NONE:
+            return AUDIO_POLICY_FORCE_NONE;
+        case AudioPolicyForceUse::DockType::BT_CAR_DOCK:
+            return AUDIO_POLICY_FORCE_BT_CAR_DOCK;
+        case AudioPolicyForceUse::DockType::BT_DESK_DOCK:
+            return AUDIO_POLICY_FORCE_BT_DESK_DOCK;
+        case AudioPolicyForceUse::DockType::ANALOG_DOCK:
+            return AUDIO_POLICY_FORCE_ANALOG_DOCK;
+        case AudioPolicyForceUse::DockType::DIGITAL_DOCK:
+            return AUDIO_POLICY_FORCE_DIGITAL_DOCK;
+        case AudioPolicyForceUse::DockType::WIRED_ACCESSORY:
+            return AUDIO_POLICY_FORCE_WIRED_ACCESSORY;
+    }
+    return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_policy_forced_cfg_t>
+        aidl2legacy_AudioPolicyForceUseEncodedSurroundConfig_audio_policy_forced_cfg_t(
+        const AudioPolicyForceUse::EncodedSurroundConfig aidl) {
+    switch (aidl) {
+        case AudioPolicyForceUse::EncodedSurroundConfig::UNSPECIFIED:
+            return AUDIO_POLICY_FORCE_NONE;
+        case AudioPolicyForceUse::EncodedSurroundConfig::NEVER:
+            return AUDIO_POLICY_FORCE_ENCODED_SURROUND_NEVER;
+        case AudioPolicyForceUse::EncodedSurroundConfig::ALWAYS:
+            return AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS;
+        case AudioPolicyForceUse::EncodedSurroundConfig::MANUAL:
+            return AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL;
+    }
+    return unexpected(BAD_VALUE);
+}
+
+ConversionResult<std::pair<audio_policy_force_use_t, audio_policy_forced_cfg_t>>
+        aidl2legacy_AudioPolicyForceUse_audio_policy_force_use_t_audio_policy_forced_cfg_t(
+        const AudioPolicyForceUse& aidl) {
+    switch (aidl.getTag()) {
+        case AudioPolicyForceUse::forCommunication:
+            return std::make_pair(
+                    AUDIO_POLICY_FORCE_FOR_COMMUNICATION,
+                    VALUE_OR_RETURN(
+                            aidl2legacy_AudioPolicyForceUseCommunicationDeviceCategory_audio_policy_forced_cfg_t(
+                                    aidl.get<AudioPolicyForceUse::forCommunication>())));
+        case AudioPolicyForceUse::forMedia:
+            return std::make_pair(
+                    AUDIO_POLICY_FORCE_FOR_MEDIA,
+                    VALUE_OR_RETURN(
+                            aidl2legacy_AudioPolicyForceUseMediaDeviceCategory_audio_policy_forced_cfg_t(
+                                    aidl.get<AudioPolicyForceUse::forMedia>())));
+        case AudioPolicyForceUse::forRecord:
+            return std::make_pair(
+                    AUDIO_POLICY_FORCE_FOR_RECORD,
+                    VALUE_OR_RETURN(
+                            aidl2legacy_AudioPolicyForceUseCommunicationDeviceCategory_audio_policy_forced_cfg_t(
+                                    aidl.get<AudioPolicyForceUse::forRecord>())));
+        case AudioPolicyForceUse::dock:
+            return std::make_pair(AUDIO_POLICY_FORCE_FOR_DOCK,
+                    VALUE_OR_RETURN(
+                            aidl2legacy_AudioPolicyForceUseDockType_audio_policy_forced_cfg_t(
+                                    aidl.get<AudioPolicyForceUse::dock>())));
+        case AudioPolicyForceUse::systemSounds:
+            return std::make_pair(AUDIO_POLICY_FORCE_FOR_SYSTEM,
+                    aidl.get<AudioPolicyForceUse::systemSounds>() ?
+                    AUDIO_POLICY_FORCE_SYSTEM_ENFORCED : AUDIO_POLICY_FORCE_NONE);
+        case AudioPolicyForceUse::hdmiSystemAudio:
+            return std::make_pair(
+                    AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO,
+                    aidl.get<AudioPolicyForceUse::hdmiSystemAudio>() ?
+                    AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED : AUDIO_POLICY_FORCE_NONE);
+        case AudioPolicyForceUse::encodedSurround:
+            return std::make_pair(AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND, VALUE_OR_RETURN(
+                aidl2legacy_AudioPolicyForceUseEncodedSurroundConfig_audio_policy_forced_cfg_t(
+                        aidl.get<AudioPolicyForceUse::encodedSurround>())));
+        case AudioPolicyForceUse::forVibrateRinging:
+            return std::make_pair(
+                    AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING,
+                    VALUE_OR_RETURN(
+                            aidl2legacy_AudioPolicyForceUseCommunicationDeviceCategory_audio_policy_forced_cfg_t(
+                                    aidl.get<AudioPolicyForceUse::forVibrateRinging>())));
+    }
+    return unexpected(BAD_VALUE);
+}
+
 ConversionResult<std::string> aidl2legacy_AudioHalCapCriterionV2_CapName(
         const AudioHalCapCriterionV2& aidl) {
     switch (aidl.getTag()) {
@@ -97,14 +227,14 @@
         case AudioHalCapCriterionV2::telephonyMode:
             return gPhoneStateCriterionName;
         case AudioHalCapCriterionV2::forceConfigForUse: {
-            auto aidlCriterion = aidl.get<AudioHalCapCriterionV2::forceConfigForUse>();
-            return gForceUseCriterionTag[VALUE_OR_RETURN(
-                    aidl2legacy_AudioPolicyForceUse_audio_policy_force_use_t(
-                            aidlCriterion.forceUse))];
+            auto aidlCriterion = aidl.get<AudioHalCapCriterionV2::forceConfigForUse>().values[0];
+            const auto [forceUse, _] = VALUE_OR_RETURN(
+                aidl2legacy_AudioPolicyForceUse_audio_policy_force_use_t_audio_policy_forced_cfg_t(
+                        aidlCriterion));
+            return gForceUseCriterionTag[forceUse];
         }
-        default:
-            return unexpected(BAD_VALUE);
     }
+    return unexpected(BAD_VALUE);
 }
 
 ConversionResult<std::string> aidl2legacy_AudioHalCapCriterionV2TypeDevice_CapCriterionValue(
@@ -121,22 +251,54 @@
             isOut ? gLegacyOutputDevicePrefix : gLegacyInputDevicePrefix);
 }
 
-ConversionResult<std::string> aidl2legacy_AudioHalCapCriterionV2Type_CapCriterionValue(
-        const AudioHalCapCriterionV2::Type& aidl) {
-    switch (aidl.getTag()) {
-        case AudioHalCapCriterionV2::Type::availableDevicesType:
-            return aidl2legacy_AudioHalCapCriterionV2TypeDevice_CapCriterionValue(
-                    aidl.get<AudioHalCapCriterionV2::Type::availableDevicesType>());
-        case AudioHalCapCriterionV2::Type::availableDevicesAddressesType:
-            return aidl.get<AudioHalCapCriterionV2::Type::availableDevicesAddressesType>().template
-                    get<AudioDeviceAddress::id>();
-        case AudioHalCapCriterionV2::Type::telephonyModeType:
-            return toString(aidl.get<AudioHalCapCriterionV2::Type::telephonyModeType>());
-        case AudioHalCapCriterionV2::Type::forcedConfigType:
-            return toString(aidl.get<AudioHalCapCriterionV2::Type::forcedConfigType>());
-        default:
-            return unexpected(BAD_VALUE);
+ConversionResult<audio_policy_forced_cfg_t>
+        aidl2legacy_AudioHalCapCriterionV2ForceUse_audio_policy_forced_cfg_t(
+        const AudioPolicyForceUse& aidl) {
+    const auto [_, legacyForcedCfg] = VALUE_OR_RETURN(
+            aidl2legacy_AudioPolicyForceUse_audio_policy_force_use_t_audio_policy_forced_cfg_t(
+                    aidl));
+    return legacyForcedCfg;
+}
+
+ConversionResult<std::string> audio_policy_forced_cfg_t_CapCriterionValue(
+        audio_policy_forced_cfg_t legacyForcedCfg) {
+    std::string legacyForcedCfgLiteral = audio_policy_forced_cfg_to_string(legacyForcedCfg);
+    if (legacyForcedCfgLiteral.empty()) {
+        ALOGE("%s Invalid forced config value %d", __func__, legacyForcedCfg);
+        return unexpected(BAD_VALUE);
     }
+    return truncatePrefix(legacyForcedCfgLiteral, gLegacyForcePrefix);
+}
+
+ConversionResult<std::string> aidl2legacy_AudioHalCapCriterionV2ForceUse_CapCriterionValue(
+        const AudioPolicyForceUse& aidl) {
+    const audio_policy_forced_cfg_t legacyForcedCfg = VALUE_OR_RETURN(
+            aidl2legacy_AudioHalCapCriterionV2ForceUse_audio_policy_forced_cfg_t(aidl));
+    return audio_policy_forced_cfg_t_CapCriterionValue(legacyForcedCfg);
+}
+
+ConversionResult<std::string> aidl2legacy_AudioHalCapCriterionV2Type_CapCriterionValue(
+        const AudioHalCapCriterionV2& aidl) {
+    switch (aidl.getTag()) {
+        case AudioHalCapCriterionV2::availableInputDevices:
+            return aidl2legacy_AudioHalCapCriterionV2TypeDevice_CapCriterionValue(
+                    aidl.get<AudioHalCapCriterionV2::availableInputDevices>().values[0]);
+        case AudioHalCapCriterionV2::availableOutputDevices:
+            return aidl2legacy_AudioHalCapCriterionV2TypeDevice_CapCriterionValue(
+                    aidl.get<AudioHalCapCriterionV2::availableOutputDevices>().values[0]);
+        case AudioHalCapCriterionV2::availableInputDevicesAddresses:
+            return aidl.get<AudioHalCapCriterionV2::availableInputDevicesAddresses>().values[0].
+                    template get<AudioDeviceAddress::id>();
+        case AudioHalCapCriterionV2::availableOutputDevicesAddresses:
+            return aidl.get<AudioHalCapCriterionV2::availableOutputDevicesAddresses>().values[0].
+                    template get<AudioDeviceAddress::id>();
+        case AudioHalCapCriterionV2::telephonyMode:
+            return toString(aidl.get<AudioHalCapCriterionV2::telephonyMode>().values[0]);
+        case AudioHalCapCriterionV2::forceConfigForUse:
+            return aidl2legacy_AudioHalCapCriterionV2ForceUse_CapCriterionValue(
+                    aidl.get<AudioHalCapCriterionV2::forceConfigForUse>().values[0]);
+    }
+    return unexpected(BAD_VALUE);
 }
 
 ConversionResult<std::string> aidl2legacy_AudioHalCapRule_CapRule(
@@ -168,11 +330,11 @@
         }
         isFirstCriterionRule = false;
         std::string selectionCriterion = VALUE_OR_RETURN(
-                aidl2legacy_AudioHalCapCriterionV2_CapName(criterionRule.criterion));
+                aidl2legacy_AudioHalCapCriterionV2_CapName(criterionRule.criterionAndValue));
         std::string matchesWhen;
         std::string value = VALUE_OR_RETURN(
                 aidl2legacy_AudioHalCapCriterionV2Type_CapCriterionValue(
-                        criterionRule.criterionTypeValue));
+                        criterionRule.criterionAndValue));
 
         switch (criterionRule.matchingRule) {
             case AudioHalCapRule::MatchingRule::IS:
@@ -325,24 +487,28 @@
     engineConfig::Criterion& criterion = capCriterion.criterion;
     engineConfig::CriterionType& criterionType = capCriterion.criterionType;
 
-    auto loadForceUseCriterion = [](const auto &aidlCriterion, auto &criterion,
-            auto &criterionType) -> status_t {
-        uint32_t legacyForceUse = VALUE_OR_RETURN_STATUS(
-                aidl2legacy_AudioPolicyForceUse_audio_policy_force_use_t(
-                        aidlCriterion.forceUse));
+    auto loadForceUseCriterion = [](const auto& aidlCriterion, auto& criterion,
+                                    auto& criterionType) -> status_t {
+        if (aidlCriterion.values.empty()) {
+            return BAD_VALUE;
+        }
+        const auto [legacyForceUse, _] = VALUE_OR_RETURN_STATUS(
+                aidl2legacy_AudioPolicyForceUse_audio_policy_force_use_t_audio_policy_forced_cfg_t(
+                        aidlCriterion.values[0]));
         criterion.typeName = criterionType.name;
         criterionType.name = criterion.typeName + gCriterionTypeSuffix;
         criterionType.isInclusive =
                 (aidlCriterion.logic == AudioHalCapCriterionV2::LogicalDisjunction::INCLUSIVE);
         criterion.name = gForceUseCriterionTag[legacyForceUse];
-        criterion.defaultLiteralValue = toString(aidlCriterion.defaultValue);
-        if (aidlCriterion.values.empty()) {
-            return BAD_VALUE;
-        }
+        criterion.defaultLiteralValue = toString(
+                aidlCriterion.defaultValue.template get<AudioPolicyForceUse::forMedia>());
         for (auto &value : aidlCriterion.values) {
-            uint32_t legacyForcedConfig = VALUE_OR_RETURN_STATUS(
-                    aidl2legacy_AudioPolicyForcedConfig_audio_policy_forced_cfg_t(value));
-            criterionType.valuePairs.push_back({legacyForcedConfig, 0, toString(value)});
+            const audio_policy_forced_cfg_t legacyForcedCfg = VALUE_OR_RETURN_STATUS(
+                    aidl2legacy_AudioHalCapCriterionV2ForceUse_audio_policy_forced_cfg_t(value));
+            const std::string legacyForcedCfgLiteral = VALUE_OR_RETURN_STATUS(
+                    audio_policy_forced_cfg_t_CapCriterionValue(legacyForcedCfg));
+            criterionType.valuePairs.push_back(
+                    {legacyForcedCfg, 0, legacyForcedCfgLiteral});
         }
         return NO_ERROR;
     };
diff --git a/services/audiopolicy/engineconfigurable/src/Engine.cpp b/services/audiopolicy/engineconfigurable/src/Engine.cpp
index 45da7b0..ad49b19 100644
--- a/services/audiopolicy/engineconfigurable/src/Engine.cpp
+++ b/services/audiopolicy/engineconfigurable/src/Engine.cpp
@@ -112,7 +112,7 @@
 
 template<typename T>
 status_t Engine::loadWithFallback(const T& configSource) {
-    auto result = EngineBase::loadAudioPolicyEngineConfig(configSource);
+    auto result = EngineBase::loadAudioPolicyEngineConfig(configSource, true /*isConfigurable*/);
     ALOGE_IF(result.nbSkippedElement != 0,
              "Policy Engine configuration is partially invalid, skipped %zu elements",
              result.nbSkippedElement);
diff --git a/services/audiopolicy/engineconfigurable/tools/capBuildPolicyCriterionTypes.py b/services/audiopolicy/engineconfigurable/tools/capBuildPolicyCriterionTypes.py
index b873830..1adc602 100755
--- a/services/audiopolicy/engineconfigurable/tools/capBuildPolicyCriterionTypes.py
+++ b/services/audiopolicy/engineconfigurable/tools/capBuildPolicyCriterionTypes.py
@@ -102,7 +102,6 @@
                 ordered_values = OrderedDict(sorted(values_dict.items(), key=lambda x: x[1]))
                 for key, value in ordered_values.items():
                     value_node = ET.SubElement(values_node, "value")
-                    value_node.set('numerical', str(value))
                     value_node.set('literal', key)
 
                     if criterion_type.get('name') == "OutputDevicesMaskType":
@@ -114,20 +113,14 @@
         for criterion_name, values_list in addressCriteria.items():
             for criterion_type in criterion_types_root.findall('criterion_type'):
                 if criterion_type.get('name') == criterion_name:
-                    index = 0
                     existing_values_node = criterion_type.find("values")
                     if existing_values_node is not None:
-                        for existing_value in existing_values_node.findall('value'):
-                            if existing_value.get('numerical') == str(1 << index):
-                                index += 1
                         values_node = existing_values_node
                     else:
                         values_node = ET.SubElement(criterion_type, "values")
 
                     for value in values_list:
                         value_node = ET.SubElement(values_node, "value", literal=value)
-                        value_node.set('numerical', str(1 << index))
-                        index += 1
 
     xmlstr = ET.tostring(criterion_types_root, encoding='utf8', method='xml')
     reparsed = MINIDOM.parseString(xmlstr)
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index 7e35cca..b495f72 100644
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -38,22 +38,8 @@
 
 namespace android::audio_policy {
 
-struct legacy_strategy_map { const char *name; legacy_strategy id; };
 static const std::vector<legacy_strategy_map>& getLegacyStrategy() {
-    static const std::vector<legacy_strategy_map> legacyStrategy = {
-        { "STRATEGY_NONE", STRATEGY_NONE },
-        { "STRATEGY_MEDIA", STRATEGY_MEDIA },
-        { "STRATEGY_PHONE", STRATEGY_PHONE },
-        { "STRATEGY_SONIFICATION", STRATEGY_SONIFICATION },
-        { "STRATEGY_SONIFICATION_RESPECTFUL", STRATEGY_SONIFICATION_RESPECTFUL },
-        { "STRATEGY_DTMF", STRATEGY_DTMF },
-        { "STRATEGY_ENFORCED_AUDIBLE", STRATEGY_ENFORCED_AUDIBLE },
-        { "STRATEGY_TRANSMITTED_THROUGH_SPEAKER", STRATEGY_TRANSMITTED_THROUGH_SPEAKER },
-        { "STRATEGY_ACCESSIBILITY", STRATEGY_ACCESSIBILITY },
-        { "STRATEGY_REROUTING", STRATEGY_REROUTING },
-        { "STRATEGY_PATCH", STRATEGY_PATCH }, // boiler to manage stream patch volume
-        { "STRATEGY_CALL_ASSISTANT", STRATEGY_CALL_ASSISTANT },
-    };
+    static const std::vector<legacy_strategy_map> legacyStrategy = getLegacyStrategyMap();
     return legacyStrategy;
 }
 
@@ -68,7 +54,7 @@
 
 template<typename T>
 status_t Engine::loadWithFallback(const T& configSource) {
-    auto result = EngineBase::loadAudioPolicyEngineConfig(configSource);
+    auto result = EngineBase::loadAudioPolicyEngineConfig(configSource, false /*isConfigurable*/);
     ALOGE_IF(result.nbSkippedElement != 0,
              "Policy Engine configuration is partially invalid, skipped %zu elements",
              result.nbSkippedElement);
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 354c59c..74e77e8 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1492,7 +1492,8 @@
         for (auto &secondaryMix : secondaryMixes) {
             sp<SwAudioOutputDescriptor> outputDesc = secondaryMix->getOutput();
             if (outputDesc != nullptr &&
-                outputDesc->mIoHandle != AUDIO_IO_HANDLE_NONE) {
+                outputDesc->mIoHandle != AUDIO_IO_HANDLE_NONE &&
+                outputDesc->mIoHandle != *output) {
                 secondaryOutputs->push_back(outputDesc->mIoHandle);
                 weakSecondaryOutputDescs.push_back(outputDesc);
             }
@@ -7432,7 +7433,8 @@
             for (auto &secondaryMix : secondaryMixes) {
                 sp<SwAudioOutputDescriptor> outputDesc = secondaryMix->getOutput();
                 if (outputDesc != nullptr &&
-                    outputDesc->mIoHandle != AUDIO_IO_HANDLE_NONE) {
+                    outputDesc->mIoHandle != AUDIO_IO_HANDLE_NONE &&
+                    outputDesc != outputDescriptor) {
                     secondaryDescs.push_back(outputDesc);
                 }
             }
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index b2f0c22..768cd07 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -60,8 +60,6 @@
 using media::audio::common::AudioFormatDescription;
 using media::audio::common::AudioMode;
 using media::audio::common::AudioOffloadInfo;
-using media::audio::common::AudioPolicyForceUse;
-using media::audio::common::AudioPolicyForcedConfig;
 using media::audio::common::AudioSource;
 using media::audio::common::AudioStreamType;
 using media::audio::common::AudioUsage;
@@ -255,8 +253,8 @@
     return Status::ok();
 }
 
-Status AudioPolicyService::setForceUse(AudioPolicyForceUse usageAidl,
-                                       AudioPolicyForcedConfig configAidl)
+Status AudioPolicyService::setForceUse(media::AudioPolicyForceUse usageAidl,
+                                       media::AudioPolicyForcedConfig configAidl)
 {
     audio_policy_force_use_t usage = VALUE_OR_RETURN_BINDER_STATUS(
             aidl2legacy_AudioPolicyForceUse_audio_policy_force_use_t(usageAidl));
@@ -285,8 +283,8 @@
     return Status::ok();
 }
 
-Status AudioPolicyService::getForceUse(AudioPolicyForceUse usageAidl,
-                                       AudioPolicyForcedConfig* _aidl_return) {
+Status AudioPolicyService::getForceUse(media::AudioPolicyForceUse usageAidl,
+                                       media::AudioPolicyForcedConfig* _aidl_return) {
     audio_policy_force_use_t usage = VALUE_OR_RETURN_BINDER_STATUS(
             aidl2legacy_AudioPolicyForceUse_audio_policy_force_use_t(usageAidl));
 
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index eccefa7..428e560 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -105,10 +105,10 @@
             const std::string& deviceName,
             const AudioFormatDescription& encodedFormat) override;
     binder::Status setPhoneState(AudioMode state, int32_t uid) override;
-    binder::Status setForceUse(android::media::audio::common::AudioPolicyForceUse usage,
-            android::media::audio::common::AudioPolicyForcedConfig config) override;
-    binder::Status getForceUse(android::media::audio::common::AudioPolicyForceUse usage,
-            android::media::audio::common::AudioPolicyForcedConfig* _aidl_return) override;
+    binder::Status setForceUse(media::AudioPolicyForceUse usage,
+                               media::AudioPolicyForcedConfig config) override;
+    binder::Status getForceUse(media::AudioPolicyForceUse usage,
+                               media::AudioPolicyForcedConfig* _aidl_return) override;
     binder::Status getOutput(AudioStreamType stream, int32_t* _aidl_return) override;
     binder::Status getOutputForAttr(const media::audio::common::AudioAttributes& attr,
                                     int32_t session,
diff --git a/services/audiopolicy/tests/Android.bp b/services/audiopolicy/tests/Android.bp
index c600fb6..154b063 100644
--- a/services/audiopolicy/tests/Android.bp
+++ b/services/audiopolicy/tests/Android.bp
@@ -53,7 +53,10 @@
         "libaudiopolicymanager_interface_headers",
     ],
 
-    srcs: ["audiopolicymanager_tests.cpp"],
+    srcs: [
+        "audiopolicymanager_tests.cpp",
+        "test_execution_tracer.cpp",
+    ],
 
     data: [":audiopolicytest_configuration_files"],
 
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index 5278b73..6974a0b 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -44,6 +44,7 @@
 #include "AudioPolicyManagerTestClient.h"
 #include "AudioPolicyTestClient.h"
 #include "AudioPolicyTestManager.h"
+#include "test_execution_tracer.h"
 
 using namespace android;
 using testing::UnorderedElementsAre;
@@ -185,6 +186,7 @@
     void SetUp() override;
     void TearDown() override;
     virtual void SetUpManagerConfig();
+    virtual std::string getEngineConfigFilePath() const { return sTestEngineConfig; }
 
     void dumpToLog();
     // When explicit routing is needed, selectedDeviceId needs to be set as the wanted port
@@ -223,6 +225,7 @@
             const std::string &address, audio_port_v7 *foundPort);
     static audio_port_handle_t getDeviceIdFromPatch(const struct audio_patch* patch);
     virtual AudioPolicyManagerTestClient* getClient() { return new AudioPolicyManagerTestClient; }
+    void verifyBuiltInStrategyIdsAreValid();
 
     sp<AudioPolicyConfig> mConfig;
     std::unique_ptr<AudioPolicyManagerTestClient> mClient;
@@ -237,7 +240,7 @@
 void AudioPolicyManagerTest::SetUp() {
     mClient.reset(getClient());
     ASSERT_NO_FATAL_FAILURE(SetUpManagerConfig());  // Subclasses may want to customize the config.
-    mManager.reset(new AudioPolicyTestManager(mConfig, mClient.get(), sTestEngineConfig));
+    mManager.reset(new AudioPolicyTestManager(mConfig, mClient.get(), getEngineConfigFilePath()));
     ASSERT_EQ(NO_ERROR, mManager->initialize());
     ASSERT_EQ(NO_ERROR, mManager->initCheck());
 }
@@ -397,6 +400,16 @@
     return AUDIO_PORT_HANDLE_NONE;
 }
 
+void AudioPolicyManagerTest::verifyBuiltInStrategyIdsAreValid() {
+    AudioProductStrategyVector strategies;
+    ASSERT_EQ(NO_ERROR, mManager->listAudioProductStrategies(strategies));
+    for (const auto& strategy : strategies) {
+        // Since ids are unsigned, this will also cover the case when the id is 'NONE' which is -1.
+        EXPECT_LT(strategy.getId(),
+                  media::audio::common::AudioHalProductStrategy::VENDOR_STRATEGY_ID_START)
+                << strategy.getName();
+    }
+}
 
 TEST_F(AudioPolicyManagerTest, InitSuccess) {
     // SetUp must finish with no assertions.
@@ -454,6 +467,20 @@
 
 // TODO: Add patch creation tests that involve already existing patch
 
+TEST_F(AudioPolicyManagerTest, BuiltInStrategyIdsAreValid) {
+    verifyBuiltInStrategyIdsAreValid();
+}
+
+class AudioPolicyManagerTestWithDefaultEngineConfig : public AudioPolicyManagerTest {
+  protected:
+    // The APM will use the default engine config from EngineDefaultConfig.h.
+    std::string getEngineConfigFilePath() const override { return ""; }
+};
+
+TEST_F(AudioPolicyManagerTestWithDefaultEngineConfig, BuiltInStrategyIdsAreValid) {
+    verifyBuiltInStrategyIdsAreValid();
+}
+
 enum
 {
     MSD_AUDIO_PATCH_COUNT_NUM_AUDIO_PATCHES_INDEX = 0,
@@ -3316,6 +3343,49 @@
     // SetUp must finish with no assertions.
 }
 
+TEST_F(AudioPolicyManagerPhoneTest, Dump) {
+    dumpToLog();
+}
+
+TEST_F(AudioPolicyManagerPhoneTest, NoPatchChangesDuringAlarmPlayback) {
+    audio_port_handle_t alarmPortId = AUDIO_PORT_HANDLE_NONE;
+    audio_io_handle_t alarmOutput = AUDIO_IO_HANDLE_NONE;
+    {
+        // Uses STRATEGY_SONIFICATION, routed to AUDIO_DEVICE_OUT_SPEAKER_SAFE.
+        audio_attributes_t attr = {
+            .content_type = AUDIO_CONTENT_TYPE_UNKNOWN,
+            .usage = AUDIO_USAGE_ALARM,
+        };
+        audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+        ASSERT_NO_FATAL_FAILURE(getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT,
+                        AUDIO_CHANNEL_OUT_STEREO, 48000,
+                        AUDIO_OUTPUT_FLAG_NONE,
+                        &alarmOutput, &alarmPortId, attr));
+        EXPECT_EQ(NO_ERROR, mManager->startOutput(alarmPortId));
+    }
+    const audio_patch lastPatchBefore = *(mClient->getLastAddedPatch());
+
+    {
+        // Uses STRATEGY_MEDIA, routed to AUDIO_DEVICE_OUT_SPEAKER.
+        audio_attributes_t attr = {
+            .content_type = AUDIO_CONTENT_TYPE_UNKNOWN,
+            .usage = AUDIO_USAGE_MEDIA,
+        };
+        audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+        audio_port_handle_t notifPortId = AUDIO_PORT_HANDLE_NONE;
+        audio_io_handle_t notifOutput = AUDIO_IO_HANDLE_NONE;
+        ASSERT_NO_FATAL_FAILURE(getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT,
+                        AUDIO_CHANNEL_OUT_STEREO, 48000,
+                        AUDIO_OUTPUT_FLAG_NONE,
+                        &notifOutput, &notifPortId, attr));
+        EXPECT_EQ(NO_ERROR, mManager->startOutput(notifPortId));
+    }
+    dumpToLog();
+    const audio_patch lastPatchAfter = *(mClient->getLastAddedPatch());
+    EXPECT_TRUE(audio_patches_are_equal(&lastPatchBefore, &lastPatchAfter)) <<
+            "Unexpected change in patches detected";
+}
+
 enum {
     MIX_PORT_ATTR_EXPECTED_NAME_PARAMETER,
     MIX_PORT_ATTR_EXPECTED_NAME_WITH_DBFM_PARAMETER,
@@ -4145,3 +4215,9 @@
         testing::Values(AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE,
                         AUDIO_USAGE_ALARM)
 );
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
+    return RUN_ALL_TESTS();
+}
diff --git a/services/audiopolicy/tests/test_execution_tracer.cpp b/services/audiopolicy/tests/test_execution_tracer.cpp
new file mode 100644
index 0000000..09de4a1
--- /dev/null
+++ b/services/audiopolicy/tests/test_execution_tracer.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TestExecutionTracer"
+
+#include "test_execution_tracer.h"
+
+#include <android-base/logging.h>
+
+void TestExecutionTracer::OnTestStart(const ::testing::TestInfo& test_info) {
+    TraceTestState("Started", test_info);
+}
+
+void TestExecutionTracer::OnTestEnd(const ::testing::TestInfo& test_info) {
+    TraceTestState("Finished", test_info);
+}
+
+void TestExecutionTracer::OnTestPartResult(const ::testing::TestPartResult& result) {
+    if (result.failed()) {
+        LOG(ERROR) << result;
+    } else {
+        LOG(INFO) << result;
+    }
+}
+
+// static
+void TestExecutionTracer::TraceTestState(const std::string& state,
+                                         const ::testing::TestInfo& test_info) {
+    LOG(INFO) << state << " " << test_info.test_suite_name() << "::" << test_info.name();
+}
diff --git a/services/audiopolicy/tests/test_execution_tracer.h b/services/audiopolicy/tests/test_execution_tracer.h
new file mode 100644
index 0000000..9031aaf
--- /dev/null
+++ b/services/audiopolicy/tests/test_execution_tracer.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+class TestExecutionTracer : public ::testing::EmptyTestEventListener {
+  public:
+    void OnTestStart(const ::testing::TestInfo& test_info) override;
+    void OnTestEnd(const ::testing::TestInfo& test_info) override;
+    void OnTestPartResult(const ::testing::TestPartResult& result) override;
+
+  private:
+    static void TraceTestState(const std::string& state, const ::testing::TestInfo& test_info);
+};
diff --git a/services/camera/virtualcamera/VirtualCameraSession.cc b/services/camera/virtualcamera/VirtualCameraSession.cc
index e1815c7..d074826 100644
--- a/services/camera/virtualcamera/VirtualCameraSession.cc
+++ b/services/camera/virtualcamera/VirtualCameraSession.cc
@@ -191,7 +191,11 @@
   }
   halStream.overrideDataSpace = stream.dataSpace;
 
-  halStream.producerUsage = BufferUsage::GPU_RENDER_TARGET;
+  halStream.producerUsage = static_cast<BufferUsage>(
+      static_cast<int64_t>(stream.usage) |
+      static_cast<int64_t>(BufferUsage::CAMERA_OUTPUT) |
+      static_cast<int64_t>(BufferUsage::GPU_RENDER_TARGET));
+
   halStream.supportOffline = false;
   return halStream;
 }