Merge "Transcoder: Don't preserve profile/level when switching codec." into sc-dev
diff --git a/MainlineFiles.cfg b/MainlineFiles.cfg
index f694a41..490bbbf 100644
--- a/MainlineFiles.cfg
+++ b/MainlineFiles.cfg
@@ -1,10 +1,13 @@
-# 
+#
 # mainline files for frameworks/av
+# this list used by tools/mainline_hook_*.sh to help separate
+# mainline changes vs framework changes, which release at different paces.
+#
 #
 # ignore comment (#) lines and blank lines
 # rest are path prefixes starting at root of the project
 # (so OWNERS, not frameworks/av/OWNERS)
-# 
+#
 # path
 # INCLUDE path
 # EXCLUDE path
@@ -24,4 +27,5 @@
 media/codec2/components/
 media/codecs/
 media/extractors/
-media/libstagefright/mpeg2ts
+media/libstagefright/mpeg2ts/
+media/libstagefright/flac/
diff --git a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
index 28a57bd..8e1fcc0 100644
--- a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
+++ b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
@@ -119,10 +119,11 @@
      * @param width Width of the input buffers
      * @param height Height of the input buffers
      * @param format Format of the input buffers. One of HAL_PIXEL_FORMAT_*.
+     * @param isMultiResolution Whether the input stream supports variable resolution image.
      *
      * @return new stream ID
      */
-    int createInputStream(int width, int height, int format);
+    int createInputStream(int width, int height, int format, boolean isMultiResolution);
 
     /**
      * Get the surface of the input stream.
diff --git a/camera/camera2/OutputConfiguration.cpp b/camera/camera2/OutputConfiguration.cpp
index 4e9b27d..2f6bc30 100644
--- a/camera/camera2/OutputConfiguration.cpp
+++ b/camera/camera2/OutputConfiguration.cpp
@@ -68,6 +68,10 @@
     return mPhysicalCameraId;
 }
 
+bool OutputConfiguration::isMultiResolution() const {
+    return mIsMultiResolution;
+}
+
 OutputConfiguration::OutputConfiguration() :
         mRotation(INVALID_ROTATION),
         mSurfaceSetID(INVALID_SET_ID),
@@ -75,7 +79,8 @@
         mWidth(0),
         mHeight(0),
         mIsDeferred(false),
-        mIsShared(false) {
+        mIsShared(false),
+        mIsMultiResolution(false) {
 }
 
 OutputConfiguration::OutputConfiguration(const android::Parcel& parcel) :
@@ -145,6 +150,12 @@
 
     parcel->readString16(&mPhysicalCameraId);
 
+    int isMultiResolution = 0;
+    if ((err = parcel->readInt32(&isMultiResolution)) != OK) {
+        ALOGE("%s: Failed to read surface isMultiResolution flag from parcel", __FUNCTION__);
+        return err;
+    }
+
     mRotation = rotation;
     mSurfaceSetID = setID;
     mSurfaceType = surfaceType;
@@ -152,6 +163,7 @@
     mHeight = height;
     mIsDeferred = isDeferred != 0;
     mIsShared = isShared != 0;
+    mIsMultiResolution = isMultiResolution != 0;
     for (auto& surface : surfaceShims) {
         ALOGV("%s: OutputConfiguration: %p, name %s", __FUNCTION__,
                 surface.graphicBufferProducer.get(),
@@ -160,8 +172,8 @@
     }
 
     ALOGV("%s: OutputConfiguration: rotation = %d, setId = %d, surfaceType = %d,"
-          " physicalCameraId = %s", __FUNCTION__, mRotation, mSurfaceSetID,
-          mSurfaceType, String8(mPhysicalCameraId).string());
+          " physicalCameraId = %s, isMultiResolution = %d", __FUNCTION__, mRotation,
+          mSurfaceSetID, mSurfaceType, String8(mPhysicalCameraId).string(), mIsMultiResolution);
 
     return err;
 }
@@ -175,6 +187,7 @@
     mIsDeferred = false;
     mIsShared = isShared;
     mPhysicalCameraId = physicalId;
+    mIsMultiResolution = false;
 }
 
 OutputConfiguration::OutputConfiguration(
@@ -183,7 +196,7 @@
     int width, int height, bool isShared)
   : mGbps(gbps), mRotation(rotation), mSurfaceSetID(surfaceSetID), mSurfaceType(surfaceType),
     mWidth(width), mHeight(height), mIsDeferred(false), mIsShared(isShared),
-    mPhysicalCameraId(physicalCameraId) { }
+    mPhysicalCameraId(physicalCameraId), mIsMultiResolution(false) { }
 
 status_t OutputConfiguration::writeToParcel(android::Parcel* parcel) const {
 
@@ -224,6 +237,9 @@
     err = parcel->writeString16(mPhysicalCameraId);
     if (err != OK) return err;
 
+    err = parcel->writeInt32(mIsMultiResolution ? 1 : 0);
+    if (err != OK) return err;
+
     return OK;
 }
 
diff --git a/camera/camera2/SessionConfiguration.cpp b/camera/camera2/SessionConfiguration.cpp
index a431a33..7cf6087 100644
--- a/camera/camera2/SessionConfiguration.cpp
+++ b/camera/camera2/SessionConfiguration.cpp
@@ -55,6 +55,12 @@
         return err;
     }
 
+    bool inputIsMultiResolution = false;
+    if ((err = parcel->readBool(&inputIsMultiResolution)) != OK) {
+        ALOGE("%s: Failed to read input multi-resolution flag from parcel", __FUNCTION__);
+        return err;
+    }
+
     std::vector<OutputConfiguration> outputStreams;
     if ((err = parcel->readParcelableVector(&outputStreams)) != OK) {
         ALOGE("%s: Failed to read output configurations from parcel", __FUNCTION__);
@@ -65,6 +71,7 @@
     mInputWidth = inputWidth;
     mInputHeight = inputHeight;
     mInputFormat = inputFormat;
+    mInputIsMultiResolution = inputIsMultiResolution;
     for (auto& stream : outputStreams) {
         mOutputStreams.push_back(stream);
     }
@@ -90,6 +97,9 @@
     err = parcel->writeInt32(mInputFormat);
     if (err != OK) return err;
 
+    err = parcel->writeBool(mInputIsMultiResolution);
+    if (err != OK) return err;
+
     err = parcel->writeParcelableVector(mOutputStreams);
     if (err != OK) return err;
 
diff --git a/camera/cameraserver/Android.bp b/camera/cameraserver/Android.bp
index 9398ec3..8ca8920 100644
--- a/camera/cameraserver/Android.bp
+++ b/camera/cameraserver/Android.bp
@@ -42,6 +42,7 @@
         "android.hardware.camera.provider@2.4",
         "android.hardware.camera.provider@2.5",
         "android.hardware.camera.provider@2.6",
+        "android.hardware.camera.provider@2.7",
         "android.hardware.camera.device@1.0",
         "android.hardware.camera.device@3.2",
         "android.hardware.camera.device@3.4",
diff --git a/camera/include/camera/camera2/OutputConfiguration.h b/camera/include/camera/camera2/OutputConfiguration.h
index 95c4f39..6009370 100644
--- a/camera/include/camera/camera2/OutputConfiguration.h
+++ b/camera/include/camera/camera2/OutputConfiguration.h
@@ -47,6 +47,8 @@
     bool                       isDeferred() const;
     bool                       isShared() const;
     String16                   getPhysicalCameraId() const;
+    bool                       isMultiResolution() const;
+
     /**
      * Keep impl up-to-date with OutputConfiguration.java in frameworks/base
      */
@@ -83,7 +85,8 @@
                 mIsDeferred == other.mIsDeferred &&
                 mIsShared == other.mIsShared &&
                 gbpsEqual(other) &&
-                mPhysicalCameraId == other.mPhysicalCameraId );
+                mPhysicalCameraId == other.mPhysicalCameraId &&
+                mIsMultiResolution == other.mIsMultiResolution);
     }
     bool operator != (const OutputConfiguration& other) const {
         return !(*this == other);
@@ -114,6 +117,9 @@
         if (mPhysicalCameraId != other.mPhysicalCameraId) {
             return mPhysicalCameraId < other.mPhysicalCameraId;
         }
+        if (mIsMultiResolution != other.mIsMultiResolution) {
+            return mIsMultiResolution < other.mIsMultiResolution;
+        }
         return gbpsLessThan(other);
     }
     bool operator > (const OutputConfiguration& other) const {
@@ -133,6 +139,7 @@
     bool                       mIsDeferred;
     bool                       mIsShared;
     String16                   mPhysicalCameraId;
+    bool                       mIsMultiResolution;
 };
 } // namespace params
 } // namespace camera2
diff --git a/camera/include/camera/camera2/SessionConfiguration.h b/camera/include/camera/camera2/SessionConfiguration.h
index 64288ed..29913f6 100644
--- a/camera/include/camera/camera2/SessionConfiguration.h
+++ b/camera/include/camera/camera2/SessionConfiguration.h
@@ -38,6 +38,7 @@
     int getInputHeight() const { return mInputHeight; }
     int getInputFormat() const { return mInputFormat; }
     int getOperatingMode() const { return mOperatingMode; }
+    bool inputIsMultiResolution() const { return mInputIsMultiResolution; }
 
     virtual status_t writeToParcel(android::Parcel* parcel) const override;
     virtual status_t readFromParcel(const android::Parcel* parcel) override;
@@ -61,7 +62,8 @@
                 mInputWidth == other.mInputWidth &&
                 mInputHeight == other.mInputHeight &&
                 mInputFormat == other.mInputFormat &&
-                mOperatingMode == other.mOperatingMode);
+                mOperatingMode == other.mOperatingMode &&
+                mInputIsMultiResolution == other.mInputIsMultiResolution);
     }
 
     bool operator != (const SessionConfiguration& other) const {
@@ -83,6 +85,10 @@
             return mInputFormat < other.mInputFormat;
         }
 
+        if (mInputIsMultiResolution != other.mInputIsMultiResolution) {
+            return mInputIsMultiResolution < other.mInputIsMultiResolution;
+        }
+
         if (mOperatingMode != other.mOperatingMode) {
             return mOperatingMode < other.mOperatingMode;
         }
@@ -104,6 +110,7 @@
 
     std::vector<OutputConfiguration> mOutputStreams;
     int                              mInputWidth, mInputHeight, mInputFormat, mOperatingMode;
+    bool                             mInputIsMultiResolution = false;
 };
 } // namespace params
 } // namespace camera2
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index c1b2712..4e07c5c 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -3868,6 +3868,35 @@
      */
     ACAMERA_SCALER_DEFAULT_SECURE_IMAGE_SIZE =                  // int32[2]
             ACAMERA_SCALER_START + 18,
+    /**
+     * <p>The available multi-resolution stream configurations that this
+     * physical camera device supports
+     * (i.e. format, width, height, output/input stream).</p>
+     *
+     * <p>Type: int32[n*4] (acamera_metadata_enum_android_scaler_physical_camera_multi_resolution_stream_configurations_t)</p>
+     *
+     * <p>This tag may appear in:
+     * <ul>
+     *   <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+     * </ul></p>
+     *
+     * <p>This list contains a subset of the parent logical camera's multi-resolution stream
+     * configurations which belong to this physical camera, and it will advertise and will only
+     * advertise the maximum supported resolutions for a particular format.</p>
+     * <p>If this camera device isn't a physical camera device constituting a logical camera,
+     * but a standalone ULTRA_HIGH_RESOLUTION_SENSOR camera, this field represents the
+     * multi-resolution input/output stream configurations of default mode and max resolution
+     * modes. The sizes will be the maximum resolution of a particular format for default mode
+     * and max resolution mode.</p>
+     * <p>This field will only be advertised if the device is a physical camera of a
+     * logical multi-camera device or an ultra high resolution sensor camera. For a logical
+     * multi-camera, the camera API will derive the logical camera’s multi-resolution stream
+     * configurations from all physical cameras. For an ultra high resolution sensor camera, this
+     * is used directly as the camera’s multi-resolution stream configurations.</p>
+     */
+    ACAMERA_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS = 
+                                                                // int32[n*4] (acamera_metadata_enum_android_scaler_physical_camera_multi_resolution_stream_configurations_t)
+            ACAMERA_SCALER_START + 19,
     ACAMERA_SCALER_END,
 
     /**
@@ -8475,6 +8504,16 @@
 
 } acamera_metadata_enum_android_scaler_rotate_and_crop_t;
 
+// ACAMERA_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS
+typedef enum acamera_metadata_enum_acamera_scaler_physical_camera_multi_resolution_stream_configurations {
+    ACAMERA_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS_OUTPUT
+                                                                      = 0,
+
+    ACAMERA_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS_INPUT
+                                                                      = 1,
+
+} acamera_metadata_enum_android_scaler_physical_camera_multi_resolution_stream_configurations_t;
+
 
 // ACAMERA_SENSOR_REFERENCE_ILLUMINANT1
 typedef enum acamera_metadata_enum_acamera_sensor_reference_illuminant1 {
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index ef4c568..03a8dc9 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -16,6 +16,9 @@
         libstagefright_foundation libjpeg libui libgui libcutils liblog \
         libhidlbase libdatasource libaudioclient \
         android.hardware.media.omx@1.0 \
+        media_permission-aidl-cpp
+
+LOCAL_STATIC_LIBRARIES := media_permission-aidl-cpp
 
 LOCAL_C_INCLUDES:= \
         frameworks/av/media/libstagefright \
@@ -48,7 +51,8 @@
 
 LOCAL_SHARED_LIBRARIES := \
         libstagefright libmedia liblog libutils libbinder \
-        libstagefright_foundation libdatasource libaudioclient
+        libstagefright_foundation libdatasource libaudioclient \
+        media_permission-aidl-cpp
 
 LOCAL_C_INCLUDES:= \
         frameworks/av/camera/include \
@@ -85,7 +89,8 @@
 LOCAL_C_INCLUDES:= \
         frameworks/av/media/libstagefright \
         frameworks/native/include/media/openmax \
-        frameworks/native/include/media/hardware
+        frameworks/native/include/media/hardware \
+        media_permission-aidl-cpp
 
 LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
 
@@ -113,7 +118,8 @@
 
 LOCAL_SHARED_LIBRARIES := \
         libstagefright libmedia liblog libutils libbinder \
-        libstagefright_foundation libaudioclient
+        libstagefright_foundation libaudioclient \
+        media_permission-aidl-cpp
 
 LOCAL_C_INCLUDES:= \
         frameworks/av/media/libstagefright \
diff --git a/cmds/stagefright/audioloop.cpp b/cmds/stagefright/audioloop.cpp
index 84a6d6b..c86a611 100644
--- a/cmds/stagefright/audioloop.cpp
+++ b/cmds/stagefright/audioloop.cpp
@@ -24,6 +24,7 @@
 
 #include <utils/String16.h>
 
+#include <android/media/permission/Identity.h>
 #include <binder/ProcessState.h>
 #include <media/mediarecorder.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -38,6 +39,8 @@
 
 using namespace android;
 
+using media::permission::Identity;
+
 static void usage(const char* name)
 {
     fprintf(stderr, "Usage: %s [-d du.ration] [-m] [-w] [-N name] [<output-file>]\n", name);
@@ -110,9 +113,10 @@
         audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
         attr.source = AUDIO_SOURCE_MIC;
 
+        // TODO b/182392769: use identity util
         source = new AudioSource(
                 &attr,
-                String16(),
+                Identity(),
                 sampleRate,
                 channels);
     } else {
diff --git a/drm/libmediadrm/CryptoHal.cpp b/drm/libmediadrm/CryptoHal.cpp
index af17679..3257f71 100644
--- a/drm/libmediadrm/CryptoHal.cpp
+++ b/drm/libmediadrm/CryptoHal.cpp
@@ -343,6 +343,7 @@
 
     Return<void> hResult;
 
+    mLock.unlock();
     if (mPluginV1_2 != NULL) {
         hResult = mPluginV1_2->decrypt_1_2(secure, toHidlArray16(keyId), toHidlArray16(iv),
                 hMode, hPattern, hSubSamples, hSource, offset, hDestination,
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index a84fd92..253a1fa 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -16,13 +16,9 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "DrmHal"
-#include <iomanip>
-
-#include <utils/Log.h>
-
-#include <android/binder_manager.h>
 
 #include <aidl/android/media/BnResourceManagerClient.h>
+#include <android/binder_manager.h>
 #include <android/hardware/drm/1.2/types.h>
 #include <android/hidl/manager/1.2/IServiceManager.h>
 #include <hidl/ServiceManagement.h>
@@ -40,7 +36,9 @@
 #include <mediadrm/DrmSessionManager.h>
 #include <mediadrm/IDrmMetricsConsumer.h>
 #include <mediadrm/DrmUtils.h>
+#include <utils/Log.h>
 
+#include <iomanip>
 #include <vector>
 
 using drm::V1_0::KeyedVector;
@@ -319,8 +317,7 @@
     closeOpenSessions();
 
     Mutex::Autolock autoLock(mLock);
-    reportPluginMetrics();
-    reportFrameworkMetrics();
+    reportFrameworkMetrics(reportPluginMetrics());
 
     setListener(NULL);
     mInitCheck = NO_INIT;
@@ -340,7 +337,7 @@
 }
 
 std::vector<sp<IDrmFactory>> DrmHal::makeDrmFactories() {
-    std::vector<sp<IDrmFactory>> factories(DrmUtils::MakeDrmFactories());
+    static std::vector<sp<IDrmFactory>> factories(DrmUtils::MakeDrmFactories());
     if (factories.size() == 0) {
         // must be in passthrough mode, load the default passthrough service
         auto passthrough = IDrmFactory::getService();
@@ -364,7 +361,7 @@
     Return<void> hResult = factory->createPlugin(uuid, appPackageName.string(),
             [&](Status status, const sp<IDrmPlugin>& hPlugin) {
                 if (status != Status::OK) {
-                    DrmUtils::LOG2BE("Failed to make drm plugin: %d", status);
+                    DrmUtils::LOG2BE(uuid, "Failed to make drm plugin: %d", status);
                     return;
                 }
                 plugin = hPlugin;
@@ -372,7 +369,8 @@
         );
 
     if (!hResult.isOk()) {
-        DrmUtils::LOG2BE("createPlugin remote call failed");
+        DrmUtils::LOG2BE(uuid, "createPlugin remote call failed: %s",
+                         hResult.description().c_str());
     }
 
     return plugin;
@@ -580,6 +578,7 @@
     }
 
     if (mPlugin == NULL) {
+        DrmUtils::LOG2BE(uuid, "No supported hal instance found");
         mInitCheck = ERROR_UNSUPPORTED;
     } else {
         mInitCheck = OK;
@@ -1464,7 +1463,7 @@
     return hResult.isOk() ? err : DEAD_OBJECT;
 }
 
-void DrmHal::reportFrameworkMetrics() const
+std::string DrmHal::reportFrameworkMetrics(const std::string& pluginMetrics) const
 {
     mediametrics_handle_t item(mediametrics_create("mediadrm"));
     mediametrics_setUid(item, mMetrics.GetAppUid());
@@ -1493,21 +1492,26 @@
     if (!b64EncodedMetrics.empty()) {
         mediametrics_setCString(item, "serialized_metrics", b64EncodedMetrics.c_str());
     }
+    if (!pluginMetrics.empty()) {
+        mediametrics_setCString(item, "plugin_metrics", pluginMetrics.c_str());
+    }
     if (!mediametrics_selfRecord(item)) {
         ALOGE("Failed to self record framework metrics");
     }
     mediametrics_delete(item);
+    return serializedMetrics;
 }
 
-void DrmHal::reportPluginMetrics() const
+std::string DrmHal::reportPluginMetrics() const
 {
     Vector<uint8_t> metricsVector;
     String8 vendor;
     String8 description;
+    std::string metricsString;
     if (getPropertyStringInternal(String8("vendor"), vendor) == OK &&
             getPropertyStringInternal(String8("description"), description) == OK &&
             getPropertyByteArrayInternal(String8("metrics"), metricsVector) == OK) {
-        std::string metricsString = toBase64StringNoPad(metricsVector.array(),
+        metricsString = toBase64StringNoPad(metricsVector.array(),
                                                         metricsVector.size());
         status_t res = android::reportDrmPluginMetrics(metricsString, vendor,
                                                        description, mMetrics.GetAppUid());
@@ -1515,6 +1519,7 @@
             ALOGE("Metrics were retrieved but could not be reported: %d", res);
         }
     }
+    return metricsString;
 }
 
 bool DrmHal::requiresSecureDecoder(const char *mime) const {
diff --git a/drm/libmediadrm/DrmUtils.cpp b/drm/libmediadrm/DrmUtils.cpp
index f7e6717..ed3848d 100644
--- a/drm/libmediadrm/DrmUtils.cpp
+++ b/drm/libmediadrm/DrmUtils.cpp
@@ -43,6 +43,9 @@
 #include <mediadrm/ICrypto.h>
 #include <mediadrm/IDrm.h>
 
+#include <map>
+#include <string>
+
 using HServiceManager = ::android::hidl::manager::V1_2::IServiceManager;
 using ::android::hardware::hidl_array;
 using ::android::hardware::hidl_string;
@@ -66,8 +69,8 @@
     return obj;
 }
 
-template <typename Hal, typename V>
-void MakeHidlFactories(const uint8_t uuid[16], V &factories) {
+template <typename Hal, typename V, typename M>
+void MakeHidlFactories(const uint8_t uuid[16], V &factories, M& instances) {
     sp<HServiceManager> serviceManager = HServiceManager::getService();
     if (serviceManager == nullptr) {
         LOG2BE("Failed to get service manager");
@@ -78,7 +81,7 @@
         for (const auto &instance : registered) {
             auto factory = Hal::getService(instance);
             if (factory != nullptr) {
-                LOG2BI("found %s %s", Hal::descriptor, instance.c_str());
+                instances[instance.c_str()] = Hal::descriptor;
                 if (!uuid || factory->isCryptoSchemeSupported(uuid)) {
                     factories.push_back(factory);
                 }
@@ -87,6 +90,12 @@
     });
 }
 
+template <typename Hal, typename V>
+void MakeHidlFactories(const uint8_t uuid[16], V &factories) {
+    std::map<std::string, std::string> instances;
+    MakeHidlFactories<Hal>(uuid, factories, instances);
+}
+
 hidl_vec<uint8_t> toHidlVec(const void *ptr, size_t size) {
     hidl_vec<uint8_t> vec(size);
     if (ptr != nullptr) {
@@ -108,7 +117,7 @@
     factory->createPlugin(toHidlArray16(uuid), hidl_string(appPackageName),
                           [&](::V1_0::Status status, const sp<::V1_0::IDrmPlugin> &hPlugin) {
                               if (status != ::V1_0::Status::OK) {
-                                  LOG2BE("MakeDrmPlugin failed: %d", status);
+                                  LOG2BE(uuid, "MakeDrmPlugin failed: %d", status);
                                   return;
                               }
                               plugin = hPlugin;
@@ -123,7 +132,7 @@
     factory->createPlugin(toHidlArray16(uuid), toHidlVec(initData, initDataSize),
                           [&](::V1_0::Status status, const sp<::V1_0::ICryptoPlugin> &hPlugin) {
                               if (status != ::V1_0::Status::OK) {
-                                  LOG2BE("MakeCryptoPlugin failed: %d", status);
+                                  LOG2BE(uuid, "MakeCryptoPlugin failed: %d", status);
                                   return;
                               }
                               plugin = hPlugin;
@@ -147,11 +156,15 @@
 
 std::vector<sp<::V1_0::IDrmFactory>> MakeDrmFactories(const uint8_t uuid[16]) {
     std::vector<sp<::V1_0::IDrmFactory>> drmFactories;
-    MakeHidlFactories<::V1_0::IDrmFactory>(uuid, drmFactories);
-    MakeHidlFactories<::V1_1::IDrmFactory>(uuid, drmFactories);
-    MakeHidlFactories<::V1_2::IDrmFactory>(uuid, drmFactories);
-    MakeHidlFactories<::V1_3::IDrmFactory>(uuid, drmFactories);
-    MakeHidlFactories<::V1_4::IDrmFactory>(uuid, drmFactories);
+    std::map<std::string, std::string> instances;
+    MakeHidlFactories<::V1_0::IDrmFactory>(uuid, drmFactories, instances);
+    MakeHidlFactories<::V1_1::IDrmFactory>(uuid, drmFactories, instances);
+    MakeHidlFactories<::V1_2::IDrmFactory>(uuid, drmFactories, instances);
+    MakeHidlFactories<::V1_3::IDrmFactory>(uuid, drmFactories, instances);
+    MakeHidlFactories<::V1_4::IDrmFactory>(uuid, drmFactories, instances);
+    for (auto const& entry : instances) {
+        LOG2BI("found instance=%s version=%s", entry.first.c_str(), entry.second.c_str());
+    }
     return drmFactories;
 }
 
@@ -255,6 +268,8 @@
         return ERROR_DRM_PROVISIONING_CONFIG;
     case ::V1_4::Status::PROVISIONING_PARSE_ERROR:
         return ERROR_DRM_PROVISIONING_PARSE;
+    case ::V1_4::Status::PROVISIONING_REQUEST_REJECTED:
+        return ERROR_DRM_PROVISIONING_REQUEST_REJECTED;
     case ::V1_4::Status::RETRYABLE_PROVISIONING_ERROR:
         return ERROR_DRM_PROVISIONING_RETRY;
     case ::V1_4::Status::SECURE_STOP_RELEASE_ERROR:
diff --git a/drm/libmediadrm/include/mediadrm/DrmHal.h b/drm/libmediadrm/include/mediadrm/DrmHal.h
index a0aac30..c5206fa 100644
--- a/drm/libmediadrm/include/mediadrm/DrmHal.h
+++ b/drm/libmediadrm/include/mediadrm/DrmHal.h
@@ -241,8 +241,8 @@
 
     void writeByteArray(Parcel &obj, const hidl_vec<uint8_t>& array);
 
-    void reportPluginMetrics() const;
-    void reportFrameworkMetrics() const;
+    std::string reportPluginMetrics() const;
+    std::string reportFrameworkMetrics(const std::string& pluginMetrics) const;
     status_t getPropertyStringInternal(String8 const &name, String8 &value) const;
     status_t getPropertyByteArrayInternal(String8 const &name,
                                           Vector<uint8_t> &value) const;
diff --git a/drm/libmediadrm/interface/mediadrm/DrmUtils.h b/drm/libmediadrm/interface/mediadrm/DrmUtils.h
index 7fe3501..988cda9 100644
--- a/drm/libmediadrm/interface/mediadrm/DrmUtils.h
+++ b/drm/libmediadrm/interface/mediadrm/DrmUtils.h
@@ -33,8 +33,10 @@
 #include <cstdint>
 #include <ctime>
 #include <deque>
+#include <endian.h>
 #include <iterator>
 #include <mutex>
+#include <string>
 #include <vector>
 
 
@@ -88,6 +90,14 @@
     }
 }
 
+template <typename... Args>
+void LogToBuffer(android_LogPriority level, const uint8_t uuid[16], const char *fmt, Args... args) {
+    const uint64_t* uuid2 = reinterpret_cast<const uint64_t*>(uuid);
+    std::string uuidFmt("uuid=[%lx %lx] ");
+    uuidFmt += fmt;
+    LogToBuffer(level, uuidFmt.c_str(), htobe64(uuid2[0]), htobe64(uuid2[1]), args...);
+}
+
 #ifndef LOG2BE
 #define LOG2BE(...) LogToBuffer(ANDROID_LOG_ERROR, __VA_ARGS__)
 #define LOG2BW(...) LogToBuffer(ANDROID_LOG_WARN, __VA_ARGS__)
@@ -196,10 +206,13 @@
         hResult = plugin->getLogMessages(cb);
     }
     if (!hResult.isOk()) {
-        LOG2BW("%s::getLogMessages remote call failed", T::descriptor);
+        LOG2BW("%s::getLogMessages remote call failed %s",
+               T::descriptor, hResult.description().c_str());
     }
 
     auto allLogs(gLogBuf.getLogs());
+    LOG2BI("framework logs size %zu; plugin logs size %zu",
+           allLogs.size(), pluginLogs.size());
     std::copy(pluginLogs.begin(), pluginLogs.end(), std::back_inserter(allLogs));
     std::sort(allLogs.begin(), allLogs.end(),
               [](const ::V1_4::LogMessage &a, const ::V1_4::LogMessage &b) {
diff --git a/drm/mediadrm/plugins/clearkey/default/JsonWebKey.cpp b/drm/mediadrm/plugins/clearkey/default/JsonWebKey.cpp
index 53ffae4..a2d506d 100644
--- a/drm/mediadrm/plugins/clearkey/default/JsonWebKey.cpp
+++ b/drm/mediadrm/plugins/clearkey/default/JsonWebKey.cpp
@@ -61,7 +61,7 @@
     // all the base64 encoded keys. Each key is also stored separately as
     // a JSON object in mJsonObjects[1..n] where n is the total
     // number of keys in the set.
-    if (!isJsonWebKeySet(mJsonObjects[0])) {
+    if (mJsonObjects.size() == 0 || !isJsonWebKeySet(mJsonObjects[0])) {
         return false;
     }
 
diff --git a/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp b/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp
index d93777d..99668a7 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp
@@ -65,7 +65,7 @@
     // all the base64 encoded keys. Each key is also stored separately as
     // a JSON object in mJsonObjects[1..n] where n is the total
     // number of keys in the set.
-    if (!isJsonWebKeySet(mJsonObjects[0])) {
+    if (mJsonObjects.size() == 0 || !isJsonWebKeySet(mJsonObjects[0])) {
         return false;
     }
 
diff --git a/media/codec2/components/avc/C2SoftAvcEnc.cpp b/media/codec2/components/avc/C2SoftAvcEnc.cpp
index cfaeb66..43b2c14 100644
--- a/media/codec2/components/avc/C2SoftAvcEnc.cpp
+++ b/media/codec2/components/avc/C2SoftAvcEnc.cpp
@@ -186,6 +186,23 @@
                 .build());
 
         addParameter(
+                DefineParam(mQuantization, C2_PARAMKEY_QUANTIZATION)
+                .withDefault(new C2StreamQuantizationInfo::output(0u,
+                                      DEFAULT_QP_MAX, DEFAULT_QP_MIN,
+                                      DEFAULT_QP_MAX, DEFAULT_QP_MIN,
+                                      DEFAULT_QP_MAX, DEFAULT_QP_MIN))
+                .withFields({
+                        C2F(mQuantization, iMax).inRange(1, 51),
+                        C2F(mQuantization, iMin).inRange(1, 51),
+                        C2F(mQuantization, pMax).inRange(1, 51),
+                        C2F(mQuantization, pMin).inRange(1, 51),
+                        C2F(mQuantization, bMax).inRange(1, 51),
+                        C2F(mQuantization, bMin).inRange(1, 51),
+                 })
+                .withSetter(QuantizationSetter)
+                .build());
+
+        addParameter(
                 DefineParam(mRequestSync, C2_PARAMKEY_REQUEST_SYNC_FRAME)
                 .withDefault(new C2StreamRequestSyncFrameTuning::output(0u, C2_FALSE))
                 .withFields({C2F(mRequestSync, value).oneOf({ C2_FALSE, C2_TRUE }) })
@@ -220,6 +237,71 @@
         return res;
     }
 
+    static C2R QuantizationSetter(bool mayBlock, C2P<C2StreamQuantizationInfo::output> &me) {
+        (void)mayBlock;
+        (void)me;
+        C2R res = C2R::Ok();
+
+        ALOGV("QuantizationSetter enters max/min i %d/%d p %d/%d b %d/%d",
+              me.v.iMax, me.v.iMin, me.v.pMax, me.v.pMin, me.v.bMax, me.v.bMin);
+
+        // bounds checking
+        constexpr int qp_lowest = 1;
+        constexpr int qp_highest = 51;
+
+        if (me.v.iMax < qp_lowest) {
+            me.set().iMax = qp_lowest;
+        } else if (me.v.iMax > qp_highest) {
+            me.set().iMax = qp_highest;
+        }
+
+        if (me.v.iMin < qp_lowest) {
+            me.set().iMin = qp_lowest;
+        } else if (me.v.iMin > qp_highest) {
+            me.set().iMin = qp_highest;
+        }
+
+        if (me.v.pMax < qp_lowest) {
+            me.set().pMax = qp_lowest;
+        } else if (me.v.pMax > qp_highest) {
+            me.set().pMax = qp_highest;
+        }
+
+        if (me.v.pMin < qp_lowest) {
+            me.set().pMin = qp_lowest;
+        } else if (me.v.pMin > qp_highest) {
+            me.set().pMin = qp_highest;
+        }
+
+        if (me.v.bMax < qp_lowest) {
+            me.set().bMax = qp_lowest;
+        } else if (me.v.bMax > qp_highest) {
+            me.set().bMax = qp_highest;
+        }
+
+        if (me.v.bMin < qp_lowest) {
+            me.set().bMin = qp_lowest;
+        } else if (me.v.bMin > qp_highest) {
+            me.set().bMin = qp_highest;
+        }
+
+        // consistency checking, e.g. min<max
+        //
+        if (me.v.iMax < me.v.iMin) {
+            me.set().iMax = me.v.iMin;
+        }
+        if (me.v.pMax < me.v.pMin) {
+            me.set().pMax = me.v.pMin;
+        }
+        if (me.v.bMax < me.v.bMin) {
+            me.set().bMax = me.v.bMin;
+        }
+
+        // TODO: enforce any sort of i_max < p_max < b_max?
+
+        return res;
+    }
+
     static C2R SizeSetter(bool mayBlock, const C2P<C2StreamPictureSizeInfo::input> &oldMe,
                           C2P<C2StreamPictureSizeInfo::input> &me) {
         (void)mayBlock;
@@ -393,6 +475,7 @@
     std::shared_ptr<C2StreamBitrateInfo::output> getBitrate_l() const { return mBitrate; }
     std::shared_ptr<C2StreamRequestSyncFrameTuning::output> getRequestSync_l() const { return mRequestSync; }
     std::shared_ptr<C2StreamGopTuning::output> getGop_l() const { return mGop; }
+    std::shared_ptr<C2StreamQuantizationInfo::output> getQuantization_l() const { return mQuantization; }
 
 private:
     std::shared_ptr<C2StreamUsageTuning::input> mUsage;
@@ -404,6 +487,7 @@
     std::shared_ptr<C2StreamProfileLevelInfo::output> mProfileLevel;
     std::shared_ptr<C2StreamSyncFrameIntervalTuning::output> mSyncFramePeriod;
     std::shared_ptr<C2StreamGopTuning::output> mGop;
+    std::shared_ptr<C2StreamQuantizationInfo::output> mQuantization;
 };
 
 #define ive_api_function  ih264e_api_function
@@ -664,6 +748,7 @@
     ive_ctl_set_qp_op_t s_qp_op;
     IV_STATUS_T status;
 
+    // set the defaults
     s_qp_ip.e_cmd = IVE_CMD_VIDEO_CTL;
     s_qp_ip.e_sub_cmd = IVE_CMD_CTL_SET_QP;
 
@@ -679,6 +764,21 @@
     s_qp_ip.u4_b_qp_max = DEFAULT_QP_MAX;
     s_qp_ip.u4_b_qp_min = DEFAULT_QP_MIN;
 
+    // parameter parsing ensured proper range 1..51, so only worry about ordering
+    bool valid = true;
+    if (mQuantization->iMax < mQuantization->iMin) valid = false;
+    if (mQuantization->pMax < mQuantization->pMin) valid = false;
+    if (mQuantization->bMax < mQuantization->bMin) valid = false;
+
+    if (valid) {
+        s_qp_ip.u4_i_qp_max = mQuantization->iMax;
+        s_qp_ip.u4_i_qp_min = mQuantization->iMin;
+        s_qp_ip.u4_p_qp_max = mQuantization->pMax;
+        s_qp_ip.u4_p_qp_min = mQuantization->pMin;
+        s_qp_ip.u4_b_qp_max = mQuantization->bMax;
+        s_qp_ip.u4_b_qp_min = mQuantization->bMin;
+    }
+
     s_qp_ip.u4_timestamp_high = -1;
     s_qp_ip.u4_timestamp_low = -1;
 
@@ -926,6 +1026,7 @@
         mIInterval = mIntf->getSyncFramePeriod_l();
         mIDRInterval = mIntf->getSyncFramePeriod_l();
         gop = mIntf->getGop_l();
+        mQuantization = mIntf->getQuantization_l();
     }
     if (gop && gop->flexCount() > 0) {
         uint32_t syncInterval = 1;
diff --git a/media/codec2/components/avc/C2SoftAvcEnc.h b/media/codec2/components/avc/C2SoftAvcEnc.h
index 555055b..e4bf0b0 100644
--- a/media/codec2/components/avc/C2SoftAvcEnc.h
+++ b/media/codec2/components/avc/C2SoftAvcEnc.h
@@ -192,6 +192,7 @@
     std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
     std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
     std::shared_ptr<C2StreamRequestSyncFrameTuning::output> mRequestSync;
+    std::shared_ptr<C2StreamQuantizationInfo::output> mQuantization;
 
     uint32_t mOutBufferSize;
     UWORD32 mHeaderGenerated;
diff --git a/media/codec2/components/flac/Android.bp b/media/codec2/components/flac/Android.bp
index 1143bed..38dfce4 100644
--- a/media/codec2/components/flac/Android.bp
+++ b/media/codec2/components/flac/Android.bp
@@ -42,11 +42,8 @@
 
     srcs: ["C2SoftFlacEnc.cpp"],
 
-    shared_libs: [
-        "libaudioutils",
-    ],
-
     static_libs: [
         "libFLAC",
+        "libaudioutils",
     ],
 }
diff --git a/media/codec2/components/mpeg4_h263/TEST_MAPPING b/media/codec2/components/mpeg4_h263/TEST_MAPPING
new file mode 100644
index 0000000..93fba22
--- /dev/null
+++ b/media/codec2/components/mpeg4_h263/TEST_MAPPING
@@ -0,0 +1,6 @@
+// mappings for frameworks/av/media/codec2/components/mpeg4_h263
+{
+  "presubmit": [
+    { "name": "C2SoftMpeg4DecTest" }
+  ]
+}
diff --git a/media/codec2/components/tests/Android.bp b/media/codec2/components/tests/Android.bp
new file mode 100644
index 0000000..3c68eee
--- /dev/null
+++ b/media/codec2/components/tests/Android.bp
@@ -0,0 +1,68 @@
+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_defaults {
+    name: "C2SoftCodecTest-defaults",
+    gtest: true,
+    host_supported: false,
+    srcs: [
+        "C2SoftCodecTest.cpp",
+    ],
+
+    static_libs: [
+        "liblog",
+        "libion",
+        "libfmq",
+        "libbase",
+        "libutils",
+        "libcutils",
+        "libcodec2",
+        "libhidlbase",
+        "libdmabufheap",
+        "libcodec2_vndk",
+        "libnativewindow",
+        "libcodec2_soft_common",
+        "libsfplugin_ccodec_utils",
+        "libstagefright_foundation",
+        "libstagefright_bufferpool@2.0.1",
+        "android.hardware.graphics.mapper@2.0",
+        "android.hardware.graphics.mapper@3.0",
+        "android.hardware.media.bufferpool@2.0",
+        "android.hardware.graphics.allocator@2.0",
+        "android.hardware.graphics.allocator@3.0",
+        "android.hardware.graphics.bufferqueue@2.0",
+    ],
+
+    shared_libs: [
+        "libui",
+        "libdl",
+        "libhardware",
+        "libvndksupport",
+        "libprocessgroup",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
+
+cc_test {
+    name: "C2SoftMpeg4DecTest",
+    defaults: ["C2SoftCodecTest-defaults"],
+
+    static_libs: [
+        "libstagefright_m4vh263dec",
+        "libcodec2_soft_mpeg4dec",
+    ],
+
+    test_suites: [
+        "general-tests",
+    ],
+}
diff --git a/media/codec2/components/tests/C2SoftCodecTest.cpp b/media/codec2/components/tests/C2SoftCodecTest.cpp
new file mode 100644
index 0000000..84c2562
--- /dev/null
+++ b/media/codec2/components/tests/C2SoftCodecTest.cpp
@@ -0,0 +1,105 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+#include <C2Config.h>
+#include <C2ComponentFactory.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+using namespace android;
+extern "C" ::C2ComponentFactory* CreateCodec2Factory();
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory);
+
+class C2SoftCodecTest : public ::testing::Test {
+public:
+  void SetUp() override {
+    mFactory = CreateCodec2Factory();
+  }
+
+  void TearDown() override {
+    if (mFactory) {
+      DestroyCodec2Factory(mFactory);
+    }
+  }
+
+  c2_status_t createComponent(
+        std::shared_ptr<C2Component>* const comp) {
+    if (!mFactory) {
+      return C2_NO_INIT;
+    }
+    return mFactory->createComponent(
+        kPlaceholderId, comp, std::default_delete<C2Component>());
+  }
+
+  c2_status_t createInterface(
+      std::shared_ptr<C2ComponentInterface>* const intf) {
+    if (!mFactory) {
+      return C2_NO_INIT;
+    }
+    return mFactory->createInterface(
+        kPlaceholderId, intf, std::default_delete<C2ComponentInterface>());
+  }
+
+  ::C2ComponentFactory *getFactory() { return mFactory; }
+
+private:
+  static constexpr ::c2_node_id_t kPlaceholderId = 0;
+
+  ::C2ComponentFactory *mFactory;
+};
+
+TEST_F(C2SoftCodecTest, PictureSizeInfoTest) {
+  std::shared_ptr<C2ComponentInterface> interface;
+  c2_status_t status = createInterface(&interface);
+  ASSERT_EQ(status, C2_OK) << "Error in createInterface";
+  ASSERT_NE(interface, nullptr) << "interface is null";
+
+  std::unique_ptr<C2StreamPictureSizeInfo::output> param =
+      std::make_unique<C2StreamPictureSizeInfo::output>();
+  std::vector<C2FieldSupportedValuesQuery> validValueInfos = {
+      C2FieldSupportedValuesQuery::Current(
+          C2ParamField(param.get(), &C2StreamPictureSizeInfo::width)),
+      C2FieldSupportedValuesQuery::Current(
+          C2ParamField(param.get(), &C2StreamPictureSizeInfo::height))};
+  status = interface->querySupportedValues_vb(validValueInfos, C2_MAY_BLOCK);
+  ASSERT_EQ(status, C2_OK) << "Error in querySupportedValues_vb";
+  ASSERT_EQ(validValueInfos.size(), 2) << "querySupportedValues_vb didn't return 2 values";
+
+  ASSERT_EQ(validValueInfos[0].values.range.max.ref<uint32_t>(), 1920)
+      << "Incorrect maximum value for width";
+  ASSERT_EQ(validValueInfos[1].values.range.max.ref<uint32_t>(), 1920)
+      << "Incorrect maximum value for height";
+  ASSERT_EQ(validValueInfos[0].values.range.min.ref<uint32_t>(), 2)
+      << "Incorrect minimum value for width";
+  ASSERT_EQ(validValueInfos[1].values.range.min.ref<uint32_t>(), 2)
+      << "Incorrect minimum value for height";
+  ASSERT_EQ(validValueInfos[0].values.range.step.ref<uint32_t>(), 2)
+      << "Incorrect alignment value for width";
+  ASSERT_EQ(validValueInfos[1].values.range.step.ref<uint32_t>(), 2)
+      << "Incorrect alignment value for height";
+
+  return;
+}
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  int status = RUN_ALL_TESTS();
+  ALOGV("Test result = %d\n", status);
+  return status;
+}
diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h
index 752140a..8e8a08b 100644
--- a/media/codec2/core/include/C2Config.h
+++ b/media/codec2/core/include/C2Config.h
@@ -187,6 +187,8 @@
     kParamIndexPictureType,
     kParamIndexHdr10PlusMetadata,
 
+    kParamIndexQuantization,
+
     /* ------------------------------------ video components ------------------------------------ */
 
     kParamIndexFrameRate = C2_PARAM_INDEX_VIDEO_PARAM_START,
@@ -708,6 +710,38 @@
         C2StreamProfileLevelInfo;
 constexpr char C2_PARAMKEY_PROFILE_LEVEL[] = "coded.pl";
 
+struct C2QuantizationStruct {
+    int32_t iMax;  ///< max/min for I frames
+    int32_t iMin;
+    int32_t pMax;  ///< max/min for P frames
+    int32_t pMin;
+    int32_t bMax;  ///< max/min for B frames
+    int32_t bMin;
+
+    C2QuantizationStruct(
+            int32_t iMax_ = INT32_MAX,
+            int32_t iMin_ = INT32_MIN,
+            int32_t pMax_ = INT32_MAX,
+            int32_t pMin_ = INT32_MIN,
+            int32_t bMax_ = INT32_MAX,
+            int32_t bMin_ = INT32_MIN)
+        : iMax(iMax_), iMin(iMin_),
+          pMax(pMax_), pMin(pMin_),
+          bMax(bMax_), bMin(bMin_) { }
+
+    DEFINE_AND_DESCRIBE_C2STRUCT(Quantization)          // reference?
+    C2FIELD(iMax, "i-max")
+    C2FIELD(iMin, "i-min")
+    C2FIELD(pMax, "p-max")
+    C2FIELD(pMin, "p-min")
+    C2FIELD(bMax, "b-max")
+    C2FIELD(bMin, "b-min")
+};
+
+typedef C2StreamParam<C2Info, C2QuantizationStruct, kParamIndexQuantization>
+        C2StreamQuantizationInfo;
+constexpr char C2_PARAMKEY_QUANTIZATION[] = "coded.qp";
+
 /**
  * Codec-specific initialization data.
  *
diff --git a/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp b/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp
index 5ec88ec..7c2e014 100644
--- a/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp
+++ b/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp
@@ -201,6 +201,8 @@
         c2_status_t err = mAllocator->priorGraphicAllocation(handle, &alloc);
         mAllocatorMutex.unlock();
         if (err != OK) {
+            native_handle_close(handle);
+            native_handle_delete(handle);
             return UNKNOWN_ERROR;
         }
         std::shared_ptr<C2GraphicBlock> block =
diff --git a/media/codec2/sfplugin/C2OMXNode.cpp b/media/codec2/sfplugin/C2OMXNode.cpp
index ab73245..466eedc 100644
--- a/media/codec2/sfplugin/C2OMXNode.cpp
+++ b/media/codec2/sfplugin/C2OMXNode.cpp
@@ -33,12 +33,14 @@
 #include <OMX_IndexExt.h>
 
 #include <android/fdsan.h>
+#include <media/stagefright/foundation/ColorUtils.h>
 #include <media/stagefright/omx/OMXUtils.h>
 #include <media/stagefright/MediaErrors.h>
 #include <ui/Fence.h>
 #include <ui/GraphicBuffer.h>
 #include <utils/Thread.h>
 
+#include "utils/Codec2Mapper.h"
 #include "C2OMXNode.h"
 
 namespace android {
@@ -71,6 +73,25 @@
         jobs->cond.broadcast();
     }
 
+    void setDataspace(android_dataspace dataspace) {
+        Mutexed<Jobs>::Locked jobs(mJobs);
+        ColorUtils::convertDataSpaceToV0(dataspace);
+        jobs->configUpdate.emplace_back(new C2StreamDataSpaceInfo::input(0u, dataspace));
+        int32_t standard = (int32_t(dataspace) & HAL_DATASPACE_STANDARD_MASK)
+            >> HAL_DATASPACE_STANDARD_SHIFT;
+        int32_t transfer = (int32_t(dataspace) & HAL_DATASPACE_TRANSFER_MASK)
+            >> HAL_DATASPACE_TRANSFER_SHIFT;
+        int32_t range = (int32_t(dataspace) & HAL_DATASPACE_RANGE_MASK)
+            >> HAL_DATASPACE_RANGE_SHIFT;
+        std::unique_ptr<C2StreamColorAspectsInfo::input> colorAspects =
+            std::make_unique<C2StreamColorAspectsInfo::input>(0u);
+        if (C2Mapper::map(standard, &colorAspects->primaries, &colorAspects->matrix)
+                && C2Mapper::map(transfer, &colorAspects->transfer)
+                && C2Mapper::map(range, &colorAspects->range)) {
+            jobs->configUpdate.push_back(std::move(colorAspects));
+        }
+    }
+
 protected:
     bool threadLoop() override {
         constexpr nsecs_t kIntervalNs = nsecs_t(10) * 1000 * 1000;  // 10ms
@@ -102,6 +123,9 @@
                     uniqueFds.push_back(std::move(queue.workList.front().fd1));
                     queue.workList.pop_front();
                 }
+                for (const std::unique_ptr<C2Param> &param : jobs->configUpdate) {
+                    items.front()->input.configUpdate.emplace_back(C2Param::Copy(*param));
+                }
 
                 jobs.unlock();
                 for (int fenceFd : fenceFds) {
@@ -119,6 +143,7 @@
                 queued = true;
             }
             if (queued) {
+                jobs->configUpdate.clear();
                 return true;
             }
             if (i == 0) {
@@ -161,6 +186,7 @@
         std::map<std::weak_ptr<Codec2Client::Component>,
                  Queue,
                  std::owner_less<std::weak_ptr<Codec2Client::Component>>> queues;
+        std::vector<std::unique_ptr<C2Param>> configUpdate;
         Condition cond;
     };
     Mutexed<Jobs> mJobs;
@@ -172,6 +198,9 @@
       mQueueThread(new QueueThread) {
     android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ALWAYS);
     mQueueThread->run("C2OMXNode", PRIORITY_AUDIO);
+
+    Mutexed<android_dataspace>::Locked ds(mDataspace);
+    *ds = HAL_DATASPACE_UNKNOWN;
 }
 
 status_t C2OMXNode::freeNode() {
@@ -392,6 +421,8 @@
         if (err != OK) {
             (void)fd0.release();
             (void)fd1.release();
+            native_handle_close(handle);
+            native_handle_delete(handle);
             return UNKNOWN_ERROR;
         }
         block = _C2BlockFactory::CreateGraphicBlock(alloc);
@@ -459,8 +490,11 @@
     android_dataspace dataSpace = (android_dataspace)msg.u.event_data.data1;
     uint32_t pixelFormat = msg.u.event_data.data3;
 
-    // TODO: set dataspace on component to see if it impacts color aspects
     ALOGD("dataspace changed to %#x pixel format: %#x", dataSpace, pixelFormat);
+    mQueueThread->setDataspace(dataSpace);
+
+    Mutexed<android_dataspace>::Locked ds(mDataspace);
+    *ds = dataSpace;
     return OK;
 }
 
@@ -493,4 +527,8 @@
     (void)mBufferSource->onInputBufferEmptied(bufferId, -1);
 }
 
+android_dataspace C2OMXNode::getDataspace() {
+    return *mDataspace.lock();
+}
+
 }  // namespace android
diff --git a/media/codec2/sfplugin/C2OMXNode.h b/media/codec2/sfplugin/C2OMXNode.h
index 1717c96..5d587bc 100644
--- a/media/codec2/sfplugin/C2OMXNode.h
+++ b/media/codec2/sfplugin/C2OMXNode.h
@@ -93,6 +93,8 @@
      */
     void onInputBufferDone(c2_cntr64_t index);
 
+    android_dataspace getDataspace();
+
 private:
     std::weak_ptr<Codec2Client::Component> mComp;
     sp<IOMXBufferSource> mBufferSource;
@@ -101,6 +103,7 @@
     uint32_t mWidth;
     uint32_t mHeight;
     uint64_t mUsage;
+    Mutexed<android_dataspace> mDataspace;
 
     // WORKAROUND: timestamp adjustment
 
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 66a2b6a..fe451f6 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -44,6 +44,7 @@
 #include <media/stagefright/BufferProducerWrapper.h>
 #include <media/stagefright/MediaCodecConstants.h>
 #include <media/stagefright/PersistentSurface.h>
+#include <utils/NativeHandle.h>
 
 #include "C2OMXNode.h"
 #include "CCodecBufferChannel.h"
@@ -210,8 +211,6 @@
                 (OMX_INDEXTYPE)OMX_IndexParamConsumerUsageBits,
                 &usage, sizeof(usage));
 
-        // NOTE: we do not use/pass through color aspects from GraphicBufferSource as we
-        // communicate that directly to the component.
         mSource->configure(
                 mOmxNode, static_cast<hardware::graphics::common::V1_0::Dataspace>(mDataSpace));
         return OK;
@@ -410,6 +409,10 @@
         mNode->onInputBufferDone(index);
     }
 
+    android_dataspace getDataspace() override {
+        return mNode->getDataspace();
+    }
+
 private:
     sp<HGraphicBufferSource> mSource;
     sp<C2OMXNode> mNode;
@@ -552,13 +555,15 @@
         }
 
         // Report to MediaCodec
-        // Note: for now we do not propagate the error code to MediaCodec as we would need
-        // to translate to a MediaCodec error.
+        // Note: for now we do not propagate the error code to MediaCodec
+        // except for C2_NO_MEMORY, as we would need to translate to a MediaCodec error.
         sp<CCodec> codec(mCodec.promote());
         if (!codec || !codec->mCallback) {
             return;
         }
-        codec->mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+        codec->mCallback->onError(
+                errorCode == C2_NO_MEMORY ? NO_MEMORY : UNKNOWN_ERROR,
+                ACTION_CODE_FATAL);
     }
 
     virtual void onDeath(
@@ -794,10 +799,30 @@
             mChannel->setMetaMode(CCodecBufferChannel::MODE_ANW);
         }
 
+        status_t err = OK;
         sp<RefBase> obj;
         sp<Surface> surface;
         if (msg->findObject("native-window", &obj)) {
             surface = static_cast<Surface *>(obj.get());
+            // setup tunneled playback
+            if (surface != nullptr) {
+                Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+                const std::unique_ptr<Config> &config = *configLocked;
+                if ((config->mDomain & Config::IS_DECODER)
+                        && (config->mDomain & Config::IS_VIDEO)) {
+                    int32_t tunneled;
+                    if (msg->findInt32("feature-tunneled-playback", &tunneled) && tunneled != 0) {
+                        ALOGI("Configuring TUNNELED video playback.");
+
+                        err = configureTunneledVideoPlayback(comp, &config->mSidebandHandle, msg);
+                        if (err != OK) {
+                            ALOGE("configureTunneledVideoPlayback failed!");
+                            return err;
+                        }
+                        config->mTunneled = true;
+                    }
+                }
+            }
             setSurface(surface);
         }
 
@@ -1006,6 +1031,29 @@
             }
         }
 
+        // get color aspects
+        getColorAspectsFromFormat(msg, config->mClientColorAspects);
+
+        /*
+         * Handle dataspace
+         */
+        int32_t usingRecorder;
+        if (msg->findInt32("android._using-recorder", &usingRecorder) && usingRecorder) {
+            android_dataspace dataSpace = HAL_DATASPACE_BT709;
+            int32_t width, height;
+            if (msg->findInt32("width", &width)
+                    && msg->findInt32("height", &height)) {
+                setDefaultCodecColorAspectsIfNeeded(config->mClientColorAspects, width, height);
+                // TODO: read dataspace / color aspect from the component
+                setColorAspectsIntoFormat(
+                        config->mClientColorAspects, const_cast<sp<AMessage> &>(msg));
+                dataSpace = getDataSpaceForColorAspects(
+                        config->mClientColorAspects, true /* mayexpand */);
+            }
+            msg->setInt32("android._dataspace", (int32_t)dataSpace);
+            ALOGD("setting dataspace to %x", dataSpace);
+        }
+
         int32_t subscribeToAllVendorParams;
         if (msg->findInt32("x-*", &subscribeToAllVendorParams) && subscribeToAllVendorParams) {
             if (config->subscribeToAllVendorParams(comp, C2_MAY_BLOCK) != OK) {
@@ -1022,7 +1070,7 @@
             sdkParams = msg->dup();
             sdkParams->removeEntryAt(sdkParams->findEntryByName(PARAMETER_KEY_VIDEO_BITRATE));
         }
-        status_t err = config->getConfigUpdateFromSdkParams(
+        err = config->getConfigUpdateFromSdkParams(
                 comp, sdkParams, Config::IS_CONFIG, C2_DONT_BLOCK, &configUpdate);
         if (err != OK) {
             ALOGW("failed to convert configuration to c2 params");
@@ -1133,10 +1181,10 @@
         int32_t clientPrepend;
         if ((config->mDomain & Config::IS_VIDEO)
                 && (config->mDomain & Config::IS_ENCODER)
-                && msg->findInt32(KEY_PREPEND_HEADERS_TO_SYNC_FRAMES, &clientPrepend)
+                && msg->findInt32(KEY_PREPEND_HEADER_TO_SYNC_FRAMES, &clientPrepend)
                 && clientPrepend
                 && (!prepend || prepend.value != PREPEND_HEADER_TO_ALL_SYNC)) {
-            ALOGE("Failed to set KEY_PREPEND_HEADERS_TO_SYNC_FRAMES");
+            ALOGE("Failed to set KEY_PREPEND_HEADER_TO_SYNC_FRAMES");
             return BAD_VALUE;
         }
 
@@ -1703,6 +1751,19 @@
 }
 
 status_t CCodec::setSurface(const sp<Surface> &surface) {
+    Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+    const std::unique_ptr<Config> &config = *configLocked;
+    if (config->mTunneled && config->mSidebandHandle != nullptr) {
+        sp<ANativeWindow> nativeWindow = static_cast<ANativeWindow *>(surface.get());
+        status_t err = native_window_set_sideband_stream(
+                nativeWindow.get(),
+                const_cast<native_handle_t *>(config->mSidebandHandle->handle()));
+        if (err != OK) {
+            ALOGE("NativeWindow(%p) native_window_set_sideband_stream(%p) failed! (err %d).",
+                    nativeWindow.get(), config->mSidebandHandle->handle(), err);
+            return err;
+        }
+    }
     return mChannel->setSurface(surface);
 }
 
@@ -1926,6 +1987,44 @@
     }
 }
 
+static void HandleDataspace(
+        android_dataspace dataspace, ColorAspects *colorAspects, sp<AMessage> *format) {
+    ColorUtils::convertDataSpaceToV0(dataspace);
+    int32_t range, standard, transfer;
+    range = (dataspace & HAL_DATASPACE_RANGE_MASK) >> HAL_DATASPACE_RANGE_SHIFT;
+    if (range == 0) {
+        range = ColorUtils::wrapColorAspectsIntoColorRange(
+                colorAspects->mRange);
+    }
+    standard = (dataspace & HAL_DATASPACE_STANDARD_MASK) >> HAL_DATASPACE_STANDARD_SHIFT;
+    if (standard == 0) {
+        standard = ColorUtils::wrapColorAspectsIntoColorStandard(
+                colorAspects->mPrimaries,
+                colorAspects->mMatrixCoeffs);
+    }
+    transfer = (dataspace & HAL_DATASPACE_TRANSFER_MASK) >> HAL_DATASPACE_TRANSFER_SHIFT;
+    if (transfer == 0) {
+        transfer = ColorUtils::wrapColorAspectsIntoColorTransfer(
+                colorAspects->mTransfer);
+    }
+    ColorAspects newColorAspects;
+    ColorUtils::convertPlatformColorAspectsToCodecAspects(
+            range, standard, transfer, newColorAspects);
+    if (ColorUtils::checkIfAspectsChangedAndUnspecifyThem(
+            newColorAspects, *colorAspects)) {
+        *format = (*format)->dup();
+        (*format)->setInt32(KEY_COLOR_RANGE, range);
+        (*format)->setInt32(KEY_COLOR_STANDARD, standard);
+        (*format)->setInt32(KEY_COLOR_TRANSFER, transfer);
+        // Record current color aspects into |colorAspects|.
+        // NOTE: newColorAspects could have been modified by
+        //       checkIfAspectsChangedAndUnspecifyThem() above,
+        //       so *colorAspects = newColorAspects does not work as intended.
+        ColorUtils::convertPlatformColorAspectsToCodecAspects(
+                range, standard, transfer, *colorAspects);
+    }
+}
+
 void CCodec::onMessageReceived(const sp<AMessage> &msg) {
     TimePoint now = std::chrono::steady_clock::now();
     CCodecWatchdog::getInstance()->watch(this);
@@ -2040,6 +2139,10 @@
 
                 sp<AMessage> outputFormat = config->mOutputFormat;
                 config->updateConfiguration(updates, config->mOutputDomain);
+                if (config->mInputSurface) {
+                    android_dataspace ds = config->mInputSurface->getDataspace();
+                    HandleDataspace(ds, &config->mClientColorAspects, &config->mOutputFormat);
+                }
                 RevertOutputFormatIfNeeded(outputFormat, config->mOutputFormat);
 
                 // copy standard infos to graphic buffers if not already present (otherwise, we
@@ -2099,6 +2202,51 @@
     deadline->set(now + (timeout * mult), name);
 }
 
+status_t CCodec::configureTunneledVideoPlayback(
+        std::shared_ptr<Codec2Client::Component> comp,
+        sp<NativeHandle> *sidebandHandle,
+        const sp<AMessage> &msg) {
+    std::vector<std::unique_ptr<C2SettingResult>> failures;
+
+    std::unique_ptr<C2PortTunneledModeTuning::output> tunneledPlayback =
+        C2PortTunneledModeTuning::output::AllocUnique(
+            1,
+            C2PortTunneledModeTuning::Struct::SIDEBAND,
+            C2PortTunneledModeTuning::Struct::REALTIME,
+            0);
+    // TODO: use KEY_AUDIO_HW_SYNC, KEY_HARDWARE_AV_SYNC_ID when they are in MediaCodecConstants.h
+    if (msg->findInt32("audio-hw-sync", &tunneledPlayback->m.syncId[0])) {
+        tunneledPlayback->m.syncType = C2PortTunneledModeTuning::Struct::sync_type_t::AUDIO_HW_SYNC;
+    } else if (msg->findInt32("hw-av-sync-id", &tunneledPlayback->m.syncId[0])) {
+        tunneledPlayback->m.syncType = C2PortTunneledModeTuning::Struct::sync_type_t::HW_AV_SYNC;
+    } else {
+        tunneledPlayback->m.syncType = C2PortTunneledModeTuning::Struct::sync_type_t::REALTIME;
+        tunneledPlayback->setFlexCount(0);
+    }
+    c2_status_t c2err = comp->config({ tunneledPlayback.get() }, C2_MAY_BLOCK, &failures);
+    if (c2err != C2_OK) {
+        return UNKNOWN_ERROR;
+    }
+
+    std::vector<std::unique_ptr<C2Param>> params;
+    c2err = comp->query({}, {C2PortTunnelHandleTuning::output::PARAM_TYPE}, C2_DONT_BLOCK, &params);
+    if (c2err == C2_OK && params.size() == 1u) {
+        C2PortTunnelHandleTuning::output *videoTunnelSideband =
+            C2PortTunnelHandleTuning::output::From(params[0].get());
+        // Currently, Codec2 only supports non-fd case for sideband native_handle.
+        native_handle_t *handle = native_handle_create(0, videoTunnelSideband->flexCount());
+        *sidebandHandle = NativeHandle::create(handle, true /* ownsHandle */);
+        if (handle != nullptr && videoTunnelSideband->flexCount()) {
+            memcpy(handle->data, videoTunnelSideband->m.values,
+                    sizeof(int32_t) * videoTunnelSideband->flexCount());
+            return OK;
+        } else {
+            return NO_MEMORY;
+        }
+    }
+    return UNKNOWN_ERROR;
+}
+
 void CCodec::initiateReleaseIfStuck() {
     std::string name;
     bool pendingDeadline = false;
@@ -2111,7 +2259,9 @@
             pendingDeadline = true;
         }
     }
-    if (name.empty()) {
+    Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+    const std::unique_ptr<Config> &config = *configLocked;
+    if (config->mTunneled == false && name.empty()) {
         constexpr std::chrono::steady_clock::duration kWorkDurationThreshold = 3s;
         std::chrono::steady_clock::duration elapsed = mChannel->elapsed();
         if (elapsed >= kWorkDurationThreshold) {
@@ -2504,4 +2654,3 @@
 }
 
 }  // namespace android
-
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 44ebf84..3f717c9 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -1728,6 +1728,17 @@
                 }
                 break;
             }
+            case C2PortTunnelSystemTime::CORE_INDEX: {
+                C2PortTunnelSystemTime::output frameRenderTime;
+                if (frameRenderTime.updateFrom(*param)) {
+                    ALOGV("[%s] onWorkDone: frame rendered (sys:%lld ns, media:%lld us)",
+                          mName, (long long)frameRenderTime.value,
+                          (long long)worklet->output.ordinal.timestamp.peekll());
+                    mCCodecCallback->onOutputFramesRendered(
+                            worklet->output.ordinal.timestamp.peek(), frameRenderTime.value);
+                }
+                break;
+            }
             default:
                 ALOGV("[%s] onWorkDone: unrecognized config update (%08X)",
                       mName, param->index());
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index d3814fb..5de7539 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "CCodecConfig"
 #include <cutils/properties.h>
 #include <log/log.h>
+#include <utils/NativeHandle.h>
 
 #include <C2Component.h>
 #include <C2Param.h>
@@ -321,7 +322,8 @@
 CCodecConfig::CCodecConfig()
     : mInputFormat(new AMessage),
       mOutputFormat(new AMessage),
-      mUsingSurface(false) { }
+      mUsingSurface(false),
+      mTunneled(false) { }
 
 void CCodecConfig::initializeStandardParams() {
     typedef Domain D;
@@ -507,7 +509,7 @@
     add(ConfigMapper(std::string(KEY_FEATURE_) + FEATURE_SecurePlayback,
                      C2_PARAMKEY_SECURE_MODE, "value"));
 
-    add(ConfigMapper(KEY_PREPEND_HEADERS_TO_SYNC_FRAMES,
+    add(ConfigMapper(KEY_PREPEND_HEADER_TO_SYNC_FRAMES,
                      C2_PARAMKEY_PREPEND_HEADER_MODE, "value")
         .limitTo(D::ENCODER & D::VIDEO)
         .withMappers([](C2Value v) -> C2Value {
@@ -531,7 +533,7 @@
             return C2Value();
         }));
     // remove when codecs switch to PARAMKEY
-    deprecated(ConfigMapper(KEY_PREPEND_HEADERS_TO_SYNC_FRAMES,
+    deprecated(ConfigMapper(KEY_PREPEND_HEADER_TO_SYNC_FRAMES,
                             "coding.add-csd-to-sync-frames", "value")
                .limitTo(D::ENCODER & D::VIDEO));
     // convert to timestamp base
@@ -727,6 +729,19 @@
             return C2Value();
         }));
 
+    add(ConfigMapper(KEY_VIDEO_QP_I_MAX, C2_PARAMKEY_QUANTIZATION, "i-max")
+        .limitTo(D::VIDEO & D::ENCODER));
+    add(ConfigMapper(KEY_VIDEO_QP_I_MIN, C2_PARAMKEY_QUANTIZATION, "i-min")
+        .limitTo(D::VIDEO & D::ENCODER));
+    add(ConfigMapper(KEY_VIDEO_QP_P_MAX, C2_PARAMKEY_QUANTIZATION, "p-max")
+        .limitTo(D::VIDEO & D::ENCODER));
+    add(ConfigMapper(KEY_VIDEO_QP_P_MIN, C2_PARAMKEY_QUANTIZATION, "p-min")
+        .limitTo(D::VIDEO & D::ENCODER));
+    add(ConfigMapper(KEY_VIDEO_QP_B_MAX, C2_PARAMKEY_QUANTIZATION, "b-max")
+        .limitTo(D::VIDEO & D::ENCODER));
+    add(ConfigMapper(KEY_VIDEO_QP_B_MIN, C2_PARAMKEY_QUANTIZATION, "b-min")
+        .limitTo(D::VIDEO & D::ENCODER));
+
     // convert to dBFS and add default
     add(ConfigMapper(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, C2_PARAMKEY_DRC_TARGET_REFERENCE_LEVEL, "value")
         .limitTo(D::AUDIO & D::DECODER & (D::CONFIG | D::PARAM | D::READ))
diff --git a/media/codec2/sfplugin/CCodecConfig.h b/media/codec2/sfplugin/CCodecConfig.h
index 2895746..d9116f7 100644
--- a/media/codec2/sfplugin/CCodecConfig.h
+++ b/media/codec2/sfplugin/CCodecConfig.h
@@ -27,6 +27,7 @@
 #include <C2Debug.h>
 
 #include <codec2/hidl/client.h>
+#include <media/stagefright/foundation/ColorUtils.h>
 #include <utils/RefBase.h>
 
 #include "InputSurfaceWrapper.h"
@@ -35,6 +36,7 @@
 namespace android {
 
 struct AMessage;
+class NativeHandle;
 struct StandardParams;
 
 /**
@@ -123,6 +125,7 @@
 
     std::shared_ptr<InputSurfaceWrapper> mInputSurface;
     std::unique_ptr<InputSurfaceWrapper::Config> mISConfig;
+    ColorAspects mClientColorAspects;
 
     /// the current configuration. Updated after configure() and based on configUpdate in
     /// onWorkDone
@@ -141,6 +144,10 @@
 
     std::set<std::string> mLastConfig;
 
+    /// Tunneled codecs
+    bool mTunneled;
+    sp<NativeHandle> mSidebandHandle;
+
     CCodecConfig();
 
     /// initializes the members required to manage the format: descriptors, reflector,
diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp
index fc4ee51..34e6a88 100644
--- a/media/codec2/sfplugin/Codec2Buffer.cpp
+++ b/media/codec2/sfplugin/Codec2Buffer.cpp
@@ -713,6 +713,8 @@
     c2_status_t err = mAlloc->priorGraphicAllocation(handle, &alloc);
     if (err != C2_OK) {
         ALOGD("Failed to wrap VideoNativeMetadata into C2GraphicAllocation");
+        native_handle_close(handle);
+        native_handle_delete(handle);
         return nullptr;
     }
     std::shared_ptr<C2GraphicBlock> block = _C2BlockFactory::CreateGraphicBlock(alloc);
diff --git a/media/codec2/sfplugin/InputSurfaceWrapper.h b/media/codec2/sfplugin/InputSurfaceWrapper.h
index 479acb1..d29738c 100644
--- a/media/codec2/sfplugin/InputSurfaceWrapper.h
+++ b/media/codec2/sfplugin/InputSurfaceWrapper.h
@@ -106,6 +106,8 @@
      */
     virtual void onInputBufferDone(c2_cntr64_t /* index */) {}
 
+    virtual android_dataspace getDataspace() { return mDataSpace; }
+
 protected:
     android_dataspace mDataSpace;
 };
diff --git a/media/codec2/sfplugin/include/media/stagefright/CCodec.h b/media/codec2/sfplugin/include/media/stagefright/CCodec.h
index dbbb5d5..ba69d7e 100644
--- a/media/codec2/sfplugin/include/media/stagefright/CCodec.h
+++ b/media/codec2/sfplugin/include/media/stagefright/CCodec.h
@@ -126,6 +126,11 @@
             const std::chrono::milliseconds &timeout,
             const char *name);
 
+    status_t configureTunneledVideoPlayback(
+            const std::shared_ptr<Codec2Client::Component> comp,
+            sp<NativeHandle> *sidebandHandle,
+            const sp<AMessage> &msg);
+
     enum {
         kWhatAllocate,
         kWhatConfigure,
diff --git a/media/codec2/tests/vndk/C2BufferTest.cpp b/media/codec2/tests/vndk/C2BufferTest.cpp
index a9f8e17..0cfb465 100644
--- a/media/codec2/tests/vndk/C2BufferTest.cpp
+++ b/media/codec2/tests/vndk/C2BufferTest.cpp
@@ -16,11 +16,12 @@
 
 #include <gtest/gtest.h>
 
-#include <C2AllocatorIon.h>
 #include <C2AllocatorGralloc.h>
 #include <C2Buffer.h>
 #include <C2BufferPriv.h>
+#include <C2Config.h>
 #include <C2ParamDef.h>
+#include <C2PlatformSupport.h>
 
 #include <system/graphics.h>
 
@@ -233,10 +234,10 @@
 public:
     C2BufferTest()
         : mBlockPoolId(C2BlockPool::PLATFORM_START),
-          mLinearAllocator(std::make_shared<C2AllocatorIon>('i')),
           mSize(0u),
           mAddr(nullptr),
           mGraphicAllocator(std::make_shared<C2AllocatorGralloc>('g')) {
+        getLinearAllocator(&mLinearAllocator);
     }
 
     ~C2BufferTest() = default;
@@ -329,6 +330,11 @@
     }
 
 private:
+    void getLinearAllocator(std::shared_ptr<C2Allocator>* mLinearAllocator) {
+        std::shared_ptr<C2AllocatorStore> store = android::GetCodec2PlatformAllocatorStore();
+        ASSERT_EQ(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, mLinearAllocator), C2_OK);
+    }
+
     C2BlockPool::local_id_t mBlockPoolId;
     std::shared_ptr<C2Allocator> mLinearAllocator;
     std::shared_ptr<C2LinearAllocation> mLinearAllocation;
diff --git a/media/codec2/vndk/include/C2AllocatorGralloc.h b/media/codec2/vndk/include/C2AllocatorGralloc.h
index 578cf76..1da3e14 100644
--- a/media/codec2/vndk/include/C2AllocatorGralloc.h
+++ b/media/codec2/vndk/include/C2AllocatorGralloc.h
@@ -37,7 +37,8 @@
  * Wrap the gralloc handle and metadata into Codec2 handle recognized by
  * C2AllocatorGralloc.
  *
- * @return a new NON-OWNING C2Handle that must be deleted using native_handle_delete.
+ * @return a new NON-OWNING C2Handle that must be closed and deleted using native_handle_close and
+ * native_handle_delete.
  */
 C2Handle *WrapNativeCodec2GrallocHandle(
         const native_handle_t *const handle,
diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp
index e14b4b1..3f6fa7d 100644
--- a/media/codec2/vndk/platform/C2BqBuffer.cpp
+++ b/media/codec2/vndk/platform/C2BqBuffer.cpp
@@ -364,6 +364,8 @@
                 std::shared_ptr<C2GraphicAllocation> alloc;
                 c2_status_t err = mAllocator->priorGraphicAllocation(c2Handle, &alloc);
                 if (err != C2_OK) {
+                    native_handle_close(c2Handle);
+                    native_handle_delete(c2Handle);
                     return err;
                 }
                 std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
diff --git a/media/janitors/OWNERS-codecs b/media/janitors/codec_OWNERS
similarity index 100%
rename from media/janitors/OWNERS-codecs
rename to media/janitors/codec_OWNERS
diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h
index 9072886..22cf254 100644
--- a/media/libaaudio/include/aaudio/AAudio.h
+++ b/media/libaaudio/include/aaudio/AAudio.h
@@ -580,6 +580,37 @@
 AAUDIO_API void AAudioStreamBuilder_setDeviceId(AAudioStreamBuilder* builder,
                                                 int32_t deviceId) __INTRODUCED_IN(26);
 
+// TODO b/182392769: reexamine if Identity can be used
+/**
+ * Declare the name of the package creating the stream.
+ *
+ * This is usually {@code Context#getPackageName()}.
+ *
+ * The default, if you do not call this function, is a random package in the calling uid.
+ *
+ * Available since API level 31.
+ *
+ * @param builder reference provided by AAudio_createStreamBuilder()
+ * @param packageName packageName of the calling app.
+ */
+AAUDIO_API void AAudioStreamBuilder_setPackageName(AAudioStreamBuilder* builder,
+                                                   const char * packageName) __INTRODUCED_IN(31);
+
+/**
+ * Declare the attribution tag of the context creating the stream.
+ *
+ * This is usually {@code Context#getAttributionTag()}.
+ *
+ * The default, if you do not call this function, is the default attribution tag.
+ *
+ * Available since API level 31.
+ *
+ * @param builder reference provided by AAudio_createStreamBuilder()
+ * @param attributionTag attributionTag of the calling context.
+ */
+AAUDIO_API void AAudioStreamBuilder_setAttributionTag(AAudioStreamBuilder* builder,
+        const char * attributionTag) __INTRODUCED_IN(31);
+
 /**
  * Request a sample rate in Hertz.
  *
diff --git a/media/libaaudio/src/Android.bp b/media/libaaudio/src/Android.bp
index 47cbbb1..fe2d98e 100644
--- a/media/libaaudio/src/Android.bp
+++ b/media/libaaudio/src/Android.bp
@@ -85,6 +85,10 @@
     ],
     export_header_lib_headers: ["libaaudio_headers"],
 
+    export_shared_lib_headers: [
+        "media_permission-aidl-cpp",
+    ],
+
     shared_libs: [
         "libaudioclient",
         "libaudioutils",
@@ -96,6 +100,12 @@
         "libutils",
         "libbinder",
         "aaudio-aidl-cpp",
+        "media_permission-aidl-cpp",
+        "libaudioclient_aidl_conversion",
+    ],
+
+    static_libs: [
+        "media_permission-aidl-cpp",
     ],
 
     cflags: [
@@ -167,6 +177,7 @@
     imports: [
         "audio_common-aidl",
         "shared-file-region-aidl",
+        "media_permission-aidl",
     ],
     backend:
     {
diff --git a/media/libaaudio/src/binding/AAudioStreamRequest.cpp b/media/libaaudio/src/binding/AAudioStreamRequest.cpp
index 536395a..5e0a4bb 100644
--- a/media/libaaudio/src/binding/AAudioStreamRequest.cpp
+++ b/media/libaaudio/src/binding/AAudioStreamRequest.cpp
@@ -31,19 +31,15 @@
 
 AAudioStreamRequest::AAudioStreamRequest(const StreamRequest& parcelable) :
         mConfiguration(std::move(parcelable.params)),
-        mUserId(parcelable.userId),
-        mProcessId(parcelable.processId),
+        mIdentity(parcelable.identity),
         mSharingModeMatchRequired(parcelable.sharingModeMatchRequired),
         mInService(parcelable.inService) {
-    static_assert(sizeof(mUserId) == sizeof(parcelable.userId));
-    static_assert(sizeof(mProcessId) == sizeof(parcelable.processId));
 }
 
 StreamRequest AAudioStreamRequest::parcelable() const {
     StreamRequest result;
     result.params = std::move(mConfiguration).parcelable();
-    result.userId = mUserId;
-    result.processId = mProcessId;
+    result.identity = mIdentity;
     result.sharingModeMatchRequired = mSharingModeMatchRequired;
     result.inService = mInService;
     return result;
@@ -54,8 +50,7 @@
 }
 
 void AAudioStreamRequest::dump() const {
-    ALOGD("mUserId    = %d", mUserId);
-    ALOGD("mProcessId = %d", mProcessId);
+    ALOGD("mIdentity  = %s", mIdentity.toString().c_str());
     ALOGD("mSharingModeMatchRequired = %d", mSharingModeMatchRequired);
     ALOGD("mInService = %d", mInService);
     mConfiguration.dump();
diff --git a/media/libaaudio/src/binding/AAudioStreamRequest.h b/media/libaaudio/src/binding/AAudioStreamRequest.h
index 31d3ea1..02341c8 100644
--- a/media/libaaudio/src/binding/AAudioStreamRequest.h
+++ b/media/libaaudio/src/binding/AAudioStreamRequest.h
@@ -23,6 +23,7 @@
 #include <aaudio/StreamRequest.h>
 
 #include "binding/AAudioStreamConfiguration.h"
+#include <android/media/permission/Identity.h>
 
 namespace aaudio {
 
@@ -33,20 +34,12 @@
     // Construct based on a parcelable representation.
     explicit AAudioStreamRequest(const StreamRequest& parcelable);
 
-    uid_t getUserId() const {
-        return mUserId;
+    const android::media::permission::Identity &getIdentity() const {
+        return mIdentity;
     }
 
-    void setUserId(uid_t userId) {
-        mUserId = userId;
-    }
-
-    pid_t getProcessId() const {
-        return mProcessId;
-    }
-
-    void setProcessId(pid_t processId) {
-        mProcessId = processId;
+    void setIdentity(const android::media::permission::Identity &identity) {
+        mIdentity = identity;
     }
 
     bool isSharingModeMatchRequired() const {
@@ -82,8 +75,7 @@
 
 private:
     AAudioStreamConfiguration  mConfiguration;
-    uid_t                      mUserId = (uid_t) -1;
-    pid_t                      mProcessId = (pid_t) -1;
+    android::media::permission::Identity mIdentity;
     bool                       mSharingModeMatchRequired = false;
     bool                       mInService = false; // Stream opened by AAudioservice
 };
diff --git a/media/libaaudio/src/binding/aidl/aaudio/StreamRequest.aidl b/media/libaaudio/src/binding/aidl/aaudio/StreamRequest.aidl
index 9bf4077..12802e6 100644
--- a/media/libaaudio/src/binding/aidl/aaudio/StreamRequest.aidl
+++ b/media/libaaudio/src/binding/aidl/aaudio/StreamRequest.aidl
@@ -17,11 +17,11 @@
 package aaudio;
 
 import aaudio.StreamParameters;
+import android.media.permission.Identity;
 
 parcelable StreamRequest {
     StreamParameters    params;
-    int                 userId; // = (uid_t) -1;
-    int                 processId; // = (pid_t) -1;
+    Identity            identity;
     boolean             sharingModeMatchRequired; // = false;
     boolean             inService; // = false; // Stream opened by AAudioservice
 }
\ No newline at end of file
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 2815c6a..dc961ad 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -28,7 +28,6 @@
 #include <cutils/properties.h>
 
 #include <media/MediaMetricsItem.h>
-#include <utils/String16.h>
 #include <utils/Trace.h>
 
 #include "AudioEndpointParcelable.h"
@@ -39,6 +38,7 @@
 #include "core/AudioStreamBuilder.h"
 #include "fifo/FifoBuffer.h"
 #include "utility/AudioClock.h"
+#include <media/AidlConversion.h>
 
 #include "AudioStreamInternal.h"
 
@@ -49,9 +49,9 @@
 // This is needed to make sense of the logs more easily.
 #define LOG_TAG (mInService ? "AudioStreamInternal_Service" : "AudioStreamInternal_Client")
 
-using android::String16;
 using android::Mutex;
 using android::WrappingBuffer;
+using android::media::permission::Identity;
 
 using namespace aaudio;
 
@@ -107,9 +107,15 @@
     // Request FLOAT for the shared mixer or the device.
     request.getConfiguration().setFormat(AUDIO_FORMAT_PCM_FLOAT);
 
+    // TODO b/182392769: use identity util
+    Identity identity;
+    identity.uid = VALUE_OR_FATAL(android::legacy2aidl_uid_t_int32_t(getuid()));
+    identity.pid = VALUE_OR_FATAL(android::legacy2aidl_pid_t_int32_t(getpid()));
+    identity.packageName = builder.getOpPackageName();
+    identity.attributionTag = builder.getAttributionTag();
+
     // Build the request to send to the server.
-    request.setUserId(getuid());
-    request.setProcessId(getpid());
+    request.setIdentity(identity);
     request.setSharingModeMatchRequired(isSharingModeMatchRequired());
     request.setInService(isInService());
 
diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp
index 7c16321..5d49759 100644
--- a/media/libaaudio/src/core/AAudioAudio.cpp
+++ b/media/libaaudio/src/core/AAudioAudio.cpp
@@ -87,6 +87,22 @@
     streamBuilder->setDeviceId(deviceId);
 }
 
+AAUDIO_API void AAudioStreamBuilder_setPackageName(AAudioStreamBuilder* builder,
+                                                   const char* packageName)
+{
+    AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
+    // Only system apps can read the op package name. For regular apps the regular package name
+    // is a sufficient replacement
+    streamBuilder->setOpPackageName(packageName);
+}
+
+AAUDIO_API void AAudioStreamBuilder_setAttributionTag(AAudioStreamBuilder* builder,
+                                                      const char* attributionTag)
+{
+    AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
+    streamBuilder->setAttributionTag(attributionTag);
+}
+
 AAUDIO_API void AAudioStreamBuilder_setSampleRate(AAudioStreamBuilder* builder,
                                               int32_t sampleRate)
 {
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp
index 2c81c91..0d60120 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.cpp
+++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp
@@ -48,6 +48,8 @@
     mInputPreset          = other.mInputPreset;
     mAllowedCapturePolicy = other.mAllowedCapturePolicy;
     mIsPrivacySensitive   = other.mIsPrivacySensitive;
+    mOpPackageName        = other.mOpPackageName;
+    mAttributionTag       = other.mAttributionTag;
 }
 
 static aaudio_result_t isFormatValid(audio_format_t format) {
@@ -203,4 +205,8 @@
     ALOGD("mInputPreset          = %6d", mInputPreset);
     ALOGD("mAllowedCapturePolicy = %6d", mAllowedCapturePolicy);
     ALOGD("mIsPrivacySensitive   = %s", mIsPrivacySensitive ? "true" : "false");
+    ALOGD("mOpPackageName        = %s", !mOpPackageName.has_value() ?
+        "(null)" : mOpPackageName.value().c_str());
+    ALOGD("mAttributionTag       = %s", !mAttributionTag.has_value() ?
+        "(null)" : mAttributionTag.value().c_str());
 }
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.h b/media/libaaudio/src/core/AAudioStreamParameters.h
index 3e65b37..bb39d8b 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.h
+++ b/media/libaaudio/src/core/AAudioStreamParameters.h
@@ -136,6 +136,23 @@
         mIsPrivacySensitive = privacySensitive;
     }
 
+    const std::optional<std::string> getOpPackageName() const {
+        return mOpPackageName;
+    }
+
+    // TODO b/182392769: reexamine if Identity can be used
+    void setOpPackageName(const std::string opPackageName) {
+        mOpPackageName = opPackageName;
+    }
+
+    const std::optional<std::string> getAttributionTag() const {
+        return mAttributionTag;
+    }
+
+    void setAttributionTag(const std::string attributionTag) {
+        mAttributionTag = attributionTag;
+    }
+
     /**
      * @return bytes per frame of getFormat()
      */
@@ -167,6 +184,8 @@
     aaudio_allowed_capture_policy_t mAllowedCapturePolicy = AAUDIO_UNSPECIFIED;
     aaudio_session_id_t             mSessionId            = AAUDIO_SESSION_ID_NONE;
     bool                            mIsPrivacySensitive   = false;
+    std::optional<std::string>      mOpPackageName        = {};
+    std::optional<std::string>      mAttributionTag       = {};
 };
 
 } /* namespace aaudio */
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp
index 50a3b38..207a8e3 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.cpp
+++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp
@@ -282,4 +282,8 @@
     ALOGI("usage  = %6d, contentType = %d, inputPreset = %d, allowedCapturePolicy = %d",
           getUsage(), getContentType(), getInputPreset(), getAllowedCapturePolicy());
     ALOGI("privacy sensitive = %s", isPrivacySensitive() ? "true" : "false");
+    ALOGI("opPackageName = %s", !getOpPackageName().has_value() ?
+        "(null)" : getOpPackageName().value().c_str());
+    ALOGI("attributionTag = %s", !getAttributionTag().has_value() ?
+        "(null)" : getAttributionTag().value().c_str());
 }
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 45b2258..7733a04 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -22,6 +22,7 @@
 
 #include <aaudio/AAudio.h>
 #include <audio_utils/primitives.h>
+#include <media/AidlConversion.h>
 #include <media/AudioRecord.h>
 #include <utils/String16.h>
 
@@ -30,6 +31,8 @@
 #include "utility/AudioClock.h"
 #include "utility/FixedBlockWriter.h"
 
+using android::media::permission::Identity;
+
 using namespace android;
 using namespace aaudio;
 
@@ -152,13 +155,20 @@
             .tags = ""
     };
 
+    // TODO b/182392769: use identity util
+    Identity identity;
+    identity.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(getuid()));
+    identity.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(getpid()));
+    identity.packageName = builder.getOpPackageName();
+    identity.attributionTag = builder.getAttributionTag();
+
     // ----------- open the AudioRecord ---------------------
     // Might retry, but never more than once.
     for (int i = 0; i < 2; i ++) {
         const audio_format_t requestedInternalFormat = getDeviceFormat();
 
         mAudioRecord = new AudioRecord(
-                mOpPackageName // const String16& opPackageName TODO does not compile
+                identity
         );
         mAudioRecord->set(
                 AUDIO_SOURCE_DEFAULT, // ignored because we pass attributes below
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.h b/media/libaaudio/src/legacy/AudioStreamRecord.h
index b2f8ba5..7d0a197 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.h
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.h
@@ -25,6 +25,7 @@
 #include "AAudioLegacy.h"
 #include "legacy/AudioStreamLegacy.h"
 #include "utility/FixedBlockWriter.h"
+#include <android/media/permission/Identity.h>
 
 namespace aaudio {
 
@@ -86,7 +87,7 @@
     FixedBlockWriter                 mFixedBlockWriter;
 
     // TODO add 64-bit position reporting to AudioRecord and use it.
-    android::String16                mOpPackageName;
+    android::media::permission::Identity mIdentity;
 
     // Only one type of conversion buffer is used.
     std::unique_ptr<float[]>         mFormatConversionBufferFloat;
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index af8ff19..142a85c 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -31,6 +31,8 @@
 using namespace android;
 using namespace aaudio;
 
+using media::permission::Identity;
+
 // Arbitrary and somewhat generous number of bursts.
 #define DEFAULT_BURSTS_PER_BUFFER_CAPACITY     8
 
@@ -147,6 +149,7 @@
     };
 
     mAudioTrack = new AudioTrack();
+    // TODO b/182392769: use identity util
     mAudioTrack->set(
             AUDIO_STREAM_DEFAULT,  // ignored because we pass attributes below
             getSampleRate(),
@@ -162,8 +165,7 @@
             sessionId,
             streamTransferType,
             NULL,    // DEFAULT audio_offload_info_t
-            AUDIO_UID_INVALID, // DEFAULT uid
-            -1,      // DEFAULT pid
+            Identity(), // DEFAULT uid and pid
             &attributes,
             // WARNING - If doNotReconnect set true then audio stops after plugging and unplugging
             // headphones a few times.
diff --git a/media/libaaudio/src/libaaudio.map.txt b/media/libaaudio/src/libaaudio.map.txt
index 2e00aa5..1dd44d1 100644
--- a/media/libaaudio/src/libaaudio.map.txt
+++ b/media/libaaudio/src/libaaudio.map.txt
@@ -23,6 +23,8 @@
     AAudioStreamBuilder_setAllowedCapturePolicy; # introduced=29
     AAudioStreamBuilder_setSessionId;   # introduced=28
     AAudioStreamBuilder_setPrivacySensitive;   # introduced=30
+    AAudioStreamBuilder_setPackageName;   # introduced=31
+    AAudioStreamBuilder_setAttributionTag;   # introduced=31
     AAudioStreamBuilder_openStream;
     AAudioStreamBuilder_delete;
     AAudioStream_close;
diff --git a/media/libaudioclient/AidlConversion.cpp b/media/libaudioclient/AidlConversion.cpp
index 4c0db3e..05ba55f 100644
--- a/media/libaudioclient/AidlConversion.cpp
+++ b/media/libaudioclient/AidlConversion.cpp
@@ -190,6 +190,27 @@
     return std::string(String8(legacy).c_str());
 }
 
+// TODO b/182392769: create an optional -> optional util
+ConversionResult<std::optional<String16>>
+aidl2legacy_optional_string_view_optional_String16(std::optional<std::string_view> aidl) {
+    if (!aidl.has_value()) {
+        return std::nullopt;
+    }
+    ConversionResult<String16> conversion =
+        VALUE_OR_RETURN(aidl2legacy_string_view_String16(aidl.value()));
+    return conversion.value();
+}
+
+ConversionResult<std::optional<std::string_view>>
+legacy2aidl_optional_String16_optional_string(std::optional<String16> legacy) {
+  if (!legacy.has_value()) {
+    return std::nullopt;
+  }
+  ConversionResult<std::string> conversion =
+      VALUE_OR_RETURN(legacy2aidl_String16_string(legacy.value()));
+  return conversion.value();
+}
+
 ConversionResult<String8> aidl2legacy_string_view_String8(std::string_view aidl) {
     return String8(aidl.data(), aidl.size());
 }
@@ -1160,20 +1181,16 @@
 ConversionResult<AudioClient> aidl2legacy_AudioClient_AudioClient(
         const media::AudioClient& aidl) {
     AudioClient legacy;
-    legacy.clientUid = VALUE_OR_RETURN(aidl2legacy_int32_t_uid_t(aidl.clientUid));
-    legacy.clientPid = VALUE_OR_RETURN(aidl2legacy_int32_t_pid_t(aidl.clientPid));
     legacy.clientTid = VALUE_OR_RETURN(aidl2legacy_int32_t_pid_t(aidl.clientTid));
-    legacy.packageName = VALUE_OR_RETURN(aidl2legacy_string_view_String16(aidl.packageName));
+    legacy.identity = aidl.identity;
     return legacy;
 }
 
 ConversionResult<media::AudioClient> legacy2aidl_AudioClient_AudioClient(
         const AudioClient& legacy) {
     media::AudioClient aidl;
-    aidl.clientUid = VALUE_OR_RETURN(legacy2aidl_uid_t_int32_t(legacy.clientUid));
-    aidl.clientPid = VALUE_OR_RETURN(legacy2aidl_pid_t_int32_t(legacy.clientPid));
     aidl.clientTid = VALUE_OR_RETURN(legacy2aidl_pid_t_int32_t(legacy.clientTid));
-    aidl.packageName = VALUE_OR_RETURN(legacy2aidl_String16_string(legacy.packageName));
+    aidl.identity = legacy.identity;
     return aidl;
 }
 
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index 79c155e..d25597d 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -132,10 +132,12 @@
         "libshmemcompat",
         "libutils",
         "libvibrator",
+        "media_permission-aidl-cpp",
     ],
     export_shared_lib_headers: [
         "audioflinger-aidl-cpp",
         "audiopolicy-aidl-cpp",
+        "media_permission-aidl-cpp",
         "libbinder",
     ],
 
@@ -162,6 +164,7 @@
         // for memory heap analysis
         "libc_malloc_debug_backtrace",
         "shared-file-region-aidl-cpp",
+        "media_permission-aidl-cpp",
     ],
     cflags: [
         "-Wall",
@@ -229,6 +232,7 @@
         "libshmemcompat",
         "libutils",
         "shared-file-region-aidl-cpp",
+        "media_permission-aidl-cpp",
     ],
     export_shared_lib_headers: [
         "audioclient-types-aidl-cpp",
@@ -346,6 +350,7 @@
     ],
     imports: [
         "audio_common-aidl",
+        "media_permission-aidl",
     ],
     backend: {
         cpp: {
@@ -427,6 +432,7 @@
         "av-types-aidl",
         "effect-aidl",
         "shared-file-region-aidl",
+        "media_permission-aidl",
     ],
     double_loadable: true,
     backend: {
@@ -460,6 +466,7 @@
         "audioclient-types-aidl",
         "audiopolicy-types-aidl",
         "capture_state_listener-aidl",
+        "media_permission-aidl",
     ],
     double_loadable: true,
     backend: {
diff --git a/media/libaudioclient/AudioEffect.cpp b/media/libaudioclient/AudioEffect.cpp
index ad8ad7b..d5047b1 100644
--- a/media/libaudioclient/AudioEffect.cpp
+++ b/media/libaudioclient/AudioEffect.cpp
@@ -42,6 +42,7 @@
 using aidl_utils::statusTFromBinderStatus;
 using binder::Status;
 using media::IAudioPolicyService;
+using media::permission::Identity;
 
 namespace {
 
@@ -57,8 +58,8 @@
 
 // ---------------------------------------------------------------------------
 
-AudioEffect::AudioEffect(const String16& opPackageName)
-    : mOpPackageName(opPackageName)
+AudioEffect::AudioEffect(const Identity& identity)
+    : mClientIdentity(identity)
 {
 }
 
@@ -107,9 +108,12 @@
     mDescriptor.type = *(type != NULL ? type : EFFECT_UUID_NULL);
     mDescriptor.uuid = *(uuid != NULL ? uuid : EFFECT_UUID_NULL);
 
+    // TODO b/182392769: use identity util
     mIEffectClient = new EffectClient(this);
-    mClientPid = IPCThreadState::self()->getCallingPid();
-    mClientUid = IPCThreadState::self()->getCallingUid();
+    pid_t pid = IPCThreadState::self()->getCallingPid();
+    mClientIdentity.pid = VALUE_OR_RETURN_STATUS(legacy2aidl_pid_t_int32_t(pid));
+    pid_t uid = IPCThreadState::self()->getCallingUid();
+    mClientIdentity.uid = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(uid));
 
     media::CreateEffectRequest request;
     request.desc = VALUE_OR_RETURN_STATUS(
@@ -119,8 +123,7 @@
     request.output = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(io));
     request.sessionId = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_session_t_int32_t(mSessionId));
     request.device = VALUE_OR_RETURN_STATUS(legacy2aidl_AudioDeviceTypeAddress(device));
-    request.opPackageName = VALUE_OR_RETURN_STATUS(legacy2aidl_String16_string(mOpPackageName));
-    request.pid = VALUE_OR_RETURN_STATUS(legacy2aidl_pid_t_int32_t(mClientPid));
+    request.identity = mClientIdentity;
     request.probe = probe;
 
     media::CreateEffectResponse response;
@@ -175,10 +178,10 @@
 
     IInterface::asBinder(iEffect)->linkToDeath(mIEffectClient);
     ALOGV("set() %p OK effect: %s id: %d status %d enabled %d pid %d", this, mDescriptor.name, mId,
-            mStatus, mEnabled, mClientPid);
+            mStatus, mEnabled, mClientIdentity.pid);
 
     if (!audio_is_global_session(mSessionId)) {
-        AudioSystem::acquireAudioSessionId(mSessionId, mClientPid, mClientUid);
+        AudioSystem::acquireAudioSessionId(mSessionId, pid, uid);
     }
 
     return mStatus;
@@ -219,7 +222,8 @@
 
     if (!mProbe && (mStatus == NO_ERROR || mStatus == ALREADY_EXISTS)) {
         if (!audio_is_global_session(mSessionId)) {
-            AudioSystem::releaseAudioSessionId(mSessionId, mClientPid);
+            AudioSystem::releaseAudioSessionId(mSessionId,
+                VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mClientIdentity.pid)));
         }
         if (mIEffect != NULL) {
             mIEffect->disconnect();
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index 00ed3b8..e15ef3d 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -35,18 +35,11 @@
 #include <media/MediaMetricsItem.h>
 #include <media/TypeConverter.h>
 
-#define VALUE_OR_FATAL(result)                   \
-    ({                                           \
-       auto _tmp = (result);                     \
-       LOG_ALWAYS_FATAL_IF(!_tmp.ok(),           \
-                           "Failed result (%d)", \
-                           _tmp.error());        \
-       std::move(_tmp.value());                  \
-     })
-
 #define WAIT_PERIOD_MS          10
 
 namespace android {
+
+using android::media::permission::Identity;
 using aidl_utils::statusTFromBinderStatus;
 
 // ---------------------------------------------------------------------------
@@ -133,9 +126,8 @@
     return NO_ERROR;
 }
 
-AudioRecord::AudioRecord(const String16 &opPackageName)
-    : mActive(false), mStatus(NO_INIT), mOpPackageName(opPackageName),
-      mSessionId(AUDIO_SESSION_ALLOCATE),
+AudioRecord::AudioRecord(const Identity &client)
+    : mActive(false), mStatus(NO_INIT), mClientIdentity(client), mSessionId(AUDIO_SESSION_ALLOCATE),
       mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT),
       mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE), mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE),
       mSelectedMicDirection(MIC_DIRECTION_UNSPECIFIED),
@@ -148,7 +140,7 @@
         uint32_t sampleRate,
         audio_format_t format,
         audio_channel_mask_t channelMask,
-        const String16& opPackageName,
+        const Identity& client,
         size_t frameCount,
         callback_t cbf,
         void* user,
@@ -156,24 +148,24 @@
         audio_session_t sessionId,
         transfer_type transferType,
         audio_input_flags_t flags,
-        uid_t uid,
-        pid_t pid,
         const audio_attributes_t* pAttributes,
         audio_port_handle_t selectedDeviceId,
         audio_microphone_direction_t selectedMicDirection,
         float microphoneFieldDimension)
     : mActive(false),
       mStatus(NO_INIT),
-      mOpPackageName(opPackageName),
+      mClientIdentity(client),
       mSessionId(AUDIO_SESSION_ALLOCATE),
       mPreviousPriority(ANDROID_PRIORITY_NORMAL),
       mPreviousSchedulingGroup(SP_DEFAULT),
       mProxy(NULL)
 {
+    uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(mClientIdentity.uid));
+    pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mClientIdentity.pid));
     (void)set(inputSource, sampleRate, format, channelMask, frameCount, cbf, user,
             notificationFrames, false /*threadCanCallJava*/, sessionId, transferType, flags,
-            uid, pid, pAttributes, selectedDeviceId,
-            selectedMicDirection, microphoneFieldDimension);
+            uid, pid, pAttributes, selectedDeviceId, selectedMicDirection,
+            microphoneFieldDimension);
 }
 
 AudioRecord::~AudioRecord()
@@ -211,7 +203,8 @@
         IPCThreadState::self()->flushCommands();
         ALOGV("%s(%d): releasing session id %d",
                 __func__, mPortId, mSessionId);
-        AudioSystem::releaseAudioSessionId(mSessionId, mClientPid);
+        pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mClientIdentity.pid));
+        AudioSystem::releaseAudioSessionId(mSessionId, pid);
     }
 }
 
@@ -237,16 +230,29 @@
 {
     status_t status = NO_ERROR;
     uint32_t channelCount;
-    pid_t callingPid;
-    pid_t myPid;
 
     // Note mPortId is not valid until the track is created, so omit mPortId in ALOG for set.
     ALOGV("%s(): inputSource %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, "
-          "notificationFrames %u, sessionId %d, transferType %d, flags %#x, opPackageName %s "
+          "notificationFrames %u, sessionId %d, transferType %d, flags %#x, identity %s"
           "uid %d, pid %d",
           __func__,
           inputSource, sampleRate, format, channelMask, frameCount, notificationFrames,
-          sessionId, transferType, flags, String8(mOpPackageName).string(), uid, pid);
+          sessionId, transferType, flags, mClientIdentity.toString().c_str(), uid, pid);
+
+    // TODO b/182392553: refactor or remove
+    pid_t callingPid = IPCThreadState::self()->getCallingPid();
+    pid_t myPid = getpid();
+    pid_t adjPid = pid;
+    if (pid == -1 || (callingPid != myPid)) {
+        adjPid = callingPid;
+    }
+    mClientIdentity.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(adjPid));
+
+    uid_t adjUid = uid;
+    if (uid == -1 || (callingPid != myPid)) {
+        adjUid = IPCThreadState::self()->getCallingUid();
+    }
+    mClientIdentity.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(adjUid));
 
     mTracker.reset(new RecordingActivityTracker());
 
@@ -341,19 +347,6 @@
     mSessionId = sessionId;
     ALOGV("%s(): mSessionId %d", __func__, mSessionId);
 
-    callingPid = IPCThreadState::self()->getCallingPid();
-    myPid = getpid();
-    if (uid == AUDIO_UID_INVALID || (callingPid != myPid)) {
-        mClientUid = IPCThreadState::self()->getCallingUid();
-    } else {
-        mClientUid = uid;
-    }
-    if (pid == -1 || (callingPid != myPid)) {
-        mClientPid = callingPid;
-    } else {
-        mClientPid = pid;
-    }
-
     mOrigFlags = mFlags = flags;
     mCbf = cbf;
 
@@ -366,7 +359,7 @@
     // create the IAudioRecord
     {
         AutoMutex lock(mLock);
-        status = createRecord_l(0 /*epoch*/, mOpPackageName);
+        status = createRecord_l(0 /*epoch*/);
     }
 
     ALOGV("%s(%d): status %d", __func__, mPortId, status);
@@ -387,7 +380,7 @@
     mMarkerReached = false;
     mNewPosition = 0;
     mUpdatePeriod = 0;
-    AudioSystem::acquireAudioSessionId(mSessionId, mClientPid, mClientUid);
+    AudioSystem::acquireAudioSessionId(mSessionId, adjPid, adjUid);
     mSequence = 1;
     mObservedSequence = mSequence;
     mInOverrun = false;
@@ -744,7 +737,7 @@
 }
 
 // must be called with mLock held
-status_t AudioRecord::createRecord_l(const Modulo<uint32_t> &epoch, const String16& opPackageName)
+status_t AudioRecord::createRecord_l(const Modulo<uint32_t> &epoch)
 {
     const int64_t beginNs = systemTime();
     const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
@@ -797,15 +790,13 @@
     input.config.sample_rate = mSampleRate;
     input.config.channel_mask = mChannelMask;
     input.config.format = mFormat;
-    input.clientInfo.clientUid = mClientUid;
-    input.clientInfo.clientPid = mClientPid;
+    input.clientInfo.identity = mClientIdentity;
     input.clientInfo.clientTid = -1;
     if (mFlags & AUDIO_INPUT_FLAG_FAST) {
         if (mAudioRecordThread != 0) {
             input.clientInfo.clientTid = mAudioRecordThread->getTid();
         }
     }
-    input.opPackageName = opPackageName;
     input.riid = mTracker->getRiid();
 
     input.flags = mFlags;
@@ -1437,7 +1428,7 @@
     // It will also delete the strong references on previous IAudioRecord and IMemory
     Modulo<uint32_t> position(mProxy->getPosition());
     mNewPosition = position + mUpdatePeriod;
-    result = createRecord_l(position, mOpPackageName);
+    result = createRecord_l(position);
 
     if (result == NO_ERROR) {
         if (mActive) {
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 74258a1..f476b7d 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -52,6 +52,7 @@
 using aidl_utils::statusTFromBinderStatus;
 using binder::Status;
 using media::IAudioPolicyService;
+using media::permission::Identity;
 
 // client singleton for AudioFlinger binder interface
 Mutex AudioSystem::gLock;
@@ -940,8 +941,7 @@
                                        audio_io_handle_t* output,
                                        audio_session_t session,
                                        audio_stream_type_t* stream,
-                                       pid_t pid,
-                                       uid_t uid,
+                                       const Identity& identity,
                                        const audio_config_t* config,
                                        audio_output_flags_t flags,
                                        audio_port_handle_t* selectedDeviceId,
@@ -974,8 +974,6 @@
     media::AudioAttributesInternal attrAidl = VALUE_OR_RETURN_STATUS(
             legacy2aidl_audio_attributes_t_AudioAttributesInternal(*attr));
     int32_t sessionAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_session_t_int32_t(session));
-    int32_t pidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_pid_t_int32_t(pid));
-    int32_t uidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(uid));
     media::AudioConfig configAidl = VALUE_OR_RETURN_STATUS(
             legacy2aidl_audio_config_t_AudioConfig(*config));
     int32_t flagsAidl = VALUE_OR_RETURN_STATUS(
@@ -986,7 +984,7 @@
     media::GetOutputForAttrResponse responseAidl;
 
     RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
-            aps->getOutputForAttr(attrAidl, sessionAidl, pidAidl, uidAidl, configAidl, flagsAidl,
+            aps->getOutputForAttr(attrAidl, sessionAidl, identity, configAidl, flagsAidl,
                                   selectedDeviceIdAidl, &responseAidl)));
 
     *output = VALUE_OR_RETURN_STATUS(
@@ -1040,9 +1038,7 @@
                                       audio_io_handle_t* input,
                                       audio_unique_id_t riid,
                                       audio_session_t session,
-                                      pid_t pid,
-                                      uid_t uid,
-                                      const String16& opPackageName,
+                                      const Identity &identity,
                                       const audio_config_base_t* config,
                                       audio_input_flags_t flags,
                                       audio_port_handle_t* selectedDeviceId,
@@ -1072,10 +1068,6 @@
     int32_t inputAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(*input));
     int32_t riidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_unique_id_t_int32_t(riid));
     int32_t sessionAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_session_t_int32_t(session));
-    int32_t pidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_pid_t_int32_t(pid));
-    int32_t uidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(uid));
-    std::string opPackageNameAidl = VALUE_OR_RETURN_STATUS(
-            legacy2aidl_String16_string(opPackageName));
     media::AudioConfigBase configAidl = VALUE_OR_RETURN_STATUS(
             legacy2aidl_audio_config_base_t_AudioConfigBase(*config));
     int32_t flagsAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_input_flags_t_int32_t_mask(flags));
@@ -1085,9 +1077,8 @@
     media::GetInputForAttrResponse response;
 
     RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
-            aps->getInputForAttr(attrAidl, inputAidl, riidAidl, sessionAidl, pidAidl, uidAidl,
-                                 opPackageNameAidl, configAidl, flagsAidl, selectedDeviceIdAidl,
-                                 &response)));
+            aps->getInputForAttr(attrAidl, inputAidl, riidAidl, sessionAidl, identity,
+                configAidl, flagsAidl, selectedDeviceIdAidl, &response)));
 
     *input = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_io_handle_t(response.input));
     *selectedDeviceId = VALUE_OR_RETURN_STATUS(
@@ -1839,8 +1830,7 @@
 
 status_t AudioSystem::getSurroundFormats(unsigned int* numSurroundFormats,
                                          audio_format_t* surroundFormats,
-                                         bool* surroundFormatsEnabled,
-                                         bool reported) {
+                                         bool* surroundFormatsEnabled) {
     if (numSurroundFormats == nullptr || (*numSurroundFormats != 0 &&
                                           (surroundFormats == nullptr ||
                                            surroundFormatsEnabled == nullptr))) {
@@ -1855,8 +1845,8 @@
     std::vector<media::audio::common::AudioFormat> surroundFormatsAidl;
     std::vector<bool> surroundFormatsEnabledAidl;
     RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
-            aps->getSurroundFormats(reported, &numSurroundFormatsAidl,
-                                    &surroundFormatsAidl, &surroundFormatsEnabledAidl)));
+            aps->getSurroundFormats(&numSurroundFormatsAidl, &surroundFormatsAidl,
+                                    &surroundFormatsEnabledAidl)));
 
     *numSurroundFormats = VALUE_OR_RETURN_STATUS(
             convertIntegral<unsigned int>(numSurroundFormatsAidl.value));
@@ -1868,6 +1858,29 @@
     return OK;
 }
 
+status_t AudioSystem::getReportedSurroundFormats(unsigned int* numSurroundFormats,
+                                                 audio_format_t* surroundFormats) {
+    if (numSurroundFormats == nullptr || (*numSurroundFormats != 0 && surroundFormats == nullptr)) {
+        return BAD_VALUE;
+    }
+
+    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+    if (aps == 0) return PERMISSION_DENIED;
+    media::Int numSurroundFormatsAidl;
+    numSurroundFormatsAidl.value =
+            VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(*numSurroundFormats));
+    std::vector<media::audio::common::AudioFormat> surroundFormatsAidl;
+    RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+            aps->getReportedSurroundFormats(&numSurroundFormatsAidl, &surroundFormatsAidl)));
+
+    *numSurroundFormats = VALUE_OR_RETURN_STATUS(
+            convertIntegral<unsigned int>(numSurroundFormatsAidl.value));
+    RETURN_STATUS_IF_ERROR(
+            convertRange(surroundFormatsAidl.begin(), surroundFormatsAidl.end(), surroundFormats,
+                         aidl2legacy_AudioFormat_audio_format_t));
+    return OK;
+}
+
 status_t AudioSystem::setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled) {
     const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
     if (aps == 0) return PERMISSION_DENIED;
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index d2f714a..8788a86 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -38,15 +38,6 @@
 #include <media/MediaMetricsItem.h>
 #include <media/TypeConverter.h>
 
-#define VALUE_OR_FATAL(result)                   \
-    ({                                           \
-       auto _tmp = (result);                     \
-       LOG_ALWAYS_FATAL_IF(!_tmp.ok(),           \
-                           "Failed result (%d)", \
-                           _tmp.error());        \
-       std::move(_tmp.value());                  \
-     })
-
 #define WAIT_PERIOD_MS                  10
 #define WAIT_STREAM_END_TIMEOUT_SEC     120
 static const int kMaxLoopCountNotifications = 32;
@@ -57,6 +48,7 @@
 // ---------------------------------------------------------------------------
 
 using media::VolumeShaper;
+using media::permission::Identity;
 
 // TODO: Move to a separate .h
 
@@ -233,11 +225,11 @@
     return NO_ERROR;
 }
 
-AudioTrack::AudioTrack() : AudioTrack("" /*opPackageName*/)
+AudioTrack::AudioTrack() : AudioTrack(Identity())
 {
 }
 
-AudioTrack::AudioTrack(const std::string& opPackageName)
+AudioTrack::AudioTrack(const Identity& identity)
     : mStatus(NO_INIT),
       mState(STATE_STOPPED),
       mPreviousPriority(ANDROID_PRIORITY_NORMAL),
@@ -245,7 +237,7 @@
       mPausedPosition(0),
       mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
       mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE),
-      mOpPackageName(opPackageName),
+      mClientIdentity(identity),
       mAudioTrackCallback(new AudioTrackCallback())
 {
     mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;
@@ -267,19 +259,16 @@
         audio_session_t sessionId,
         transfer_type transferType,
         const audio_offload_info_t *offloadInfo,
-        uid_t uid,
-        pid_t pid,
+        const Identity& identity,
         const audio_attributes_t* pAttributes,
         bool doNotReconnect,
         float maxRequiredSpeed,
-        audio_port_handle_t selectedDeviceId,
-        const std::string& opPackageName)
+        audio_port_handle_t selectedDeviceId)
     : mStatus(NO_INIT),
       mState(STATE_STOPPED),
       mPreviousPriority(ANDROID_PRIORITY_NORMAL),
       mPreviousSchedulingGroup(SP_DEFAULT),
       mPausedPosition(0),
-      mOpPackageName(opPackageName),
       mAudioTrackCallback(new AudioTrackCallback())
 {
     mAttributes = AUDIO_ATTRIBUTES_INITIALIZER;
@@ -287,7 +276,7 @@
     (void)set(streamType, sampleRate, format, channelMask,
             frameCount, flags, cbf, user, notificationFrames,
             0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId, transferType,
-            offloadInfo, uid, pid, pAttributes, doNotReconnect, maxRequiredSpeed, selectedDeviceId);
+            offloadInfo, identity, pAttributes, doNotReconnect, maxRequiredSpeed, selectedDeviceId);
 }
 
 AudioTrack::AudioTrack(
@@ -303,19 +292,16 @@
         audio_session_t sessionId,
         transfer_type transferType,
         const audio_offload_info_t *offloadInfo,
-        uid_t uid,
-        pid_t pid,
+        const Identity& identity,
         const audio_attributes_t* pAttributes,
         bool doNotReconnect,
-        float maxRequiredSpeed,
-        const std::string& opPackageName)
+        float maxRequiredSpeed)
     : mStatus(NO_INIT),
       mState(STATE_STOPPED),
       mPreviousPriority(ANDROID_PRIORITY_NORMAL),
       mPreviousSchedulingGroup(SP_DEFAULT),
       mPausedPosition(0),
       mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
-      mOpPackageName(opPackageName),
       mAudioTrackCallback(new AudioTrackCallback())
 {
     mAttributes = AUDIO_ATTRIBUTES_INITIALIZER;
@@ -323,7 +309,7 @@
     (void)set(streamType, sampleRate, format, channelMask,
             0 /*frameCount*/, flags, cbf, user, notificationFrames,
             sharedBuffer, false /*threadCanCallJava*/, sessionId, transferType, offloadInfo,
-            uid, pid, pAttributes, doNotReconnect, maxRequiredSpeed);
+            identity, pAttributes, doNotReconnect, maxRequiredSpeed);
 }
 
 AudioTrack::~AudioTrack()
@@ -361,10 +347,11 @@
         mCblkMemory.clear();
         mSharedBuffer.clear();
         IPCThreadState::self()->flushCommands();
+        pid_t clientPid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mClientIdentity.pid));
         ALOGV("%s(%d), releasing session id %d from %d on behalf of %d",
                 __func__, mPortId,
-                mSessionId, IPCThreadState::self()->getCallingPid(), mClientPid);
-        AudioSystem::releaseAudioSessionId(mSessionId, mClientPid);
+                mSessionId, IPCThreadState::self()->getCallingPid(), clientPid);
+        AudioSystem::releaseAudioSessionId(mSessionId, clientPid);
     }
 }
 
@@ -383,8 +370,7 @@
         audio_session_t sessionId,
         transfer_type transferType,
         const audio_offload_info_t *offloadInfo,
-        uid_t uid,
-        pid_t pid,
+        const Identity& identity,
         const audio_attributes_t* pAttributes,
         bool doNotReconnect,
         float maxRequiredSpeed,
@@ -394,13 +380,15 @@
     uint32_t channelCount;
     pid_t callingPid;
     pid_t myPid;
+    uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
+    pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(identity.pid));
 
     // Note mPortId is not valid until the track is created, so omit mPortId in ALOG for set.
     ALOGV("%s(): streamType %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, "
           "flags #%x, notificationFrames %d, sessionId %d, transferType %d, uid %d, pid %d",
           __func__,
           streamType, sampleRate, format, channelMask, frameCount, flags, notificationFrames,
-          sessionId, transferType, uid, pid);
+          sessionId, transferType, identity.uid, identity.pid);
 
     mThreadCanCallJava = threadCanCallJava;
     mSelectedDeviceId = selectedDeviceId;
@@ -596,17 +584,19 @@
                 notificationFrames, minNotificationsPerBuffer, maxNotificationsPerBuffer);
     }
     mNotificationFramesAct = 0;
+    // TODO b/182392553: refactor or remove
     callingPid = IPCThreadState::self()->getCallingPid();
     myPid = getpid();
-    if (uid == AUDIO_UID_INVALID || (callingPid != myPid)) {
-        mClientUid = IPCThreadState::self()->getCallingUid();
+    if (uid == -1 || (callingPid != myPid)) {
+        mClientIdentity.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(
+            IPCThreadState::self()->getCallingUid()));
     } else {
-        mClientUid = uid;
+        mClientIdentity.uid = identity.uid;
     }
-    if (pid == -1 || (callingPid != myPid)) {
-        mClientPid = callingPid;
+    if (pid == (pid_t)-1 || (callingPid != myPid)) {
+        mClientIdentity.pid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(callingPid));
     } else {
-        mClientPid = pid;
+        mClientIdentity.pid = identity.pid;
     }
     mAuxEffectId = 0;
     mOrigFlags = mFlags = flags;
@@ -645,7 +635,7 @@
     mReleased = 0;
     mStartNs = 0;
     mStartFromZeroUs = 0;
-    AudioSystem::acquireAudioSessionId(mSessionId, mClientPid, mClientUid);
+    AudioSystem::acquireAudioSessionId(mSessionId, pid, uid);
     mSequence = 1;
     mObservedSequence = mSequence;
     mInUnderrun = false;
@@ -691,10 +681,13 @@
         float maxRequiredSpeed,
         audio_port_handle_t selectedDeviceId)
 {
+    Identity identity;
+    identity.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(uid));
+    identity.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(pid));
     return set(streamType, sampleRate, format,
             static_cast<audio_channel_mask_t>(channelMask),
             frameCount, flags, cbf, user, notificationFrames, sharedBuffer,
-            threadCanCallJava, sessionId, transferType, offloadInfo, uid, pid,
+            threadCanCallJava, sessionId, transferType, offloadInfo, identity,
             pAttributes, doNotReconnect, maxRequiredSpeed, selectedDeviceId);
 }
 
@@ -1656,8 +1649,7 @@
     input.config.channel_mask = mChannelMask;
     input.config.format = mFormat;
     input.config.offload_info = mOffloadInfoCopy;
-    input.clientInfo.clientUid = mClientUid;
-    input.clientInfo.clientPid = mClientPid;
+    input.clientInfo.identity = mClientIdentity;
     input.clientInfo.clientTid = -1;
     if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
         // It is currently meaningless to request SCHED_FIFO for a Java thread.  Even if the
@@ -1681,7 +1673,6 @@
     input.selectedDeviceId = mSelectedDeviceId;
     input.sessionId = mSessionId;
     input.audioTrackCallback = mAudioTrackCallback;
-    input.opPackageName = mOpPackageName;
 
     media::CreateTrackResponse response;
     status = audioFlinger->createTrack(VALUE_OR_FATAL(input.toAidl()), response);
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 20124df..4103630 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -61,7 +61,6 @@
     aidl.notificationsPerBuffer = VALUE_OR_RETURN(convertIntegral<int32_t>(notificationsPerBuffer));
     aidl.speed = speed;
     aidl.audioTrackCallback = audioTrackCallback;
-    aidl.opPackageName = opPackageName;
     aidl.flags = VALUE_OR_RETURN(legacy2aidl_audio_output_flags_t_int32_t_mask(flags));
     aidl.frameCount = VALUE_OR_RETURN(convertIntegral<int64_t>(frameCount));
     aidl.notificationFrameCount = VALUE_OR_RETURN(convertIntegral<int64_t>(notificationFrameCount));
@@ -82,7 +81,6 @@
             convertIntegral<uint32_t>(aidl.notificationsPerBuffer));
     legacy.speed = aidl.speed;
     legacy.audioTrackCallback = aidl.audioTrackCallback;
-    legacy.opPackageName = aidl.opPackageName;
     legacy.flags = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_output_flags_t_mask(aidl.flags));
     legacy.frameCount = VALUE_OR_RETURN(convertIntegral<size_t>(aidl.frameCount));
     legacy.notificationFrameCount = VALUE_OR_RETURN(
@@ -139,7 +137,6 @@
     aidl.attr = VALUE_OR_RETURN(legacy2aidl_audio_attributes_t_AudioAttributesInternal(attr));
     aidl.config = VALUE_OR_RETURN(legacy2aidl_audio_config_base_t_AudioConfigBase(config));
     aidl.clientInfo = VALUE_OR_RETURN(legacy2aidl_AudioClient_AudioClient(clientInfo));
-    aidl.opPackageName = VALUE_OR_RETURN(legacy2aidl_String16_string(opPackageName));
     aidl.riid = VALUE_OR_RETURN(legacy2aidl_audio_unique_id_t_int32_t(riid));
     aidl.flags = VALUE_OR_RETURN(legacy2aidl_audio_input_flags_t_int32_t_mask(flags));
     aidl.frameCount = VALUE_OR_RETURN(convertIntegral<int64_t>(frameCount));
@@ -157,7 +154,6 @@
     legacy.attr = VALUE_OR_RETURN(aidl2legacy_AudioAttributesInternal_audio_attributes_t(aidl.attr));
     legacy.config = VALUE_OR_RETURN(aidl2legacy_AudioConfigBase_audio_config_base_t(aidl.config));
     legacy.clientInfo = VALUE_OR_RETURN(aidl2legacy_AudioClient_AudioClient(aidl.clientInfo));
-    legacy.opPackageName = VALUE_OR_RETURN(aidl2legacy_string_view_String16(aidl.opPackageName));
     legacy.riid = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_unique_id_t(aidl.riid));
     legacy.flags = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_input_flags_t_mask(aidl.flags));
     legacy.frameCount = VALUE_OR_RETURN(convertIntegral<size_t>(aidl.frameCount));
diff --git a/media/libaudioclient/ToneGenerator.cpp b/media/libaudioclient/ToneGenerator.cpp
index c9f3ab9..451c4b1 100644
--- a/media/libaudioclient/ToneGenerator.cpp
+++ b/media/libaudioclient/ToneGenerator.cpp
@@ -27,6 +27,7 @@
 
 namespace android {
 
+using media::permission::Identity;
 
 // Descriptors for all available tones (See ToneGenerator::ToneDescriptor class declaration for details)
 const ToneGenerator::ToneDescriptor ToneGenerator::sToneDescriptors[] = {
@@ -1259,7 +1260,10 @@
 ////////////////////////////////////////////////////////////////////////////////
 bool ToneGenerator::initAudioTrack() {
     // Open audio track in mono, PCM 16bit, default sampling rate.
-    mpAudioTrack = new AudioTrack(mOpPackageName);
+    // TODO b/182392769: use identity util
+    Identity identity = Identity();
+    identity.packageName = mOpPackageName;
+    mpAudioTrack = new AudioTrack(identity);
     ALOGV("AudioTrack(%p) created", mpAudioTrack.get());
 
     audio_attributes_t attr;
@@ -1285,8 +1289,7 @@
             AUDIO_SESSION_ALLOCATE,
             AudioTrack::TRANSFER_CALLBACK,
             nullptr,
-            AUDIO_UID_INVALID,
-            -1,
+            identity,
             &attr);
     // Set caller name so it can be logged in destructor.
     // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_TONEGENERATOR
diff --git a/media/libaudioclient/aidl/android/media/AudioClient.aidl b/media/libaudioclient/aidl/android/media/AudioClient.aidl
index 7bff0d6..aa4d8f5 100644
--- a/media/libaudioclient/aidl/android/media/AudioClient.aidl
+++ b/media/libaudioclient/aidl/android/media/AudioClient.aidl
@@ -16,15 +16,13 @@
 
 package android.media;
 
+import android.media.permission.Identity;
+
 /**
  * {@hide}
  */
 parcelable AudioClient {
-    /** Interpreted as uid_t. */
-    int clientUid;
-    /** Interpreted as pid_t. */
-    int clientPid;
     /** Interpreted as pid_t. */
     int clientTid;
-    @utf8InCpp String packageName;
+    Identity identity;
 }
diff --git a/media/libaudioclient/aidl/android/media/CreateEffectRequest.aidl b/media/libaudioclient/aidl/android/media/CreateEffectRequest.aidl
index 8368854..5737fcd 100644
--- a/media/libaudioclient/aidl/android/media/CreateEffectRequest.aidl
+++ b/media/libaudioclient/aidl/android/media/CreateEffectRequest.aidl
@@ -19,6 +19,7 @@
 import android.media.AudioDevice;
 import android.media.EffectDescriptor;
 import android.media.IEffectClient;
+import android.media.permission.Identity;
 
 /**
  * Input arguments of the createEffect() method.
@@ -34,8 +35,6 @@
     /** Interpreted as audio_session_t. */
     int sessionId;
     AudioDevice device;
-    @utf8InCpp String opPackageName;
-    /** Interpreted as pid_t. */
-    int pid;
+    Identity identity;
     boolean probe;
 }
diff --git a/media/libaudioclient/aidl/android/media/CreateRecordRequest.aidl b/media/libaudioclient/aidl/android/media/CreateRecordRequest.aidl
index 6da743a..62007da 100644
--- a/media/libaudioclient/aidl/android/media/CreateRecordRequest.aidl
+++ b/media/libaudioclient/aidl/android/media/CreateRecordRequest.aidl
@@ -19,6 +19,7 @@
 import android.media.AudioAttributesInternal;
 import android.media.AudioClient;
 import android.media.AudioConfigBase;
+import android.media.permission.Identity;
 
 /**
  * CreateRecordRequest contains all input arguments sent by AudioRecord to AudioFlinger
@@ -31,7 +32,6 @@
     AudioAttributesInternal attr;
     AudioConfigBase config;
     AudioClient clientInfo;
-    @utf8InCpp String opPackageName;
     /** Interpreted as audio_unique_id_t. */
     int riid;
     /** Bitmask, indexed by AudioInputFlags. */
diff --git a/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl b/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
index 89fad5a..f8924f3 100644
--- a/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
@@ -48,6 +48,7 @@
 import android.media.IAudioPolicyServiceClient;
 import android.media.ICaptureStateListener;
 import android.media.Int;
+import android.media.permission.Identity;
 import android.media.SoundTriggerSession;
 
 /**
@@ -80,8 +81,7 @@
 
     GetOutputForAttrResponse getOutputForAttr(in AudioAttributesInternal attr,
                                               int /* audio_session_t */ session,
-                                              int /* pid_t */ pid,
-                                              int /* uid_t */ uid,
+                                              in Identity identity,
                                               in AudioConfig config,
                                               int /* Bitmask, indexed by AudioOutputFlags */ flags,
                                               int /* audio_port_handle_t */ selectedDeviceId);
@@ -96,9 +96,7 @@
                                             int /* audio_io_handle_t */ input,
                                             int /* audio_unique_id_t */ riid,
                                             int /* audio_session_t */ session,
-                                            int /* pid_t */ pid,
-                                            int /* uid_t */ uid,
-                                            @utf8InCpp String opPackageName,
+                                            in Identity identity,
                                             in AudioConfigBase config,
                                             int /* Bitmask, indexed by AudioInputFlags */ flags,
                                             int /* audio_port_handle_t */ selectedDeviceId);
@@ -279,11 +277,21 @@
      * Passing '0' on input and inspecting the value on output is a common way of determining the
      * number of elements without actually retrieving them.
      */
-    void getSurroundFormats(boolean reported,
-                            inout Int count,
+    void getSurroundFormats(inout Int count,
                             out AudioFormat[] formats,
                             out boolean[] formatsEnabled);
 
+    /**
+     * Populates the surround formats reported by the HDMI devices in formats.
+     *
+     * On input, count represents the maximum length of the returned array.
+     * On output, count is the total number of elements, which may be larger than the array size.
+     * Passing '0' on input and inspecting the value on output is a common way of determining the
+     * number of elements without actually retrieving them.
+     */
+    void getReportedSurroundFormats(inout Int count,
+                                    out AudioFormat[] formats);
+
     AudioFormat[] getHwOffloadEncodingFormatsSupportedForA2DP();
 
     void setSurroundFormatEnabled(AudioFormat audioFormat, boolean enabled);
diff --git a/media/libaudioclient/fuzzer/Android.bp b/media/libaudioclient/fuzzer/Android.bp
index 18a3704..21e25b9 100644
--- a/media/libaudioclient/fuzzer/Android.bp
+++ b/media/libaudioclient/fuzzer/Android.bp
@@ -65,6 +65,7 @@
         "libutils",
         "libxml2",
         "mediametricsservice-aidl-cpp",
+        "media_permission-aidl-cpp",
     ],
     header_libs: [
         "libaudiofoundation_headers",
diff --git a/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp b/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp
index db2b0b8..1b75917 100644
--- a/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp
+++ b/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp
@@ -23,8 +23,10 @@
  */
 
 #include <android_audio_policy_configuration_V7_0-enums.h>
+#include <android/media/permission/Identity.h>
 #include <binder/IServiceManager.h>
 #include <binder/MemoryDealer.h>
+#include <media/AidlConversion.h>
 #include <media/AudioEffect.h>
 #include <media/AudioRecord.h>
 #include <media/AudioSystem.h>
@@ -35,6 +37,10 @@
 #define MAX_STRING_LENGTH 256
 #define MAX_ARRAY_LENGTH 256
 
+constexpr int32_t kMinSampleRateHz = 4000;
+constexpr int32_t kMaxSampleRateHz = 192000;
+constexpr int32_t kSampleRateUnspecified = 0;
+
 using namespace std;
 using namespace android;
 
@@ -42,6 +48,8 @@
 using namespace ::android::audio::policy::configuration::V7_0;
 }
 
+using media::permission::Identity;
+
 constexpr audio_unique_id_use_t kUniqueIds[] = {
     AUDIO_UNIQUE_ID_USE_UNSPECIFIED, AUDIO_UNIQUE_ID_USE_SESSION, AUDIO_UNIQUE_ID_USE_MODULE,
     AUDIO_UNIQUE_ID_USE_EFFECT,      AUDIO_UNIQUE_ID_USE_PATCH,   AUDIO_UNIQUE_ID_USE_OUTPUT,
@@ -130,29 +138,20 @@
         xsdc_enum_range<xsd::AudioInOutFlag>{}, audio_output_flag_from_string, "_OUTPUT_");
 
 template <typename T, size_t size>
-T getValueFromArray(FuzzedDataProvider *fdp, const T (&arr)[size]) {
-    return arr[fdp->ConsumeIntegralInRange<int32_t>(0, size - 1)];
-}
-
-template <typename T, size_t size>
 T getValue(FuzzedDataProvider *fdp, const T (&arr)[size]) {
-    if (fdp->ConsumeBool()) {
-        return static_cast<T>(fdp->ConsumeIntegral<int32_t>());
-    }
-    return getValueFromArray(fdp, arr);
-}
-
-template <typename T>
-T getValueFromVector(FuzzedDataProvider *fdp, std::vector<T> vec) {
-    return vec[fdp->ConsumeIntegralInRange<int32_t>(0, vec.size() - 1)];
+    return arr[fdp->ConsumeIntegralInRange<int32_t>(0, size - 1)];
 }
 
 template <typename T>
 T getValue(FuzzedDataProvider *fdp, std::vector<T> vec) {
+    return vec[fdp->ConsumeIntegralInRange<int32_t>(0, vec.size() - 1)];
+}
+
+int32_t getSampleRate(FuzzedDataProvider *fdp) {
     if (fdp->ConsumeBool()) {
-        return static_cast<T>(fdp->ConsumeIntegral<int32_t>());
+        return fdp->ConsumeIntegralInRange<int32_t>(kMinSampleRateHz, kMaxSampleRateHz);
     }
-    return getValueFromVector(fdp, vec);
+    return kSampleRateUnspecified;
 }
 
 class DeathNotifier : public IBinder::DeathRecipient {
@@ -189,7 +188,7 @@
 }
 
 void AudioFlingerFuzzer::invokeAudioTrack() {
-    uint32_t sampleRate = mFdp.ConsumeIntegral<uint32_t>();
+    uint32_t sampleRate = getSampleRate(&mFdp);
     audio_format_t format = getValue(&mFdp, kFormats);
     audio_channel_mask_t channelMask = getValue(&mFdp, kChannelMasks);
     size_t frameCount = static_cast<size_t>(mFdp.ConsumeIntegral<uint32_t>());
@@ -226,11 +225,15 @@
     attributes.usage = usage;
     sp<AudioTrack> track = new AudioTrack();
 
+    // TODO b/182392769: use identity util
+    Identity i;
+    i.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(getuid()));
+    i.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(getpid()));
     track->set(AUDIO_STREAM_DEFAULT, sampleRate, format, channelMask, frameCount, flags, nullptr,
                nullptr, notificationFrames, sharedBuffer, false, sessionId,
                ((fast && sharedBuffer == 0) || offload) ? AudioTrack::TRANSFER_CALLBACK
                                                         : AudioTrack::TRANSFER_DEFAULT,
-               offload ? &offloadInfo : nullptr, getuid(), getpid(), &attributes, false, 1.0f,
+               offload ? &offloadInfo : nullptr, i, &attributes, false, 1.0f,
                AUDIO_PORT_HANDLE_NONE);
 
     status_t status = track->initCheck();
@@ -259,7 +262,7 @@
 
     float auxEffectSendLevel;
     track->getAuxEffectSendLevel(&auxEffectSendLevel);
-    track->setSampleRate(mFdp.ConsumeIntegral<uint32_t>());
+    track->setSampleRate(getSampleRate(&mFdp));
     track->getSampleRate();
     track->getOriginalSampleRate();
 
@@ -292,7 +295,7 @@
 
 void AudioFlingerFuzzer::invokeAudioRecord() {
     int32_t notificationFrames = mFdp.ConsumeIntegral<int32_t>();
-    uint32_t sampleRate = mFdp.ConsumeIntegral<uint32_t>();
+    uint32_t sampleRate = getSampleRate(&mFdp);
     size_t frameCount = static_cast<size_t>(mFdp.ConsumeIntegral<uint32_t>());
     audio_format_t format = getValue(&mFdp, kFormats);
     audio_channel_mask_t channelMask = getValue(&mFdp, kChannelMasks);
@@ -305,7 +308,10 @@
 
     attributes.source = inputSource;
 
-    sp<AudioRecord> record = new AudioRecord(String16(mFdp.ConsumeRandomLengthString().c_str()));
+    // TODO b/182392769: use identity util
+    Identity i;
+    i.packageName = std::string(mFdp.ConsumeRandomLengthString().c_str());
+    sp<AudioRecord> record = new AudioRecord(i);
     record->set(AUDIO_SOURCE_DEFAULT, sampleRate, format, channelMask, frameCount, nullptr, nullptr,
                 notificationFrames, false, sessionId,
                 fast ? AudioRecord::TRANSFER_CALLBACK : AudioRecord::TRANSFER_DEFAULT, flags,
@@ -396,7 +402,7 @@
     const int32_t priority = mFdp.ConsumeIntegral<int32_t>();
     audio_session_t sessionId = static_cast<audio_session_t>(mFdp.ConsumeIntegral<int32_t>());
     const audio_io_handle_t io = mFdp.ConsumeIntegral<int32_t>();
-    String16 opPackageName = static_cast<String16>(mFdp.ConsumeRandomLengthString().c_str());
+    std::string opPackageName = static_cast<std::string>(mFdp.ConsumeRandomLengthString().c_str());
     AudioDeviceTypeAddr device;
 
     sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
@@ -412,8 +418,9 @@
     request.output = io;
     request.sessionId = sessionId;
     request.device = VALUE_OR_RETURN_STATUS(legacy2aidl_AudioDeviceTypeAddress(device));
-    request.opPackageName = VALUE_OR_RETURN_STATUS(legacy2aidl_String16_string(opPackageName));
-    request.pid = getpid();
+    // TODO b/182392769: use identity util
+    request.identity.packageName = opPackageName;
+    request.identity.pid = VALUE_OR_RETURN_STATUS(legacy2aidl_pid_t_int32_t(getpid()));
     request.probe = false;
 
     media::CreateEffectResponse response{};
@@ -518,7 +525,7 @@
     AudioSystem::getFrameCountHAL(mFdp.ConsumeIntegral<int32_t>(), &frameCount);
 
     size_t buffSize;
-    uint32_t sampleRate = mFdp.ConsumeIntegral<uint32_t>();
+    uint32_t sampleRate = getSampleRate(&mFdp);
     audio_format_t format = getValue(&mFdp, kFormats);
     audio_channel_mask_t channelMask = getValue(&mFdp, kChannelMasks);
     AudioSystem::getInputBufferSize(sampleRate, format, channelMask, &buffSize);
@@ -572,12 +579,12 @@
     config.offload_info.format = getValue(&mFdp, kFormats);
     config.offload_info.has_video = mFdp.ConsumeBool();
     config.offload_info.is_streaming = mFdp.ConsumeBool();
-    config.offload_info.sample_rate = (mFdp.ConsumeIntegral<uint32_t>());
+    config.offload_info.sample_rate = getSampleRate(&mFdp);
     config.offload_info.sync_id = mFdp.ConsumeIntegral<uint32_t>();
     config.offload_info.stream_type = getValue(&mFdp, kStreamtypes);
     config.offload_info.usage = getValue(&mFdp, kUsages);
 
-    config.sample_rate = mFdp.ConsumeIntegral<uint32_t>();
+    config.sample_rate = getSampleRate(&mFdp);
 
     audio_devices_t device = getValue(&mFdp, kDevices);
     audio_source_t source = getValue(&mFdp, kInputSources);
@@ -628,13 +635,13 @@
     config.offload_info.format = getValue(&mFdp, kFormats);
     config.offload_info.has_video = mFdp.ConsumeBool();
     config.offload_info.is_streaming = mFdp.ConsumeBool();
-    config.offload_info.sample_rate = mFdp.ConsumeIntegral<uint32_t>();
+    config.offload_info.sample_rate = getSampleRate(&mFdp);
     config.offload_info.stream_type = getValue(&mFdp, kStreamtypes);
     config.offload_info.sync_id = mFdp.ConsumeIntegral<uint32_t>();
     config.offload_info.usage = getValue(&mFdp, kUsages);
 
     config.format = getValue(&mFdp, kFormats);
-    config.sample_rate = mFdp.ConsumeIntegral<uint32_t>();
+    config.sample_rate = getSampleRate(&mFdp);
 
     sp<DeviceDescriptorBase> device = new DeviceDescriptorBase(getValue(&mFdp, kDevices));
     audio_output_flags_t flags = getValue(&mFdp, kOutputFlags);
@@ -683,7 +690,7 @@
         patch.sources[i].gain.ramp_duration_ms = mFdp.ConsumeIntegral<uint32_t>();
         patch.sources[i].id = static_cast<audio_format_t>(mFdp.ConsumeIntegral<int32_t>());
         patch.sources[i].role = getValue(&mFdp, kPortRoles);
-        patch.sources[i].sample_rate = mFdp.ConsumeIntegral<uint32_t>();
+        patch.sources[i].sample_rate = getSampleRate(&mFdp);
         patch.sources[i].type = getValue(&mFdp, kPortTypes);
 
         patch.sinks[i].config_mask = mFdp.ConsumeIntegral<uint32_t>();
@@ -695,7 +702,7 @@
         patch.sinks[i].gain.ramp_duration_ms = mFdp.ConsumeIntegral<uint32_t>();
         patch.sinks[i].id = static_cast<audio_format_t>(mFdp.ConsumeIntegral<int32_t>());
         patch.sinks[i].role = getValue(&mFdp, kPortRoles);
-        patch.sinks[i].sample_rate = mFdp.ConsumeIntegral<uint32_t>();
+        patch.sinks[i].sample_rate = getSampleRate(&mFdp);
         patch.sinks[i].type = getValue(&mFdp, kPortTypes);
     }
 
diff --git a/media/libaudioclient/include/media/AidlConversion.h b/media/libaudioclient/include/media/AidlConversion.h
index bde20cd..fd87dc2 100644
--- a/media/libaudioclient/include/media/AidlConversion.h
+++ b/media/libaudioclient/include/media/AidlConversion.h
@@ -108,6 +108,11 @@
 ConversionResult<String16> aidl2legacy_string_view_String16(std::string_view aidl);
 ConversionResult<std::string> legacy2aidl_String16_string(const String16& legacy);
 
+ConversionResult<std::optional<String16>>
+aidl2legacy_optional_string_view_optional_String16(std::optional<std::string_view> aidl);
+ConversionResult<std::optional<std::string_view>>
+legacy2aidl_optional_String16_optional_string(std::optional<String16> legacy);
+
 ConversionResult<audio_io_config_event> aidl2legacy_AudioIoConfigEvent_audio_io_config_event(
         media::AudioIoConfigEvent aidl);
 ConversionResult<media::AudioIoConfigEvent> legacy2aidl_audio_io_config_event_AudioIoConfigEvent(
diff --git a/media/libaudioclient/include/media/AidlConversionUtil.h b/media/libaudioclient/include/media/AidlConversionUtil.h
index bf2d800..c1a2be3 100644
--- a/media/libaudioclient/include/media/AidlConversionUtil.h
+++ b/media/libaudioclient/include/media/AidlConversionUtil.h
@@ -48,6 +48,15 @@
        std::move(_tmp.value());             \
      })
 
+#define VALUE_OR_FATAL(result)                                        \
+    ({                                                                \
+       auto _tmp = (result);                                          \
+       LOG_ALWAYS_FATAL_IF(!_tmp.ok(),                                \
+                           "Function: %s Line: %d Failed result (%d)",\
+                           __FUNCTION__, __LINE__, _tmp.error());     \
+       std::move(_tmp.value());                                       \
+     })
+
 /**
  * A generic template to safely cast between integral types, respecting limits of the destination
  * type.
diff --git a/media/libaudioclient/include/media/AudioClient.h b/media/libaudioclient/include/media/AudioClient.h
index 0b89d15..295fd4f 100644
--- a/media/libaudioclient/include/media/AudioClient.h
+++ b/media/libaudioclient/include/media/AudioClient.h
@@ -19,19 +19,17 @@
 #define ANDROID_AUDIO_CLIENT_H
 
 #include <sys/types.h>
-#include <utils/String16.h>
+#include <android/media/permission/Identity.h>
 
 namespace android {
 
 class AudioClient {
  public:
     AudioClient() :
-        clientUid(-1), clientPid(-1), clientTid(-1), packageName("") {}
+        clientTid(-1) {}
 
-    uid_t clientUid;
-    pid_t clientPid;
     pid_t clientTid;
-    String16 packageName;
+    android::media::permission::Identity identity;
 };
 
 }; // namespace android
diff --git a/media/libaudioclient/include/media/AudioEffect.h b/media/libaudioclient/include/media/AudioEffect.h
index 0d18fb1..974ce62 100644
--- a/media/libaudioclient/include/media/AudioEffect.h
+++ b/media/libaudioclient/include/media/AudioEffect.h
@@ -23,6 +23,7 @@
 #include <media/IAudioFlinger.h>
 #include <media/AudioSystem.h>
 #include <system/audio_effect.h>
+#include <android/media/permission/Identity.h>
 
 #include <utils/RefBase.h>
 #include <utils/Errors.h>
@@ -31,7 +32,6 @@
 #include "android/media/IEffect.h"
 #include "android/media/BnEffectClient.h"
 
-
 namespace android {
 
 // ----------------------------------------------------------------------------
@@ -337,9 +337,9 @@
      *
      * Parameters:
      *
-     * opPackageName:      The package name used for app op checks.
+     * client:      Identity for app-op checks
      */
-    explicit AudioEffect(const String16& opPackageName);
+    explicit AudioEffect(const media::permission::Identity& client);
 
     /* Terminates the AudioEffect and unregisters it from AudioFlinger.
      * The effect engine is also destroyed if this AudioEffect was the last controlling
@@ -531,7 +531,7 @@
      static const uint32_t kMaxPreProcessing = 10;
 
 protected:
-     const String16          mOpPackageName;     // The package name used for app op checks.
+     media::permission::Identity mClientIdentity; // Identity used for app op checks.
      bool                    mEnabled = false;   // enable state
      audio_session_t         mSessionId = AUDIO_SESSION_OUTPUT_MIX; // audio session ID
      int32_t                 mPriority = 0;      // priority for effect control
@@ -606,8 +606,6 @@
     sp<EffectClient>        mIEffectClient;     // IEffectClient implementation
     sp<IMemory>             mCblkMemory;        // shared memory for deferred parameter setting
     effect_param_cblk_t*    mCblk = nullptr;    // control block for deferred parameter setting
-    pid_t                   mClientPid = (pid_t)-1;
-    uid_t                   mClientUid = (uid_t)-1;
 };
 
 
diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h
index d70d91e..82a29d4 100644
--- a/media/libaudioclient/include/media/AudioRecord.h
+++ b/media/libaudioclient/include/media/AudioRecord.h
@@ -32,6 +32,7 @@
 #include <utils/threads.h>
 
 #include "android/media/IAudioRecord.h"
+#include <android/media/permission/Identity.h>
 
 namespace android {
 
@@ -148,9 +149,9 @@
      *
      * Parameters:
      *
-     * opPackageName:      The package name used for app ops.
+     * clientIdentity:          The identity of the owner of the record
      */
-                        AudioRecord(const String16& opPackageName);
+                        AudioRecord(const media::permission::Identity& clientIdentity);
 
     /* Creates an AudioRecord object and registers it with AudioFlinger.
      * Once created, the track needs to be started before it can be used.
@@ -163,7 +164,7 @@
      * format:             Audio format (e.g AUDIO_FORMAT_PCM_16_BIT for signed
      *                     16 bits per sample).
      * channelMask:        Channel mask, such that audio_is_input_channel(channelMask) is true.
-     * opPackageName:      The package name used for app ops.
+     * client:             The identity of the owner of the record
      * frameCount:         Minimum size of track PCM buffer in frames. This defines the
      *                     application's contribution to the
      *                     latency of the track.  The actual size selected by the AudioRecord could
@@ -186,7 +187,7 @@
                                     uint32_t sampleRate,
                                     audio_format_t format,
                                     audio_channel_mask_t channelMask,
-                                    const String16& opPackageName,
+                                    const media::permission::Identity& clientIdentity,
                                     size_t frameCount = 0,
                                     callback_t cbf = NULL,
                                     void* user = NULL,
@@ -194,8 +195,6 @@
                                     audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
                                     transfer_type transferType = TRANSFER_DEFAULT,
                                     audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE,
-                                    uid_t uid = AUDIO_UID_INVALID,
-                                    pid_t pid = -1,
                                     const audio_attributes_t* pAttributes = NULL,
                                     audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
                                     audio_microphone_direction_t
@@ -638,7 +637,7 @@
 
             // caller must hold lock on mLock for all _l methods
 
-            status_t createRecord_l(const Modulo<uint32_t> &epoch, const String16& opPackageName);
+            status_t createRecord_l(const Modulo<uint32_t> &epoch);
 
             // FIXME enum is faster than strcmp() for parameter 'from'
             status_t restoreRecord_l(const char *from);
@@ -679,7 +678,7 @@
 
     status_t                mStatus;
 
-    String16                mOpPackageName;         // The package name used for app ops.
+    media::permission::Identity mClientIdentity;    // The identity of the owner of this record
 
     size_t                  mFrameCount;            // corresponds to current IAudioRecord, value is
                                                     // reported back by AudioFlinger to the client
@@ -754,8 +753,6 @@
 
     sp<DeathNotifier>       mDeathNotifier;
     uint32_t                mSequence;              // incremented for each new IAudioRecord attempt
-    uid_t                   mClientUid;
-    pid_t                   mClientPid;
     audio_attributes_t      mAttributes;
 
     // For Device Selection API
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 694f2d0..c63d29f 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -19,6 +19,7 @@
 
 #include <sys/types.h>
 
+#include <android/media/permission/Identity.h>
 #include <android/media/BnAudioFlingerClient.h>
 #include <android/media/BnAudioPolicyServiceClient.h>
 #include <media/AidlConversionUtil.h>
@@ -262,8 +263,7 @@
                                      audio_io_handle_t *output,
                                      audio_session_t session,
                                      audio_stream_type_t *stream,
-                                     pid_t pid,
-                                     uid_t uid,
+                                     const media::permission::Identity& identity,
                                      const audio_config_t *config,
                                      audio_output_flags_t flags,
                                      audio_port_handle_t *selectedDeviceId,
@@ -279,9 +279,7 @@
                                     audio_io_handle_t *input,
                                     audio_unique_id_t riid,
                                     audio_session_t session,
-                                    pid_t pid,
-                                    uid_t uid,
-                                    const String16& opPackageName,
+                                    const media::permission::Identity& identity,
                                     const audio_config_base_t *config,
                                     audio_input_flags_t flags,
                                     audio_port_handle_t *selectedDeviceId,
@@ -417,8 +415,9 @@
     // populated. The actual number of surround formats should be returned at numSurroundFormats.
     static status_t getSurroundFormats(unsigned int *numSurroundFormats,
                                        audio_format_t *surroundFormats,
-                                       bool *surroundFormatsEnabled,
-                                       bool reported);
+                                       bool *surroundFormatsEnabled);
+    static status_t getReportedSurroundFormats(unsigned int *numSurroundFormats,
+                                               audio_format_t *surroundFormats);
     static status_t setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled);
 
     static status_t setAssistantUid(uid_t uid);
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index 2609ec1..b1650ed 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -26,6 +26,7 @@
 #include <media/Modulo.h>
 #include <media/VolumeShaper.h>
 #include <utils/threads.h>
+#include <android/media/permission/Identity.h>
 
 #include <string>
 
@@ -181,7 +182,7 @@
      */
                         AudioTrack();
 
-                        AudioTrack(const std::string& opPackageName);
+                        AudioTrack(const media::permission::Identity& identity);
 
     /* Creates an AudioTrack object and registers it with AudioFlinger.
      * Once created, the track needs to be started before it can be used.
@@ -229,10 +230,9 @@
      * transferType:       How data is transferred to AudioTrack.
      * offloadInfo:        If not NULL, provides offload parameters for
      *                     AudioSystem::getOutputForAttr().
-     * uid:                User ID of the app which initially requested this AudioTrack
-     *                     for power management tracking, or -1 for current user ID.
-     * pid:                Process ID of the app which initially requested this AudioTrack
-     *                     for power management tracking, or -1 for current process ID.
+     * identity:           The identity of the app which initiallly requested this AudioTrack.
+     *                     Includes the UID and PID for power management tracking, or -1 for
+     *                     current user/process ID, plus the package name.
      * pAttributes:        If not NULL, supersedes streamType for use case selection.
      * doNotReconnect:     If set to true, AudioTrack won't automatically recreate the IAudioTrack
                            binder to AudioFlinger.
@@ -259,13 +259,12 @@
                                     audio_session_t sessionId  = AUDIO_SESSION_ALLOCATE,
                                     transfer_type transferType = TRANSFER_DEFAULT,
                                     const audio_offload_info_t *offloadInfo = NULL,
-                                    uid_t uid = AUDIO_UID_INVALID,
-                                    pid_t pid = -1,
+                                    const media::permission::Identity& identity =
+                                        media::permission::Identity(),
                                     const audio_attributes_t* pAttributes = NULL,
                                     bool doNotReconnect = false,
                                     float maxRequiredSpeed = 1.0f,
-                                    audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
-                                    const std::string& opPackageName = "");
+                                    audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE);
 
     /* Creates an audio track and registers it with AudioFlinger.
      * With this constructor, the track is configured for static buffer mode.
@@ -291,12 +290,11 @@
                                     audio_session_t sessionId   = AUDIO_SESSION_ALLOCATE,
                                     transfer_type transferType = TRANSFER_DEFAULT,
                                     const audio_offload_info_t *offloadInfo = NULL,
-                                    uid_t uid = AUDIO_UID_INVALID,
-                                    pid_t pid = -1,
+                                    const media::permission::Identity& identity =
+                                        media::permission::Identity(),
                                     const audio_attributes_t* pAttributes = NULL,
                                     bool doNotReconnect = false,
-                                    float maxRequiredSpeed = 1.0f,
-                                    const std::string& opPackageName = "");
+                                    float maxRequiredSpeed = 1.0f);
 
     /* Terminates the AudioTrack and unregisters it from AudioFlinger.
      * Also destroys all resources associated with the AudioTrack.
@@ -340,8 +338,8 @@
                             audio_session_t sessionId  = AUDIO_SESSION_ALLOCATE,
                             transfer_type transferType = TRANSFER_DEFAULT,
                             const audio_offload_info_t *offloadInfo = NULL,
-                            uid_t uid = AUDIO_UID_INVALID,
-                            pid_t pid = -1,
+                            const media::permission::Identity& identity =
+                                media::permission::Identity(),
                             const audio_attributes_t* pAttributes = NULL,
                             bool doNotReconnect = false,
                             float maxRequiredSpeed = 1.0f,
@@ -1311,8 +1309,6 @@
 
     sp<media::VolumeHandler>       mVolumeHandler;
 
-    const std::string      mOpPackageName;
-
 private:
     class DeathNotifier : public IBinder::DeathRecipient {
     public:
@@ -1325,8 +1321,7 @@
 
     sp<DeathNotifier>       mDeathNotifier;
     uint32_t                mSequence;              // incremented for each new IAudioTrack attempt
-    uid_t                   mClientUid;
-    pid_t                   mClientPid;
+    media::permission::Identity mClientIdentity;
 
     wp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
 
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index 9a8014d..efd7fed 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -37,6 +37,7 @@
 
 #include <android/media/BnAudioFlingerService.h>
 #include <android/media/BpAudioFlingerService.h>
+#include <android/media/permission/Identity.h>
 #include "android/media/CreateEffectRequest.h"
 #include "android/media/CreateEffectResponse.h"
 #include "android/media/CreateRecordRequest.h"
@@ -78,7 +79,6 @@
         uint32_t notificationsPerBuffer;
         float speed;
         sp<media::IAudioTrackCallback> audioTrackCallback;
-        std::string opPackageName;
 
         /* input/output */
         audio_output_flags_t flags;
@@ -127,7 +127,7 @@
         audio_attributes_t attr;
         audio_config_base_t config;
         AudioClient clientInfo;
-        String16 opPackageName;
+        media::permission::Identity identity;
         audio_unique_id_t riid;
 
         /* input/output */
diff --git a/media/libaudioclient/tests/test_create_audiorecord.cpp b/media/libaudioclient/tests/test_create_audiorecord.cpp
index cf6a734..57676c1 100644
--- a/media/libaudioclient/tests/test_create_audiorecord.cpp
+++ b/media/libaudioclient/tests/test_create_audiorecord.cpp
@@ -19,6 +19,7 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <android/media/permission/Identity.h>
 #include <binder/MemoryBase.h>
 #include <binder/MemoryDealer.h>
 #include <binder/MemoryHeapBase.h>
@@ -32,19 +33,24 @@
 
 namespace android {
 
+using media::permission::Identity;
+
 int testRecord(FILE *inputFile, int outputFileFd)
 {
     char line[MAX_INPUT_FILE_LINE_LENGTH];
     uint32_t testCount = 0;
     Vector<String16> args;
     int ret = 0;
+    // TODO b/182392769: use identity util
+    Identity identity;
+    identity.packageName = PACKAGE_NAME;
 
     if (inputFile == nullptr) {
         sp<AudioRecord> record = new AudioRecord(AUDIO_SOURCE_DEFAULT,
                                               0 /* sampleRate */,
                                               AUDIO_FORMAT_DEFAULT,
                                               AUDIO_CHANNEL_IN_MONO,
-                                              String16(PACKAGE_NAME));
+                                              identity);
         if (record == 0 || record->initCheck() != NO_ERROR) {
             write(outputFileFd, "Error creating AudioRecord\n",
                   sizeof("Error creating AudioRecord\n"));
@@ -90,7 +96,7 @@
         memset(&attributes, 0, sizeof(attributes));
         attributes.source = inputSource;
 
-        sp<AudioRecord> record = new AudioRecord(String16(PACKAGE_NAME));
+        sp<AudioRecord> record = new AudioRecord(identity);
 
         record->set(AUDIO_SOURCE_DEFAULT,
                    sampleRate,
diff --git a/media/libeffects/lvm/lib/Android.bp b/media/libeffects/lvm/lib/Android.bp
index a044295..5d75055 100644
--- a/media/libeffects/lvm/lib/Android.bp
+++ b/media/libeffects/lvm/lib/Android.bp
@@ -59,7 +59,6 @@
         "Eq/src/LVEQNB_Init.cpp",
         "Eq/src/LVEQNB_Process.cpp",
         "Eq/src/LVEQNB_Tables.cpp",
-        "Common/src/InstAlloc.cpp",
         "Common/src/DC_2I_D16_TRC_WRA_01.cpp",
         "Common/src/DC_2I_D16_TRC_WRA_01_Init.cpp",
         "Common/src/Copy_16.cpp",
@@ -146,11 +145,9 @@
         "Reverb/src/LVREV_ClearAudioBuffers.cpp",
         "Reverb/src/LVREV_GetControlParameters.cpp",
         "Reverb/src/LVREV_GetInstanceHandle.cpp",
-        "Reverb/src/LVREV_GetMemoryTable.cpp",
         "Reverb/src/LVREV_Process.cpp",
         "Reverb/src/LVREV_SetControlParameters.cpp",
         "Reverb/src/LVREV_Tables.cpp",
-        "Common/src/InstAlloc.cpp",
         "Common/src/LoadConst_32.cpp",
         "Common/src/From2iToMono_32.cpp",
         "Common/src/Mult3s_32x16.cpp",
diff --git a/media/libeffects/lvm/lib/Bass/src/LVDBE_Control.cpp b/media/libeffects/lvm/lib/Bass/src/LVDBE_Control.cpp
index 3fc9e95..9fe8116 100644
--- a/media/libeffects/lvm/lib/Bass/src/LVDBE_Control.cpp
+++ b/media/libeffects/lvm/lib/Bass/src/LVDBE_Control.cpp
@@ -277,13 +277,15 @@
     /*
      * Create biquad instance
      */
-    pInstance->pHPFBiquad.reset(
-            new android::audio_utils::BiquadFilter<LVM_FLOAT>(pParams->NrChannels));
-
+    if (pInstance->Params.NrChannels != pParams->NrChannels) {
+        pInstance->pHPFBiquad.reset(
+                new android::audio_utils::BiquadFilter<LVM_FLOAT>(pParams->NrChannels));
+    }
     /*
      * Update the filters
      */
     if ((pInstance->Params.SampleRate != pParams->SampleRate) ||
+        (pInstance->Params.NrChannels != pParams->NrChannels) ||
         (pInstance->Params.CentreFrequency != pParams->CentreFrequency)) {
         LVDBE_SetFilters(pInstance, /* Instance pointer */
                          pParams);  /* New parameters */
diff --git a/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.cpp b/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.cpp
index 761c6ce..b113f48 100644
--- a/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.cpp
+++ b/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.cpp
@@ -57,7 +57,7 @@
      * Create the instance handle if not already initialised
      */
     if (*phInstance == LVM_NULL) {
-        *phInstance = new LVDBE_Instance_t;
+        *phInstance = new LVDBE_Instance_t{};
     }
     pInstance = (LVDBE_Instance_t*)*phInstance;
 
@@ -79,6 +79,7 @@
     pInstance->Params.SampleRate = LVDBE_FS_8000;
     pInstance->Params.VolumeControl = LVDBE_VOLUME_OFF;
     pInstance->Params.VolumedB = 0;
+    pInstance->Params.NrChannels = FCC_2;
 
     /*
      * Create pointer to data and coef memory
@@ -91,7 +92,7 @@
      * Create biquad instance
      */
     pInstance->pHPFBiquad.reset(
-            new android::audio_utils::BiquadFilter<LVM_FLOAT>(LVM_MAX_CHANNELS));
+            new android::audio_utils::BiquadFilter<LVM_FLOAT>(pInstance->Params.NrChannels));
     pInstance->pBPFBiquad.reset(new android::audio_utils::BiquadFilter<LVM_FLOAT>(FCC_1));
 
     /*
diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Init.cpp b/media/libeffects/lvm/lib/Bundle/src/LVM_Init.cpp
index b092970..9f5f448 100644
--- a/media/libeffects/lvm/lib/Bundle/src/LVM_Init.cpp
+++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Init.cpp
@@ -25,7 +25,6 @@
 #include "LVM_Private.h"
 #include "LVM_Tables.h"
 #include "VectorArithmetic.h"
-#include "InstAlloc.h"
 
 /****************************************************************************************/
 /*                                                                                      */
@@ -93,7 +92,7 @@
     /*
      * Create the instance handle
      */
-    *phInstance = new LVM_Instance_t;
+    *phInstance = new LVM_Instance_t{};
     pInstance = (LVM_Instance_t*)*phInstance;
 
     pInstance->InstParams = *pInstParams;
diff --git a/media/libeffects/lvm/lib/Common/lib/InstAlloc.h b/media/libeffects/lvm/lib/Common/lib/InstAlloc.h
deleted file mode 100644
index 17699ef..0000000
--- a/media/libeffects/lvm/lib/Common/lib/InstAlloc.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2004-2010 NXP Software
- * Copyright (C) 2010 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 __INSTALLOC_H__
-#define __INSTALLOC_H__
-
-#include "LVM_Types.h"
-/*######################################################################################*/
-/*  Type declarations                                                                   */
-/*######################################################################################*/
-typedef struct {
-    LVM_UINT32 TotalSize;  /*  Accumulative total memory size                      */
-    uintptr_t pNextMember; /*  Pointer to the next instance member to be allocated */
-} INST_ALLOC;
-
-/*######################################################################################*/
-/*  Function prototypes                                                          */
-/*######################################################################################*/
-
-/****************************************************************************************
- *  Name        : InstAlloc_Init()
- *  Input       : pms  - Pointer to the INST_ALLOC instance
-                  StartAddr - Base address of the instance memory
- *  Returns     : Error code
- *  Description : Initializes the instance distribution and memory size calculation function
- *  Remarks     :
- ****************************************************************************************/
-
-void InstAlloc_Init(INST_ALLOC* pms, void* StartAddr);
-
-/****************************************************************************************
- *  Name        : InstAlloc_AddMember()
- *  Input       : pms  - Pointer to the INST_ALLOC instance
-                  Size - The size in bytes of the new added member
- *  Returns     : A pointer to the new added member
- *  Description : Allocates space for a new member in the instance memory and returns
-                  a pointer to this new member.  The start address of all members will
-                  be 32 bit alligned.
- *  Remarks     :
- ****************************************************************************************/
-
-void* InstAlloc_AddMember(INST_ALLOC* pms, LVM_UINT32 Size);
-
-/****************************************************************************************
- *  Name        : InstAlloc_GetTotal()
- *  Input       : pms  - Pointer to the INST_ALLOC instance
- *  Returns     : The instance memory size
- *  Description : This functions returns the calculated instance memory size
- *  Remarks     :
- ****************************************************************************************/
-
-LVM_UINT32 InstAlloc_GetTotal(INST_ALLOC* pms);
-
-void* InstAlloc_AddMemberAllRet(INST_ALLOC* pms, LVM_UINT32 Size[], void** ptr);
-
-void* InstAlloc_AddMemberAll(INST_ALLOC* pms, LVM_UINT32 Size[], LVM_MemoryTable_st* pMemoryTable);
-
-void InstAlloc_InitAll(INST_ALLOC* pms, LVM_MemoryTable_st* pMemoryTable);
-
-void InstAlloc_InitAll_NULL(INST_ALLOC* pms);
-
-#endif /* __JBS_INSTALLOC_H__ */
diff --git a/media/libeffects/lvm/lib/Common/lib/LVM_Types.h b/media/libeffects/lvm/lib/Common/lib/LVM_Types.h
index 5cdcf35..7cfaf27 100644
--- a/media/libeffects/lvm/lib/Common/lib/LVM_Types.h
+++ b/media/libeffects/lvm/lib/Common/lib/LVM_Types.h
@@ -134,27 +134,6 @@
     return LVM_FS_INVALID;
 }
 
-/* Memory Types */
-typedef enum {
-    LVM_PERSISTENT_SLOW_DATA = LVM_MEMREGION_PERSISTENT_SLOW_DATA,
-    LVM_PERSISTENT_FAST_DATA = LVM_MEMREGION_PERSISTENT_FAST_DATA,
-    LVM_PERSISTENT_FAST_COEF = LVM_MEMREGION_PERSISTENT_FAST_COEF,
-    LVM_TEMPORARY_FAST = LVM_MEMREGION_TEMPORARY_FAST,
-    LVM_MEMORYTYPE_DUMMY = LVM_MAXENUM
-} LVM_MemoryTypes_en;
-
-/* Memory region definition */
-typedef struct {
-    LVM_UINT32 Size;         /* Region size in bytes */
-    LVM_MemoryTypes_en Type; /* Region type */
-    void* pBaseAddress;      /* Pointer to the region base address */
-} LVM_MemoryRegion_st;
-
-/* Memory table containing the region definitions */
-typedef struct {
-    LVM_MemoryRegion_st Region[LVM_NR_MEMORY_REGIONS]; /* One definition for each region */
-} LVM_MemoryTable_st;
-
 /****************************************************************************************/
 /*                                                                                      */
 /*  Standard Function Prototypes                                                        */
diff --git a/media/libeffects/lvm/lib/Common/src/InstAlloc.cpp b/media/libeffects/lvm/lib/Common/src/InstAlloc.cpp
deleted file mode 100644
index 2cfe056..0000000
--- a/media/libeffects/lvm/lib/Common/src/InstAlloc.cpp
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2004-2010 NXP Software
- * Copyright (C) 2010 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 "InstAlloc.h"
-
-/****************************************************************************************
- *  Name        : InstAlloc_Init()
- *  Input       : pms  - Pointer to the INST_ALLOC instance
-                  StartAddr - Base address of the instance memory
- *  Returns     : Error code
- *  Description : Initializes the instance distribution and memory size calculation function
- *  Remarks     :
- ****************************************************************************************/
-
-void InstAlloc_Init(INST_ALLOC* pms, void* StartAddr) {
-    pms->TotalSize = 3;
-    pms->pNextMember = (((uintptr_t)StartAddr + 3) & (uintptr_t)~3);
-}
-
-/****************************************************************************************
- *  Name        : InstAlloc_AddMember()
- *  Input       : pms  - Pointer to the INST_ALLOC instance
-                  Size - The size in bytes of the new added member
- *  Returns     : A pointer to the new added member
- *  Description : Allocates space for a new member in the instance memory and returns
-                  a pointer to this new member.  The start address of all members will
-                  be 32 bit alligned.
- *  Remarks     :
- ****************************************************************************************/
-
-void* InstAlloc_AddMember(INST_ALLOC* pms, LVM_UINT32 Size) {
-    void* NewMemberAddress; /* Variable to temporarily store the return value */
-    NewMemberAddress = (void*)pms->pNextMember;
-
-    Size = ((Size + 3) & (LVM_UINT32)~3); /* Ceil the size to a multiple of four */
-
-    pms->TotalSize += Size;
-    pms->pNextMember += Size;
-
-    return (NewMemberAddress);
-}
-
-/****************************************************************************************
- *  Name        : InstAlloc_GetTotal()
- *  Input       : pms  - Pointer to the INST_ALLOC instance
- *  Returns     : The instance memory size
- *  Description : This functions returns the calculated instance memory size
- *  Remarks     :
- ****************************************************************************************/
-
-LVM_UINT32 InstAlloc_GetTotal(INST_ALLOC* pms) {
-    if (pms->TotalSize > 3) {
-        return (pms->TotalSize);
-    } else {
-        return 0; /* No memory added */
-    }
-}
-
-void InstAlloc_InitAll(INST_ALLOC* pms, LVM_MemoryTable_st* pMemoryTable) {
-    uintptr_t StartAddr;
-
-    StartAddr = (uintptr_t)pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].pBaseAddress;
-
-    pms[0].TotalSize = 3;
-    pms[0].pNextMember = ((StartAddr + 3) & (uintptr_t)~3);
-
-    StartAddr = (uintptr_t)pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].pBaseAddress;
-
-    pms[1].TotalSize = 3;
-    pms[1].pNextMember = ((StartAddr + 3) & (uintptr_t)~3);
-
-    StartAddr = (uintptr_t)pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].pBaseAddress;
-
-    pms[2].TotalSize = 3;
-    pms[2].pNextMember = ((StartAddr + 3) & (uintptr_t)~3);
-
-    StartAddr = (uintptr_t)pMemoryTable->Region[LVM_TEMPORARY_FAST].pBaseAddress;
-
-    pms[3].TotalSize = 3;
-    pms[3].pNextMember = ((StartAddr + 3) & (uintptr_t)~3);
-}
-
-/****************************************************************************************
- *  Name        : InstAlloc_InitAll_NULL()
- *  Input       : pms  - Pointer to array of four INST_ALLOC instances
- *  Returns     : Nothing
- *  Description : This function reserves Size of 3 bytes for all memory regions and
- *                intializes pNextMember for all regions to 0
- *  Remarks     :
- ****************************************************************************************/
-
-void InstAlloc_InitAll_NULL(INST_ALLOC* pms) {
-    pms[0].TotalSize = 3;
-    pms[0].pNextMember = 0;
-
-    pms[1].TotalSize = 3;
-    pms[1].pNextMember = 0;
-
-    pms[2].TotalSize = 3;
-    pms[2].pNextMember = 0;
-
-    pms[3].TotalSize = 3;
-    pms[3].pNextMember = 0;
-}
-
-void* InstAlloc_AddMemberAll(INST_ALLOC* pms, LVM_UINT32 Size[], LVM_MemoryTable_st* pMemoryTable) {
-    void* NewMemberAddress; /* Variable to temporarily store the return value */
-
-    /* coverity[returned_pointer] Ignore coverity warning that ptr is not used */
-    NewMemberAddress =
-            InstAlloc_AddMember(&pms[LVM_PERSISTENT_SLOW_DATA], Size[LVM_PERSISTENT_SLOW_DATA]);
-
-    pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].Size =
-            InstAlloc_GetTotal(&pms[LVM_PERSISTENT_SLOW_DATA]);
-    pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].Type = LVM_PERSISTENT_SLOW_DATA;
-    pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].pBaseAddress = LVM_NULL;
-
-    NewMemberAddress =
-            InstAlloc_AddMember(&pms[LVM_PERSISTENT_FAST_DATA], Size[LVM_PERSISTENT_FAST_DATA]);
-
-    pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].Size =
-            InstAlloc_GetTotal(&pms[LVM_PERSISTENT_FAST_DATA]);
-    pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].Type = LVM_PERSISTENT_FAST_DATA;
-    pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].pBaseAddress = LVM_NULL;
-
-    NewMemberAddress =
-            InstAlloc_AddMember(&pms[LVM_PERSISTENT_FAST_COEF], Size[LVM_PERSISTENT_FAST_COEF]);
-
-    pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].Size =
-            InstAlloc_GetTotal(&pms[LVM_PERSISTENT_FAST_COEF]);
-    pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].Type = LVM_PERSISTENT_FAST_COEF;
-    pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].pBaseAddress = LVM_NULL;
-
-    NewMemberAddress = InstAlloc_AddMember(&pms[LVM_TEMPORARY_FAST], Size[LVM_TEMPORARY_FAST]);
-
-    pMemoryTable->Region[LVM_TEMPORARY_FAST].Size = InstAlloc_GetTotal(&pms[LVM_TEMPORARY_FAST]);
-    pMemoryTable->Region[LVM_TEMPORARY_FAST].Type = LVM_TEMPORARY_FAST;
-    pMemoryTable->Region[LVM_TEMPORARY_FAST].pBaseAddress = LVM_NULL;
-
-    return (NewMemberAddress);
-}
-
-void* InstAlloc_AddMemberAllRet(INST_ALLOC* pms, LVM_UINT32 Size[], void** ptr) {
-    ptr[0] = InstAlloc_AddMember(&pms[LVM_PERSISTENT_SLOW_DATA], Size[LVM_PERSISTENT_SLOW_DATA]);
-    ptr[1] = InstAlloc_AddMember(&pms[LVM_PERSISTENT_FAST_DATA], Size[LVM_PERSISTENT_FAST_DATA]);
-    ptr[2] = InstAlloc_AddMember(&pms[LVM_PERSISTENT_FAST_COEF], Size[LVM_PERSISTENT_FAST_COEF]);
-    ptr[3] = InstAlloc_AddMember(&pms[LVM_TEMPORARY_FAST], Size[LVM_TEMPORARY_FAST]);
-
-    return (ptr[0]);
-}
diff --git a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.cpp b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.cpp
index 37e6d4d..3473262 100644
--- a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.cpp
+++ b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.cpp
@@ -24,7 +24,6 @@
 #include <stdlib.h>
 #include "LVEQNB.h"
 #include "LVEQNB_Private.h"
-#include "InstAlloc.h"
 #include <string.h> /* For memset */
 
 /****************************************************************************************/
@@ -52,7 +51,7 @@
                                    LVEQNB_Capabilities_t* pCapabilities, void* pScratch) {
     LVEQNB_Instance_t* pInstance;
 
-    *phInstance = new LVEQNB_Instance_t;
+    *phInstance = new LVEQNB_Instance_t{};
     pInstance = (LVEQNB_Instance_t*)*phInstance;
 
     pInstance->Capabilities = *pCapabilities;
diff --git a/media/libeffects/lvm/lib/Reverb/lib/LVREV.h b/media/libeffects/lvm/lib/Reverb/lib/LVREV.h
index 489bc6f..82e94da 100644
--- a/media/libeffects/lvm/lib/Reverb/lib/LVREV.h
+++ b/media/libeffects/lvm/lib/Reverb/lib/LVREV.h
@@ -78,11 +78,6 @@
 /*                                                                                      */
 /****************************************************************************************/
 
-/* Memory table containing the region definitions */
-typedef struct {
-    LVM_MemoryRegion_st Region[LVREV_NR_MEMORY_REGIONS]; /* One definition for each region */
-} LVREV_MemoryTable_st;
-
 /* Control Parameter structure */
 typedef struct {
     /* General parameters */
@@ -121,46 +116,6 @@
 
 /****************************************************************************************/
 /*                                                                                      */
-/* FUNCTION:                LVREV_GetMemoryTable                                        */
-/*                                                                                      */
-/* DESCRIPTION:                                                                         */
-/*  This function is used to obtain the LVREV module memory requirements to support     */
-/*  memory allocation. It can also be used to return the memory base address provided   */
-/*  during memory allocation to support freeing of memory when the LVREV module is no   */
-/*  longer required. It is called in two ways:                                          */
-/*                                                                                      */
-/*  hInstance = NULL                Returns the memory requirements                     */
-/*  hInstance = Instance handle     Returns the memory requirements and allocated       */
-/*                                  base addresses.                                     */
-/*                                                                                      */
-/*  When this function is called with hInstance = NULL the memory base address pointers */
-/*  will be NULL on return.                                                             */
-/*                                                                                      */
-/*  When the function is called for freeing memory, hInstance = Instance Handle the     */
-/*  memory table returns the allocated memory and base addresses used during            */
-/*  initialisation.                                                                     */
-/*                                                                                      */
-/* PARAMETERS:                                                                          */
-/*  hInstance               Instance Handle                                             */
-/*  pMemoryTable            Pointer to an empty memory table                            */
-/*  pInstanceParams         Pointer to the instance parameters                          */
-/*                                                                                      */
-/* RETURNS:                                                                             */
-/*  LVREV_SUCCESS           Succeeded                                                   */
-/*  LVREV_NULLADDRESS       When pMemoryTable is NULL                                   */
-/*  LVREV_NULLADDRESS       When requesting memory requirements and pInstanceParams     */
-/*                          is NULL                                                     */
-/*                                                                                      */
-/* NOTES:                                                                               */
-/*  1.  This function may be interrupted by the LVREV_Process function                  */
-/*                                                                                      */
-/****************************************************************************************/
-LVREV_ReturnStatus_en LVREV_GetMemoryTable(LVREV_Handle_t hInstance,
-                                           LVREV_MemoryTable_st* pMemoryTable,
-                                           LVREV_InstanceParams_st* pInstanceParams);
-
-/****************************************************************************************/
-/*                                                                                      */
 /* FUNCTION:                LVREV_GetInstanceHandle                                     */
 /*                                                                                      */
 /* DESCRIPTION:                                                                         */
@@ -174,7 +129,6 @@
 /*                                                                                      */
 /* PARAMETERS:                                                                          */
 /*  phInstance              Pointer to the instance handle                              */
-/*  pMemoryTable            Pointer to the memory definition table                      */
 /*  pInstanceParams         Pointer to the instance parameters                          */
 /*                                                                                      */
 /* RETURNS:                                                                             */
@@ -186,7 +140,6 @@
 /*                                                                                      */
 /****************************************************************************************/
 LVREV_ReturnStatus_en LVREV_GetInstanceHandle(LVREV_Handle_t* phInstance,
-                                              LVREV_MemoryTable_st* pMemoryTable,
                                               LVREV_InstanceParams_st* pInstanceParams);
 
 /****************************************************************************************/
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.cpp b/media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.cpp
index bf71634..3a63698 100644
--- a/media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.cpp
+++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.cpp
@@ -21,7 +21,6 @@
 /*                                                                                      */
 /****************************************************************************************/
 #include "LVREV_Private.h"
-#include "InstAlloc.h"
 
 /****************************************************************************************/
 /*                                                                                      */
@@ -34,7 +33,6 @@
 /*                                                                                      */
 /* PARAMETERS:                                                                          */
 /*  phInstance              pointer to the instance handle                              */
-/*  pMemoryTable            Pointer to the memory definition table                      */
 /*  pInstanceParams         Pointer to the instance parameters                          */
 /*                                                                                      */
 /* RETURNS:                                                                             */
@@ -46,12 +44,7 @@
 /*                                                                                      */
 /****************************************************************************************/
 LVREV_ReturnStatus_en LVREV_GetInstanceHandle(LVREV_Handle_t* phInstance,
-                                              LVREV_MemoryTable_st* pMemoryTable,
                                               LVREV_InstanceParams_st* pInstanceParams) {
-    INST_ALLOC SlowData;
-    INST_ALLOC FastData;
-    INST_ALLOC FastCoef;
-    INST_ALLOC Temporary;
     LVREV_Instance_st* pLVREV_Private;
     LVM_INT16 i;
     LVM_UINT16 MaxBlockSize;
@@ -60,18 +53,9 @@
      * Check for error conditions
      */
     /* Check for NULL pointers */
-    if ((phInstance == LVM_NULL) || (pMemoryTable == LVM_NULL) || (pInstanceParams == LVM_NULL)) {
+    if ((phInstance == LVM_NULL) || (pInstanceParams == LVM_NULL)) {
         return LVREV_NULLADDRESS;
     }
-    /* Check the memory table for NULL pointers */
-    for (i = 0; i < LVREV_NR_MEMORY_REGIONS; i++) {
-        if (pMemoryTable->Region[i].Size != 0) {
-            if (pMemoryTable->Region[i].pBaseAddress == LVM_NULL) {
-                return (LVREV_NULLADDRESS);
-            }
-        }
-    }
-
     /*
      * Check all instance parameters are in range
      */
@@ -88,36 +72,12 @@
     }
 
     /*
-     * Initialise the InstAlloc instances
-     */
-    InstAlloc_Init(&SlowData, pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].pBaseAddress);
-    InstAlloc_Init(&FastData, pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].pBaseAddress);
-    InstAlloc_Init(&FastCoef, pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].pBaseAddress);
-    InstAlloc_Init(&Temporary, pMemoryTable->Region[LVM_TEMPORARY_FAST].pBaseAddress);
-
-    /*
-     * Zero all memory regions
-     */
-    LoadConst_Float(
-            0, (LVM_FLOAT*)pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].pBaseAddress,
-            (LVM_INT16)((pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].Size) / sizeof(LVM_FLOAT)));
-    LoadConst_Float(
-            0, (LVM_FLOAT*)pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].pBaseAddress,
-            (LVM_INT16)((pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].Size) / sizeof(LVM_FLOAT)));
-    LoadConst_Float(
-            0, (LVM_FLOAT*)pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].pBaseAddress,
-            (LVM_INT16)((pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].Size) / sizeof(LVM_FLOAT)));
-    LoadConst_Float(
-            0, (LVM_FLOAT*)pMemoryTable->Region[LVM_TEMPORARY_FAST].pBaseAddress,
-            (LVM_INT16)((pMemoryTable->Region[LVM_TEMPORARY_FAST].Size) / sizeof(LVM_FLOAT)));
-    /*
      * Set the instance handle if not already initialised
      */
     if (*phInstance == LVM_NULL) {
-        *phInstance = new LVREV_Instance_st;
+        *phInstance = new LVREV_Instance_st{};
     }
     pLVREV_Private = (LVREV_Instance_st*)*phInstance;
-    pLVREV_Private->MemoryTable = *pMemoryTable;
 
     if (pInstanceParams->NumDelays == LVREV_DELAYLINES_4) {
         MaxBlockSize = LVREV_MAX_AP_DELAY[3];
@@ -135,12 +95,9 @@
      * Set the data, coefficient and temporary memory pointers
      */
     for (size_t i = 0; i < pInstanceParams->NumDelays; i++) {
-        pLVREV_Private->pDelay_T[i] = (LVM_FLOAT*)InstAlloc_AddMember(
-                &FastData, LVREV_MAX_T_DELAY[i] * sizeof(LVM_FLOAT));
+        pLVREV_Private->pDelay_T[i] = (LVM_FLOAT*)calloc(LVREV_MAX_T_DELAY[i], sizeof(LVM_FLOAT));
         /* Scratch for each delay line output */
-        pLVREV_Private->pScratchDelayLine[i] =
-                (LVM_FLOAT*)InstAlloc_AddMember(&Temporary, sizeof(LVM_FLOAT) * MaxBlockSize);
-        LoadConst_Float(0, pLVREV_Private->pDelay_T[i], LVREV_MAX_T_DELAY[i]);
+        pLVREV_Private->pScratchDelayLine[i] = (LVM_FLOAT*)calloc(MaxBlockSize, sizeof(LVM_FLOAT));
     }
     /* All-pass delay buffer addresses and sizes */
     for (size_t i = 0; i < LVREV_DELAYLINES_4; i++) {
@@ -149,12 +106,9 @@
     pLVREV_Private->AB_Selection = 1; /* Select smoothing A to B */
 
     /* General purpose scratch */
-    pLVREV_Private->pScratch =
-            (LVM_FLOAT*)InstAlloc_AddMember(&Temporary, sizeof(LVM_FLOAT) * MaxBlockSize);
+    pLVREV_Private->pScratch = (LVM_FLOAT*)calloc(MaxBlockSize, sizeof(LVM_FLOAT));
     /* Mono->stereo input save for end mix */
-    pLVREV_Private->pInputSave =
-            (LVM_FLOAT*)InstAlloc_AddMember(&Temporary, 2 * sizeof(LVM_FLOAT) * MaxBlockSize);
-    LoadConst_Float(0, pLVREV_Private->pInputSave, (LVM_INT16)(MaxBlockSize * 2));
+    pLVREV_Private->pInputSave = (LVM_FLOAT*)calloc(FCC_2 * MaxBlockSize, sizeof(LVM_FLOAT));
 
     /*
      * Save the instance parameters in the instance structure
@@ -289,7 +243,28 @@
         return LVREV_NULLADDRESS;
     }
 
-    delete (LVREV_Instance_st*)hInstance;
+    LVREV_Instance_st* pLVREV_Private = (LVREV_Instance_st*)hInstance;
+
+    for (size_t i = 0; i < pLVREV_Private->InstanceParams.NumDelays; i++) {
+        if (pLVREV_Private->pDelay_T[i]) {
+            free(pLVREV_Private->pDelay_T[i]);
+            pLVREV_Private->pDelay_T[i] = LVM_NULL;
+        }
+        if (pLVREV_Private->pScratchDelayLine[i]) {
+            free(pLVREV_Private->pScratchDelayLine[i]);
+            pLVREV_Private->pScratchDelayLine[i] = LVM_NULL;
+        }
+    }
+    if (pLVREV_Private->pScratch) {
+        free(pLVREV_Private->pScratch);
+        pLVREV_Private->pScratch = LVM_NULL;
+    }
+    if (pLVREV_Private->pInputSave) {
+        free(pLVREV_Private->pInputSave);
+        pLVREV_Private->pInputSave = LVM_NULL;
+    }
+
+    delete pLVREV_Private;
     return LVREV_SUCCESS;
 }
 /* End of file */
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_GetMemoryTable.cpp b/media/libeffects/lvm/lib/Reverb/src/LVREV_GetMemoryTable.cpp
deleted file mode 100644
index 02ceb16..0000000
--- a/media/libeffects/lvm/lib/Reverb/src/LVREV_GetMemoryTable.cpp
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2004-2010 NXP Software
- * Copyright (C) 2010 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.
- */
-
-/****************************************************************************************/
-/*                                                                                      */
-/*  Includes                                                                            */
-/*                                                                                      */
-/****************************************************************************************/
-#include "LVREV_Private.h"
-#include "InstAlloc.h"
-
-/****************************************************************************************/
-/*                                                                                      */
-/* FUNCTION:                LVREV_GetMemoryTable                                        */
-/*                                                                                      */
-/* DESCRIPTION:                                                                         */
-/*  This function is used for memory allocation and free. It can be called in           */
-/*  two ways:                                                                           */
-/*                                                                                      */
-/*  hInstance = NULL                Returns the memory requirements                     */
-/*  hInstance = Instance handle     Returns the memory requirements and allocated       */
-/*                                  base addresses.                                     */
-/*                                                                                      */
-/*  When this function is called for memory allocation (hInstance=NULL) the memory      */
-/*  base address pointers are NULL on return.                                           */
-/*                                                                                      */
-/*  When the function is called for free (hInstance = Instance Handle) the memory       */
-/*  table returns the allocated memory and base addresses used during initialisation.   */
-/*                                                                                      */
-/* PARAMETERS:                                                                          */
-/*  hInstance               Instance Handle                                             */
-/*  pMemoryTable            Pointer to an empty memory table                            */
-/*  pInstanceParams         Pointer to the instance parameters                          */
-/*                                                                                      */
-/* RETURNS:                                                                             */
-/*  LVREV_Success           Succeeded                                                   */
-/*  LVREV_NULLADDRESS       When pMemoryTable is NULL                                   */
-/*  LVREV_NULLADDRESS       When requesting memory requirements and pInstanceParams     */
-/*                          is NULL                                                     */
-/*                                                                                      */
-/* NOTES:                                                                               */
-/*  1.  This function may be interrupted by the LVREV_Process function                  */
-/*                                                                                      */
-/****************************************************************************************/
-LVREV_ReturnStatus_en LVREV_GetMemoryTable(LVREV_Handle_t hInstance,
-                                           LVREV_MemoryTable_st* pMemoryTable,
-                                           LVREV_InstanceParams_st* pInstanceParams) {
-    INST_ALLOC SlowData;
-    INST_ALLOC FastData;
-    INST_ALLOC FastCoef;
-    INST_ALLOC Temporary;
-    LVM_UINT16 MaxBlockSize;
-
-    /*
-     * Check for error conditions
-     */
-    /* Check for NULL pointer */
-    if (pMemoryTable == LVM_NULL) {
-        return (LVREV_NULLADDRESS);
-    }
-
-    /*
-     * Check all instance parameters are in range
-     */
-    if (pInstanceParams != LVM_NULL) {
-        /*
-         * Call for memory allocation, so check the parameters
-         */
-        /* Check for a non-zero block size */
-        if (pInstanceParams->MaxBlockSize == 0) {
-            return LVREV_OUTOFRANGE;
-        }
-
-        /* Check for a valid number of delay lines */
-        if ((pInstanceParams->NumDelays != LVREV_DELAYLINES_1) &&
-            (pInstanceParams->NumDelays != LVREV_DELAYLINES_2) &&
-            (pInstanceParams->NumDelays != LVREV_DELAYLINES_4)) {
-            return LVREV_OUTOFRANGE;
-        }
-    }
-
-    /*
-     * Initialise the InstAlloc instances
-     */
-    InstAlloc_Init(&SlowData, (void*)LVM_NULL);
-    InstAlloc_Init(&FastData, (void*)LVM_NULL);
-    InstAlloc_Init(&FastCoef, (void*)LVM_NULL);
-    InstAlloc_Init(&Temporary, (void*)LVM_NULL);
-
-    /*
-     * Fill in the memory table
-     */
-    if (hInstance == LVM_NULL) {
-        /*
-         * Check for null pointers
-         */
-        if (pInstanceParams == LVM_NULL) {
-            return (LVREV_NULLADDRESS);
-        }
-
-        /*
-         * Select the maximum internal block size
-         */
-        if (pInstanceParams->NumDelays == LVREV_DELAYLINES_4) {
-            MaxBlockSize = LVREV_MAX_AP_DELAY[3];
-        } else if (pInstanceParams->NumDelays == LVREV_DELAYLINES_2) {
-            MaxBlockSize = LVREV_MAX_AP_DELAY[1];
-        } else {
-            MaxBlockSize = LVREV_MAX_AP_DELAY[0];
-        }
-
-        if (MaxBlockSize > pInstanceParams->MaxBlockSize) {
-            MaxBlockSize = pInstanceParams->MaxBlockSize;
-        }
-
-        /*
-         * Slow data memory
-         */
-        InstAlloc_AddMember(&SlowData, sizeof(LVREV_Instance_st));
-        pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].Size = InstAlloc_GetTotal(&SlowData);
-        pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].Type = LVM_PERSISTENT_SLOW_DATA;
-        pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].pBaseAddress = LVM_NULL;
-
-        /*
-         * Persistent fast data memory
-         */
-        for (size_t i = 0; i < pInstanceParams->NumDelays; i++) {
-            InstAlloc_AddMember(&FastData, LVREV_MAX_T_DELAY[i] * sizeof(LVM_FLOAT));
-        }
-
-        pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].Size = InstAlloc_GetTotal(&FastData);
-        pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].Type = LVM_PERSISTENT_FAST_DATA;
-        pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].pBaseAddress = LVM_NULL;
-
-        /*
-         * Persistent fast coefficient memory
-         */
-        pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].Size = InstAlloc_GetTotal(&FastCoef);
-        pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].Type = LVM_PERSISTENT_FAST_COEF;
-        pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].pBaseAddress = LVM_NULL;
-
-        /*
-         * Temporary fast memory
-         */
-        /* General purpose scratch memory */
-        InstAlloc_AddMember(&Temporary, sizeof(LVM_FLOAT) * MaxBlockSize);
-        /* Mono->stereo input saved for end mix */
-        InstAlloc_AddMember(&Temporary, 2 * sizeof(LVM_FLOAT) * MaxBlockSize);
-        for (size_t i = 0; i < pInstanceParams->NumDelays; i++) {
-            /* A Scratch buffer for each delay line */
-            InstAlloc_AddMember(&Temporary, sizeof(LVM_FLOAT) * MaxBlockSize);
-        }
-
-        pMemoryTable->Region[LVM_TEMPORARY_FAST].Size = InstAlloc_GetTotal(&Temporary);
-        pMemoryTable->Region[LVM_TEMPORARY_FAST].Type = LVM_TEMPORARY_FAST;
-        pMemoryTable->Region[LVM_TEMPORARY_FAST].pBaseAddress = LVM_NULL;
-
-    } else {
-        LVREV_Instance_st* pLVREV_Private = (LVREV_Instance_st*)hInstance;
-
-        /*
-         * Read back memory allocation table
-         */
-        *pMemoryTable = pLVREV_Private->MemoryTable;
-    }
-
-    return (LVREV_SUCCESS);
-}
-
-/* End of file */
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_Private.h b/media/libeffects/lvm/lib/Reverb/src/LVREV_Private.h
index 33f8165..9a2f9ca 100644
--- a/media/libeffects/lvm/lib/Reverb/src/LVREV_Private.h
+++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_Private.h
@@ -103,11 +103,9 @@
 /*                                                                                      */
 /****************************************************************************************/
 
-
 typedef struct {
     /* General */
     LVREV_InstanceParams_st InstanceParams; /* Initialisation time instance parameters */
-    LVREV_MemoryTable_st MemoryTable;       /* Memory table */
     LVREV_ControlParams_st CurrentParams;   /* Parameters being used */
     LVREV_ControlParams_st NewParams;       /* New parameters from the \
                                                calling application */
diff --git a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Init.cpp b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Init.cpp
index 5ca8543..a0f28b1 100644
--- a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Init.cpp
+++ b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Init.cpp
@@ -18,7 +18,6 @@
 #include <stdlib.h>
 #include "LVPSA.h"
 #include "LVPSA_Private.h"
-#include "InstAlloc.h"
 
 /************************************************************************************/
 /*                                                                                  */
@@ -49,7 +48,7 @@
     LVM_UINT32 BufferLength = 0;
 
     /* Set the instance handle if not already initialised */
-    *phInstance = new LVPSA_InstancePr_t;
+    *phInstance = new LVPSA_InstancePr_t{};
     pLVPSA_Inst = (LVPSA_InstancePr_t*)*phInstance;
 
     pLVPSA_Inst->pScratch = pScratch;
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.cpp b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.cpp
index 1746786..2b628f1 100644
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.cpp
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.cpp
@@ -72,8 +72,8 @@
 
         std::array<LVM_FLOAT, android::audio_utils::kBiquadNumCoefs> coefs = {
                 pEqualiserCoefTable[Offset].A0, pEqualiserCoefTable[Offset].A1,
-                pEqualiserCoefTable[Offset].A2, -(pEqualiserCoefTable[Offset].B1),
-                -(pEqualiserCoefTable[Offset].B2)};
+                pEqualiserCoefTable[Offset].A2, pEqualiserCoefTable[Offset].B1,
+                pEqualiserCoefTable[Offset].B2};
         pInstance->pEqBiquad.reset(new android::audio_utils::BiquadFilter<LVM_FLOAT>(
                 (pParams->NrChannels == FCC_1) ? FCC_1 : FCC_2, coefs));
     }
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Init.cpp b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Init.cpp
index d60b360..dd1baf3 100644
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Init.cpp
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Init.cpp
@@ -55,7 +55,7 @@
      * Create the instance handle if not already initialised
      */
     if (*phInstance == LVM_NULL) {
-        *phInstance = new LVCS_Instance_t;
+        *phInstance = new LVCS_Instance_t{};
     }
     pInstance = (LVCS_Instance_t*)*phInstance;
 
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.cpp b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.cpp
index 12b1dc3..c5b6598 100644
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.cpp
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.cpp
@@ -93,8 +93,8 @@
 
         std::array<LVM_FLOAT, android::audio_utils::kBiquadNumCoefs> coefs = {
                 pReverbCoefTable[Offset].A0, pReverbCoefTable[Offset].A1,
-                pReverbCoefTable[Offset].A2, -(pReverbCoefTable[Offset].B1),
-                -(pReverbCoefTable[Offset].B2)};
+                pReverbCoefTable[Offset].A2, pReverbCoefTable[Offset].B1,
+                pReverbCoefTable[Offset].B2};
         pInstance->pRevBiquad.reset(new android::audio_utils::BiquadFilter<LVM_FLOAT>(
                 (pParams->NrChannels == FCC_1) ? FCC_1 : FCC_2, coefs));
 
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.cpp b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.cpp
index e3ff604..3ca25f9 100644
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.cpp
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.cpp
@@ -68,7 +68,7 @@
 
         std::array<LVM_FLOAT, android::audio_utils::kBiquadNumCoefs> coefs = {
                 LVCS_SEMidCoefTable[Offset].A0, LVCS_SEMidCoefTable[Offset].A1, 0.0,
-                -(LVCS_SEMidCoefTable[Offset].B1), 0.0};
+                LVCS_SEMidCoefTable[Offset].B1, 0.0};
         pInstance->pSEMidBiquad.reset(
                 new android::audio_utils::BiquadFilter<LVM_FLOAT>(FCC_1, coefs));
 
@@ -77,7 +77,7 @@
 
         /* Side filter */
         coefs = {pSESideCoefs[Offset].A0, pSESideCoefs[Offset].A1, pSESideCoefs[Offset].A2,
-                 -(pSESideCoefs[Offset].B1), -(pSESideCoefs[Offset].B2)};
+                 pSESideCoefs[Offset].B1, pSESideCoefs[Offset].B2};
         pInstance->pSESideBiquad.reset(
                 new android::audio_utils::BiquadFilter<LVM_FLOAT>(FCC_1, coefs));
     }
diff --git a/media/libeffects/lvm/tests/Android.bp b/media/libeffects/lvm/tests/Android.bp
index 639af4d..9939ed1 100644
--- a/media/libeffects/lvm/tests/Android.bp
+++ b/media/libeffects/lvm/tests/Android.bp
@@ -10,12 +10,42 @@
 }
 
 cc_test {
+    name: "EffectReverbTest",
+    vendor: true,
+    gtest: true,
+    host_supported: true,
+    srcs: [
+        "EffectReverbTest.cpp",
+        "EffectTestHelper.cpp",
+    ],
+    include_dirs: [
+        "frameworks/av/media/libeffects/lvm/lib/Common/lib",
+        "frameworks/av/media/libeffects/lvm/wrapper/Reverb",
+    ],
+    static_libs: [
+        "libaudioutils",
+        "libreverb",
+        "libreverbwrapper",
+    ],
+    shared_libs: [
+        "liblog",
+    ],
+    header_libs: [
+        "libaudioeffects",
+        "libhardware_headers",
+    ],
+}
+
+cc_test {
     name: "EffectBundleTest",
     vendor: true,
     gtest: true,
     host_supported: true,
     test_suites: ["device-tests"],
-    srcs: ["EffectBundleTest.cpp"],
+    srcs: [
+        "EffectBundleTest.cpp",
+        "EffectTestHelper.cpp",
+    ],
     static_libs: [
         "libaudioutils",
         "libbundlewrapper",
diff --git a/media/libeffects/lvm/tests/EffectBundleTest.cpp b/media/libeffects/lvm/tests/EffectBundleTest.cpp
index aae09de..881ffb1 100644
--- a/media/libeffects/lvm/tests/EffectBundleTest.cpp
+++ b/media/libeffects/lvm/tests/EffectBundleTest.cpp
@@ -14,22 +14,8 @@
  * limitations under the License.
  */
 
-#include <array>
-#include <audio_utils/channels.h>
-#include <audio_utils/primitives.h>
-#include <climits>
-#include <cstdlib>
-#include <gtest/gtest.h>
-#include <hardware/audio_effect.h>
-#include <log/log.h>
-#include <random>
-#include <system/audio.h>
-#include <vector>
-
-extern audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM;
-
-// Corresponds to SNR for 1 bit difference between two int16_t signals
-constexpr float kSNRThreshold = 90.308998;
+#include "EffectTestHelper.h"
+using namespace android;
 
 // Update isBassBoost, if the order of effects is updated
 constexpr effect_uuid_t kEffectUuids[] = {
@@ -50,120 +36,15 @@
 
 constexpr size_t kNumEffectUuids = std::size(kEffectUuids);
 
-constexpr audio_channel_mask_t kChMasks[] = {
-        AUDIO_CHANNEL_OUT_MONO,          AUDIO_CHANNEL_OUT_STEREO,
-        AUDIO_CHANNEL_OUT_2POINT1,       AUDIO_CHANNEL_OUT_2POINT0POINT2,
-        AUDIO_CHANNEL_OUT_QUAD,          AUDIO_CHANNEL_OUT_QUAD_BACK,
-        AUDIO_CHANNEL_OUT_QUAD_SIDE,     AUDIO_CHANNEL_OUT_SURROUND,
-        AUDIO_CHANNEL_INDEX_MASK_4,      AUDIO_CHANNEL_OUT_2POINT1POINT2,
-        AUDIO_CHANNEL_OUT_3POINT0POINT2, AUDIO_CHANNEL_OUT_PENTA,
-        AUDIO_CHANNEL_INDEX_MASK_5,      AUDIO_CHANNEL_OUT_3POINT1POINT2,
-        AUDIO_CHANNEL_OUT_5POINT1,       AUDIO_CHANNEL_OUT_5POINT1_BACK,
-        AUDIO_CHANNEL_OUT_5POINT1_SIDE,  AUDIO_CHANNEL_INDEX_MASK_6,
-        AUDIO_CHANNEL_OUT_6POINT1,       AUDIO_CHANNEL_INDEX_MASK_7,
-        AUDIO_CHANNEL_OUT_5POINT1POINT2, AUDIO_CHANNEL_OUT_7POINT1,
-        AUDIO_CHANNEL_INDEX_MASK_8,      AUDIO_CHANNEL_INDEX_MASK_9,
-        AUDIO_CHANNEL_INDEX_MASK_10,     AUDIO_CHANNEL_INDEX_MASK_11,
-        AUDIO_CHANNEL_INDEX_MASK_12,     AUDIO_CHANNEL_INDEX_MASK_13,
-        AUDIO_CHANNEL_INDEX_MASK_14,     AUDIO_CHANNEL_INDEX_MASK_15,
-        AUDIO_CHANNEL_INDEX_MASK_16,     AUDIO_CHANNEL_INDEX_MASK_17,
-        AUDIO_CHANNEL_INDEX_MASK_18,     AUDIO_CHANNEL_INDEX_MASK_19,
-        AUDIO_CHANNEL_INDEX_MASK_20,     AUDIO_CHANNEL_INDEX_MASK_21,
-        AUDIO_CHANNEL_INDEX_MASK_22,     AUDIO_CHANNEL_INDEX_MASK_23,
-        AUDIO_CHANNEL_INDEX_MASK_24,
-};
-
-constexpr size_t kNumChMasks = std::size(kChMasks);
-
-constexpr size_t kSampleRates[] = {8000,  11025, 12000, 16000, 22050,  24000, 32000,
-                                   44100, 48000, 88200, 96000, 176400, 192000};
-
-constexpr size_t kNumSampleRates = std::size(kSampleRates);
-
-constexpr size_t kFrameCounts[] = {4, 2048};
-
-constexpr size_t kNumFrameCounts = std::size(kFrameCounts);
-
-constexpr size_t kLoopCounts[] = {1, 4};
-
-constexpr size_t kNumLoopCounts = std::size(kLoopCounts);
-
-class EffectBundleHelper {
-  public:
-    EffectBundleHelper(const effect_uuid_t* uuid, size_t chMask, size_t sampleRate,
-                       size_t frameCount, size_t loopCount)
-        : mUuid(uuid),
-          mChMask(chMask),
-          mChannelCount(audio_channel_count_from_out_mask(mChMask)),
-          mSampleRate(sampleRate),
-          mFrameCount(frameCount),
-          mLoopCount(loopCount) {}
-    void createEffect();
-    void releaseEffect();
-    void configEffect();
-    void process(float* input, float* output);
-
-  private:
-    const effect_uuid_t* mUuid;
-    const size_t mChMask;
-    const size_t mChannelCount;
-    const size_t mSampleRate;
-    const size_t mFrameCount;
-    const size_t mLoopCount;
-    effect_handle_t mEffectHandle{};
-};
-
-void EffectBundleHelper::createEffect() {
-    int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.create_effect(mUuid, 1, 1, &mEffectHandle);
-    ASSERT_EQ(status, 0) << "create_effect returned an error " << status << "\n";
-}
-
-void EffectBundleHelper::releaseEffect() {
-    int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.release_effect(mEffectHandle);
-    ASSERT_EQ(status, 0) << "release_effect returned an error " << status << "\n";
-}
-
-void EffectBundleHelper::configEffect() {
-    effect_config_t config{};
-    config.inputCfg.samplingRate = config.outputCfg.samplingRate = mSampleRate;
-    config.inputCfg.channels = config.outputCfg.channels = mChMask;
-    config.inputCfg.format = config.outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
-
-    int reply = 0;
-    uint32_t replySize = sizeof(reply);
-    int status = (*mEffectHandle)
-                         ->command(mEffectHandle, EFFECT_CMD_SET_CONFIG, sizeof(effect_config_t),
-                                   &config, &replySize, &reply);
-    ASSERT_EQ(status, 0) << "command returned an error " << status << "\n";
-    ASSERT_EQ(reply, 0) << "command reply non zero " << reply << "\n";
-
-    status = (*mEffectHandle)
-                     ->command(mEffectHandle, EFFECT_CMD_ENABLE, 0, nullptr, &replySize, &reply);
-    ASSERT_EQ(status, 0) << "command enable returned an error " << status << "\n";
-    ASSERT_EQ(reply, 0) << "command reply non zero " << reply << "\n";
-}
-
-void EffectBundleHelper::process(float* input, float* output) {
-    audio_buffer_t inBuffer = {.frameCount = mFrameCount, .f32 = input};
-    audio_buffer_t outBuffer = {.frameCount = mFrameCount, .f32 = output};
-    for (size_t i = 0; i < mLoopCount; i++) {
-        int status = (*mEffectHandle)->process(mEffectHandle, &inBuffer, &outBuffer);
-        ASSERT_EQ(status, 0) << "process returned an error " << status << "\n";
-
-        inBuffer.f32 += mFrameCount * mChannelCount;
-        outBuffer.f32 += mFrameCount * mChannelCount;
-    }
-}
-
 typedef std::tuple<int, int, int, int, int> SingleEffectTestParam;
 class SingleEffectTest : public ::testing::TestWithParam<SingleEffectTestParam> {
   public:
     SingleEffectTest()
-        : mChMask(kChMasks[std::get<0>(GetParam())]),
+        : mChMask(EffectTestHelper::kChMasks[std::get<0>(GetParam())]),
           mChannelCount(audio_channel_count_from_out_mask(mChMask)),
-          mSampleRate(kSampleRates[std::get<1>(GetParam())]),
-          mFrameCount(kFrameCounts[std::get<2>(GetParam())]),
-          mLoopCount(kLoopCounts[std::get<3>(GetParam())]),
+          mSampleRate(EffectTestHelper::kSampleRates[std::get<1>(GetParam())]),
+          mFrameCount(EffectTestHelper::kFrameCounts[std::get<2>(GetParam())]),
+          mLoopCount(EffectTestHelper::kLoopCounts[std::get<3>(GetParam())]),
           mTotalFrameCount(mFrameCount * mLoopCount),
           mUuid(&kEffectUuids[std::get<4>(GetParam())]) {}
 
@@ -182,10 +63,10 @@
                  << "chMask: " << mChMask << " sampleRate: " << mSampleRate
                  << " frameCount: " << mFrameCount << " loopCount: " << mLoopCount);
 
-    EffectBundleHelper effect(mUuid, mChMask, mSampleRate, mFrameCount, mLoopCount);
+    EffectTestHelper effect(mUuid, mChMask, mChMask, mSampleRate, mFrameCount, mLoopCount);
 
     ASSERT_NO_FATAL_FAILURE(effect.createEffect());
-    ASSERT_NO_FATAL_FAILURE(effect.configEffect());
+    ASSERT_NO_FATAL_FAILURE(effect.setConfig());
 
     // Initialize input buffer with deterministic pseudo-random values
     std::vector<float> input(mTotalFrameCount * mChannelCount);
@@ -199,21 +80,22 @@
     ASSERT_NO_FATAL_FAILURE(effect.releaseEffect());
 }
 
-INSTANTIATE_TEST_SUITE_P(EffectBundleTestAll, SingleEffectTest,
-                         ::testing::Combine(::testing::Range(0, (int)kNumChMasks),
-                                            ::testing::Range(0, (int)kNumSampleRates),
-                                            ::testing::Range(0, (int)kNumFrameCounts),
-                                            ::testing::Range(0, (int)kNumLoopCounts),
-                                            ::testing::Range(0, (int)kNumEffectUuids)));
+INSTANTIATE_TEST_SUITE_P(
+        EffectBundleTestAll, SingleEffectTest,
+        ::testing::Combine(::testing::Range(0, (int)EffectTestHelper::kNumChMasks),
+                           ::testing::Range(0, (int)EffectTestHelper::kNumSampleRates),
+                           ::testing::Range(0, (int)EffectTestHelper::kNumFrameCounts),
+                           ::testing::Range(0, (int)EffectTestHelper::kNumLoopCounts),
+                           ::testing::Range(0, (int)kNumEffectUuids)));
 
 typedef std::tuple<int, int, int, int> SingleEffectComparisonTestParam;
 class SingleEffectComparisonTest
     : public ::testing::TestWithParam<SingleEffectComparisonTestParam> {
   public:
     SingleEffectComparisonTest()
-        : mSampleRate(kSampleRates[std::get<0>(GetParam())]),
-          mFrameCount(kFrameCounts[std::get<1>(GetParam())]),
-          mLoopCount(kLoopCounts[std::get<2>(GetParam())]),
+        : mSampleRate(EffectTestHelper::kSampleRates[std::get<0>(GetParam())]),
+          mFrameCount(EffectTestHelper::kFrameCounts[std::get<1>(GetParam())]),
+          mLoopCount(EffectTestHelper::kLoopCounts[std::get<2>(GetParam())]),
           mTotalFrameCount(mFrameCount * mLoopCount),
           mUuid(&kEffectUuids[std::get<3>(GetParam())]) {}
 
@@ -224,26 +106,6 @@
     const effect_uuid_t* mUuid;
 };
 
-template <typename T>
-float computeSnr(const T* ref, const T* tst, size_t count) {
-    double signal{};
-    double noise{};
-
-    for (size_t i = 0; i < count; ++i) {
-        const double value(ref[i]);
-        const double diff(tst[i] - value);
-        signal += value * value;
-        noise += diff * diff;
-    }
-    // Initialized to a value greater than kSNRThreshold to handle
-    // cases where ref and tst match exactly
-    float snr = kSNRThreshold + 1.0f;
-    if (signal > 0.0f && noise > 0.0f) {
-        snr = 10.f * log(signal / noise);
-    }
-    return snr;
-}
-
 // Compares first two channels in multi-channel output to stereo output when same effect is applied
 TEST_P(SingleEffectComparisonTest, SimpleProcess) {
     SCOPED_TRACE(testing::Message() << " sampleRate: " << mSampleRate << " frameCount: "
@@ -264,11 +126,11 @@
                     mTotalFrameCount * sizeof(float) * FCC_1);
 
     // Apply effect on stereo channels
-    EffectBundleHelper stereoEffect(mUuid, AUDIO_CHANNEL_OUT_STEREO, mSampleRate, mFrameCount,
-                                    mLoopCount);
+    EffectTestHelper stereoEffect(mUuid, AUDIO_CHANNEL_OUT_STEREO, AUDIO_CHANNEL_OUT_STEREO,
+                                  mSampleRate, mFrameCount, mLoopCount);
 
     ASSERT_NO_FATAL_FAILURE(stereoEffect.createEffect());
-    ASSERT_NO_FATAL_FAILURE(stereoEffect.configEffect());
+    ASSERT_NO_FATAL_FAILURE(stereoEffect.setConfig());
 
     std::vector<float> stereoOutput(mTotalFrameCount * FCC_2);
     ASSERT_NO_FATAL_FAILURE(stereoEffect.process(stereoInput.data(), stereoOutput.data()));
@@ -278,12 +140,12 @@
     std::vector<int16_t> stereoRefI16(mTotalFrameCount * FCC_2);
     memcpy_to_i16_from_float(stereoRefI16.data(), stereoOutput.data(), mTotalFrameCount * FCC_2);
 
-    for (size_t chMask : kChMasks) {
+    for (size_t chMask : EffectTestHelper::kChMasks) {
         size_t channelCount = audio_channel_count_from_out_mask(chMask);
-        EffectBundleHelper testEffect(mUuid, chMask, mSampleRate, mFrameCount, mLoopCount);
+        EffectTestHelper testEffect(mUuid, chMask, chMask, mSampleRate, mFrameCount, mLoopCount);
 
         ASSERT_NO_FATAL_FAILURE(testEffect.createEffect());
-        ASSERT_NO_FATAL_FAILURE(testEffect.configEffect());
+        ASSERT_NO_FATAL_FAILURE(testEffect.setConfig());
 
         std::vector<float> testInput(mTotalFrameCount * channelCount);
 
@@ -312,7 +174,8 @@
             // SNR must be above the threshold
             float snr = computeSnr<int16_t>(stereoRefI16.data(), stereoTestI16.data(),
                                             mTotalFrameCount * FCC_2);
-            ASSERT_GT(snr, kSNRThreshold) << "SNR " << snr << "is lower than " << kSNRThreshold;
+            ASSERT_GT(snr, EffectTestHelper::kSNRThreshold)
+                    << "SNR " << snr << "is lower than " << EffectTestHelper::kSNRThreshold;
         } else {
             ASSERT_EQ(0,
                       memcmp(stereoRefI16.data(), stereoTestI16.data(), mTotalFrameCount * FCC_2))
@@ -321,11 +184,12 @@
     }
 }
 
-INSTANTIATE_TEST_SUITE_P(EffectBundleTestAll, SingleEffectComparisonTest,
-                         ::testing::Combine(::testing::Range(0, (int)kNumSampleRates),
-                                            ::testing::Range(0, (int)kNumFrameCounts),
-                                            ::testing::Range(0, (int)kNumLoopCounts),
-                                            ::testing::Range(0, (int)kNumEffectUuids)));
+INSTANTIATE_TEST_SUITE_P(
+        EffectBundleTestAll, SingleEffectComparisonTest,
+        ::testing::Combine(::testing::Range(0, (int)EffectTestHelper::kNumSampleRates),
+                           ::testing::Range(0, (int)EffectTestHelper::kNumFrameCounts),
+                           ::testing::Range(0, (int)EffectTestHelper::kNumLoopCounts),
+                           ::testing::Range(0, (int)kNumEffectUuids)));
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
diff --git a/media/libeffects/lvm/tests/EffectReverbTest.cpp b/media/libeffects/lvm/tests/EffectReverbTest.cpp
new file mode 100644
index 0000000..59453eb
--- /dev/null
+++ b/media/libeffects/lvm/tests/EffectReverbTest.cpp
@@ -0,0 +1,239 @@
+/*
+ * Copyright 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.
+ */
+
+#include <audio_effects/effect_presetreverb.h>
+#include <VectorArithmetic.h>
+
+#include "EffectTestHelper.h"
+using namespace android;
+
+constexpr effect_uuid_t kEffectUuids[] = {
+        // NXP SW insert environmental reverb
+        {0xc7a511a0, 0xa3bb, 0x11df, 0x860e, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        // NXP SW insert preset reverb
+        {0x172cdf00, 0xa3bc, 0x11df, 0xa72f, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        // NXP SW auxiliary environmental reverb
+        {0x4a387fc0, 0x8ab3, 0x11df, 0x8bad, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        // NXP SW auxiliary preset reverb
+        {0xf29a1400, 0xa3bb, 0x11df, 0x8ddc, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+};
+
+constexpr size_t kNumEffectUuids = std::size(kEffectUuids);
+
+static bool isAuxMode(const effect_uuid_t* uuid) {
+    // Update this, if the order of effects in kEffectUuids is updated
+    return (uuid == &kEffectUuids[2] || uuid == &kEffectUuids[3]);
+}
+
+constexpr int kPresets[] = {
+        REVERB_PRESET_NONE,      REVERB_PRESET_SMALLROOM,  REVERB_PRESET_MEDIUMROOM,
+        REVERB_PRESET_LARGEROOM, REVERB_PRESET_MEDIUMHALL, REVERB_PRESET_LARGEHALL,
+        REVERB_PRESET_PLATE,
+};
+
+constexpr size_t kNumPresets = std::size(kPresets);
+
+typedef std::tuple<int, int, int, int, int, int> SingleEffectTestParam;
+class SingleEffectTest : public ::testing::TestWithParam<SingleEffectTestParam> {
+  public:
+    SingleEffectTest()
+        : mSampleRate(EffectTestHelper::kSampleRates[std::get<1>(GetParam())]),
+          mFrameCount(EffectTestHelper::kFrameCounts[std::get<2>(GetParam())]),
+          mLoopCount(EffectTestHelper::kLoopCounts[std::get<3>(GetParam())]),
+          mTotalFrameCount(mFrameCount * mLoopCount),
+          mUuid(&kEffectUuids[std::get<4>(GetParam())]),
+          mInChMask(isAuxMode(mUuid) ? AUDIO_CHANNEL_OUT_MONO
+                                     : EffectTestHelper::kChMasks[std::get<0>(GetParam())]),
+          mInChannelCount(audio_channel_count_from_out_mask(mInChMask)),
+          mOutChMask(EffectTestHelper::kChMasks[std::get<0>(GetParam())]),
+          mOutChannelCount(audio_channel_count_from_out_mask(mOutChMask)),
+          mPreset(kPresets[std::get<5>(GetParam())]) {}
+
+    const size_t mSampleRate;
+    const size_t mFrameCount;
+    const size_t mLoopCount;
+    const size_t mTotalFrameCount;
+    const effect_uuid_t* mUuid;
+    const size_t mInChMask;
+    const size_t mInChannelCount;
+    const size_t mOutChMask;
+    const size_t mOutChannelCount;
+    const size_t mPreset;
+};
+
+// Tests applying a single effect
+TEST_P(SingleEffectTest, SimpleProcess) {
+    SCOPED_TRACE(testing::Message() << "outChMask: " << mOutChMask << " sampleRate: " << mSampleRate
+                                    << " frameCount: " << mFrameCount
+                                    << " loopCount: " << mLoopCount << " preset: " << mPreset);
+
+    EffectTestHelper effect(mUuid, mInChMask, mOutChMask, mSampleRate, mFrameCount, mLoopCount);
+
+    ASSERT_NO_FATAL_FAILURE(effect.createEffect());
+    ASSERT_NO_FATAL_FAILURE(effect.setConfig());
+    ASSERT_NO_FATAL_FAILURE(effect.setParam(REVERB_PARAM_PRESET, mPreset));
+
+    // Initialize input buffer with deterministic pseudo-random values
+    std::vector<float> input(mTotalFrameCount * mInChannelCount);
+    std::vector<float> output(mTotalFrameCount * mOutChannelCount);
+    std::minstd_rand gen(mOutChMask);
+    std::uniform_real_distribution<> dis(-1.0f, 1.0f);
+    for (auto& in : input) {
+        in = dis(gen);
+    }
+    ASSERT_NO_FATAL_FAILURE(effect.process(input.data(), output.data()));
+    ASSERT_NO_FATAL_FAILURE(effect.releaseEffect());
+}
+
+INSTANTIATE_TEST_SUITE_P(
+        EffectReverbTestAll, SingleEffectTest,
+        ::testing::Combine(::testing::Range(0, (int)EffectTestHelper::kNumChMasks),
+                           ::testing::Range(0, (int)EffectTestHelper::kNumSampleRates),
+                           ::testing::Range(0, (int)EffectTestHelper::kNumFrameCounts),
+                           ::testing::Range(0, (int)EffectTestHelper::kNumLoopCounts),
+                           ::testing::Range(0, (int)kNumEffectUuids),
+                           ::testing::Range(0, (int)kNumPresets)));
+
+typedef std::tuple<int, int, int, int, int> SingleEffectComparisonTestParam;
+class SingleEffectComparisonTest
+    : public ::testing::TestWithParam<SingleEffectComparisonTestParam> {
+  public:
+    SingleEffectComparisonTest()
+        : mSampleRate(EffectTestHelper::kSampleRates[std::get<0>(GetParam())]),
+          mFrameCount(EffectTestHelper::kFrameCounts[std::get<1>(GetParam())]),
+          mLoopCount(EffectTestHelper::kLoopCounts[std::get<2>(GetParam())]),
+          mTotalFrameCount(mFrameCount * mLoopCount),
+          mUuid(&kEffectUuids[std::get<3>(GetParam())]),
+          mPreset(kPresets[std::get<4>(GetParam())]) {}
+
+    const size_t mSampleRate;
+    const size_t mFrameCount;
+    const size_t mLoopCount;
+    const size_t mTotalFrameCount;
+    const effect_uuid_t* mUuid;
+    const size_t mPreset;
+};
+
+// Compares first two channels in multi-channel output to stereo output when same effect is applied
+TEST_P(SingleEffectComparisonTest, SimpleProcess) {
+    SCOPED_TRACE(testing::Message()
+                 << " sampleRate: " << mSampleRate << " frameCount: " << mFrameCount
+                 << " loopCount: " << mLoopCount << " preset: " << mPreset);
+
+    // Initialize mono input buffer with deterministic pseudo-random values
+    std::vector<float> monoInput(mTotalFrameCount);
+
+    std::minstd_rand gen(mSampleRate);
+    std::uniform_real_distribution<> dis(-1.0f, 1.0f);
+    for (auto& in : monoInput) {
+        in = dis(gen);
+    }
+
+    // Generate stereo by repeating mono channel data
+    std::vector<float> stereoInput(mTotalFrameCount * FCC_2);
+    adjust_channels(monoInput.data(), FCC_1, stereoInput.data(), FCC_2, sizeof(float),
+                    mTotalFrameCount * sizeof(float) * FCC_1);
+
+    // Apply effect on stereo channels
+    EffectTestHelper stereoEffect(
+            mUuid, isAuxMode(mUuid) ? AUDIO_CHANNEL_OUT_MONO : AUDIO_CHANNEL_OUT_STEREO,
+            AUDIO_CHANNEL_OUT_STEREO, mSampleRate, mFrameCount, mLoopCount);
+
+    ASSERT_NO_FATAL_FAILURE(stereoEffect.createEffect());
+    ASSERT_NO_FATAL_FAILURE(stereoEffect.setConfig());
+    ASSERT_NO_FATAL_FAILURE(stereoEffect.setParam(REVERB_PARAM_PRESET, mPreset));
+
+    std::vector<float> stereoOutput(mTotalFrameCount * FCC_2);
+    ASSERT_NO_FATAL_FAILURE(stereoEffect.process(
+            (isAuxMode(mUuid) ? monoInput.data() : stereoInput.data()), stereoOutput.data()));
+    ASSERT_NO_FATAL_FAILURE(stereoEffect.releaseEffect());
+
+    // Average of both channels data is stored for mono comparison
+    std::vector<float> monoOutput(mTotalFrameCount);
+    From2iToMono_Float((const float*)stereoOutput.data(), monoOutput.data(), mTotalFrameCount);
+
+    // Convert stereo float data to stereo int16_t to be used as reference
+    std::vector<int16_t> stereoRefI16(mTotalFrameCount * FCC_2);
+    memcpy_to_i16_from_float(stereoRefI16.data(), stereoOutput.data(), mTotalFrameCount * FCC_2);
+
+    // mono int16_t to be used as refernece for mono comparison
+    std::vector<int16_t> monoRefI16(mTotalFrameCount);
+    memcpy_to_i16_from_float(monoRefI16.data(), monoOutput.data(), mTotalFrameCount);
+
+    for (size_t outChMask : EffectTestHelper::kChMasks) {
+        size_t outChannelCount = audio_channel_count_from_out_mask(outChMask);
+        size_t inChMask = isAuxMode(mUuid) ? AUDIO_CHANNEL_OUT_MONO : outChMask;
+
+        EffectTestHelper testEffect(mUuid, inChMask, outChMask, mSampleRate, mFrameCount,
+                                    mLoopCount);
+
+        ASSERT_NO_FATAL_FAILURE(testEffect.createEffect());
+        ASSERT_NO_FATAL_FAILURE(testEffect.setConfig());
+        ASSERT_NO_FATAL_FAILURE(testEffect.setParam(REVERB_PARAM_PRESET, mPreset));
+
+        std::vector<float> testInput(mTotalFrameCount * outChannelCount);
+
+        // Repeat mono channel data to all the channels
+        // adjust_channels() zero fills channels > 2, hence can't be used here
+        for (size_t i = 0; i < mTotalFrameCount; ++i) {
+            auto* fp = &testInput[i * outChannelCount];
+            std::fill(fp, fp + outChannelCount, monoInput[i]);
+        }
+
+        std::vector<float> testOutput(mTotalFrameCount * outChannelCount);
+        ASSERT_NO_FATAL_FAILURE(testEffect.process(
+                (isAuxMode(mUuid) ? monoInput.data() : testInput.data()), testOutput.data()));
+        ASSERT_NO_FATAL_FAILURE(testEffect.releaseEffect());
+
+        if (outChannelCount == FCC_1) {
+            // Convert the test data to int16_t
+            std::vector<int16_t> monoTestI16(mTotalFrameCount);
+            memcpy_to_i16_from_float(monoTestI16.data(), testOutput.data(), mTotalFrameCount);
+
+            ASSERT_EQ(0, memcmp(monoRefI16.data(), monoTestI16.data(), mTotalFrameCount * FCC_2))
+                    << "Mono channel do not match with reference output \n";
+        } else {
+            // Extract first two channels
+            std::vector<float> stereoTestOutput(mTotalFrameCount * FCC_2);
+            adjust_channels(testOutput.data(), outChannelCount, stereoTestOutput.data(), FCC_2,
+                            sizeof(float), mTotalFrameCount * sizeof(float) * outChannelCount);
+
+            // Convert the test data to int16_t
+            std::vector<int16_t> stereoTestI16(mTotalFrameCount * FCC_2);
+            memcpy_to_i16_from_float(stereoTestI16.data(), stereoTestOutput.data(),
+                                     mTotalFrameCount * FCC_2);
+
+            ASSERT_EQ(0,
+                      memcmp(stereoRefI16.data(), stereoTestI16.data(), mTotalFrameCount * FCC_2))
+                    << "First two channels do not match with stereo output \n";
+        }
+    }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+        EffectReverbTestAll, SingleEffectComparisonTest,
+        ::testing::Combine(::testing::Range(0, (int)EffectTestHelper::kNumSampleRates),
+                           ::testing::Range(0, (int)EffectTestHelper::kNumFrameCounts),
+                           ::testing::Range(0, (int)EffectTestHelper::kNumLoopCounts),
+                           ::testing::Range(0, (int)kNumEffectUuids),
+                           ::testing::Range(0, (int)kNumPresets)));
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    int status = RUN_ALL_TESTS();
+    ALOGV("Test result = %d\n", status);
+    return status;
+}
diff --git a/media/libeffects/lvm/tests/EffectTestHelper.cpp b/media/libeffects/lvm/tests/EffectTestHelper.cpp
new file mode 100644
index 0000000..625c15a
--- /dev/null
+++ b/media/libeffects/lvm/tests/EffectTestHelper.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright 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.
+ */
+
+#include "EffectTestHelper.h"
+extern audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM;
+
+namespace android {
+
+void EffectTestHelper::createEffect() {
+    int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.create_effect(mUuid, 1, 1, &mEffectHandle);
+    ASSERT_EQ(status, 0) << "create_effect returned an error " << status;
+}
+
+void EffectTestHelper::releaseEffect() {
+    int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.release_effect(mEffectHandle);
+    ASSERT_EQ(status, 0) << "release_effect returned an error " << status;
+}
+
+void EffectTestHelper::setConfig() {
+    effect_config_t config{};
+    config.inputCfg.samplingRate = config.outputCfg.samplingRate = mSampleRate;
+    config.inputCfg.channels = mInChMask;
+    config.outputCfg.channels = mOutChMask;
+    config.inputCfg.format = config.outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
+
+    int reply = 0;
+    uint32_t replySize = sizeof(reply);
+    int status = (*mEffectHandle)
+                         ->command(mEffectHandle, EFFECT_CMD_SET_CONFIG, sizeof(effect_config_t),
+                                   &config, &replySize, &reply);
+    ASSERT_EQ(status, 0) << "set_config returned an error " << status;
+    ASSERT_EQ(reply, 0) << "set_config reply non zero " << reply;
+
+    status = (*mEffectHandle)
+                     ->command(mEffectHandle, EFFECT_CMD_ENABLE, 0, nullptr, &replySize, &reply);
+    ASSERT_EQ(status, 0) << "cmd_enable returned an error " << status;
+    ASSERT_EQ(reply, 0) << "cmd_enable reply non zero " << reply;
+}
+
+void EffectTestHelper::setParam(uint32_t type, uint32_t value) {
+    int reply = 0;
+    uint32_t replySize = sizeof(reply);
+    uint32_t paramData[2] = {type, value};
+    auto effectParam = new effect_param_t[sizeof(effect_param_t) + sizeof(paramData)];
+    memcpy(&effectParam->data[0], &paramData[0], sizeof(paramData));
+    effectParam->psize = sizeof(paramData[0]);
+    effectParam->vsize = sizeof(paramData[1]);
+    int status = (*mEffectHandle)
+                         ->command(mEffectHandle, EFFECT_CMD_SET_PARAM,
+                                   sizeof(effect_param_t) + sizeof(paramData), effectParam,
+                                   &replySize, &reply);
+    delete[] effectParam;
+    ASSERT_EQ(status, 0) << "set_param returned an error " << status;
+    ASSERT_EQ(reply, 0) << "set_param reply non zero " << reply;
+}
+
+void EffectTestHelper::process(float* input, float* output) {
+    audio_buffer_t inBuffer = {.frameCount = mFrameCount, .f32 = input};
+    audio_buffer_t outBuffer = {.frameCount = mFrameCount, .f32 = output};
+    for (size_t i = 0; i < mLoopCount; i++) {
+        int status = (*mEffectHandle)->process(mEffectHandle, &inBuffer, &outBuffer);
+        ASSERT_EQ(status, 0) << "process returned an error " << status;
+
+        inBuffer.f32 += mFrameCount * mInChannelCount;
+        outBuffer.f32 += mFrameCount * mOutChannelCount;
+    }
+}
+}  // namespace android
diff --git a/media/libeffects/lvm/tests/EffectTestHelper.h b/media/libeffects/lvm/tests/EffectTestHelper.h
new file mode 100644
index 0000000..3854d46
--- /dev/null
+++ b/media/libeffects/lvm/tests/EffectTestHelper.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <array>
+#include <audio_utils/channels.h>
+#include <audio_utils/primitives.h>
+#include <climits>
+#include <cstdlib>
+#include <gtest/gtest.h>
+#include <hardware/audio_effect.h>
+#include <log/log.h>
+#include <random>
+#include <stdint.h>
+#include <system/audio.h>
+#include <vector>
+
+namespace android {
+template <typename T>
+static float computeSnr(const T* ref, const T* tst, size_t count) {
+    double signal{};
+    double noise{};
+
+    for (size_t i = 0; i < count; ++i) {
+        const double value(ref[i]);
+        const double diff(tst[i] - value);
+        signal += value * value;
+        noise += diff * diff;
+    }
+    // Initialized to large value to handle
+    // cases where ref and tst match exactly
+    float snr = FLT_MAX;
+    if (signal > 0.0f && noise > 0.0f) {
+        snr = 10.f * log(signal / noise);
+    }
+    return snr;
+}
+
+class EffectTestHelper {
+  public:
+    EffectTestHelper(const effect_uuid_t* uuid, size_t inChMask, size_t outChMask,
+                     size_t sampleRate, size_t frameCount, size_t loopCount)
+        : mUuid(uuid),
+          mInChMask(inChMask),
+          mInChannelCount(audio_channel_count_from_out_mask(mInChMask)),
+          mOutChMask(outChMask),
+          mOutChannelCount(audio_channel_count_from_out_mask(mOutChMask)),
+          mSampleRate(sampleRate),
+          mFrameCount(frameCount),
+          mLoopCount(loopCount) {}
+    void createEffect();
+    void releaseEffect();
+    void setConfig();
+    void setParam(uint32_t type, uint32_t val);
+    void process(float* input, float* output);
+
+    // Corresponds to SNR for 1 bit difference between two int16_t signals
+    static constexpr float kSNRThreshold = 90.308998;
+
+    static constexpr audio_channel_mask_t kChMasks[] = {
+            AUDIO_CHANNEL_OUT_MONO,          AUDIO_CHANNEL_OUT_STEREO,
+            AUDIO_CHANNEL_OUT_2POINT1,       AUDIO_CHANNEL_OUT_2POINT0POINT2,
+            AUDIO_CHANNEL_OUT_QUAD,          AUDIO_CHANNEL_OUT_QUAD_BACK,
+            AUDIO_CHANNEL_OUT_QUAD_SIDE,     AUDIO_CHANNEL_OUT_SURROUND,
+            AUDIO_CHANNEL_INDEX_MASK_4,      AUDIO_CHANNEL_OUT_2POINT1POINT2,
+            AUDIO_CHANNEL_OUT_3POINT0POINT2, AUDIO_CHANNEL_OUT_PENTA,
+            AUDIO_CHANNEL_INDEX_MASK_5,      AUDIO_CHANNEL_OUT_3POINT1POINT2,
+            AUDIO_CHANNEL_OUT_5POINT1,       AUDIO_CHANNEL_OUT_5POINT1_BACK,
+            AUDIO_CHANNEL_OUT_5POINT1_SIDE,  AUDIO_CHANNEL_INDEX_MASK_6,
+            AUDIO_CHANNEL_OUT_6POINT1,       AUDIO_CHANNEL_INDEX_MASK_7,
+            AUDIO_CHANNEL_OUT_5POINT1POINT2, AUDIO_CHANNEL_OUT_7POINT1,
+            AUDIO_CHANNEL_INDEX_MASK_8,      AUDIO_CHANNEL_INDEX_MASK_9,
+            AUDIO_CHANNEL_INDEX_MASK_10,     AUDIO_CHANNEL_INDEX_MASK_11,
+            AUDIO_CHANNEL_INDEX_MASK_12,     AUDIO_CHANNEL_INDEX_MASK_13,
+            AUDIO_CHANNEL_INDEX_MASK_14,     AUDIO_CHANNEL_INDEX_MASK_15,
+            AUDIO_CHANNEL_INDEX_MASK_16,     AUDIO_CHANNEL_INDEX_MASK_17,
+            AUDIO_CHANNEL_INDEX_MASK_18,     AUDIO_CHANNEL_INDEX_MASK_19,
+            AUDIO_CHANNEL_INDEX_MASK_20,     AUDIO_CHANNEL_INDEX_MASK_21,
+            AUDIO_CHANNEL_INDEX_MASK_22,     AUDIO_CHANNEL_INDEX_MASK_23,
+            AUDIO_CHANNEL_INDEX_MASK_24,
+    };
+
+    static constexpr size_t kNumChMasks = std::size(kChMasks);
+
+    static constexpr size_t kSampleRates[] = {8000,  11025, 12000, 16000, 22050,  24000, 32000,
+                                              44100, 48000, 88200, 96000, 176400, 192000};
+
+    static constexpr size_t kNumSampleRates = std::size(kSampleRates);
+
+    static constexpr size_t kFrameCounts[] = {4, 2048};
+
+    static constexpr size_t kNumFrameCounts = std::size(kFrameCounts);
+
+    static constexpr size_t kLoopCounts[] = {1, 4};
+
+    static constexpr size_t kNumLoopCounts = std::size(kLoopCounts);
+
+  private:
+    const effect_uuid_t* mUuid;
+    const size_t mInChMask;
+    const size_t mInChannelCount;
+    const size_t mOutChMask;
+    const size_t mOutChannelCount;
+    const size_t mSampleRate;
+    const size_t mFrameCount;
+    const size_t mLoopCount;
+    effect_handle_t mEffectHandle{};
+};
+}  // namespace android
diff --git a/media/libeffects/lvm/tests/reverb_test.cpp b/media/libeffects/lvm/tests/reverb_test.cpp
index cecc975..dfb6970 100644
--- a/media/libeffects/lvm/tests/reverb_test.cpp
+++ b/media/libeffects/lvm/tests/reverb_test.cpp
@@ -212,7 +212,9 @@
         printUsage();
         return EXIT_FAILURE;
     }
-
+    for (int i = 1; i < argc; i++) {
+        printf("%s ", argv[i]);
+    }
     reverbConfigParams_t revConfigParams{};  // default initialize
     const char* inputFile = nullptr;
     const char* outputFile = nullptr;
@@ -312,9 +314,6 @@
     config.inputCfg.samplingRate = config.outputCfg.samplingRate = revConfigParams.sampleRate;
     config.inputCfg.channels = config.outputCfg.channels = revConfigParams.chMask;
     config.inputCfg.format = config.outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
-    if (AUDIO_CHANNEL_OUT_MONO == revConfigParams.chMask) {
-        config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
-    }
     if (int status = reverbCreateEffect(&effectHandle, &config, sessionId, ioId,
                                         revConfigParams.auxiliary);
         status != 0) {
@@ -346,19 +345,11 @@
     const int ioChannelCount = revConfigParams.fChannels;
     const int ioFrameSize = ioChannelCount * sizeof(short);
     const int maxChannelCount = std::max(channelCount, ioChannelCount);
-    /*
-     * Mono input will be converted to 2 channels internally in the process call
-     * by copying the same data into the second channel.
-     * Hence when channelCount is 1, output buffer should be allocated for
-     * 2 channels. The outChannelCount takes care of allocation of sufficient
-     * memory for the output buffer.
-     */
-    const int outChannelCount = (channelCount == 1 ? 2 : channelCount);
 
     std::vector<short> in(frameLength * maxChannelCount);
-    std::vector<short> out(frameLength * outChannelCount);
+    std::vector<short> out(frameLength * maxChannelCount);
     std::vector<float> floatIn(frameLength * channelCount);
-    std::vector<float> floatOut(frameLength * outChannelCount);
+    std::vector<float> floatOut(frameLength * channelCount);
 
     int frameCounter = 0;
 
@@ -392,11 +383,11 @@
 #else
         memcpy(floatOut.data(), floatIn.data(), frameLength * frameSize);
 #endif
-        memcpy_to_i16_from_float(out.data(), floatOut.data(), frameLength * outChannelCount);
+        memcpy_to_i16_from_float(out.data(), floatOut.data(), frameLength * channelCount);
 
-        if (ioChannelCount != outChannelCount) {
-            adjust_channels(out.data(), outChannelCount, out.data(), ioChannelCount, sizeof(short),
-                            frameLength * outChannelCount * sizeof(short));
+        if (ioChannelCount != channelCount) {
+            adjust_channels(out.data(), channelCount, out.data(), ioChannelCount, sizeof(short),
+                            frameLength * channelCount * sizeof(short));
         }
         (void)fwrite(out.data(), ioFrameSize, frameLength, outputFp.get());
         frameCounter += frameLength;
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
index 4489e81..290a7b1 100644
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -33,6 +33,7 @@
 #include "EffectReverb.h"
 // from Reverb/lib
 #include "LVREV.h"
+#include "VectorArithmetic.h"
 
 // effect_handle_t interface implementation for reverb
 extern "C" const struct effect_interface_s gReverbInterface;
@@ -332,6 +333,7 @@
 //----------------------------------------------------------------------------
 int process(effect_buffer_t* pIn, effect_buffer_t* pOut, int frameCount, ReverbContext* pContext) {
     int channels = audio_channel_count_from_out_mask(pContext->config.inputCfg.channels);
+    int outChannels = audio_channel_count_from_out_mask(pContext->config.outputCfg.channels);
     LVREV_ReturnStatus_en LvmStatus = LVREV_SUCCESS; /* Function call status */
 
     // Reverb only effects the stereo channels in multichannel source.
@@ -454,33 +456,49 @@
         }
     }
 
-    if (channels > 2) {
+    if (outChannels > 2) {
         // Accumulate if required
         if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
             for (int i = 0; i < frameCount; i++) {
-                pOut[channels * i] += pContext->OutFrames[FCC_2 * i];
-                pOut[channels * i + 1] += pContext->OutFrames[FCC_2 * i + 1];
+                pOut[outChannels * i] += pContext->OutFrames[FCC_2 * i];
+                pOut[outChannels * i + 1] += pContext->OutFrames[FCC_2 * i + 1];
             }
         } else {
             for (int i = 0; i < frameCount; i++) {
-                pOut[channels * i] = pContext->OutFrames[FCC_2 * i];
-                pOut[channels * i + 1] = pContext->OutFrames[FCC_2 * i + 1];
+                pOut[outChannels * i] = pContext->OutFrames[FCC_2 * i];
+                pOut[outChannels * i + 1] = pContext->OutFrames[FCC_2 * i + 1];
             }
         }
-        for (int i = 0; i < frameCount; i++) {
-            for (int j = FCC_2; j < channels; j++) {
-                pOut[channels * i + j] = pIn[channels * i + j];
+        if (!pContext->auxiliary) {
+            for (int i = 0; i < frameCount; i++) {
+                // channels and outChannels are expected to be same.
+                for (int j = FCC_2; j < outChannels; j++) {
+                    pOut[outChannels * i + j] = pIn[outChannels * i + j];
+                }
             }
         }
     } else {
         if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
-            for (int i = 0; i < frameCount * FCC_2; i++) {
-                pOut[i] += pContext->OutFrames[i];
+            if (outChannels == FCC_1) {
+                for (int i = 0; i < frameCount; i++) {
+                    pOut[i] +=
+                            ((pContext->OutFrames[i * FCC_2] + pContext->OutFrames[i * FCC_2 + 1]) *
+                             0.5f);
+                }
+            } else {
+                for (int i = 0; i < frameCount * FCC_2; i++) {
+                    pOut[i] += pContext->OutFrames[i];
+                }
             }
         } else {
-            memcpy(pOut, pContext->OutFrames, frameCount * sizeof(*pOut) * FCC_2);
+            if (outChannels == FCC_1) {
+                From2iToMono_Float((const process_buffer_t*)pContext->OutFrames, pOut, frameCount);
+            } else {
+                memcpy(pOut, pContext->OutFrames, frameCount * sizeof(*pOut) * FCC_2);
+            }
         }
     }
+
     return 0;
 } /* end process */
 
@@ -498,25 +516,6 @@
 
 void Reverb_free(ReverbContext* pContext) {
     LVREV_ReturnStatus_en LvmStatus = LVREV_SUCCESS; /* Function call status */
-    LVREV_MemoryTable_st MemTab;
-
-    /* Free the algorithm memory */
-    LvmStatus = LVREV_GetMemoryTable(pContext->hInstance, &MemTab, LVM_NULL);
-
-    LVM_ERROR_CHECK(LvmStatus, "LVM_GetMemoryTable", "Reverb_free")
-
-    for (int i = 0; i < LVM_NR_MEMORY_REGIONS; i++) {
-        if (MemTab.Region[i].Size != 0) {
-            if (MemTab.Region[i].pBaseAddress != NULL) {
-                free(MemTab.Region[i].pBaseAddress);
-            } else {
-                ALOGV("\tLVM_ERROR : free() - trying to free with NULL pointer %" PRIu32
-                      " bytes "
-                      "for region %u at %p ERROR\n",
-                      MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress);
-            }
-        }
-    }
 
     LvmStatus = LVREV_FreeInstance(pContext->hInstance);
     LVM_ERROR_CHECK(LvmStatus, "LVREV_FreeInstance", "Reverb_free")
@@ -549,7 +548,7 @@
     CHECK_ARG((pContext->auxiliary && pConfig->inputCfg.channels == AUDIO_CHANNEL_OUT_MONO) ||
               ((!pContext->auxiliary) && (inputChannels <= LVM_MAX_CHANNELS)));
     int outputChannels = audio_channel_count_from_out_mask(pConfig->outputCfg.channels);
-    CHECK_ARG(outputChannels >= FCC_2 && outputChannels <= LVM_MAX_CHANNELS);
+    CHECK_ARG(outputChannels <= LVM_MAX_CHANNELS);
     CHECK_ARG(pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_WRITE ||
               pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
     CHECK_ARG(pConfig->inputCfg.format == EFFECT_BUFFER_FORMAT);
@@ -659,65 +658,17 @@
     LVREV_ReturnStatus_en LvmStatus = LVREV_SUCCESS; /* Function call status */
     LVREV_ControlParams_st params;                   /* Control Parameters */
     LVREV_InstanceParams_st InstParams;              /* Instance parameters */
-    LVREV_MemoryTable_st MemTab;                     /* Memory allocation table */
-    bool bMallocFailure = LVM_FALSE;
 
     /* Set the capabilities */
     InstParams.MaxBlockSize = MAX_CALL_SIZE;
     InstParams.SourceFormat = LVM_STEREO;  // Max format, could be mono during process
     InstParams.NumDelays = LVREV_DELAYLINES_4;
 
-    /* Allocate memory, forcing alignment */
-    LvmStatus = LVREV_GetMemoryTable(LVM_NULL, &MemTab, &InstParams);
-
-    LVM_ERROR_CHECK(LvmStatus, "LVREV_GetMemoryTable", "Reverb_init")
-    if (LvmStatus != LVREV_SUCCESS) return -EINVAL;
-
-    ALOGV("\tCreateInstance Successfully called LVM_GetMemoryTable\n");
-
-    /* Allocate memory */
-    for (int i = 0; i < LVM_NR_MEMORY_REGIONS; i++) {
-        if (MemTab.Region[i].Size != 0) {
-            MemTab.Region[i].pBaseAddress = calloc(1, MemTab.Region[i].Size);
-
-            if (MemTab.Region[i].pBaseAddress == LVM_NULL) {
-                ALOGV("\tLVREV_ERROR :Reverb_init CreateInstance Failed to allocate %" PRIu32
-                      " bytes for region %u\n",
-                      MemTab.Region[i].Size, i);
-                bMallocFailure = LVM_TRUE;
-            } else {
-                ALOGV("\tReverb_init CreateInstance allocate %" PRIu32
-                      " bytes for region %u at %p\n",
-                      MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress);
-            }
-        }
-    }
-
-    /* If one or more of the memory regions failed to allocate, free the regions that were
-     * succesfully allocated and return with an error
-     */
-    if (bMallocFailure == LVM_TRUE) {
-        for (int i = 0; i < LVM_NR_MEMORY_REGIONS; i++) {
-            if (MemTab.Region[i].pBaseAddress == LVM_NULL) {
-                ALOGV("\tLVM_ERROR :Reverb_init CreateInstance Failed to allocate %" PRIu32
-                      " bytes for region %u - Not freeing\n",
-                      MemTab.Region[i].Size, i);
-            } else {
-                ALOGV("\tLVM_ERROR :Reverb_init CreateInstance Failed: but allocated %" PRIu32
-                      " bytes for region %u at %p- free\n",
-                      MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress);
-                free(MemTab.Region[i].pBaseAddress);
-            }
-        }
-        return -EINVAL;
-    }
-    ALOGV("\tReverb_init CreateInstance Successfully malloc'd memory\n");
-
     /* Initialise */
     pContext->hInstance = LVM_NULL;
 
     /* Init sets the instance handle */
-    LvmStatus = LVREV_GetInstanceHandle(&pContext->hInstance, &MemTab, &InstParams);
+    LvmStatus = LVREV_GetInstanceHandle(&pContext->hInstance, &InstParams);
 
     LVM_ERROR_CHECK(LvmStatus, "LVM_GetInstanceHandle", "Reverb_init")
     if (LvmStatus != LVREV_SUCCESS) return -EINVAL;
diff --git a/media/libeffects/preprocessing/Android.bp b/media/libeffects/preprocessing/Android.bp
index eb3ce34..87ed8b6 100644
--- a/media/libeffects/preprocessing/Android.bp
+++ b/media/libeffects/preprocessing/Android.bp
@@ -22,6 +22,7 @@
     name: "libaudiopreprocessing",
     vendor: true,
     relative_install_path: "soundfx",
+    host_supported: true,
     srcs: ["PreProcessing.cpp"],
     local_include_dirs: [
         ".",
@@ -47,4 +48,9 @@
         "libhardware_headers",
         "libwebrtc_absl_headers",
     ],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
 }
diff --git a/media/libeffects/preprocessing/PreProcessing.cpp b/media/libeffects/preprocessing/PreProcessing.cpp
index 03ccc34..3b0b6d6 100644
--- a/media/libeffects/preprocessing/PreProcessing.cpp
+++ b/media/libeffects/preprocessing/PreProcessing.cpp
@@ -670,8 +670,8 @@
     return 0;
 }
 
-int NsGetParameter(preproc_effect_t* effect __unused, void* pParam __unused,
-                   uint32_t* pValueSize __unused, void* pValue __unused) {
+int NsGetParameter(preproc_effect_t* /*effect __unused*/, void* /*pParam __unused*/,
+                   uint32_t* /*pValueSize __unused*/, void* /*pValue __unused*/) {
     int status = 0;
     return status;
 }
@@ -910,7 +910,7 @@
         session->apm = NULL;
         delete session->inBuf;
         session->inBuf = NULL;
-        delete session->outBuf;
+        free(session->outBuf);
         session->outBuf = NULL;
         delete session->revBuf;
         session->revBuf = NULL;
@@ -1551,7 +1551,7 @@
 }
 
 int PreProcessingFx_ProcessReverse(effect_handle_t self, audio_buffer_t* inBuffer,
-                                   audio_buffer_t* outBuffer __unused) {
+                                   audio_buffer_t* outBuffer) {
     preproc_effect_t* effect = (preproc_effect_t*)self;
 
     if (effect == NULL) {
diff --git a/media/libeffects/preprocessing/tests/Android.bp b/media/libeffects/preprocessing/tests/Android.bp
index 8848e79..6413945 100644
--- a/media/libeffects/preprocessing/tests/Android.bp
+++ b/media/libeffects/preprocessing/tests/Android.bp
@@ -13,6 +13,8 @@
 cc_test {
     name: "AudioPreProcessingTest",
     vendor: true,
+    host_supported: true,
+    gtest: false,
     srcs: ["PreProcessingTest.cpp"],
     shared_libs: [
         "libaudioutils",
@@ -27,7 +29,11 @@
         "libaudioeffects",
         "libhardware_headers",
     ],
-    gtest: false,
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
 }
 
 cc_test {
diff --git a/media/libeffects/preprocessing/tests/PreProcessingTest.cpp b/media/libeffects/preprocessing/tests/PreProcessingTest.cpp
index 5f223c9..e0025fe 100644
--- a/media/libeffects/preprocessing/tests/PreProcessingTest.cpp
+++ b/media/libeffects/preprocessing/tests/PreProcessingTest.cpp
@@ -24,6 +24,7 @@
 #include <audio_effects/effect_agc.h>
 #include <audio_effects/effect_agc2.h>
 #include <audio_effects/effect_ns.h>
+#include <audio_utils/channels.h>
 #include <log/log.h>
 
 // This is the only symbol that needs to be imported
@@ -55,7 +56,9 @@
     ARG_NS_LVL,
     ARG_AGC2_GAIN,
     ARG_AGC2_LVL,
-    ARG_AGC2_SAT_MGN
+    ARG_AGC2_SAT_MGN,
+    ARG_FILE_CHANNELS,
+    ARG_MONO_MODE
 };
 
 struct preProcConfigParams_t {
@@ -68,6 +71,8 @@
     float agc2SaturationMargin = 2.f;  // in dB
     int agc2Level = 0;                 // either kRms(0) or kPeak(1)
     int aecDelay = 0;  // in ms
+    int fileChannels = 1;
+    int monoMode = 0;
 };
 
 const effect_uuid_t kPreProcUuids[PREPROC_NUM_EFFECTS] = {
@@ -106,7 +111,7 @@
     printf("\n           Prints this usage information");
     printf("\n     --fs <sampling_freq>");
     printf("\n           Sampling frequency in Hz, default 16000.");
-    printf("\n     -ch_mask <channel_mask>\n");
+    printf("\n     --ch_mask <channel_mask>\n");
     printf("\n         0  - AUDIO_CHANNEL_IN_MONO");
     printf("\n         1  - AUDIO_CHANNEL_IN_STEREO");
     printf("\n         2  - AUDIO_CHANNEL_IN_FRONT_BACK");
@@ -144,6 +149,10 @@
     printf("\n           AGC Adaptive Digital Saturation Margin in dB, default value 2dB");
     printf("\n     --aec_delay <delay>");
     printf("\n           AEC delay value in ms, default value 0ms");
+    printf("\n     --fch <fileChannels>");
+    printf("\n           number of channels in the input file");
+    printf("\n     --mono <Mono Mode>");
+    printf("\n           Mode to make data of all channels the same as first channel");
     printf("\n");
 }
 
@@ -189,10 +198,17 @@
         printUsage();
         return EXIT_FAILURE;
     }
+
+    // Print the arguments passed
+    for (int i = 1; i < argc; i++) {
+        printf("%s ", argv[i]);
+    }
+
     const char* inputFile = nullptr;
     const char* outputFile = nullptr;
     const char* farFile = nullptr;
     int effectEn[PREPROC_NUM_EFFECTS] = {0};
+    struct preProcConfigParams_t preProcCfgParams {};
 
     const option long_opts[] = {
             {"help", no_argument, nullptr, ARG_HELP},
@@ -212,9 +228,10 @@
             {"agc", no_argument, &effectEn[PREPROC_AGC], 1},
             {"agc2", no_argument, &effectEn[PREPROC_AGC2], 1},
             {"ns", no_argument, &effectEn[PREPROC_NS], 1},
+            {"fch", required_argument, nullptr, ARG_FILE_CHANNELS},
+            {"mono", no_argument, &preProcCfgParams.monoMode, 1},
             {nullptr, 0, nullptr, 0},
     };
-    struct preProcConfigParams_t preProcCfgParams {};
 
     while (true) {
         const int opt = getopt_long(argc, (char* const*)argv, "i:o:", long_opts, nullptr);
@@ -279,6 +296,14 @@
                 preProcCfgParams.nsLevel = atoi(optarg);
                 break;
             }
+            case ARG_FILE_CHANNELS: {
+                preProcCfgParams.fileChannels = atoi(optarg);
+                break;
+            }
+            case ARG_MONO_MODE: {
+                preProcCfgParams.monoMode = 1;
+                break;
+            }
             default:
                 break;
         }
@@ -402,16 +427,28 @@
     // Process Call
     const int frameLength = (int)(preProcCfgParams.samplingFreq * kTenMilliSecVal);
     const int ioChannelCount = audio_channel_count_from_in_mask(preProcCfgParams.chMask);
+    const int fileChannelCount = preProcCfgParams.fileChannels;
     const int ioFrameSize = ioChannelCount * sizeof(short);
+    const int inFrameSize = fileChannelCount * sizeof(short);
     int frameCounter = 0;
     while (true) {
         std::vector<short> in(frameLength * ioChannelCount);
         std::vector<short> out(frameLength * ioChannelCount);
         std::vector<short> farIn(frameLength * ioChannelCount);
-        size_t samplesRead = fread(in.data(), ioFrameSize, frameLength, inputFp.get());
+        size_t samplesRead = fread(in.data(), inFrameSize, frameLength, inputFp.get());
         if (samplesRead == 0) {
             break;
         }
+        if (fileChannelCount != ioChannelCount) {
+            adjust_channels(in.data(), fileChannelCount, in.data(), ioChannelCount, sizeof(short),
+                            frameLength * inFrameSize);
+            if (preProcCfgParams.monoMode == 1) {
+                for (int i = 0; i < frameLength; ++i) {
+                    auto* fp = &in[i * ioChannelCount];
+                    std::fill(fp + 1, fp + ioChannelCount, *fp);  // replicate ch 0
+                }
+            }
+        }
         audio_buffer_t inputBuffer, outputBuffer;
         audio_buffer_t farInBuffer{};
         inputBuffer.frameCount = samplesRead;
@@ -420,10 +457,21 @@
         outputBuffer.s16 = out.data();
 
         if (farFp != nullptr) {
-            samplesRead = fread(farIn.data(), ioFrameSize, frameLength, farFp.get());
+            samplesRead = fread(farIn.data(), inFrameSize, frameLength, farFp.get());
             if (samplesRead == 0) {
                 break;
             }
+            if (fileChannelCount != ioChannelCount) {
+                adjust_channels(farIn.data(), fileChannelCount, farIn.data(), ioChannelCount,
+                                sizeof(short), frameLength * inFrameSize);
+                if (preProcCfgParams.monoMode == 1) {
+                    for (int i = 0; i < frameLength; ++i) {
+                        auto* fp = &farIn[i * ioChannelCount];
+                        std::fill(fp + 1, fp + ioChannelCount, *fp);  // replicate ch 0
+                    }
+                }
+            }
+
             farInBuffer.frameCount = samplesRead;
             farInBuffer.s16 = farIn.data();
         }
@@ -458,8 +506,12 @@
             }
         }
         if (outputFp != nullptr) {
+            if (fileChannelCount != ioChannelCount) {
+                adjust_channels(out.data(), ioChannelCount, out.data(), fileChannelCount,
+                                sizeof(short), frameLength * ioFrameSize);
+            }
             size_t samplesWritten =
-                    fwrite(out.data(), ioFrameSize, outputBuffer.frameCount, outputFp.get());
+                    fwrite(out.data(), inFrameSize, outputBuffer.frameCount, outputFp.get());
             if (samplesWritten != outputBuffer.frameCount) {
                 ALOGE("\nError: Output file writing failed");
                 break;
diff --git a/media/libeffects/preprocessing/tests/build_and_run_all_unit_tests.sh b/media/libeffects/preprocessing/tests/build_and_run_all_unit_tests.sh
new file mode 100755
index 0000000..942f2ec
--- /dev/null
+++ b/media/libeffects/preprocessing/tests/build_and_run_all_unit_tests.sh
@@ -0,0 +1,115 @@
+#!/bin/bash
+#
+# Run tests in this directory.
+#
+
+if [ -z "$ANDROID_BUILD_TOP" ]; then
+    echo "Android build environment not set"
+    exit -1
+fi
+
+# ensure we have mm
+. $ANDROID_BUILD_TOP/build/envsetup.sh
+
+mm -j
+
+echo "waiting for device"
+
+adb root && adb wait-for-device remount
+
+# location of test files
+testdir="/data/local/tmp/AudioPreProcessingTest"
+
+echo "========================================"
+echo "testing PreProcessing modules"
+adb shell mkdir -p $testdir
+adb push $ANDROID_BUILD_TOP/frameworks/av/media/libeffects/res/raw/sinesweepraw.raw $testdir
+adb push $OUT/testcases/snr/arm64/snr $testdir
+
+E_VAL=1
+if [ -z "$1" ]
+then
+    cmds=("adb push $OUT/testcases/AudioPreProcessingTest/arm64/AudioPreProcessingTest $testdir"
+          "adb push $OUT/testcases/AudioPreProcessingTest/arm/AudioPreProcessingTest $testdir"
+)
+elif [ "$1" == "32" ]
+then
+    cmds="adb push $OUT/testcases/AudioPreProcessingTest/arm/AudioPreProcessingTest $testdir"
+elif [ "$1" == "64" ]
+then
+    cmds="adb push $OUT/testcases/AudioPreProcessingTest/arm64/AudioPreProcessingTest $testdir"
+else
+    echo ""
+    echo "Invalid \"val\""
+    echo "Usage:"
+    echo "      "$0" [val]"
+    echo "      where, val can be either 32 or 64."
+    echo ""
+    echo "      If val is not specified then both 32 bit and 64 bit binaries"
+    echo "      are tested."
+    exit $E_VAL
+fi
+
+flags_arr=(
+    "--agc --mono"
+    "--ns --mono"
+    "--agc2 --mono"
+    "--aec --mono"
+)
+
+fs_arr=(
+    8000
+    16000
+    24000
+    32000
+    48000
+)
+
+# run multichannel effects at different configs, saving only the mono channel
+error_count=0
+test_count=0
+for cmd in "${cmds[@]}"
+do
+    $cmd
+    for flags in "${flags_arr[@]}"
+    do
+        for fs in ${fs_arr[*]}
+        do
+            for chMask in {0..7}
+            do
+                adb shell $testdir/AudioPreProcessingTest $flags \
+                    --i $testdir/sinesweepraw.raw --far $testdir/sinesweepraw.raw \
+                    --output $testdir/sinesweep_$((chMask))_$((fs)).raw --ch_mask $chMask \
+                    --fs $fs --fch 1
+
+                shell_ret=$?
+                if [ $shell_ret -ne 0 ]; then
+                    echo "error shell_ret here is zero: $shell_ret"
+                    ((++error_count))
+                fi
+
+
+                # single channel files should be identical to higher channel
+                # computation (first channel).
+                if  [[ "$chMask" -gt 1 ]]
+                then
+                    adb shell cmp $testdir/sinesweep_1_$((fs)).raw \
+                        $testdir/sinesweep_$((chMask))_$((fs)).raw
+                fi
+
+                # cmp return EXIT_FAILURE on mismatch.
+                shell_ret=$?
+                if [ $shell_ret -ne 0 ]; then
+                    echo "error: $shell_ret"
+                    ((++error_count))
+                fi
+                ((++test_count))
+            done
+        done
+    done
+done
+
+adb shell rm -r $testdir
+echo "$test_count tests performed"
+echo "$error_count errors"
+exit $error_count
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 0223cfd..9e33610 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -359,6 +359,7 @@
         "libaudioclient",
         "libmedia_codeclist",
         "libmedia_omx",
+        "media_permission-aidl-cpp",
     ],
 
     export_shared_lib_headers: [
@@ -367,14 +368,17 @@
         "libandroidicu",
         //"libsonivox",
         "libmedia_omx",
+        "media_permission-aidl-cpp",
     ],
 
     static_libs: [
         "resourcemanager_aidl_interface-ndk_platform",
+        "media_permission-aidl-cpp",
     ],
 
     export_static_lib_headers: [
         "resourcemanager_aidl_interface-ndk_platform",
+        "media_permission-aidl-cpp",
     ],
 
     export_include_dirs: [
diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp
index 11005c6..0f189ee 100644
--- a/media/libmedia/IMediaPlayerService.cpp
+++ b/media/libmedia/IMediaPlayerService.cpp
@@ -35,6 +35,8 @@
 
 namespace android {
 
+using media::permission::Identity;
+
 enum {
     CREATE = IBinder::FIRST_CALL_TRANSACTION,
     CREATE_MEDIA_RECORDER,
@@ -63,22 +65,22 @@
 
     virtual sp<IMediaPlayer> create(
             const sp<IMediaPlayerClient>& client, audio_session_t audioSessionId,
-            const std::string opPackageName) {
+            const Identity& identity) {
         Parcel data, reply;
         data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
         data.writeStrongBinder(IInterface::asBinder(client));
         data.writeInt32(audioSessionId);
-        data.writeCString(opPackageName.c_str());
+        data.writeParcelable(identity);
 
         remote()->transact(CREATE, data, &reply);
         return interface_cast<IMediaPlayer>(reply.readStrongBinder());
     }
 
-    virtual sp<IMediaRecorder> createMediaRecorder(const String16 &opPackageName)
+    virtual sp<IMediaRecorder> createMediaRecorder(const Identity& identity)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
-        data.writeString16(opPackageName);
+        data.writeParcelable(identity);
         remote()->transact(CREATE_MEDIA_RECORDER, data, &reply);
         return interface_cast<IMediaRecorder>(reply.readStrongBinder());
     }
@@ -129,19 +131,23 @@
             sp<IMediaPlayerClient> client =
                 interface_cast<IMediaPlayerClient>(data.readStrongBinder());
             audio_session_t audioSessionId = (audio_session_t) data.readInt32();
-            const char* opPackageName = data.readCString();
-            if (opPackageName == nullptr) {
-                return FAILED_TRANSACTION;
+            Identity identity;
+            status_t status = data.readParcelable(&identity);
+            if (status != NO_ERROR) {
+                return status;
             }
-            std::string opPackageNameStr(opPackageName);
-            sp<IMediaPlayer> player = create(client, audioSessionId, opPackageNameStr);
+            sp<IMediaPlayer> player = create(client, audioSessionId, identity);
             reply->writeStrongBinder(IInterface::asBinder(player));
             return NO_ERROR;
         } break;
         case CREATE_MEDIA_RECORDER: {
             CHECK_INTERFACE(IMediaPlayerService, data, reply);
-            const String16 opPackageName = data.readString16();
-            sp<IMediaRecorder> recorder = createMediaRecorder(opPackageName);
+            Identity identity;
+            status_t status = data.readParcelable(&identity);
+            if (status != NO_ERROR) {
+                return status;
+            }
+            sp<IMediaRecorder> recorder = createMediaRecorder(identity);
             reply->writeStrongBinder(IInterface::asBinder(recorder));
             return NO_ERROR;
         } break;
diff --git a/media/libmedia/include/media/IMediaPlayerService.h b/media/libmedia/include/media/IMediaPlayerService.h
index a4207eb..243e9c7 100644
--- a/media/libmedia/include/media/IMediaPlayerService.h
+++ b/media/libmedia/include/media/IMediaPlayerService.h
@@ -27,6 +27,7 @@
 
 #include <media/IMediaPlayerClient.h>
 #include <media/IMediaMetadataRetriever.h>
+#include <android/media/permission/Identity.h>
 
 #include <string>
 
@@ -46,11 +47,13 @@
 public:
     DECLARE_META_INTERFACE(MediaPlayerService);
 
-    virtual sp<IMediaRecorder> createMediaRecorder(const String16 &opPackageName) = 0;
+    virtual sp<IMediaRecorder> createMediaRecorder(
+        const android::media::permission::Identity &identity) = 0;
     virtual sp<IMediaMetadataRetriever> createMetadataRetriever() = 0;
     virtual sp<IMediaPlayer> create(const sp<IMediaPlayerClient>& client,
             audio_session_t audioSessionId = AUDIO_SESSION_ALLOCATE,
-            const std::string opPackage = "") = 0;
+            const android::media::permission::Identity& identity =
+                android::media::permission::Identity()) = 0;
     virtual sp<IMediaCodecList> getCodecList() const = 0;
 
     // Connects to a remote display.
diff --git a/media/libmedia/include/media/MediaRecorderBase.h b/media/libmedia/include/media/MediaRecorderBase.h
index d9a7efb..b5325ce 100644
--- a/media/libmedia/include/media/MediaRecorderBase.h
+++ b/media/libmedia/include/media/MediaRecorderBase.h
@@ -21,6 +21,7 @@
 #include <media/AudioSystem.h>
 #include <media/MicrophoneInfo.h>
 #include <media/mediarecorder.h>
+#include <android/media/permission/Identity.h>
 
 #include <system/audio.h>
 
@@ -33,8 +34,8 @@
 struct PersistentSurface;
 
 struct MediaRecorderBase {
-    MediaRecorderBase(const String16 &opPackageName)
-        : mOpPackageName(opPackageName) {}
+    explicit MediaRecorderBase(const media::permission::Identity &client)
+        : mClient(client) {}
     virtual ~MediaRecorderBase() {}
 
     virtual status_t init() = 0;
@@ -83,7 +84,7 @@
 
 protected:
 
-    String16 mOpPackageName;
+    media::permission::Identity mClient;
 
 private:
     MediaRecorderBase(const MediaRecorderBase &);
diff --git a/media/libmedia/include/media/mediaplayer.h b/media/libmedia/include/media/mediaplayer.h
index 71c0bc5..fbba398 100644
--- a/media/libmedia/include/media/mediaplayer.h
+++ b/media/libmedia/include/media/mediaplayer.h
@@ -29,6 +29,7 @@
 #include <media/IMediaPlayer.h>
 #include <media/IMediaDeathNotifier.h>
 #include <media/IStreamSource.h>
+#include <android/media/permission/Identity.h>
 
 #include <utils/KeyedVector.h>
 #include <utils/String8.h>
@@ -211,7 +212,8 @@
                     public virtual IMediaDeathNotifier
 {
 public:
-    MediaPlayer(const std::string opPackageName = "");
+    explicit MediaPlayer(const android::media::permission::Identity& mIdentity =
+        android::media::permission::Identity());
     ~MediaPlayer();
             void            died();
             void            disconnect();
@@ -315,7 +317,7 @@
     float                       mSendLevel;
     struct sockaddr_in          mRetransmitEndpoint;
     bool                        mRetransmitEndpointValid;
-    const std::string           mOpPackageName;
+    const android::media::permission::Identity mIdentity;
 };
 
 }; // namespace android
diff --git a/media/libmedia/include/media/mediarecorder.h b/media/libmedia/include/media/mediarecorder.h
index 84c92f6..96a3293 100644
--- a/media/libmedia/include/media/mediarecorder.h
+++ b/media/libmedia/include/media/mediarecorder.h
@@ -25,6 +25,7 @@
 #include <media/IMediaRecorderClient.h>
 #include <media/IMediaDeathNotifier.h>
 #include <media/MicrophoneInfo.h>
+#include <android/media/permission/Identity.h>
 
 namespace android {
 
@@ -226,7 +227,7 @@
                       public virtual IMediaDeathNotifier
 {
 public:
-    MediaRecorder(const String16& opPackageName);
+    explicit MediaRecorder(const media::permission::Identity& identity);
     ~MediaRecorder();
 
     void        died();
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index 30c5006..7504787 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -40,8 +40,9 @@
 namespace android {
 
 using media::VolumeShaper;
+using media::permission::Identity;
 
-MediaPlayer::MediaPlayer(const std::string opPackageName) : mOpPackageName(opPackageName)
+MediaPlayer::MediaPlayer(const Identity& identity) : mIdentity(identity)
 {
     ALOGV("constructor");
     mListener = NULL;
@@ -152,7 +153,7 @@
     if (url != NULL) {
         const sp<IMediaPlayerService> service(getMediaPlayerService());
         if (service != 0) {
-            sp<IMediaPlayer> player(service->create(this, mAudioSessionId, mOpPackageName));
+            sp<IMediaPlayer> player(service->create(this, mAudioSessionId, mIdentity));
             if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
                 (NO_ERROR != player->setDataSource(httpService, url, headers))) {
                 player.clear();
@@ -169,7 +170,7 @@
     status_t err = UNKNOWN_ERROR;
     const sp<IMediaPlayerService> service(getMediaPlayerService());
     if (service != 0) {
-        sp<IMediaPlayer> player(service->create(this, mAudioSessionId, mOpPackageName));
+        sp<IMediaPlayer> player(service->create(this, mAudioSessionId, mIdentity));
         if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
             (NO_ERROR != player->setDataSource(fd, offset, length))) {
             player.clear();
@@ -185,7 +186,7 @@
     status_t err = UNKNOWN_ERROR;
     const sp<IMediaPlayerService> service(getMediaPlayerService());
     if (service != 0) {
-        sp<IMediaPlayer> player(service->create(this, mAudioSessionId, mOpPackageName));
+        sp<IMediaPlayer> player(service->create(this, mAudioSessionId, mIdentity));
         if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
             (NO_ERROR != player->setDataSource(source))) {
             player.clear();
@@ -201,7 +202,7 @@
     status_t err = UNKNOWN_ERROR;
     const sp<IMediaPlayerService> service(getMediaPlayerService());
     if (service != 0) {
-        sp<IMediaPlayer> player(service->create(this, mAudioSessionId, mOpPackageName));
+        sp<IMediaPlayer> player(service->create(this, mAudioSessionId, mIdentity));
         if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
             (NO_ERROR != player->setDataSource(rtpParams))) {
             player.clear();
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index e3cd9d8..da2b190 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -33,6 +33,8 @@
 
 namespace android {
 
+using media::permission::Identity;
+
 status_t MediaRecorder::setCamera(const sp<hardware::ICamera>& camera,
         const sp<ICameraRecordingProxy>& proxy)
 {
@@ -758,13 +760,13 @@
     return INVALID_OPERATION;
 }
 
-MediaRecorder::MediaRecorder(const String16& opPackageName) : mSurfaceMediaSource(NULL)
+MediaRecorder::MediaRecorder(const Identity &identity) : mSurfaceMediaSource(NULL)
 {
     ALOGV("constructor");
 
     const sp<IMediaPlayerService> service(getMediaPlayerService());
     if (service != NULL) {
-        mMediaRecorder = service->createMediaRecorder(opPackageName);
+        mMediaRecorder = service->createMediaRecorder(identity);
     }
     if (mMediaRecorder != NULL) {
         mCurrentState = MEDIA_RECORDER_IDLE;
diff --git a/media/libmedia/tests/mediaplayer/Android.bp b/media/libmedia/tests/mediaplayer/Android.bp
index 0fff7b4..50f35ea 100644
--- a/media/libmedia/tests/mediaplayer/Android.bp
+++ b/media/libmedia/tests/mediaplayer/Android.bp
@@ -39,6 +39,7 @@
         "libstagefright",
         "libstagefright_foundation",
         "libutils",
+        "media_permission-aidl-cpp",
     ],
     compile_multilib: "first",
     cflags: [
diff --git a/media/libmediaplayerservice/Android.bp b/media/libmediaplayerservice/Android.bp
index 56c8368..d250976 100644
--- a/media/libmediaplayerservice/Android.bp
+++ b/media/libmediaplayerservice/Android.bp
@@ -35,6 +35,8 @@
         "android.hardware.media.c2@1.0",
         "android.hardware.media.omx@1.0",
         "av-types-aidl-cpp",
+        "media_permission-aidl-cpp",
+        "libaudioclient_aidl_conversion",
         "libbase",
         "libandroid_net",
         "libaudioclient",
@@ -73,9 +75,13 @@
         "libstagefright_nuplayer",
         "libstagefright_rtsp",
         "libstagefright_timedtext",
+        "media_permission-aidl-cpp",
     ],
 
-    export_shared_lib_headers: ["libmedia"],
+    export_shared_lib_headers: [
+        "libmedia",
+        "media_permission-aidl-cpp",
+    ],
 
     include_dirs: [
         "frameworks/av/media/libstagefright/rtsp",
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 1d5ccca..dc4aea5 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -49,6 +49,7 @@
 
 #include <codec2/hidl/client.h>
 #include <datasource/HTTPBase.h>
+#include <media/AidlConversion.h>
 #include <media/IMediaHTTPService.h>
 #include <media/IRemoteDisplay.h>
 #include <media/IRemoteDisplayClient.h>
@@ -94,6 +95,7 @@
 using android::NOT_ENOUGH_DATA;
 using android::Parcel;
 using android::media::VolumeShaper;
+using android::media::permission::Identity;
 
 // Max number of entries in the filter.
 const int kMaxFilterSize = 64;  // I pulled that out of thin air.
@@ -453,14 +455,21 @@
     ALOGV("MediaPlayerService destroyed");
 }
 
-sp<IMediaRecorder> MediaPlayerService::createMediaRecorder(const String16 &opPackageName)
+sp<IMediaRecorder> MediaPlayerService::createMediaRecorder(const Identity& identity)
 {
-    pid_t pid = IPCThreadState::self()->getCallingPid();
-    sp<MediaRecorderClient> recorder = new MediaRecorderClient(this, pid, opPackageName);
+    // TODO b/182392769: use identity util
+    Identity verifiedIdentity = identity;
+    verifiedIdentity.uid = VALUE_OR_FATAL(
+      legacy2aidl_uid_t_int32_t(IPCThreadState::self()->getCallingUid()));
+    verifiedIdentity.pid = VALUE_OR_FATAL(
+        legacy2aidl_pid_t_int32_t(IPCThreadState::self()->getCallingPid()));
+    sp<MediaRecorderClient> recorder =
+        new MediaRecorderClient(this, verifiedIdentity);
     wp<MediaRecorderClient> w = recorder;
     Mutex::Autolock lock(mLock);
     mMediaRecorderClients.add(w);
-    ALOGV("Create new media recorder client from pid %d", pid);
+    ALOGV("Create new media recorder client from pid %s",
+        verifiedIdentity.toString().c_str());
     return recorder;
 }
 
@@ -480,17 +489,21 @@
 }
 
 sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client,
-        audio_session_t audioSessionId, std::string opPackageName)
+        audio_session_t audioSessionId, const Identity& identity)
 {
-    pid_t pid = IPCThreadState::self()->getCallingPid();
     int32_t connId = android_atomic_inc(&mNextConnId);
+    // TODO b/182392769: use identity util
+    Identity verifiedIdentity = identity;
+    verifiedIdentity.pid = VALUE_OR_FATAL(
+        legacy2aidl_pid_t_int32_t(IPCThreadState::self()->getCallingPid()));
+    verifiedIdentity.uid = VALUE_OR_FATAL(
+        legacy2aidl_uid_t_int32_t(IPCThreadState::self()->getCallingUid()));
 
     sp<Client> c = new Client(
-            this, pid, connId, client, audioSessionId,
-            IPCThreadState::self()->getCallingUid(), opPackageName);
+            this, verifiedIdentity, connId, client, audioSessionId);
 
-    ALOGV("Create new client(%d) from pid %d, uid %d, ", connId, pid,
-         IPCThreadState::self()->getCallingUid());
+    ALOGV("Create new client(%d) from %s, ", connId,
+        verifiedIdentity.toString().c_str());
 
     wp<Client> w = c;
     {
@@ -543,8 +556,8 @@
     char buffer[SIZE];
     String8 result;
     result.append(" Client\n");
-    snprintf(buffer, 255, "  pid(%d), connId(%d), status(%d), looping(%s)\n",
-            mPid, mConnId, mStatus, mLoop?"true": "false");
+    snprintf(buffer, 255, "  Identity(%s), connId(%d), status(%d), looping(%s)\n",
+        mIdentity.toString().c_str(), mConnId, mStatus, mLoop?"true": "false");
     result.append(buffer);
 
     sp<MediaPlayerBase> p;
@@ -608,7 +621,7 @@
             for (int i = 0, n = mMediaRecorderClients.size(); i < n; ++i) {
                 sp<MediaRecorderClient> c = mMediaRecorderClients[i].promote();
                 if (c != 0) {
-                    snprintf(buffer, 255, " MediaRecorderClient pid(%d)\n", c->mPid);
+                    snprintf(buffer, 255, " MediaRecorderClient pid(%d)\n", c->mIdentity.pid);
                     result.append(buffer);
                     write(fd, result.string(), result.size());
                     result = "\n";
@@ -731,20 +744,18 @@
 }
 
 MediaPlayerService::Client::Client(
-        const sp<MediaPlayerService>& service, pid_t pid,
+        const sp<MediaPlayerService>& service, const Identity& identity,
         int32_t connId, const sp<IMediaPlayerClient>& client,
-        audio_session_t audioSessionId, uid_t uid, const std::string& opPackageName)
-        : mOpPackageName(opPackageName)
+        audio_session_t audioSessionId)
+        : mIdentity(identity)
 {
     ALOGV("Client(%d) constructor", connId);
-    mPid = pid;
     mConnId = connId;
     mService = service;
     mClient = client;
     mLoop = false;
     mStatus = NO_INIT;
     mAudioSessionId = audioSessionId;
-    mUid = uid;
     mRetransmitEndpointValid = false;
     mAudioAttributes = NULL;
     mListener = new Listener(this);
@@ -757,7 +768,7 @@
 
 MediaPlayerService::Client::~Client()
 {
-    ALOGV("Client(%d) destructor pid = %d", mConnId, mPid);
+    ALOGV("Client(%d) destructor identity = %s", mConnId, mIdentity.toString().c_str());
     mAudioOutput.clear();
     wp<Client> client(this);
     disconnect();
@@ -770,7 +781,7 @@
 
 void MediaPlayerService::Client::disconnect()
 {
-    ALOGV("disconnect(%d) from pid %d", mConnId, mPid);
+    ALOGV("disconnect(%d) from identity %s", mConnId, mIdentity.toString().c_str());
     // grab local reference and clear main reference to prevent future
     // access to object
     sp<MediaPlayerBase> p;
@@ -810,11 +821,12 @@
         p.clear();
     }
     if (p == NULL) {
-        p = MediaPlayerFactory::createPlayer(playerType, mListener, mPid);
+        p = MediaPlayerFactory::createPlayer(playerType, mListener,
+            VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mIdentity.pid)));
     }
 
     if (p != NULL) {
-        p->setUID(mUid);
+        p->setUID(VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(mIdentity.uid)));
     }
 
     return p;
@@ -922,8 +934,8 @@
     mAudioDeviceUpdatedListener = new AudioDeviceUpdatedNotifier(p);
 
     if (!p->hardwareOutput()) {
-        mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid(),
-                mPid, mAudioAttributes, mAudioDeviceUpdatedListener, mOpPackageName);
+        mAudioOutput = new AudioOutput(mAudioSessionId, mIdentity,
+                mAudioAttributes, mAudioDeviceUpdatedListener);
         static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
     }
 
@@ -1772,9 +1784,8 @@
 
 #undef LOG_TAG
 #define LOG_TAG "AudioSink"
-MediaPlayerService::AudioOutput::AudioOutput(audio_session_t sessionId, uid_t uid, int pid,
-        const audio_attributes_t* attr, const sp<AudioSystem::AudioDeviceCallback>& deviceCallback,
-        const std::string& opPackageName)
+MediaPlayerService::AudioOutput::AudioOutput(audio_session_t sessionId, const Identity& identity,
+        const audio_attributes_t* attr, const sp<AudioSystem::AudioDeviceCallback>& deviceCallback)
     : mCallback(NULL),
       mCallbackCookie(NULL),
       mCallbackData(NULL),
@@ -1786,8 +1797,7 @@
       mMsecsPerFrame(0),
       mFrameSize(0),
       mSessionId(sessionId),
-      mUid(uid),
-      mPid(pid),
+      mIdentity(identity),
       mSendLevel(0.0),
       mAuxEffectId(0),
       mFlags(AUDIO_OUTPUT_FLAG_NONE),
@@ -1795,8 +1805,7 @@
       mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
       mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE),
       mDeviceCallbackEnabled(false),
-      mDeviceCallback(deviceCallback),
-      mOpPackageName(opPackageName)
+      mDeviceCallback(deviceCallback)
 {
     ALOGV("AudioOutput(%d)", sessionId);
     if (attr != NULL) {
@@ -1822,8 +1831,7 @@
 //static
 void MediaPlayerService::AudioOutput::setMinBufferCount()
 {
-    char value[PROPERTY_VALUE_MAX];
-    if (property_get("ro.kernel.qemu", value, 0)) {
+    if (property_get_bool("ro.boot.qemu", false)) {
         mIsOnEmulator = true;
         mMinBufferCount = 12;  // to prevent systematic buffer underrun for emulator
     }
@@ -2185,13 +2193,11 @@
                     mSessionId,
                     AudioTrack::TRANSFER_CALLBACK,
                     offloadInfo,
-                    mUid,
-                    mPid,
+                    mIdentity,
                     mAttributes,
                     doNotReconnect,
                     1.0f,  // default value for maxRequiredSpeed
-                    mSelectedDeviceId,
-                    mOpPackageName);
+                    mSelectedDeviceId);
         } else {
             // TODO: Due to buffer memory concerns, we use a max target playback speed
             // based on mPlaybackRate at the time of open (instead of kMaxRequiredSpeed),
@@ -2214,13 +2220,11 @@
                     mSessionId,
                     AudioTrack::TRANSFER_DEFAULT,
                     NULL, // offload info
-                    mUid,
-                    mPid,
+                    mIdentity,
                     mAttributes,
                     doNotReconnect,
                     targetSpeed,
-                    mSelectedDeviceId,
-                    mOpPackageName);
+                    mSelectedDeviceId);
         }
         // Set caller name so it can be logged in destructor.
         // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_MEDIA
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index aca4369..35a65d3 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -27,12 +27,13 @@
 #include <utils/String8.h>
 #include <utils/Vector.h>
 
+#include <media/AidlConversion.h>
 #include <media/AudioResamplerPublic.h>
 #include <media/AudioSystem.h>
 #include <media/MediaPlayerInterface.h>
 #include <media/Metadata.h>
 #include <media/stagefright/foundation/ABase.h>
-
+#include <android/media/permission/Identity.h>
 
 #include <system/audio.h>
 
@@ -79,11 +80,9 @@
      public:
                                 AudioOutput(
                                         audio_session_t sessionId,
-                                        uid_t uid,
-                                        int pid,
+                                        const media::permission::Identity& identity,
                                         const audio_attributes_t * attr,
-                                        const sp<AudioSystem::AudioDeviceCallback>& deviceCallback,
-                                        const std::string& opPackageName);
+                                        const sp<AudioSystem::AudioDeviceCallback>& deviceCallback);
         virtual                 ~AudioOutput();
 
         virtual bool            ready() const { return mTrack != 0; }
@@ -170,8 +169,7 @@
         float                   mMsecsPerFrame;
         size_t                  mFrameSize;
         audio_session_t         mSessionId;
-        uid_t                   mUid;
-        int                     mPid;
+        media::permission::Identity mIdentity;
         float                   mSendLevel;
         int                     mAuxEffectId;
         audio_output_flags_t    mFlags;
@@ -181,7 +179,6 @@
         bool                    mDeviceCallbackEnabled;
         wp<AudioSystem::AudioDeviceCallback>        mDeviceCallback;
         mutable Mutex           mLock;
-        const std::string       mOpPackageName;
 
         // static variables below not protected by mutex
         static bool             mIsOnEmulator;
@@ -234,13 +231,13 @@
     static  void                instantiate();
 
     // IMediaPlayerService interface
-    virtual sp<IMediaRecorder>  createMediaRecorder(const String16 &opPackageName);
+    virtual sp<IMediaRecorder>  createMediaRecorder(const media::permission::Identity &identity);
     void    removeMediaRecorderClient(const wp<MediaRecorderClient>& client);
     virtual sp<IMediaMetadataRetriever> createMetadataRetriever();
 
     virtual sp<IMediaPlayer>    create(const sp<IMediaPlayerClient>& client,
                                        audio_session_t audioSessionId,
-                                       const std::string opPackageName);
+                                       const media::permission::Identity& identity);
 
     virtual sp<IMediaCodecList> getCodecList() const;
 
@@ -382,7 +379,9 @@
 
                 void            notify(int msg, int ext1, int ext2, const Parcel *obj);
 
-                pid_t           pid() const { return mPid; }
+                pid_t           pid() const {
+                    return VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mIdentity.pid));
+                }
         virtual status_t        dump(int fd, const Vector<String16>& args);
 
                 audio_session_t getAudioSessionId() { return mAudioSessionId; }
@@ -412,12 +411,10 @@
 
         friend class MediaPlayerService;
                                 Client( const sp<MediaPlayerService>& service,
-                                        pid_t pid,
+                                        const media::permission::Identity& identity,
                                         int32_t connId,
                                         const sp<IMediaPlayerClient>& client,
-                                        audio_session_t audioSessionId,
-                                        uid_t uid,
-                                        const std::string& opPackageName);
+                                        audio_session_t audioSessionId);
                                 Client();
         virtual                 ~Client();
 
@@ -461,20 +458,18 @@
                     sp<MediaPlayerService>        mService;
                     sp<IMediaPlayerClient>        mClient;
                     sp<AudioOutput>               mAudioOutput;
-                    pid_t                         mPid;
+                    const media::permission::Identity mIdentity;
                     status_t                      mStatus;
                     bool                          mLoop;
                     int32_t                       mConnId;
                     audio_session_t               mAudioSessionId;
                     audio_attributes_t *          mAudioAttributes;
-                    uid_t                         mUid;
                     sp<ANativeWindow>             mConnectedWindow;
                     sp<IBinder>                   mConnectedWindowBinder;
                     struct sockaddr_in            mRetransmitEndpoint;
                     bool                          mRetransmitEndpointValid;
                     sp<Client>                    mNextClient;
                     sp<MediaPlayerBase::Listener> mListener;
-                    const std::string             mOpPackageName;
 
         // Metadata filters.
         media::Metadata::Filter mMetadataAllow;  // protected by mLock
diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index 1e72dd1..e2c8f8f 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -124,12 +124,10 @@
         ALOGE("Invalid audio source: %d", as);
         return BAD_VALUE;
     }
-    pid_t pid = IPCThreadState::self()->getCallingPid();
-    uid_t uid = IPCThreadState::self()->getCallingUid();
 
     if ((as == AUDIO_SOURCE_FM_TUNER
-            && !(captureAudioOutputAllowed(pid, uid) || captureTunerAudioInputAllowed(pid, uid)))
-            || !recordingAllowed(String16(""), pid, uid)) {
+            && !(captureAudioOutputAllowed(mIdentity) || captureTunerAudioInputAllowed(mIdentity)))
+            || !recordingAllowed(mIdentity)) {
         return PERMISSION_DENIED;
     }
     Mutex::Autolock lock(mLock);
@@ -378,12 +376,13 @@
     return NO_ERROR;
 }
 
-MediaRecorderClient::MediaRecorderClient(const sp<MediaPlayerService>& service, pid_t pid,
-        const String16& opPackageName)
+MediaRecorderClient::MediaRecorderClient(const sp<MediaPlayerService>& service,
+        const Identity& identity)
 {
     ALOGV("Client constructor");
-    mPid = pid;
-    mRecorder = new StagefrightRecorder(opPackageName);
+    // identity already validated in createMediaRecorder
+    mIdentity = identity;
+    mRecorder = new StagefrightRecorder(identity);
     mMediaPlayerService = service;
 }
 
diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h
index e041855..24c6ee1 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.h
+++ b/media/libmediaplayerservice/MediaRecorderClient.h
@@ -22,6 +22,7 @@
 
 #include <media/AudioSystem.h>
 #include <media/IMediaRecorder.h>
+#include <android/media/permission/Identity.h>
 
 #include <vector>
 
@@ -93,14 +94,13 @@
 
                            MediaRecorderClient(
                                    const sp<MediaPlayerService>& service,
-                                                               pid_t pid,
-                                                               const String16& opPackageName);
+                                   const media::permission::Identity& identity);
     virtual                ~MediaRecorderClient();
 
     std::vector<DeathNotifier> mDeathNotifiers;
     sp<AudioDeviceUpdatedNotifier> mAudioDeviceUpdatedNotifier;
 
-    pid_t                  mPid;
+    media::permission::Identity mIdentity;
     mutable Mutex          mLock;
     MediaRecorderBase      *mRecorder;
     sp<MediaPlayerService> mMediaPlayerService;
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index ecbdf61..b485b1e 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -33,6 +33,7 @@
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 
+#include <media/AidlConversion.h>
 #include <media/IMediaPlayerService.h>
 #include <media/MediaMetricsItem.h>
 #include <media/stagefright/foundation/ABuffer.h>
@@ -114,8 +115,8 @@
 }
 
 
-StagefrightRecorder::StagefrightRecorder(const String16 &opPackageName)
-    : MediaRecorderBase(opPackageName),
+StagefrightRecorder::StagefrightRecorder(const Identity& clientIdentity)
+    : MediaRecorderBase(clientIdentity),
       mWriter(NULL),
       mOutputFd(-1),
       mAudioSource((audio_source_t)AUDIO_SOURCE_CNT), // initialize with invalid value
@@ -157,7 +158,7 @@
 
     // we run as part of the media player service; what we really want to
     // know is the app which requested the recording.
-    mMetricsItem->setUid(mClientUid);
+    mMetricsItem->setUid(VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(mClient.uid)));
 
     // populate the values from the raw fields.
 
@@ -1129,7 +1130,8 @@
 }
 
 status_t StagefrightRecorder::setClientName(const String16& clientName) {
-    mClientName = clientName;
+
+    mClient.packageName = VALUE_OR_RETURN_STATUS(legacy2aidl_String16_string(clientName));
 
     return OK;
 }
@@ -1141,10 +1143,6 @@
         return INVALID_OPERATION;
     }
 
-    // Get UID and PID here for permission checking
-    mClientUid = IPCThreadState::self()->getCallingUid();
-    mClientPid = IPCThreadState::self()->getCallingPid();
-
     status_t status = OK;
 
     switch (mOutputFormat) {
@@ -1344,12 +1342,10 @@
     sp<AudioSource> audioSource =
         new AudioSource(
                 &attr,
-                mOpPackageName,
+                mClient,
                 sourceSampleRate,
                 mAudioChannels,
                 mSampleRate,
-                mClientUid,
-                mClientPid,
                 mSelectedDeviceId,
                 mSelectedMicDirection,
                 mSelectedMicFieldDimension);
@@ -1871,6 +1867,10 @@
     Size videoSize;
     videoSize.width = mVideoWidth;
     videoSize.height = mVideoHeight;
+    uid_t uid = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_uid_t(mClient.uid));
+    pid_t pid = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_pid_t(mClient.pid));
+    String16 clientName = VALUE_OR_RETURN_STATUS(
+        aidl2legacy_string_view_String16(mClient.packageName.value_or("")));
     if (mCaptureFpsEnable) {
         if (!(mCaptureFps > 0.)) {
             ALOGE("Invalid mCaptureFps value: %lf", mCaptureFps);
@@ -1878,13 +1878,13 @@
         }
 
         mCameraSourceTimeLapse = CameraSourceTimeLapse::CreateFromCamera(
-                mCamera, mCameraProxy, mCameraId, mClientName, mClientUid, mClientPid,
+                mCamera, mCameraProxy, mCameraId, clientName, uid, pid,
                 videoSize, mFrameRate, mPreviewSurface,
                 std::llround(1e6 / mCaptureFps));
         *cameraSource = mCameraSourceTimeLapse;
     } else {
         *cameraSource = CameraSource::CreateFromCamera(
-                mCamera, mCameraProxy, mCameraId, mClientName, mClientUid, mClientPid,
+                mCamera, mCameraProxy, mCameraId, clientName, uid, pid,
                 videoSize, mFrameRate,
                 mPreviewSurface);
     }
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 4bba869..278f348 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -26,6 +26,9 @@
 #include <system/audio.h>
 
 #include <media/hardware/MetadataBufferType.h>
+#include <android/media/permission/Identity.h>
+
+using namespace android::media::permission;
 
 namespace android {
 
@@ -42,7 +45,7 @@
 struct ALooper;
 
 struct StagefrightRecorder : public MediaRecorderBase {
-    explicit StagefrightRecorder(const String16 &opPackageName);
+    explicit StagefrightRecorder(const Identity& clientIdentity);
     virtual ~StagefrightRecorder();
     virtual status_t init();
     virtual status_t setAudioSource(audio_source_t as);
@@ -98,9 +101,6 @@
     sp<IGraphicBufferProducer> mPreviewSurface;
     sp<PersistentSurface> mPersistentSurface;
     sp<IMediaRecorderClient> mListener;
-    String16 mClientName;
-    uid_t mClientUid;
-    pid_t mClientPid;
     sp<MediaWriter> mWriter;
     int mOutputFd;
     sp<AudioSource> mAudioSourceNode;
diff --git a/media/libmediaplayerservice/tests/stagefrightRecorder/Android.bp b/media/libmediaplayerservice/tests/stagefrightRecorder/Android.bp
index b84d64b..5b16911 100644
--- a/media/libmediaplayerservice/tests/stagefrightRecorder/Android.bp
+++ b/media/libmediaplayerservice/tests/stagefrightRecorder/Android.bp
@@ -73,6 +73,8 @@
         "libstagefright",
         "libstagefright_foundation",
         "libutils",
+        "media_permission-aidl-cpp",
+        "libaudioclient_aidl_conversion",
     ],
 
     cflags: [
diff --git a/media/libmediaplayerservice/tests/stagefrightRecorder/StagefrightRecorderTest.cpp b/media/libmediaplayerservice/tests/stagefrightRecorder/StagefrightRecorderTest.cpp
index 5751631..6dea53d 100644
--- a/media/libmediaplayerservice/tests/stagefrightRecorder/StagefrightRecorderTest.cpp
+++ b/media/libmediaplayerservice/tests/stagefrightRecorder/StagefrightRecorderTest.cpp
@@ -59,7 +59,10 @@
     }
 
     void SetUp() override {
-        mStfRecorder = new StagefrightRecorder(String16(LOG_TAG));
+        // TODO b/182392769: use identity util
+        Identity identity;
+        identity.packageName = std::string(LOG_TAG);
+        mStfRecorder = new StagefrightRecorder(identity);
         ASSERT_NE(mStfRecorder, nullptr) << "Failed to create the instance of recorder";
 
         mOutputAudioFp = fopen(OUTPUT_FILE_NAME_AUDIO, "wb");
diff --git a/media/libmediatranscoding/Android.bp b/media/libmediatranscoding/Android.bp
index 534fa91..042850c 100644
--- a/media/libmediatranscoding/Android.bp
+++ b/media/libmediatranscoding/Android.bp
@@ -82,6 +82,7 @@
     srcs: [
         "TranscoderWrapper.cpp",
         "TranscodingClientManager.cpp",
+        "TranscodingLogger.cpp",
         "TranscodingResourcePolicy.cpp",
         "TranscodingSessionController.cpp",
         "TranscodingThermalPolicy.cpp",
@@ -96,6 +97,7 @@
         "libutils",
         "libmediatranscoder",
         "libmediandk",
+        "libstatssocket#30",
     ],
     export_shared_lib_headers: [
         "libmediandk",
@@ -106,6 +108,7 @@
     static_libs: [
         "mediatranscoding_aidl_interface-ndk_platform",
         "resourceobserver_aidl_interface-V1-ndk_platform",
+        "libstatslog_media",
     ],
 
     cflags: [
@@ -126,3 +129,43 @@
         cfi: true,
     },
 }
+
+cc_library_static {
+    name: "libstatslog_media",
+    generated_sources: ["statslog_media.cpp"],
+    generated_headers: ["statslog_media.h"],
+    min_sdk_version: "29",
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    export_generated_headers: ["statslog_media.h"],
+    apex_available: [
+        "com.android.media",
+        "test_com.android.media",
+    ],
+    shared_libs: [
+        "libcutils",
+        "liblog",
+        "libstatssocket#30",
+        "libutils",
+    ],
+}
+
+genrule {
+    name: "statslog_media.h",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_media.h --module media --namespace android,media,stats",
+    out: [
+        "statslog_media.h",
+    ],
+}
+
+genrule {
+    name: "statslog_media.cpp",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_media.cpp --module media --namespace android,media,stats --importHeader statslog_media.h",
+    out: [
+        "statslog_media.cpp",
+    ],
+}
\ No newline at end of file
diff --git a/media/libmediatranscoding/TranscoderWrapper.cpp b/media/libmediatranscoding/TranscoderWrapper.cpp
index 4bd4105..d9c98c6 100644
--- a/media/libmediatranscoding/TranscoderWrapper.cpp
+++ b/media/libmediatranscoding/TranscoderWrapper.cpp
@@ -56,34 +56,34 @@
     }
 }
 
-static AMediaFormat* getVideoFormat(
+static std::shared_ptr<AMediaFormat> getVideoFormat(
         const char* originalMime,
         const std::optional<TranscodingVideoTrackFormat>& requestedFormat) {
     if (requestedFormat == std::nullopt) {
         return nullptr;
     }
 
-    AMediaFormat* format = AMediaFormat_new();
+    std::shared_ptr<AMediaFormat> format =
+            std::shared_ptr<AMediaFormat>(AMediaFormat_new(), &AMediaFormat_delete);
     bool changed = false;
     if (requestedFormat->codecType == TranscodingVideoCodecType::kHevc &&
         strcmp(originalMime, AMEDIA_MIMETYPE_VIDEO_HEVC)) {
-        AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_VIDEO_HEVC);
+        AMediaFormat_setString(format.get(), AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_VIDEO_HEVC);
         changed = true;
     } else if (requestedFormat->codecType == TranscodingVideoCodecType::kAvc &&
                strcmp(originalMime, AMEDIA_MIMETYPE_VIDEO_AVC)) {
-        AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_VIDEO_AVC);
+        AMediaFormat_setString(format.get(), AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_VIDEO_AVC);
         changed = true;
     }
     if (requestedFormat->bitrateBps > 0) {
-        AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, requestedFormat->bitrateBps);
+        AMediaFormat_setInt32(format.get(), AMEDIAFORMAT_KEY_BIT_RATE, requestedFormat->bitrateBps);
         changed = true;
     }
     // TODO: translate other fields from requestedFormat to the format for MediaTranscoder.
     // Also need to determine more settings to expose in TranscodingVideoTrackFormat.
     if (!changed) {
-        AMediaFormat_delete(format);
         // Use null format for passthru.
-        format = nullptr;
+        format.reset();
     }
     return format;
 }
@@ -180,8 +180,10 @@
 };
 
 TranscoderWrapper::TranscoderWrapper(const std::shared_ptr<TranscoderCallbackInterface>& cb,
+                                     const std::shared_ptr<TranscodingLogger>& logger,
                                      int64_t heartBeatIntervalUs)
       : mCallback(cb),
+        mLogger(logger),
         mHeartBeatIntervalUs(heartBeatIntervalUs),
         mCurrentClientId(0),
         mCurrentSessionId(-1),
@@ -219,10 +221,10 @@
 }
 
 void TranscoderWrapper::start(ClientIdType clientId, SessionIdType sessionId,
-                              const TranscodingRequestParcel& request,
+                              const TranscodingRequestParcel& request, uid_t callingUid,
                               const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
     queueEvent(Event::Start, clientId, sessionId, [=, &request] {
-        media_status_t err = handleStart(clientId, sessionId, request, clientCb);
+        media_status_t err = handleStart(clientId, sessionId, request, callingUid, clientCb);
         if (err != AMEDIA_OK) {
             cleanup();
             reportError(clientId, sessionId, err);
@@ -253,10 +255,10 @@
 }
 
 void TranscoderWrapper::resume(ClientIdType clientId, SessionIdType sessionId,
-                               const TranscodingRequestParcel& request,
+                               const TranscodingRequestParcel& request, uid_t callingUid,
                                const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
     queueEvent(Event::Resume, clientId, sessionId, [=, &request] {
-        media_status_t err = handleResume(clientId, sessionId, request, clientCb);
+        media_status_t err = handleResume(clientId, sessionId, request, callingUid, clientCb);
         if (err != AMEDIA_OK) {
             cleanup();
             reportError(clientId, sessionId, err);
@@ -280,6 +282,7 @@
             } else {
                 ALOGI("transcoder stopped");
             }
+            logSessionEnded(TranscodingLogger::SessionEndedReason::CANCELLED, err);
             cleanup();
         } else {
             // For sessions that's not currently running, release any pausedState for the session.
@@ -297,6 +300,7 @@
     queueEvent(Event::Finish, clientId, sessionId, [=] {
         if (mTranscoder != nullptr && clientId == mCurrentClientId &&
             sessionId == mCurrentSessionId) {
+            logSessionEnded(TranscodingLogger::SessionEndedReason::FINISHED, AMEDIA_OK);
             cleanup();
         }
 
@@ -314,6 +318,7 @@
             [=] {
                 if (mTranscoder != nullptr && clientId == mCurrentClientId &&
                     sessionId == mCurrentSessionId) {
+                    logSessionEnded(TranscodingLogger::SessionEndedReason::ERROR, error);
                     cleanup();
                 }
                 reportError(clientId, sessionId, error);
@@ -345,7 +350,8 @@
 
 media_status_t TranscoderWrapper::setupTranscoder(
         ClientIdType clientId, SessionIdType sessionId, const TranscodingRequestParcel& request,
-        const std::shared_ptr<ITranscodingClientCallback>& clientCb,
+        uid_t callingUid, const std::shared_ptr<ITranscodingClientCallback>& clientCb,
+        TranscodingLogger::SessionEndedReason* failureReason,
         const std::shared_ptr<ndk::ScopedAParcel>& pausedState) {
     if (clientCb == nullptr) {
         ALOGE("client callback is null");
@@ -364,6 +370,7 @@
         status = clientCb->openFileDescriptor(request.sourceFilePath, "r", &srcFd);
         if (!status.isOk() || srcFd.get() < 0) {
             ALOGE("failed to open source");
+            *failureReason = TranscodingLogger::SessionEndedReason::OPEN_SRC_FD_FAILED;
             return AMEDIA_ERROR_IO;
         }
         srcFdInt = srcFd.get();
@@ -377,6 +384,7 @@
         status = clientCb->openFileDescriptor(request.destinationFilePath, "rw", &dstFd);
         if (!status.isOk() || dstFd.get() < 0) {
             ALOGE("failed to open destination");
+            *failureReason = TranscodingLogger::SessionEndedReason::OPEN_DST_FD_FAILED;
             return AMEDIA_ERROR_IO;
         }
         dstFdInt = dstFd.get();
@@ -384,41 +392,46 @@
 
     mCurrentClientId = clientId;
     mCurrentSessionId = sessionId;
+    mCurrentCallingUid = callingUid;
     mTranscoderCb = std::make_shared<CallbackImpl>(shared_from_this(), clientId, sessionId);
     mTranscoder = MediaTranscoder::create(mTranscoderCb, mHeartBeatIntervalUs, request.clientPid,
                                           request.clientUid, pausedState);
     if (mTranscoder == nullptr) {
         ALOGE("failed to create transcoder");
+        *failureReason = TranscodingLogger::SessionEndedReason::CREATE_FAILED;
         return AMEDIA_ERROR_UNKNOWN;
     }
 
     media_status_t err = mTranscoder->configureSource(srcFdInt);
     if (err != AMEDIA_OK) {
         ALOGE("failed to configure source: %d", err);
+        *failureReason = TranscodingLogger::SessionEndedReason::CONFIG_SRC_FAILED;
         return err;
     }
 
     std::vector<std::shared_ptr<AMediaFormat>> trackFormats = mTranscoder->getTrackFormats();
     if (trackFormats.size() == 0) {
         ALOGE("failed to get track formats!");
+        *failureReason = TranscodingLogger::SessionEndedReason::NO_TRACKS;
         return AMEDIA_ERROR_MALFORMED;
     }
 
     for (int i = 0; i < trackFormats.size(); ++i) {
-        AMediaFormat* format = nullptr;
+        std::shared_ptr<AMediaFormat> format;
         const char* mime = nullptr;
         AMediaFormat_getString(trackFormats[i].get(), AMEDIAFORMAT_KEY_MIME, &mime);
 
         if (!strncmp(mime, "video/", 6)) {
             format = getVideoFormat(mime, request.requestedVideoTrackFormat);
+
+            mSrcFormat = trackFormats[i];
+            mDstFormat = format;
         }
 
-        err = mTranscoder->configureTrackFormat(i, format);
-        if (format != nullptr) {
-            AMediaFormat_delete(format);
-        }
+        err = mTranscoder->configureTrackFormat(i, format.get());
         if (err != AMEDIA_OK) {
             ALOGE("failed to configure track format for track %d: %d", i, err);
+            *failureReason = TranscodingLogger::SessionEndedReason::CONFIG_TRACK_FAILED;
             return err;
         }
     }
@@ -426,6 +439,7 @@
     err = mTranscoder->configureDestination(dstFdInt);
     if (err != AMEDIA_OK) {
         ALOGE("failed to configure dest: %d", err);
+        *failureReason = TranscodingLogger::SessionEndedReason::CONFIG_DST_FAILED;
         return err;
     }
 
@@ -434,17 +448,23 @@
 
 media_status_t TranscoderWrapper::handleStart(
         ClientIdType clientId, SessionIdType sessionId, const TranscodingRequestParcel& request,
-        const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
+        uid_t callingUid, const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
     ALOGI("%s: setting up transcoder for start", __FUNCTION__);
-    media_status_t err = setupTranscoder(clientId, sessionId, request, clientCb);
+    TranscodingLogger::SessionEndedReason reason = TranscodingLogger::SessionEndedReason::UNKNOWN;
+    media_status_t err =
+            setupTranscoder(clientId, sessionId, request, callingUid, clientCb, &reason);
     if (err != AMEDIA_OK) {
         ALOGI("%s: failed to setup transcoder", __FUNCTION__);
+        logSessionEnded(reason, err);
         return err;
     }
 
+    mTranscodeStartTime = std::chrono::steady_clock::now();
+
     err = mTranscoder->start();
     if (err != AMEDIA_OK) {
         ALOGE("%s: failed to start transcoder: %d", __FUNCTION__, err);
+        logSessionEnded(TranscodingLogger::SessionEndedReason::START_FAILED, err);
         return err;
     }
 
@@ -467,6 +487,7 @@
 
     std::shared_ptr<ndk::ScopedAParcel> pauseStates;
     media_status_t err = mTranscoder->pause(&pauseStates);
+    logSessionEnded(TranscodingLogger::SessionEndedReason::PAUSED, err);
     if (err != AMEDIA_OK) {
         ALOGE("%s: failed to pause transcoder: %d", __FUNCTION__, err);
         return err;
@@ -479,7 +500,7 @@
 
 media_status_t TranscoderWrapper::handleResume(
         ClientIdType clientId, SessionIdType sessionId, const TranscodingRequestParcel& request,
-        const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
+        uid_t callingUid, const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
     std::shared_ptr<ndk::ScopedAParcel> pausedState;
     auto it = mPausedStateMap.find(SessionKeyType(clientId, sessionId));
     if (it != mPausedStateMap.end()) {
@@ -491,15 +512,23 @@
     }
 
     ALOGI("%s: setting up transcoder for resume", __FUNCTION__);
-    media_status_t err = setupTranscoder(clientId, sessionId, request, clientCb, pausedState);
+    TranscodingLogger::SessionEndedReason reason = TranscodingLogger::SessionEndedReason::UNKNOWN;
+    media_status_t err = setupTranscoder(clientId, sessionId, request, callingUid, clientCb,
+                                         &reason, pausedState);
     if (err != AMEDIA_OK) {
         ALOGE("%s: failed to setup transcoder: %d", __FUNCTION__, err);
+        logSessionEnded(reason, err);
         return err;
     }
 
+    // Note: For now resume() will just restart transcoding from the beginning, so there is no need
+    // to distinguish between resume and start from a performance perspective.
+    mTranscodeStartTime = std::chrono::steady_clock::now();
+
     err = mTranscoder->resume();
     if (err != AMEDIA_OK) {
         ALOGE("%s: failed to resume transcoder: %d", __FUNCTION__, err);
+        logSessionEnded(TranscodingLogger::SessionEndedReason::RESUME_FAILED, err);
         return err;
     }
 
@@ -510,8 +539,23 @@
 void TranscoderWrapper::cleanup() {
     mCurrentClientId = 0;
     mCurrentSessionId = -1;
+    mCurrentCallingUid = -1;
     mTranscoderCb = nullptr;
     mTranscoder = nullptr;
+    mSrcFormat = nullptr;
+    mDstFormat = nullptr;
+}
+
+void TranscoderWrapper::logSessionEnded(const TranscodingLogger::SessionEndedReason& reason,
+                                        int error) {
+    std::chrono::microseconds transcodeDuration(-1);
+    if (reason == TranscodingLogger::SessionEndedReason::FINISHED && error == AMEDIA_OK) {
+        transcodeDuration = std::chrono::duration_cast<std::chrono::microseconds>(
+                std::chrono::steady_clock::now() - mTranscodeStartTime);
+    }
+
+    mLogger->logSessionEnded(reason, mCurrentCallingUid, error, transcodeDuration, mSrcFormat.get(),
+                             mDstFormat.get());
 }
 
 void TranscoderWrapper::queueEvent(Event::Type type, ClientIdType clientId, SessionIdType sessionId,
@@ -555,5 +599,4 @@
         lock.lock();
     }
 }
-
 }  // namespace android
diff --git a/media/libmediatranscoding/TranscodingClientManager.cpp b/media/libmediatranscoding/TranscodingClientManager.cpp
index 76bb33e..06c5421 100644
--- a/media/libmediatranscoding/TranscodingClientManager.cpp
+++ b/media/libmediatranscoding/TranscodingClientManager.cpp
@@ -162,8 +162,8 @@
 
     int32_t sessionId = mNextSessionId.fetch_add(1);
 
-    *_aidl_return = owner->mSessionController->submit(mClientId, sessionId, in_clientUid,
-                                                      in_request, mClientCallback);
+    *_aidl_return = owner->mSessionController->submit(mClientId, sessionId, callingUid,
+                                                      in_clientUid, in_request, mClientCallback);
 
     if (*_aidl_return) {
         out_session->sessionId = sessionId;
diff --git a/media/libmediatranscoding/TranscodingLogger.cpp b/media/libmediatranscoding/TranscodingLogger.cpp
new file mode 100644
index 0000000..29a52b0
--- /dev/null
+++ b/media/libmediatranscoding/TranscodingLogger.cpp
@@ -0,0 +1,194 @@
+/*
+ * 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.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "TranscodingLogger"
+
+#include <media/NdkCommon.h>
+#include <media/TranscodingLogger.h>
+#include <statslog_media.h>
+#include <utils/Log.h>
+
+#include <cmath>
+#include <string>
+
+namespace android {
+
+static_assert(TranscodingLogger::UNKNOWN ==
+                      android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__UNKNOWN,
+              "Session event mismatch");
+static_assert(TranscodingLogger::FINISHED ==
+                      android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__FINISHED,
+              "Session event mismatch");
+static_assert(TranscodingLogger::ERROR ==
+                      android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__ERROR,
+              "Session event mismatch");
+static_assert(TranscodingLogger::PAUSED ==
+                      android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__PAUSED,
+              "Session event mismatch");
+static_assert(TranscodingLogger::CANCELLED ==
+                      android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__CANCELLED,
+              "Session event mismatch");
+static_assert(TranscodingLogger::START_FAILED ==
+                      android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__START_FAILED,
+              "Session event mismatch");
+static_assert(TranscodingLogger::RESUME_FAILED ==
+                      android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__RESUME_FAILED,
+              "Session event mismatch");
+static_assert(TranscodingLogger::CREATE_FAILED ==
+                      android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__CREATE_FAILED,
+              "Session event mismatch");
+static_assert(
+        TranscodingLogger::CONFIG_SRC_FAILED ==
+                android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__CONFIG_SRC_FAILED,
+        "Session event mismatch");
+static_assert(
+        TranscodingLogger::CONFIG_DST_FAILED ==
+                android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__CONFIG_DST_FAILED,
+        "Session event mismatch");
+static_assert(
+        TranscodingLogger::CONFIG_TRACK_FAILED ==
+                android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__CONFIG_TRACK_FAILED,
+        "Session event mismatch");
+static_assert(
+        TranscodingLogger::OPEN_SRC_FD_FAILED ==
+                android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__OPEN_SRC_FD_FAILED,
+        "Session event mismatch");
+static_assert(
+        TranscodingLogger::OPEN_DST_FD_FAILED ==
+                android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__OPEN_DST_FD_FAILED,
+        "Session event mismatch");
+static_assert(TranscodingLogger::NO_TRACKS ==
+                      android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__NO_TRACKS,
+              "Session event mismatch");
+
+static inline int32_t getInt32(AMediaFormat* fmt, const char* key, int32_t defaultValue = -1) {
+    int32_t value;
+    if (fmt == nullptr || !AMediaFormat_getInt32(fmt, key, &value)) {
+        ALOGW("Unable to get %s", key);
+        value = defaultValue;
+    }
+    return value;
+}
+
+// Note: returned string is owned by format and only valid until the next getString.
+static inline const char* getString(AMediaFormat* fmt, const char* key,
+                                    const char* defaultValue = "(null)") {
+    const char* value;
+    if (fmt == nullptr || !AMediaFormat_getString(fmt, key, &value)) {
+        ALOGW("Unable to get %s", key);
+        value = defaultValue;
+    }
+    return value;
+}
+
+TranscodingLogger::TranscodingLogger()
+      : mSessionEndedAtomWriter(&android::media::stats::stats_write) {}
+
+void TranscodingLogger::logSessionEnded(enum SessionEndedReason reason, uid_t callingUid,
+                                        int status, std::chrono::microseconds duration,
+                                        AMediaFormat* srcFormat, AMediaFormat* dstFormat) {
+    logSessionEnded(std::chrono::steady_clock::now(), reason, callingUid, status, duration,
+                    srcFormat, dstFormat);
+}
+
+void TranscodingLogger::logSessionEnded(const std::chrono::steady_clock::time_point& now,
+                                        enum SessionEndedReason reason, uid_t callingUid,
+                                        int status, std::chrono::microseconds duration,
+                                        AMediaFormat* srcFormat, AMediaFormat* dstFormat) {
+    if (srcFormat == nullptr) {
+        ALOGE("Source format is null. Dropping event.");
+        return;
+    }
+
+    if (!shouldLogAtom(now, status)) {
+        ALOGD("Maximum logged event count reached. Dropping event.");
+        return;
+    }
+
+    // Extract the pieces of information to log.
+    const int32_t srcWidth = getInt32(srcFormat, AMEDIAFORMAT_KEY_WIDTH);
+    const int32_t srcHeight = getInt32(srcFormat, AMEDIAFORMAT_KEY_HEIGHT);
+    const char* srcMime = getString(srcFormat, AMEDIAFORMAT_KEY_MIME);
+    const int32_t srcProfile = getInt32(srcFormat, AMEDIAFORMAT_KEY_PROFILE);
+    const int32_t srcLevel = getInt32(srcFormat, AMEDIAFORMAT_KEY_LEVEL);
+    const int32_t srcFrameRate = getInt32(srcFormat, AMEDIAFORMAT_KEY_FRAME_RATE);
+    const int32_t srcFrameCount = getInt32(srcFormat, AMEDIAFORMAT_KEY_FRAME_COUNT);
+    const bool srcIsHdr = AMediaFormatUtils::VideoIsHdr(srcFormat);
+
+    int32_t dstWidth = getInt32(dstFormat, AMEDIAFORMAT_KEY_WIDTH, srcWidth);
+    int32_t dstHeight = getInt32(dstFormat, AMEDIAFORMAT_KEY_HEIGHT, srcHeight);
+    const char* dstMime = dstFormat == nullptr
+                                  ? "passthrough"
+                                  : getString(dstFormat, AMEDIAFORMAT_KEY_MIME, srcMime);
+    const bool dstIsHdr = false;  // Transcoder always request SDR output.
+
+    int64_t tmpDurationUs;
+    const int32_t srcDurationMs =
+            AMediaFormat_getInt64(srcFormat, AMEDIAFORMAT_KEY_DURATION, &tmpDurationUs)
+                    ? static_cast<int32_t>(tmpDurationUs / 1000)
+                    : -1;
+
+    int32_t transcodeFrameRate = -1;
+    if (status == 0 && srcFrameCount > 0 && duration.count() > 0) {
+        std::chrono::duration<double> seconds{duration};
+        transcodeFrameRate = static_cast<int32_t>(
+                std::round(static_cast<double>(srcFrameCount) / seconds.count()));
+    }
+
+    // Write the atom.
+    mSessionEndedAtomWriter(android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED,
+                            static_cast<int>(reason), callingUid, status, transcodeFrameRate,
+                            srcWidth, srcHeight, srcMime, srcProfile, srcLevel, srcFrameRate,
+                            srcDurationMs, srcIsHdr, dstWidth, dstHeight, dstMime, dstIsHdr);
+}
+
+bool TranscodingLogger::shouldLogAtom(const std::chrono::steady_clock::time_point& now,
+                                      int status) {
+    std::scoped_lock lock{mLock};
+    static const std::chrono::hours oneDay(24);
+
+    // Remove events older than one day.
+    while (mLastLoggedAtoms.size() > 0 && (now - mLastLoggedAtoms.front().first) >= oneDay) {
+        if (mLastLoggedAtoms.front().second == AMEDIA_OK) {
+            --mSuccessfulCount;
+        }
+        mLastLoggedAtoms.pop();
+    }
+
+    // Don't log if maximum number of events is reached.
+    if (mLastLoggedAtoms.size() >= kMaxAtomsPerDay) {
+        return false;
+    }
+
+    // Don't log if the event is successful and the maximum number of successful events is reached.
+    if (status == AMEDIA_OK && mSuccessfulCount >= kMaxSuccessfulAtomsPerDay) {
+        return false;
+    }
+
+    // Record the event.
+    if (status == AMEDIA_OK) {
+        ++mSuccessfulCount;
+    }
+    mLastLoggedAtoms.emplace(now, status);
+    return true;
+}
+
+void TranscodingLogger::setSessionEndedAtomWriter(const SessionEndedAtomWriter& writer) {
+    mSessionEndedAtomWriter = writer;
+}
+
+}  // namespace android
diff --git a/media/libmediatranscoding/TranscodingSessionController.cpp b/media/libmediatranscoding/TranscodingSessionController.cpp
index d12af21..aeabe0f 100644
--- a/media/libmediatranscoding/TranscodingSessionController.cpp
+++ b/media/libmediatranscoding/TranscodingSessionController.cpp
@@ -87,15 +87,12 @@
     // Whether watchdog is aborted and the monitoring thread should exit.
     bool mAbort GUARDED_BY(mLock);
     // When watchdog is active, the next timeout time point.
-    std::chrono::system_clock::time_point mNextTimeoutTime GUARDED_BY(mLock);
+    std::chrono::steady_clock::time_point mNextTimeoutTime GUARDED_BY(mLock);
     // When watchdog is active, the session being watched.
     SessionKeyType mSessionToWatch GUARDED_BY(mLock);
     std::thread mThread;
 };
 
-static constexpr int64_t kWatchdogTimeoutUs = 3000000LL;
-static constexpr int64_t kTranscoderHeartBeatIntervalUs = 1000000LL;
-
 TranscodingSessionController::Watchdog::Watchdog(TranscodingSessionController* owner,
                                                  int64_t timeoutUs)
       : mOwner(owner),
@@ -159,7 +156,7 @@
 // updateTimer_l() is only called with lock held.
 void TranscodingSessionController::Watchdog::updateTimer_l() NO_THREAD_SAFETY_ANALYSIS {
     std::chrono::microseconds timeout(mTimeoutUs);
-    mNextTimeoutTime = std::chrono::system_clock::now() + timeout;
+    mNextTimeoutTime = std::chrono::steady_clock::now() + timeout;
 }
 
 // Unfortunately std::unique_lock is incompatible with -Wthread-safety.
@@ -188,12 +185,84 @@
     }
 }
 ///////////////////////////////////////////////////////////////////////////////
+struct TranscodingSessionController::Pacer {
+    Pacer(const ControllerConfig& config)
+          : mBurstThresholdMs(config.pacerBurstThresholdMs),
+            mBurstCountQuota(config.pacerBurstCountQuota),
+            mBurstTimeQuotaSec(config.pacerBurstTimeQuotaSeconds) {}
+
+    ~Pacer() = default;
+
+    void onSessionCompleted(uid_t uid, std::chrono::microseconds runningTime);
+    bool onSessionStarted(uid_t uid);
+
+private:
+    // Threshold of time between finish/start below which a back-to-back start is counted.
+    int32_t mBurstThresholdMs;
+    // Maximum allowed back-to-back start count.
+    int32_t mBurstCountQuota;
+    // Maximum allowed back-to-back running time.
+    int32_t mBurstTimeQuotaSec;
+
+    struct UidHistoryEntry {
+        std::chrono::steady_clock::time_point lastCompletedTime;
+        int32_t burstCount = 0;
+        std::chrono::steady_clock::duration burstDuration{0};
+    };
+    std::map<uid_t, UidHistoryEntry> mUidHistoryMap;
+};
+
+void TranscodingSessionController::Pacer::onSessionCompleted(
+        uid_t uid, std::chrono::microseconds runningTime) {
+    if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) {
+        mUidHistoryMap.emplace(uid, UidHistoryEntry{});
+    }
+    mUidHistoryMap[uid].lastCompletedTime = std::chrono::steady_clock::now();
+    mUidHistoryMap[uid].burstCount++;
+    mUidHistoryMap[uid].burstDuration += runningTime;
+}
+
+bool TranscodingSessionController::Pacer::onSessionStarted(uid_t uid) {
+    // If uid doesn't exist, this uid has no completed sessions. Skip.
+    if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) {
+        return true;
+    }
+
+    // TODO: if Thermal throttling or resoure lost happened to occurr between this start
+    // and the previous completion, we should deduct the paused time from the elapsed time.
+    // (Individual session's pause time, on the other hand, doesn't need to be deducted
+    // because it doesn't affect the gap between last completion and the start.
+    auto timeSinceLastComplete =
+            std::chrono::steady_clock::now() - mUidHistoryMap[uid].lastCompletedTime;
+    if (mUidHistoryMap[uid].burstCount >= mBurstCountQuota &&
+        mUidHistoryMap[uid].burstDuration >= std::chrono::seconds(mBurstTimeQuotaSec)) {
+        ALOGW("Pacer: uid %d: over quota, burst count %d, time %lldms", uid,
+              mUidHistoryMap[uid].burstCount, (long long)mUidHistoryMap[uid].burstDuration.count());
+        return false;
+    }
+
+    // If not over quota, allow the session, and reset as long as this is not too close
+    // to previous completion.
+    if (timeSinceLastComplete > std::chrono::milliseconds(mBurstThresholdMs)) {
+        ALOGV("Pacer: uid %d: reset quota", uid);
+        mUidHistoryMap[uid].burstCount = 0;
+        mUidHistoryMap[uid].burstDuration = std::chrono::milliseconds(0);
+    } else {
+        ALOGV("Pacer: uid %d: burst count %d, time %lldms", uid, mUidHistoryMap[uid].burstCount,
+              (long long)mUidHistoryMap[uid].burstDuration.count());
+    }
+
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
 
 TranscodingSessionController::TranscodingSessionController(
         const TranscoderFactoryType& transcoderFactory,
         const std::shared_ptr<UidPolicyInterface>& uidPolicy,
         const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy,
-        const std::shared_ptr<ThermalPolicyInterface>& thermalPolicy)
+        const std::shared_ptr<ThermalPolicyInterface>& thermalPolicy,
+        const ControllerConfig* config)
       : mTranscoderFactory(transcoderFactory),
         mUidPolicy(uidPolicy),
         mResourcePolicy(resourcePolicy),
@@ -206,6 +275,13 @@
     mSessionQueues.emplace(OFFLINE_UID, SessionQueueType());
     mUidPackageNames[OFFLINE_UID] = "(offline)";
     mThermalThrottling = thermalPolicy->getThrottlingStatus();
+    if (config != nullptr) {
+        mConfig = *config;
+    }
+    mPacer.reset(new Pacer(mConfig));
+    ALOGD("@@@ watchdog %lld, burst count %d, burst time %d, burst threshold %d",
+          (long long)mConfig.watchdogTimeoutUs, mConfig.pacerBurstCountQuota,
+          mConfig.pacerBurstTimeQuotaSeconds, mConfig.pacerBurstThresholdMs);
 }
 
 TranscodingSessionController::~TranscodingSessionController() {}
@@ -280,10 +356,21 @@
     write(fd, result.string(), result.size());
 }
 
+/*
+ * Returns nullptr if there is no session, or we're paused globally (due to resource lost,
+ * thermal throttling, etc.). Otherwise, return the session that should be run next.
+ */
 TranscodingSessionController::Session* TranscodingSessionController::getTopSession_l() {
     if (mSessionMap.empty()) {
         return nullptr;
     }
+
+    // Return nullptr if we're paused globally due to resource lost or thermal throttling.
+    if (((mResourcePolicy != nullptr && mResourceLost) ||
+         (mThermalPolicy != nullptr && mThermalThrottling))) {
+        return nullptr;
+    }
+
     uid_t topUid = *mUidSortedList.begin();
     SessionKeyType topSessionKey = *mSessionQueues[topUid].begin();
     return &mSessionMap[topSessionKey];
@@ -313,9 +400,10 @@
     if (state == newState) {
         return;
     }
-    auto nowTime = std::chrono::system_clock::now();
+    auto nowTime = std::chrono::steady_clock::now();
     if (state != INVALID) {
-        std::chrono::microseconds elapsedTime = (nowTime - stateEnterTime);
+        std::chrono::microseconds elapsedTime =
+                std::chrono::duration_cast<std::chrono::microseconds>(nowTime - stateEnterTime);
         switch (state) {
         case PAUSED:
             pausedTime = pausedTime + elapsedTime;
@@ -338,47 +426,60 @@
 }
 
 void TranscodingSessionController::updateCurrentSession_l() {
-    Session* topSession = getTopSession_l();
     Session* curSession = mCurrentSession;
-    ALOGV("updateCurrentSession: topSession is %s, curSession is %s",
-          topSession == nullptr ? "null" : sessionToString(topSession->key).c_str(),
-          curSession == nullptr ? "null" : sessionToString(curSession->key).c_str());
+    Session* topSession = getTopSession_l();
 
-    if (topSession == nullptr) {
-        mCurrentSession = nullptr;
-        return;
+    // Delayed init of transcoder and watchdog.
+    if (mTranscoder == nullptr) {
+        mTranscoder = mTranscoderFactory(shared_from_this());
+        mWatchdog = std::make_shared<Watchdog>(this, mConfig.watchdogTimeoutUs);
     }
 
-    bool shouldBeRunning = !((mResourcePolicy != nullptr && mResourceLost) ||
-                             (mThermalPolicy != nullptr && mThermalThrottling));
-    // If we found a topSession that should be run, and it's not already running,
-    // take some actions to ensure it's running.
-    if (topSession != curSession ||
-        (shouldBeRunning ^ (topSession->getState() == Session::RUNNING))) {
-        if (mTranscoder == nullptr) {
-            mTranscoder = mTranscoderFactory(shared_from_this(), kTranscoderHeartBeatIntervalUs);
-            mWatchdog = std::make_shared<Watchdog>(this, kWatchdogTimeoutUs);
-        }
+    // If we found a different top session, or the top session's running state is not
+    // correct. Take some actions to ensure it's correct.
+    while ((topSession = getTopSession_l()) != curSession ||
+           (topSession != nullptr && !topSession->isRunning())) {
+        ALOGV("updateCurrentSession_l: topSession is %s, curSession is %s",
+              topSession == nullptr ? "null" : sessionToString(topSession->key).c_str(),
+              curSession == nullptr ? "null" : sessionToString(curSession->key).c_str());
 
-        // If current session is running, pause it first. Note this is true for either
-        // cases: 1) If top session is changing, or 2) if top session is not changing but
-        // the topSession's state is changing.
+        // If current session is running, pause it first. Note this is needed for either
+        // cases: 1) Top session is changing to another session, or 2) Top session is
+        // changing to null (which means we should be globally paused).
         if (curSession != nullptr && curSession->getState() == Session::RUNNING) {
             mTranscoder->pause(curSession->key.first, curSession->key.second);
             setSessionState_l(curSession, Session::PAUSED);
         }
-        // If we are not experiencing resource loss nor thermal throttling, we can start
-        // or resume the topSession now.
-        if (shouldBeRunning) {
-            if (topSession->getState() == Session::NOT_STARTED) {
-                mTranscoder->start(topSession->key.first, topSession->key.second,
-                                   topSession->request, topSession->callback.lock());
-            } else if (topSession->getState() == Session::PAUSED) {
-                mTranscoder->resume(topSession->key.first, topSession->key.second,
-                                    topSession->request, topSession->callback.lock());
+
+        if (topSession == nullptr) {
+            // Nothing more to run (either no session or globally paused).
+            break;
+        }
+
+        // Otherwise, ensure topSession is running.
+        if (topSession->getState() == Session::NOT_STARTED) {
+            if (!mPacer->onSessionStarted(topSession->clientUid)) {
+                // Unfortunately this uid is out of quota for new sessions.
+                // Drop this sesion and try another one.
+                {
+                    auto clientCallback = mSessionMap[topSession->key].callback.lock();
+                    if (clientCallback != nullptr) {
+                        clientCallback->onTranscodingFailed(
+                                topSession->key.second, TranscodingErrorCode::kDroppedByService);
+                    }
+                }
+                removeSession_l(topSession->key, Session::DROPPED_BY_PACER);
+                continue;
             }
+            mTranscoder->start(topSession->key.first, topSession->key.second, topSession->request,
+                               topSession->callingUid, topSession->callback.lock());
+            setSessionState_l(topSession, Session::RUNNING);
+        } else if (topSession->getState() == Session::PAUSED) {
+            mTranscoder->resume(topSession->key.first, topSession->key.second, topSession->request,
+                                topSession->callingUid, topSession->callback.lock());
             setSessionState_l(topSession, Session::RUNNING);
         }
+        break;
     }
     mCurrentSession = topSession;
 }
@@ -393,7 +494,7 @@
     }
 
     // Remove session from uid's queue.
-    const uid_t uid = mSessionMap[sessionKey].uid;
+    const uid_t uid = mSessionMap[sessionKey].clientUid;
     SessionQueueType& sessionQueue = mSessionQueues[uid];
     auto it = std::find(sessionQueue.begin(), sessionQueue.end(), sessionKey);
     if (it == sessionQueue.end()) {
@@ -419,6 +520,12 @@
     }
 
     setSessionState_l(&mSessionMap[sessionKey], finalState);
+
+    if (finalState == Session::FINISHED || finalState == Session::ERROR) {
+        mPacer->onSessionCompleted(mSessionMap[sessionKey].clientUid,
+                                   mSessionMap[sessionKey].runningTime);
+    }
+
     mSessionHistory.push_back(mSessionMap[sessionKey]);
     if (mSessionHistory.size() > kSessionHistoryMax) {
         mSessionHistory.erase(mSessionHistory.begin());
@@ -482,13 +589,13 @@
 }
 
 bool TranscodingSessionController::submit(
-        ClientIdType clientId, SessionIdType sessionId, uid_t uid,
+        ClientIdType clientId, SessionIdType sessionId, uid_t callingUid, uid_t clientUid,
         const TranscodingRequestParcel& request,
         const std::weak_ptr<ITranscodingClientCallback>& callback) {
     SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
 
     ALOGV("%s: session %s, uid %d, prioirty %d", __FUNCTION__, sessionToString(sessionKey).c_str(),
-          uid, (int32_t)request.priority);
+          clientUid, (int32_t)request.priority);
 
     std::scoped_lock lock{mLock};
 
@@ -498,21 +605,20 @@
     }
 
     // Add the uid package name to the store of package names we already know.
-    if (mUidPackageNames.count(uid) == 0) {
-        mUidPackageNames.emplace(uid, request.clientPackageName);
+    if (mUidPackageNames.count(clientUid) == 0) {
+        mUidPackageNames.emplace(clientUid, request.clientPackageName);
     }
 
     // TODO(chz): only support offline vs real-time for now. All kUnspecified sessions
     // go to offline queue.
     if (request.priority == TranscodingSessionPriority::kUnspecified) {
-        uid = OFFLINE_UID;
+        clientUid = OFFLINE_UID;
     }
 
     // Add session to session map.
     mSessionMap[sessionKey].key = sessionKey;
-    mSessionMap[sessionKey].uid = uid;
-    mSessionMap[sessionKey].lastProgress = 0;
-    mSessionMap[sessionKey].pauseCount = 0;
+    mSessionMap[sessionKey].clientUid = clientUid;
+    mSessionMap[sessionKey].callingUid = callingUid;
     mSessionMap[sessionKey].request = request;
     mSessionMap[sessionKey].callback = callback;
     setSessionState_l(&mSessionMap[sessionKey], Session::NOT_STARTED);
@@ -520,25 +626,25 @@
     // If it's an offline session, the queue was already added in constructor.
     // If it's a real-time sessions, check if a queue is already present for the uid,
     // and add a new queue if needed.
-    if (uid != OFFLINE_UID) {
-        if (mSessionQueues.count(uid) == 0) {
-            mUidPolicy->registerMonitorUid(uid);
-            if (mUidPolicy->isUidOnTop(uid)) {
-                mUidSortedList.push_front(uid);
+    if (clientUid != OFFLINE_UID) {
+        if (mSessionQueues.count(clientUid) == 0) {
+            mUidPolicy->registerMonitorUid(clientUid);
+            if (mUidPolicy->isUidOnTop(clientUid)) {
+                mUidSortedList.push_front(clientUid);
             } else {
                 // Shouldn't be submitting real-time requests from non-top app,
                 // put it in front of the offline queue.
-                mUidSortedList.insert(mOfflineUidIterator, uid);
+                mUidSortedList.insert(mOfflineUidIterator, clientUid);
             }
-        } else if (uid != *mUidSortedList.begin()) {
-            if (mUidPolicy->isUidOnTop(uid)) {
-                mUidSortedList.remove(uid);
-                mUidSortedList.push_front(uid);
+        } else if (clientUid != *mUidSortedList.begin()) {
+            if (mUidPolicy->isUidOnTop(clientUid)) {
+                mUidSortedList.remove(clientUid);
+                mUidSortedList.push_front(clientUid);
             }
         }
     }
     // Append this session to the uid's queue.
-    mSessionQueues[uid].push_back(sessionKey);
+    mSessionQueues[clientUid].push_back(sessionKey);
 
     updateCurrentSession_l();
 
@@ -557,7 +663,7 @@
 
     if (sessionId < 0) {
         for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) {
-            if (it->first.first == clientId && it->second.uid != OFFLINE_UID) {
+            if (it->first.first == clientId && it->second.clientUid != OFFLINE_UID) {
                 sessionsToRemove.push_back(it->first);
             }
         }
@@ -687,7 +793,7 @@
             mTranscoder->stop(clientId, sessionId, true /*abandon*/);
             // Clear the last ref count before we create new transcoder.
             mTranscoder = nullptr;
-            mTranscoder = mTranscoderFactory(shared_from_this(), kTranscoderHeartBeatIntervalUs);
+            mTranscoder = mTranscoderFactory(shared_from_this());
         }
 
         {
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingErrorCode.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingErrorCode.aidl
index 23072ff..5349fe1 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingErrorCode.aidl
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingErrorCode.aidl
@@ -23,13 +23,19 @@
  */
 @Backing(type = "int")
 enum TranscodingErrorCode {
+    // Errors exposed to client side.
     kNoError = 0,
-    kUnknown = 1,
-    kMalformed = 2,
-    kUnsupported = 3,
-    kInvalidParameter = 4,
-    kInvalidOperation = 5,
-    kErrorIO = 6,
-    kInsufficientResources = 7,
-    kWatchdogTimeout = 8,
+    kDroppedByService = 1,
+    kServiceUnavailable = 2,
+
+    // Other private errors.
+    kPrivateErrorFirst     = 1000,
+    kUnknown               = kPrivateErrorFirst + 0,
+    kMalformed             = kPrivateErrorFirst + 1,
+    kUnsupported           = kPrivateErrorFirst + 2,
+    kInvalidParameter      = kPrivateErrorFirst + 3,
+    kInvalidOperation      = kPrivateErrorFirst + 4,
+    kErrorIO               = kPrivateErrorFirst + 5,
+    kInsufficientResources = kPrivateErrorFirst + 6,
+    kWatchdogTimeout       = kPrivateErrorFirst + 7,
 }
\ No newline at end of file
diff --git a/media/libmediatranscoding/include/media/ControllerClientInterface.h b/media/libmediatranscoding/include/media/ControllerClientInterface.h
index 3fd4f0c..0d13607 100644
--- a/media/libmediatranscoding/include/media/ControllerClientInterface.h
+++ b/media/libmediatranscoding/include/media/ControllerClientInterface.h
@@ -36,8 +36,8 @@
      * Returns true on success and false on failure. This call will fail is a session identified
      * by <clientId, sessionId> already exists.
      */
-    virtual bool submit(ClientIdType clientId, SessionIdType sessionId, uid_t uid,
-                        const TranscodingRequestParcel& request,
+    virtual bool submit(ClientIdType clientId, SessionIdType sessionId, uid_t callingUid,
+                        uid_t clientUid, const TranscodingRequestParcel& request,
                         const std::weak_ptr<ITranscodingClientCallback>& clientCallback) = 0;
 
     /**
diff --git a/media/libmediatranscoding/include/media/TranscoderInterface.h b/media/libmediatranscoding/include/media/TranscoderInterface.h
index 5f27d82..3b0bd3b 100644
--- a/media/libmediatranscoding/include/media/TranscoderInterface.h
+++ b/media/libmediatranscoding/include/media/TranscoderInterface.h
@@ -33,11 +33,11 @@
 class TranscoderInterface {
 public:
     virtual void start(ClientIdType clientId, SessionIdType sessionId,
-                       const TranscodingRequestParcel& request,
+                       const TranscodingRequestParcel& request, uid_t callingUid,
                        const std::shared_ptr<ITranscodingClientCallback>& clientCallback) = 0;
     virtual void pause(ClientIdType clientId, SessionIdType sessionId) = 0;
     virtual void resume(ClientIdType clientId, SessionIdType sessionId,
-                        const TranscodingRequestParcel& request,
+                        const TranscodingRequestParcel& request, uid_t callingUid,
                         const std::shared_ptr<ITranscodingClientCallback>& clientCallback) = 0;
     // Stop the specified session. If abandon is true, the transcoder wrapper will be discarded
     // after the session stops.
diff --git a/media/libmediatranscoding/include/media/TranscoderWrapper.h b/media/libmediatranscoding/include/media/TranscoderWrapper.h
index 7935bbe..d3d4c86 100644
--- a/media/libmediatranscoding/include/media/TranscoderWrapper.h
+++ b/media/libmediatranscoding/include/media/TranscoderWrapper.h
@@ -18,8 +18,11 @@
 #define ANDROID_TRANSCODER_WRAPPER_H
 
 #include <media/NdkMediaError.h>
+#include <media/NdkMediaFormat.h>
 #include <media/TranscoderInterface.h>
+#include <media/TranscodingLogger.h>
 
+#include <chrono>
 #include <list>
 #include <map>
 #include <mutex>
@@ -37,16 +40,17 @@
                           public std::enable_shared_from_this<TranscoderWrapper> {
 public:
     TranscoderWrapper(const std::shared_ptr<TranscoderCallbackInterface>& cb,
+                      const std::shared_ptr<TranscodingLogger>& logger,
                       int64_t heartBeatIntervalUs);
     ~TranscoderWrapper();
 
     // TranscoderInterface
     void start(ClientIdType clientId, SessionIdType sessionId,
-               const TranscodingRequestParcel& request,
+               const TranscodingRequestParcel& request, uid_t callingUid,
                const std::shared_ptr<ITranscodingClientCallback>& clientCallback) override;
     void pause(ClientIdType clientId, SessionIdType sessionId) override;
     void resume(ClientIdType clientId, SessionIdType sessionId,
-                const TranscodingRequestParcel& request,
+                const TranscodingRequestParcel& request, uid_t callingUid,
                 const std::shared_ptr<ITranscodingClientCallback>& clientCallback) override;
     void stop(ClientIdType clientId, SessionIdType sessionId, bool abandon = false) override;
     // ~TranscoderInterface
@@ -76,6 +80,9 @@
     std::shared_ptr<CallbackImpl> mTranscoderCb;
     std::shared_ptr<MediaTranscoder> mTranscoder;
     std::weak_ptr<TranscoderCallbackInterface> mCallback;
+    std::shared_ptr<TranscodingLogger> mLogger;
+    std::shared_ptr<AMediaFormat> mSrcFormat;
+    std::shared_ptr<AMediaFormat> mDstFormat;
     int64_t mHeartBeatIntervalUs;
     std::mutex mLock;
     std::condition_variable mCondition;
@@ -83,6 +90,9 @@
     std::map<SessionKeyType, std::shared_ptr<ndk::ScopedAParcel>> mPausedStateMap;
     ClientIdType mCurrentClientId;
     SessionIdType mCurrentSessionId;
+    uid_t mCurrentCallingUid;
+    std::chrono::steady_clock::time_point mTranscodeStartTime;
+
     // Whether the looper has been created.
     bool mLooperReady;
 
@@ -93,18 +103,20 @@
     void onHeartBeat(ClientIdType clientId, SessionIdType sessionId);
 
     media_status_t handleStart(ClientIdType clientId, SessionIdType sessionId,
-                               const TranscodingRequestParcel& request,
+                               const TranscodingRequestParcel& request, uid_t callingUid,
                                const std::shared_ptr<ITranscodingClientCallback>& callback);
     media_status_t handlePause(ClientIdType clientId, SessionIdType sessionId);
     media_status_t handleResume(ClientIdType clientId, SessionIdType sessionId,
-                                const TranscodingRequestParcel& request,
+                                const TranscodingRequestParcel& request, uid_t callingUid,
                                 const std::shared_ptr<ITranscodingClientCallback>& callback);
     media_status_t setupTranscoder(
             ClientIdType clientId, SessionIdType sessionId, const TranscodingRequestParcel& request,
-            const std::shared_ptr<ITranscodingClientCallback>& callback,
+            uid_t callingUid, const std::shared_ptr<ITranscodingClientCallback>& callback,
+            TranscodingLogger::SessionEndedReason* failureReason /* nonnull */,
             const std::shared_ptr<ndk::ScopedAParcel>& pausedState = nullptr);
 
     void cleanup();
+    void logSessionEnded(const TranscodingLogger::SessionEndedReason& reason, int error);
     void reportError(ClientIdType clientId, SessionIdType sessionId, media_status_t err);
     void queueEvent(Event::Type type, ClientIdType clientId, SessionIdType sessionId,
                     const std::function<void()> runnable, int32_t arg = 0);
diff --git a/media/libmediatranscoding/include/media/TranscodingLogger.h b/media/libmediatranscoding/include/media/TranscodingLogger.h
new file mode 100644
index 0000000..dc24551
--- /dev/null
+++ b/media/libmediatranscoding/include/media/TranscodingLogger.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_TRANSCODING_LOGGER_H
+#define ANDROID_MEDIA_TRANSCODING_LOGGER_H
+
+#include <media/NdkMediaFormat.h>
+#include <utils/Condition.h>
+
+#include <chrono>
+#include <memory>
+#include <mutex>
+#include <queue>
+
+namespace android {
+
+/** Class for logging transcoding events. */
+class TranscodingLogger {
+public:
+    /** The maximum number of atoms pushed to statsd per day. */
+    static constexpr int kMaxAtomsPerDay = 50;
+
+    /** The maximum number of successful transcoding atoms pushed to statsd per day. */
+    static constexpr int kMaxSuccessfulAtomsPerDay = 35;
+
+    /** Reason transcoding session ended. Maps to MediaTranscodingSessionEnded atom's Reason. */
+    enum SessionEndedReason {
+        UNKNOWN = 0,
+        FINISHED,
+        ERROR,
+        PAUSED,
+        CANCELLED,
+        START_FAILED,
+        RESUME_FAILED,
+        CREATE_FAILED,
+        CONFIG_SRC_FAILED,
+        CONFIG_DST_FAILED,
+        CONFIG_TRACK_FAILED,
+        OPEN_SRC_FD_FAILED,
+        OPEN_DST_FD_FAILED,
+        NO_TRACKS,
+    };
+
+    TranscodingLogger();
+    ~TranscodingLogger() = default;
+
+    /**
+     * Logs a transcoding session ended event (MediaTranscodingSessionEnded atom).
+     * @param reason Reason for the transcoding session to end.
+     * @param callingUid UID of the caller connecting to the transcoding service.
+     * @param status Status (error code) of the transcoding session.
+     * @param duration Duration of the transcoding session.
+     * @param srcFormat The source video track format.
+     * @param dstFormat The destination video track format.
+     */
+    void logSessionEnded(enum SessionEndedReason reason, uid_t callingUid, int status,
+                         std::chrono::microseconds duration, AMediaFormat* srcFormat,
+                         AMediaFormat* dstFormat);
+
+private:
+    friend class TranscodingLoggerTest;
+
+    // Function prototype for writing out the session ended atom.
+    using SessionEndedAtomWriter = std::function<int(
+            int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, char const*, int32_t,
+            int32_t, int32_t, int32_t, bool arg12, int32_t, int32_t, char const*, bool)>;
+
+    std::mutex mLock;
+    std::queue<std::pair<std::chrono::steady_clock::time_point, int>> mLastLoggedAtoms
+            GUARDED_BY(mLock);
+    uint32_t mSuccessfulCount = 0;
+    SessionEndedAtomWriter mSessionEndedAtomWriter;
+
+    void logSessionEnded(const std::chrono::steady_clock::time_point& now,
+                         enum SessionEndedReason reason, uid_t callingUid, int status,
+                         std::chrono::microseconds duration, AMediaFormat* srcFormat,
+                         AMediaFormat* dstFormat);
+    bool shouldLogAtom(const std::chrono::steady_clock::time_point& now, int status);
+    // Used for testing to validate what gets sent to statsd.
+    void setSessionEndedAtomWriter(const SessionEndedAtomWriter& writer);
+};
+
+}  // namespace android
+#endif  // ANDROID_MEDIA_TRANSCODING_LOGGER_H
diff --git a/media/libmediatranscoding/include/media/TranscodingSessionController.h b/media/libmediatranscoding/include/media/TranscodingSessionController.h
index 34e9506..b2d6f0a 100644
--- a/media/libmediatranscoding/include/media/TranscodingSessionController.h
+++ b/media/libmediatranscoding/include/media/TranscodingSessionController.h
@@ -48,7 +48,7 @@
     virtual ~TranscodingSessionController();
 
     // ControllerClientInterface
-    bool submit(ClientIdType clientId, SessionIdType sessionId, uid_t uid,
+    bool submit(ClientIdType clientId, SessionIdType sessionId, uid_t callingUid, uid_t clientUid,
                 const TranscodingRequestParcel& request,
                 const std::weak_ptr<ITranscodingClientCallback>& clientCallback) override;
     bool cancel(ClientIdType clientId, SessionIdType sessionId) override;
@@ -93,7 +93,18 @@
     using SessionKeyType = std::pair<ClientIdType, SessionIdType>;
     using SessionQueueType = std::list<SessionKeyType>;
     using TranscoderFactoryType = std::function<std::shared_ptr<TranscoderInterface>(
-            const std::shared_ptr<TranscoderCallbackInterface>&, int64_t)>;
+            const std::shared_ptr<TranscoderCallbackInterface>&)>;
+
+    struct ControllerConfig {
+        // Watchdog timeout.
+        int64_t watchdogTimeoutUs = 3000000LL;
+        // Threshold of time between finish/start below which a back-to-back start is counted.
+        int32_t pacerBurstThresholdMs = 1000;
+        // Maximum allowed back-to-back start count.
+        int32_t pacerBurstCountQuota = 10;
+        // Maximum allowed back-to-back running time.
+        int32_t pacerBurstTimeQuotaSeconds = 180;  // 3-min
+    };
 
     struct Session {
         enum State {
@@ -106,15 +117,17 @@
             FINISHED,
             CANCELED,
             ERROR,
+            DROPPED_BY_PACER,
         };
         SessionKeyType key;
-        uid_t uid;
-        int32_t lastProgress;
-        int32_t pauseCount;
-        std::chrono::time_point<std::chrono::system_clock> stateEnterTime;
-        std::chrono::microseconds waitingTime;
-        std::chrono::microseconds runningTime;
-        std::chrono::microseconds pausedTime;
+        uid_t clientUid;
+        uid_t callingUid;
+        int32_t lastProgress = 0;
+        int32_t pauseCount = 0;
+        std::chrono::time_point<std::chrono::steady_clock> stateEnterTime;
+        std::chrono::microseconds waitingTime{0};
+        std::chrono::microseconds runningTime{0};
+        std::chrono::microseconds pausedTime{0};
 
         TranscodingRequest request;
         std::weak_ptr<ITranscodingClientCallback> callback;
@@ -122,12 +135,16 @@
         // Must use setState to change state.
         void setState(Session::State state);
         State getState() const { return state; }
+        bool isRunning() { return state == RUNNING; }
 
     private:
         State state = INVALID;
     };
 
     struct Watchdog;
+    struct Pacer;
+
+    ControllerConfig mConfig;
 
     // TODO(chz): call transcoder without global lock.
     // Use mLock for all entrypoints for now.
@@ -155,12 +172,14 @@
     bool mThermalThrottling;
     std::list<Session> mSessionHistory;
     std::shared_ptr<Watchdog> mWatchdog;
+    std::shared_ptr<Pacer> mPacer;
 
     // Only allow MediaTranscodingService and unit tests to instantiate.
     TranscodingSessionController(const TranscoderFactoryType& transcoderFactory,
                                  const std::shared_ptr<UidPolicyInterface>& uidPolicy,
                                  const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy,
-                                 const std::shared_ptr<ThermalPolicyInterface>& thermalPolicy);
+                                 const std::shared_ptr<ThermalPolicyInterface>& thermalPolicy,
+                                 const ControllerConfig* config = nullptr);
 
     void dumpSession_l(const Session& session, String8& result, bool closedSession = false);
     Session* getTopSession_l();
diff --git a/media/libmediatranscoding/tests/Android.bp b/media/libmediatranscoding/tests/Android.bp
index 06b9b17..603611a 100644
--- a/media/libmediatranscoding/tests/Android.bp
+++ b/media/libmediatranscoding/tests/Android.bp
@@ -72,3 +72,15 @@
 
     srcs: ["AdjustableMaxPriorityQueue_tests.cpp"],
 }
+
+//
+// TranscodingLogger unit test
+//
+cc_test {
+    name: "TranscodingLogger_tests",
+    defaults: ["libmediatranscoding_test_defaults"],
+    shared_libs: ["libmediandk", "libstatssocket#30"],
+    static_libs: ["libmediatranscoder", "libstatslog_media"],
+
+    srcs: ["TranscodingLogger_tests.cpp"],
+}
diff --git a/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp b/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp
index 1a50923..57a2e27 100644
--- a/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp
+++ b/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp
@@ -135,7 +135,7 @@
 
     virtual ~TestController() { ALOGI("TestController Destroyed"); }
 
-    bool submit(ClientIdType clientId, SessionIdType sessionId, uid_t /*uid*/,
+    bool submit(ClientIdType clientId, SessionIdType sessionId, uid_t /*callingUid*/, uid_t /*uid*/,
                 const TranscodingRequestParcel& request,
                 const std::weak_ptr<ITranscodingClientCallback>& clientCallback) override {
         SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
diff --git a/media/libmediatranscoding/tests/TranscodingLogger_tests.cpp b/media/libmediatranscoding/tests/TranscodingLogger_tests.cpp
new file mode 100644
index 0000000..39e5cd4
--- /dev/null
+++ b/media/libmediatranscoding/tests/TranscodingLogger_tests.cpp
@@ -0,0 +1,286 @@
+/*
+ * 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.
+ */
+
+// Unit Test for TranscodingLogger
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "TranscodingLoggerTest"
+
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+#include <media/NdkCommon.h>
+#include <media/TranscodingLogger.h>
+#include <statslog_media.h>
+#include <utils/Log.h>
+
+#include <chrono>
+
+namespace android {
+
+using Reason = TranscodingLogger::SessionEndedReason;
+
+// Data structure corresponding to MediaTranscodingEnded atom.
+struct SessionEndedAtom {
+    SessionEndedAtom(int32_t atomCode, int32_t reason, int32_t callingUid, int32_t status,
+                     int32_t transcoderFps, int32_t srcWidth, int32_t srcHeight,
+                     char const* srcMime, int32_t srcProfile, int32_t srcLevel, int32_t srcFps,
+                     int32_t srcDurationMs, bool srcIsHdr, int32_t dstWidth, int32_t dstHeight,
+                     char const* dstMime, bool dstIsHdr)
+          : atomCode(atomCode),
+            reason(reason),
+            callingUid(callingUid),
+            status(status),
+            transcoderFps(transcoderFps),
+            srcWidth(srcWidth),
+            srcHeight(srcHeight),
+            srcMime(srcMime),
+            srcProfile(srcProfile),
+            srcLevel(srcLevel),
+            srcFps(srcFps),
+            srcDurationMs(srcDurationMs),
+            srcIsHdr(srcIsHdr),
+            dstWidth(dstWidth),
+            dstHeight(dstHeight),
+            dstMime(dstMime),
+            dstIsHdr(dstIsHdr) {}
+
+    int32_t atomCode;
+    int32_t reason;
+    int32_t callingUid;
+    int32_t status;
+    int32_t transcoderFps;
+    int32_t srcWidth;
+    int32_t srcHeight;
+    std::string srcMime;
+    int32_t srcProfile;
+    int32_t srcLevel;
+    int32_t srcFps;
+    int32_t srcDurationMs;
+    bool srcIsHdr;
+    int32_t dstWidth;
+    int32_t dstHeight;
+    std::string dstMime;
+    bool dstIsHdr;
+};
+
+// Default configuration values.
+static constexpr int32_t kDefaultCallingUid = 1;
+static constexpr std::chrono::microseconds kDefaultTranscodeDuration = std::chrono::seconds{2};
+
+static constexpr int32_t kDefaultSrcWidth = 1920;
+static constexpr int32_t kDefaultSrcHeight = 1080;
+static const std::string kDefaultSrcMime{AMEDIA_MIMETYPE_VIDEO_HEVC};
+static constexpr int32_t kDefaultSrcProfile = 1;    // HEVC Main
+static constexpr int32_t kDefaultSrcLevel = 65536;  // HEVCMainTierLevel51
+static constexpr int32_t kDefaultSrcFps = 30;
+static constexpr int32_t kDefaultSrcFrameCount = 120;
+static constexpr int64_t kDefaultSrcDurationUs = 1000000 * kDefaultSrcFrameCount / kDefaultSrcFps;
+
+static constexpr int32_t kDefaultDstWidth = 1280;
+static constexpr int32_t kDefaultDstHeight = 720;
+static const std::string kDefaultDstMime{AMEDIA_MIMETYPE_VIDEO_AVC};
+
+// Util for creating a default source video format.
+static AMediaFormat* CreateSrcFormat() {
+    AMediaFormat* fmt = AMediaFormat_new();
+    AMediaFormat_setInt32(fmt, AMEDIAFORMAT_KEY_WIDTH, kDefaultSrcWidth);
+    AMediaFormat_setInt32(fmt, AMEDIAFORMAT_KEY_HEIGHT, kDefaultSrcHeight);
+    AMediaFormat_setString(fmt, AMEDIAFORMAT_KEY_MIME, kDefaultSrcMime.c_str());
+    AMediaFormat_setInt32(fmt, AMEDIAFORMAT_KEY_PROFILE, kDefaultSrcProfile);
+    AMediaFormat_setInt32(fmt, AMEDIAFORMAT_KEY_LEVEL, kDefaultSrcLevel);
+    AMediaFormat_setInt32(fmt, AMEDIAFORMAT_KEY_FRAME_RATE, kDefaultSrcFps);
+    AMediaFormat_setInt32(fmt, AMEDIAFORMAT_KEY_FRAME_COUNT, kDefaultSrcFrameCount);
+    AMediaFormat_setInt64(fmt, AMEDIAFORMAT_KEY_DURATION, kDefaultSrcDurationUs);
+    return fmt;
+}
+
+// Util for creating a default destination video format.
+static AMediaFormat* CreateDstFormat() {
+    AMediaFormat* fmt = AMediaFormat_new();
+    AMediaFormat_setInt32(fmt, AMEDIAFORMAT_KEY_WIDTH, kDefaultDstWidth);
+    AMediaFormat_setInt32(fmt, AMEDIAFORMAT_KEY_HEIGHT, kDefaultDstHeight);
+    AMediaFormat_setString(fmt, AMEDIAFORMAT_KEY_MIME, kDefaultDstMime.c_str());
+    return fmt;
+}
+
+class TranscodingLoggerTest : public ::testing::Test {
+public:
+    TranscodingLoggerTest() { ALOGI("TranscodingLoggerTest created"); }
+
+    void SetUp() override {
+        ALOGI("TranscodingLoggerTest set up");
+        mLogger.reset(new TranscodingLogger());
+        mLoggedAtoms.clear();
+        mSrcFormat.reset();
+        mDstFormat.reset();
+
+        // Set a custom atom writer that saves all data, so the test can validate it afterwards.
+        mLogger->setSessionEndedAtomWriter(
+                [=](int32_t atomCode, int32_t reason, int32_t callingUid, int32_t status,
+                    int32_t transcoderFps, int32_t srcWidth, int32_t srcHeight, char const* srcMime,
+                    int32_t srcProfile, int32_t srcLevel, int32_t srcFps, int32_t srcDurationMs,
+                    bool srcIsHdr, int32_t dstWidth, int32_t dstHeight, char const* dstMime,
+                    bool dstIsHdr) -> int {
+                    mLoggedAtoms.emplace_back(atomCode, reason, callingUid, status, transcoderFps,
+                                              srcWidth, srcHeight, srcMime, srcProfile, srcLevel,
+                                              srcFps, srcDurationMs, srcIsHdr, dstWidth, dstHeight,
+                                              dstMime, dstIsHdr);
+                    return 0;
+                });
+    }
+
+    void logSession(const std::chrono::steady_clock::time_point& time, Reason reason, int status,
+                    AMediaFormat* srcFormat, AMediaFormat* dstFormat) {
+        mLogger->logSessionEnded(time, reason, kDefaultCallingUid, status,
+                                 kDefaultTranscodeDuration, srcFormat, dstFormat);
+    }
+
+    void logSession(const std::chrono::steady_clock::time_point& time, Reason reason, int status) {
+        if (!mSrcFormat) {
+            mSrcFormat = std::shared_ptr<AMediaFormat>(CreateSrcFormat(), &AMediaFormat_delete);
+        }
+        if (!mDstFormat) {
+            mDstFormat = std::shared_ptr<AMediaFormat>(CreateDstFormat(), &AMediaFormat_delete);
+        }
+        logSession(time, reason, status, mSrcFormat.get(), mDstFormat.get());
+    }
+
+    void logSessionFinished(const std::chrono::steady_clock::time_point& time) {
+        logSession(time, Reason::FINISHED, 0);
+    }
+
+    void logSessionFailed(const std::chrono::steady_clock::time_point& time) {
+        logSession(time, Reason::ERROR, AMEDIA_ERROR_UNKNOWN);
+    }
+
+    int logCount() const { return mLoggedAtoms.size(); }
+
+    void validateLatestAtom(Reason reason, int status, bool passthrough = false) {
+        const SessionEndedAtom& atom = mLoggedAtoms.back();
+
+        EXPECT_EQ(atom.atomCode, android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED);
+        EXPECT_EQ(atom.reason, static_cast<int>(reason));
+        EXPECT_EQ(atom.callingUid, kDefaultCallingUid);
+        EXPECT_EQ(atom.status, status);
+        EXPECT_EQ(atom.srcWidth, kDefaultSrcWidth);
+        EXPECT_EQ(atom.srcHeight, kDefaultSrcHeight);
+        EXPECT_EQ(atom.srcMime, kDefaultSrcMime);
+        EXPECT_EQ(atom.srcProfile, kDefaultSrcProfile);
+        EXPECT_EQ(atom.srcLevel, kDefaultSrcLevel);
+        EXPECT_EQ(atom.srcFps, kDefaultSrcFps);
+        EXPECT_EQ(atom.srcDurationMs, kDefaultSrcDurationUs / 1000);
+        EXPECT_FALSE(atom.srcIsHdr);
+        EXPECT_EQ(atom.dstWidth, passthrough ? kDefaultSrcWidth : kDefaultDstWidth);
+        EXPECT_EQ(atom.dstHeight, passthrough ? kDefaultSrcHeight : kDefaultDstHeight);
+        EXPECT_EQ(atom.dstMime, passthrough ? "passthrough" : kDefaultDstMime);
+        EXPECT_FALSE(atom.dstIsHdr);
+
+        // Transcoder frame rate is only present on successful sessions.
+        if (status == AMEDIA_OK) {
+            std::chrono::duration<double> seconds{kDefaultTranscodeDuration};
+            const int32_t transcoderFps =
+                    static_cast<int32_t>(kDefaultSrcFrameCount / seconds.count());
+            EXPECT_EQ(atom.transcoderFps, transcoderFps);
+        } else {
+            EXPECT_EQ(atom.transcoderFps, -1);
+        }
+    }
+
+    void TearDown() override { ALOGI("TranscodingLoggerTest tear down"); }
+    ~TranscodingLoggerTest() { ALOGD("TranscodingLoggerTest destroyed"); }
+
+    std::shared_ptr<TranscodingLogger> mLogger;
+    std::vector<SessionEndedAtom> mLoggedAtoms;
+
+    std::shared_ptr<AMediaFormat> mSrcFormat;
+    std::shared_ptr<AMediaFormat> mDstFormat;
+};
+
+TEST_F(TranscodingLoggerTest, TestDailyLogQuota) {
+    ALOGD("TestDailyLogQuota");
+    auto start = std::chrono::steady_clock::now();
+
+    EXPECT_LT(TranscodingLogger::kMaxSuccessfulAtomsPerDay, TranscodingLogger::kMaxAtomsPerDay);
+
+    // 1. Check that the first kMaxSuccessfulAtomsPerDay successful atoms are logged.
+    for (int i = 0; i < TranscodingLogger::kMaxSuccessfulAtomsPerDay; ++i) {
+        logSessionFinished(start + std::chrono::seconds{i});
+        EXPECT_EQ(logCount(), i + 1);
+    }
+
+    // 2. Check that subsequent successful atoms within the same 24h interval are not logged.
+    for (int i = 1; i < 24; ++i) {
+        logSessionFinished(start + std::chrono::hours{i});
+        EXPECT_EQ(logCount(), TranscodingLogger::kMaxSuccessfulAtomsPerDay);
+    }
+
+    // 3. Check that failed atoms are logged up to kMaxAtomsPerDay.
+    for (int i = TranscodingLogger::kMaxSuccessfulAtomsPerDay;
+         i < TranscodingLogger::kMaxAtomsPerDay; ++i) {
+        logSessionFailed(start + std::chrono::seconds{i});
+        EXPECT_EQ(logCount(), i + 1);
+    }
+
+    // 4. Check that subsequent failed atoms within the same 24h interval are not logged.
+    for (int i = 1; i < 24; ++i) {
+        logSessionFailed(start + std::chrono::hours{i});
+        EXPECT_EQ(logCount(), TranscodingLogger::kMaxAtomsPerDay);
+    }
+
+    // 5. Check that failed and successful atoms are logged again after 24h.
+    logSessionFinished(start + std::chrono::hours{24});
+    EXPECT_EQ(logCount(), TranscodingLogger::kMaxAtomsPerDay + 1);
+
+    logSessionFailed(start + std::chrono::hours{24} + std::chrono::seconds{1});
+    EXPECT_EQ(logCount(), TranscodingLogger::kMaxAtomsPerDay + 2);
+}
+
+TEST_F(TranscodingLoggerTest, TestNullFormats) {
+    ALOGD("TestNullFormats");
+    auto srcFormat = std::shared_ptr<AMediaFormat>(CreateSrcFormat(), &AMediaFormat_delete);
+    auto dstFormat = std::shared_ptr<AMediaFormat>(CreateDstFormat(), &AMediaFormat_delete);
+    auto now = std::chrono::steady_clock::now();
+
+    // Source format null, should not log.
+    logSession(now, Reason::FINISHED, AMEDIA_OK, nullptr /*srcFormat*/, dstFormat.get());
+    EXPECT_EQ(logCount(), 0);
+
+    // Both formats null, should not log.
+    logSession(now, Reason::FINISHED, AMEDIA_OK, nullptr /*srcFormat*/, nullptr /*dstFormat*/);
+    EXPECT_EQ(logCount(), 0);
+
+    // Destination format null (passthrough mode), should log.
+    logSession(now, Reason::FINISHED, AMEDIA_OK, srcFormat.get(), nullptr /*dstFormat*/);
+    EXPECT_EQ(logCount(), 1);
+    validateLatestAtom(Reason::FINISHED, AMEDIA_OK, true /*passthrough*/);
+}
+
+TEST_F(TranscodingLoggerTest, TestAtomContentCorrectness) {
+    ALOGD("TestAtomContentCorrectness");
+    auto now = std::chrono::steady_clock::now();
+
+    // Log and validate a failure.
+    logSession(now, Reason::ERROR, AMEDIA_ERROR_MALFORMED);
+    EXPECT_EQ(logCount(), 1);
+    validateLatestAtom(Reason::ERROR, AMEDIA_ERROR_MALFORMED);
+
+    // Log and validate a success.
+    logSession(now, Reason::FINISHED, AMEDIA_OK);
+    EXPECT_EQ(logCount(), 2);
+    validateLatestAtom(Reason::FINISHED, AMEDIA_OK);
+}
+
+}  // namespace android
diff --git a/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp b/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp
index 2e9daee..560d1fe 100644
--- a/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp
+++ b/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp
@@ -118,12 +118,12 @@
 
 class TestTranscoder : public TranscoderInterface {
 public:
-    TestTranscoder() : mLastError(TranscodingErrorCode::kUnknown), mGeneration(0) {}
+    TestTranscoder() : mGeneration(0) {}
     virtual ~TestTranscoder() {}
 
     // TranscoderInterface
     void start(ClientIdType clientId, SessionIdType sessionId,
-               const TranscodingRequestParcel& /*request*/,
+               const TranscodingRequestParcel& /*request*/, uid_t /*callingUid*/,
                const std::shared_ptr<ITranscodingClientCallback>& /*clientCallback*/) override {
         append(Start(clientId, sessionId));
     }
@@ -131,7 +131,7 @@
         append(Pause(clientId, sessionId));
     }
     void resume(ClientIdType clientId, SessionIdType sessionId,
-                const TranscodingRequestParcel& /*request*/,
+                const TranscodingRequestParcel& /*request*/, uid_t /*callingUid*/,
                 const std::shared_ptr<ITranscodingClientCallback>& /*clientCallback*/) override {
         append(Resume(clientId, sessionId));
     }
@@ -152,19 +152,6 @@
         mGeneration++;
     }
 
-    TranscodingErrorCode getLastError() {
-        std::scoped_lock lock{mLock};
-        // Clear last error.
-        TranscodingErrorCode result = mLastError;
-        mLastError = TranscodingErrorCode::kNoError;
-        return result;
-    }
-
-    int32_t getGeneration() {
-        std::scoped_lock lock{mLock};
-        return mGeneration;
-    }
-
     struct Event {
         enum { NoEvent, Start, Pause, Resume, Stop, Finished, Failed, Abandon } type;
         ClientIdType clientId;
@@ -195,7 +182,7 @@
         // Error is sticky, non-error event will not erase it, only getLastError()
         // clears last error.
         if (err != TranscodingErrorCode::kNoError) {
-            mLastError = err;
+            mLastErrorQueue.push_back(err);
         }
         mCondition.notify_one();
     }
@@ -218,12 +205,27 @@
         return mPoppedEvent;
     }
 
+    TranscodingErrorCode getLastError() {
+        std::scoped_lock lock{mLock};
+        if (mLastErrorQueue.empty()) {
+            return TranscodingErrorCode::kNoError;
+        }
+        TranscodingErrorCode err = mLastErrorQueue.front();
+        mLastErrorQueue.pop_front();
+        return err;
+    }
+
+    int32_t getGeneration() {
+        std::scoped_lock lock{mLock};
+        return mGeneration;
+    }
+
 private:
     std::mutex mLock;
     std::condition_variable mCondition;
     Event mPoppedEvent;
     std::list<Event> mEventQueue;
-    TranscodingErrorCode mLastError;
+    std::list<TranscodingErrorCode> mLastErrorQueue;
     int32_t mGeneration;
 };
 
@@ -291,16 +293,21 @@
         mUidPolicy.reset(new TestUidPolicy());
         mResourcePolicy.reset(new TestResourcePolicy());
         mThermalPolicy.reset(new TestThermalPolicy());
+        // Overrid default burst params with shorter values for testing.
+        TranscodingSessionController::ControllerConfig config = {
+                .pacerBurstThresholdMs = 500,
+                .pacerBurstCountQuota = 10,
+                .pacerBurstTimeQuotaSeconds = 3,
+        };
         mController.reset(new TranscodingSessionController(
-                [this](const std::shared_ptr<TranscoderCallbackInterface>& /*cb*/,
-                       int64_t /*heartBeatIntervalUs*/) {
+                [this](const std::shared_ptr<TranscoderCallbackInterface>& /*cb*/) {
                     // Here we require that the SessionController clears out all its refcounts of
                     // the transcoder object when it calls create.
                     EXPECT_EQ(mTranscoder.use_count(), 1);
                     mTranscoder->onCreated();
                     return mTranscoder;
                 },
-                mUidPolicy, mResourcePolicy, mThermalPolicy));
+                mUidPolicy, mResourcePolicy, mThermalPolicy, &config));
         mUidPolicy->setCallback(mController);
 
         // Set priority only, ignore other fields for now.
@@ -328,6 +335,40 @@
         EXPECT_EQ(mTranscoder.use_count(), 2);
     }
 
+    void testPacerHelper(int numSubmits, int sessionDurationMs, int expectedSuccess,
+                         bool pauseLastSuccessSession = false) {
+        for (int i = 0; i < numSubmits; i++) {
+            mController->submit(CLIENT(0), SESSION(i), UID(0), UID(0),
+                                mRealtimeRequest, mClientCallback0);
+        }
+        for (int i = 0; i < expectedSuccess; i++) {
+            EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(i)));
+            if ((i == expectedSuccess - 1) && pauseLastSuccessSession) {
+                // Insert a pause of 3 sec to the last success running session
+                mController->onThrottlingStarted();
+                EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(i)));
+                sleep(3);
+                mController->onThrottlingStopped();
+                EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(i)));
+            }
+            usleep(sessionDurationMs * 1000);
+            // Test half of Finish and half of Error, both should be counted as burst runs.
+            if (i & 1) {
+                mController->onFinish(CLIENT(0), SESSION(i));
+                EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(i)));
+            } else {
+                mController->onError(CLIENT(0), SESSION(i), TranscodingErrorCode::kUnknown);
+                EXPECT_EQ(mTranscoder->popEvent(100000),
+                          TestTranscoder::Failed(CLIENT(0), SESSION(i)));
+                EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUnknown);
+            }
+        }
+        for (int i = expectedSuccess; i < numSubmits; i++) {
+            EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(i)));
+            EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kDroppedByService);
+        }
+    }
+
     std::shared_ptr<TestTranscoder> mTranscoder;
     std::shared_ptr<TestUidPolicy> mUidPolicy;
     std::shared_ptr<TestResourcePolicy> mResourcePolicy;
@@ -349,32 +390,32 @@
 
     // Submit offline session to CLIENT(0) in UID(0).
     // Should start immediately (because this is the only session).
-    mController->submit(CLIENT(0), SESSION(0), UID(0), mOfflineRequest, mClientCallback0);
+    mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mOfflineRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), 0));
 
     // Submit real-time session to CLIENT(0).
     // Should pause offline session and start new session,  even if UID(0) is not on top.
-    mController->submit(CLIENT(0), SESSION(1), UID(0), mRealtimeRequest, mClientCallback0);
+    mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
 
     // Submit real-time session to CLIENT(0), should be queued after the previous session.
-    mController->submit(CLIENT(0), SESSION(2), UID(0), mRealtimeRequest, mClientCallback0);
+    mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Submit real-time session to CLIENT(1) in same uid, should be queued after the previous
     // session.
-    mController->submit(CLIENT(1), SESSION(0), UID(0), mRealtimeRequest, mClientCallback1);
+    mController->submit(CLIENT(1), SESSION(0), UID(1), UID(0), mRealtimeRequest, mClientCallback1);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Submit real-time session to CLIENT(2) in UID(1).
     // Should pause previous session and start new session, because UID(1) is (has been) top.
-    mController->submit(CLIENT(2), SESSION(0), UID(1), mRealtimeRequest, mClientCallback2);
+    mController->submit(CLIENT(2), SESSION(0), UID(2), UID(1), mRealtimeRequest, mClientCallback2);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(1)));
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), SESSION(0)));
 
     // Submit offline session, shouldn't generate any event.
-    mController->submit(CLIENT(2), SESSION(1), UID(1), mOfflineRequest, mClientCallback2);
+    mController->submit(CLIENT(2), SESSION(1), UID(2), UID(1), mOfflineRequest, mClientCallback2);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Bring UID(0) to top.
@@ -388,15 +429,15 @@
     ALOGD("TestCancelSession");
 
     // Submit real-time session SESSION(0), should start immediately.
-    mController->submit(CLIENT(0), SESSION(0), UID(0), mRealtimeRequest, mClientCallback0);
+    mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
 
     // Submit real-time session SESSION(1), should not start.
-    mController->submit(CLIENT(0), SESSION(1), UID(0), mRealtimeRequest, mClientCallback0);
+    mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Submit offline session SESSION(2), should not start.
-    mController->submit(CLIENT(0), SESSION(2), UID(0), mOfflineRequest, mClientCallback0);
+    mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mOfflineRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Cancel queued real-time session.
@@ -408,7 +449,7 @@
     EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(2)));
 
     // Submit offline session SESSION(3), shouldn't cause any event.
-    mController->submit(CLIENT(0), SESSION(3), UID(0), mOfflineRequest, mClientCallback0);
+    mController->submit(CLIENT(0), SESSION(3), UID(0), UID(0), mOfflineRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Cancel running real-time session SESSION(0).
@@ -420,7 +461,7 @@
 
     // Submit real-time session SESSION(4), offline SESSION(3) should pause and SESSION(4)
     // should start.
-    mController->submit(CLIENT(0), SESSION(4), UID(0), mRealtimeRequest, mClientCallback0);
+    mController->submit(CLIENT(0), SESSION(4), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(3)));
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(4)));
 
@@ -438,16 +479,16 @@
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Submit offline session SESSION(0), should start immediately.
-    mController->submit(CLIENT(0), SESSION(0), UID(0), mOfflineRequest, mClientCallback0);
+    mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mOfflineRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
 
     // Submit real-time session SESSION(1), should pause offline session and start immediately.
-    mController->submit(CLIENT(0), SESSION(1), UID(0), mRealtimeRequest, mClientCallback0);
+    mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
 
     // Submit real-time session SESSION(2), should not start.
-    mController->submit(CLIENT(0), SESSION(2), UID(0), mRealtimeRequest, mClientCallback0);
+    mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Finish when the session never started, should be ignored.
@@ -458,7 +499,7 @@
     mUidPolicy->setTop(UID(1));
     // Submit real-time session to CLIENT(1) in UID(1), should pause previous session and start
     // new session.
-    mController->submit(CLIENT(1), SESSION(0), UID(1), mRealtimeRequest, mClientCallback1);
+    mController->submit(CLIENT(1), SESSION(0), UID(1), UID(1), mRealtimeRequest, mClientCallback1);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(1)));
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), SESSION(0)));
 
@@ -495,16 +536,16 @@
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Submit offline session SESSION(0), should start immediately.
-    mController->submit(CLIENT(0), SESSION(0), UID(0), mOfflineRequest, mClientCallback0);
+    mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mOfflineRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
 
     // Submit real-time session SESSION(1), should pause offline session and start immediately.
-    mController->submit(CLIENT(0), SESSION(1), UID(0), mRealtimeRequest, mClientCallback0);
+    mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
 
     // Submit real-time session SESSION(2), should not start.
-    mController->submit(CLIENT(0), SESSION(2), UID(0), mRealtimeRequest, mClientCallback0);
+    mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Fail when the session never started, should be ignored.
@@ -515,7 +556,7 @@
     mUidPolicy->setTop(UID(1));
     // Submit real-time session to CLIENT(1) in UID(1), should pause previous session and start
     // new session.
-    mController->submit(CLIENT(1), SESSION(0), UID(1), mRealtimeRequest, mClientCallback1);
+    mController->submit(CLIENT(1), SESSION(0), UID(1), UID(1), mRealtimeRequest, mClientCallback1);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(1)));
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), SESSION(0)));
 
@@ -523,15 +564,18 @@
     // Should still be propagated to client, but shouldn't trigger any new start.
     mController->onError(CLIENT(0), SESSION(1), TranscodingErrorCode::kUnknown);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(1)));
+    EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUnknown);
 
     // Fail running real-time session, should start next real-time session in queue.
     mController->onError(CLIENT(1), SESSION(0), TranscodingErrorCode::kUnknown);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(1), SESSION(0)));
+    EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUnknown);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(2)));
 
     // Fail running real-time session, should resume next session (offline session) in queue.
     mController->onError(CLIENT(0), SESSION(2), TranscodingErrorCode::kUnknown);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(2)));
+    EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUnknown);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
 
     // Fail running offline session, and test error code propagation.
@@ -549,11 +593,11 @@
 
     // Start with unspecified top UID.
     // Submit real-time session to CLIENT(0), session should start immediately.
-    mController->submit(CLIENT(0), SESSION(0), UID(0), mRealtimeRequest, mClientCallback0);
+    mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
 
     // Submit offline session to CLIENT(0), should not start.
-    mController->submit(CLIENT(1), SESSION(0), UID(0), mOfflineRequest, mClientCallback1);
+    mController->submit(CLIENT(1), SESSION(0), UID(1), UID(0), mOfflineRequest, mClientCallback1);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Move UID(1) to top.
@@ -562,7 +606,7 @@
 
     // Submit real-time session to CLIENT(2) in different uid UID(1).
     // Should pause previous session and start new session.
-    mController->submit(CLIENT(2), SESSION(0), UID(1), mRealtimeRequest, mClientCallback2);
+    mController->submit(CLIENT(2), SESSION(0), UID(2), UID(1), mRealtimeRequest, mClientCallback2);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), SESSION(0)));
 
@@ -591,11 +635,11 @@
 
     // Start with unspecified top UID.
     // Submit real-time session to CLIENT(0), session should start immediately.
-    mController->submit(CLIENT(0), SESSION(0), UID(0), mRealtimeRequest, mClientCallback0);
+    mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
 
     // Submit offline session to CLIENT(0), should not start.
-    mController->submit(CLIENT(1), SESSION(0), UID(0), mOfflineRequest, mClientCallback1);
+    mController->submit(CLIENT(1), SESSION(0), UID(1), UID(0), mOfflineRequest, mClientCallback1);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Set UID(0), UID(1) to top set.
@@ -605,7 +649,7 @@
 
     // Submit real-time session to CLIENT(2) in different uid UID(1).
     // UID(0) should pause and UID(1) should start.
-    mController->submit(CLIENT(2), SESSION(0), UID(1), mRealtimeRequest, mClientCallback2);
+    mController->submit(CLIENT(2), SESSION(0), UID(2), UID(1), mRealtimeRequest, mClientCallback2);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), SESSION(0)));
 
@@ -647,12 +691,12 @@
     // Start with unspecified top UID.
     // Submit real-time session to CLIENT(0), session should start immediately.
     mRealtimeRequest.clientPid = PID(0);
-    mController->submit(CLIENT(0), SESSION(0), UID(0), mRealtimeRequest, mClientCallback0);
+    mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
 
     // Submit offline session to CLIENT(0), should not start.
     mOfflineRequest.clientPid = PID(0);
-    mController->submit(CLIENT(1), SESSION(0), UID(0), mOfflineRequest, mClientCallback1);
+    mController->submit(CLIENT(1), SESSION(0), UID(1), UID(0), mOfflineRequest, mClientCallback1);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Move UID(1) to top.
@@ -662,7 +706,7 @@
     // Submit real-time session to CLIENT(2) in different uid UID(1).
     // Should pause previous session and start new session.
     mRealtimeRequest.clientPid = PID(1);
-    mController->submit(CLIENT(2), SESSION(0), UID(1), mRealtimeRequest, mClientCallback2);
+    mController->submit(CLIENT(2), SESSION(0), UID(2), UID(1), mRealtimeRequest, mClientCallback2);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), SESSION(0)));
 
@@ -719,7 +763,7 @@
 
     // Submit real-time session to CLIENT(3) in UID(2), session shouldn't start due to no resource.
     mRealtimeRequest.clientPid = PID(2);
-    mController->submit(CLIENT(3), SESSION(0), UID(2), mRealtimeRequest, mClientCallback3);
+    mController->submit(CLIENT(3), SESSION(0), UID(3), UID(2), mRealtimeRequest, mClientCallback3);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Signal resource available, CLIENT(3)'s session should start.
@@ -734,12 +778,12 @@
     // Start with unspecified top UID.
     // Submit real-time session to CLIENT(0), session should start immediately.
     mRealtimeRequest.clientPid = PID(0);
-    mController->submit(CLIENT(0), SESSION(0), UID(0), mRealtimeRequest, mClientCallback0);
+    mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
 
     // Submit offline session to CLIENT(0), should not start.
     mOfflineRequest.clientPid = PID(0);
-    mController->submit(CLIENT(1), SESSION(0), UID(0), mOfflineRequest, mClientCallback1);
+    mController->submit(CLIENT(1), SESSION(0), UID(1), UID(0), mOfflineRequest, mClientCallback1);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Move UID(1) to top.
@@ -749,7 +793,7 @@
     // Submit real-time session to CLIENT(2) in different uid UID(1).
     // Should pause previous session and start new session.
     mRealtimeRequest.clientPid = PID(1);
-    mController->submit(CLIENT(2), SESSION(0), UID(1), mRealtimeRequest, mClientCallback2);
+    mController->submit(CLIENT(2), SESSION(0), UID(2), UID(1), mRealtimeRequest, mClientCallback2);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), SESSION(0)));
 
@@ -788,7 +832,7 @@
     mUidPolicy->setTop(UID(2));
     // Submit real-time session to CLIENT(3) in UID(2), session shouldn't start during throttling.
     mRealtimeRequest.clientPid = PID(2);
-    mController->submit(CLIENT(3), SESSION(0), UID(2), mRealtimeRequest, mClientCallback3);
+    mController->submit(CLIENT(3), SESSION(0), UID(3), UID(2), mRealtimeRequest, mClientCallback3);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
     // Throttling stops, CLIENT(3)'s session should start.
     mController->onThrottlingStopped();
@@ -802,12 +846,12 @@
     // Start with unspecified top UID.
     // Submit real-time session to CLIENT(0), session should start immediately.
     mRealtimeRequest.clientPid = PID(0);
-    mController->submit(CLIENT(0), SESSION(0), UID(0), mRealtimeRequest, mClientCallback0);
+    mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
 
     // Submit offline session to CLIENT(0), should not start.
     mOfflineRequest.clientPid = PID(0);
-    mController->submit(CLIENT(1), SESSION(0), UID(0), mOfflineRequest, mClientCallback1);
+    mController->submit(CLIENT(1), SESSION(0), UID(1), UID(0), mOfflineRequest, mClientCallback1);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Move UID(1) to top.
@@ -817,7 +861,7 @@
     // Submit real-time session to CLIENT(2) in different uid UID(1).
     // Should pause previous session and start new session.
     mRealtimeRequest.clientPid = PID(1);
-    mController->submit(CLIENT(2), SESSION(0), UID(1), mRealtimeRequest, mClientCallback2);
+    mController->submit(CLIENT(2), SESSION(0), UID(2), UID(1), mRealtimeRequest, mClientCallback2);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), SESSION(0)));
 
@@ -854,29 +898,35 @@
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0)));
 }
 
-TEST_F(TranscodingSessionControllerTest, TestTranscoderWatchdogTimeout) {
+TEST_F(TranscodingSessionControllerTest, TestTranscoderWatchdogNoHeartbeat) {
     ALOGD("TestTranscoderWatchdogTimeout");
 
     // Submit session to CLIENT(0) in UID(0).
     // Should start immediately (because this is the only session).
-    mController->submit(CLIENT(0), SESSION(0), UID(0), mRealtimeRequest, mClientCallback0);
+    mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
 
-    int32_t expectedGen = 2;
     // Test 1: If not sending keep-alive at all, timeout after 3 seconds.
-    expectTimeout(CLIENT(0), SESSION(0), expectedGen++);
+    expectTimeout(CLIENT(0), SESSION(0), 2);
+}
 
+TEST_F(TranscodingSessionControllerTest, TestTranscoderWatchdogHeartbeat) {
     // Test 2: No timeout as long as keep-alive coming; timeout after keep-alive stops.
-    mController->submit(CLIENT(0), SESSION(1), UID(0), mRealtimeRequest, mClientCallback0);
+    mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
+
     for (int i = 0; i < 5; i++) {
         EXPECT_EQ(mTranscoder->popEvent(1000000), TestTranscoder::NoEvent);
         mController->onHeartBeat(CLIENT(0), SESSION(1));
     }
-    expectTimeout(CLIENT(0), SESSION(1), expectedGen++);
+    expectTimeout(CLIENT(0), SESSION(1), 2);
+}
+
+TEST_F(TranscodingSessionControllerTest, TestTranscoderWatchdogDuringPause) {
+    int expectedGen = 2;
 
     // Test 3a: No timeout for paused session even if no keep-alive is sent.
-    mController->submit(CLIENT(0), SESSION(2), UID(0), mOfflineRequest, mClientCallback0);
+    mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mOfflineRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(2)));
     // Trigger a pause by sending a resource lost.
     mController->onResourceLost(CLIENT(0), SESSION(2));
@@ -886,12 +936,12 @@
     expectTimeout(CLIENT(0), SESSION(2), expectedGen++);
 
     // Test 3b: No timeout for paused session even if no keep-alive is sent.
-    mController->submit(CLIENT(0), SESSION(3), UID(0), mOfflineRequest, mClientCallback0);
+    mController->submit(CLIENT(0), SESSION(3), UID(0), UID(0), mOfflineRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(3)));
     // Let the session run almost to timeout, to test timeout reset after pause.
     EXPECT_EQ(mTranscoder->popEvent(2900000), TestTranscoder::NoEvent);
     // Trigger a pause by submitting a higher-priority request.
-    mController->submit(CLIENT(0), SESSION(4), UID(0), mRealtimeRequest, mClientCallback0);
+    mController->submit(CLIENT(0), SESSION(4), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(3)));
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(4)));
     // Finish the higher-priority session, lower-priority session should resume,
@@ -902,4 +952,25 @@
     expectTimeout(CLIENT(0), SESSION(3), expectedGen++);
 }
 
+TEST_F(TranscodingSessionControllerTest, TestTranscoderPacerOverCountOnly) {
+    ALOGD("TestTranscoderPacerOverCountOnly");
+    testPacerHelper(12 /*numSubmits*/, 100 /*sessionDurationMs*/, 12 /*expectedSuccess*/);
+}
+
+TEST_F(TranscodingSessionControllerTest, TestTranscoderPacerOverTimeOnly) {
+    ALOGD("TestTranscoderPacerOverTimeOnly");
+    testPacerHelper(5 /*numSubmits*/, 1000 /*sessionDurationMs*/, 5 /*expectedSuccess*/);
+}
+
+TEST_F(TranscodingSessionControllerTest, TestTranscoderPacerOverQuota) {
+    ALOGD("TestTranscoderPacerOverQuota");
+    testPacerHelper(12 /*numSubmits*/, 400 /*sessionDurationMs*/, 10 /*expectedSuccess*/);
+}
+
+TEST_F(TranscodingSessionControllerTest, TestTranscoderPacerWithPause) {
+    ALOGD("TestTranscoderPacerDuringPause");
+    testPacerHelper(12 /*numSubmits*/, 400 /*sessionDurationMs*/, 10 /*expectedSuccess*/,
+                    true /*pauseLastSuccessSession*/);
+}
+
 }  // namespace android
diff --git a/media/libmediatranscoding/tests/build_and_run_all_unit_tests.sh b/media/libmediatranscoding/tests/build_and_run_all_unit_tests.sh
index 5db9258..3cbf1dd 100644
--- a/media/libmediatranscoding/tests/build_and_run_all_unit_tests.sh
+++ b/media/libmediatranscoding/tests/build_and_run_all_unit_tests.sh
@@ -32,3 +32,7 @@
 echo "testing TranscodingSessionController"
 #adb shell /data/nativetest64/TranscodingSessionController_tests/TranscodingSessionController_tests
 adb shell /data/nativetest/TranscodingSessionController_tests/TranscodingSessionController_tests
+
+echo "testing TranscodingLogger"
+#adb shell /data/nativetest64/TranscodingLogger_tests/TranscodingLogger_tests
+adb shell /data/nativetest/TranscodingLogger_tests/TranscodingLogger_tests
diff --git a/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp b/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
index 0efe85d..88c1c42 100644
--- a/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
+++ b/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
@@ -237,8 +237,8 @@
     }
 
     std::chrono::microseconds updateInterval(mHeartBeatIntervalUs);
-    std::chrono::system_clock::time_point nextUpdateTime =
-            std::chrono::system_clock::now() + updateInterval;
+    std::chrono::steady_clock::time_point nextUpdateTime =
+            std::chrono::steady_clock::now() + updateInterval;
 
     while (true) {
         if (trackEosCount >= mTracks.size()) {
diff --git a/media/libmediatranscoding/transcoder/NdkCommon.cpp b/media/libmediatranscoding/transcoder/NdkCommon.cpp
index d78c038..2d85df7 100644
--- a/media/libmediatranscoding/transcoder/NdkCommon.cpp
+++ b/media/libmediatranscoding/transcoder/NdkCommon.cpp
@@ -90,4 +90,29 @@
 DEFINE_SET_DEFAULT_FORMAT_VALUE_FUNC(float, Float);
 DEFINE_SET_DEFAULT_FORMAT_VALUE_FUNC(int32_t, Int32);
 
-}  // namespace AMediaFormatUtils
\ No newline at end of file
+// Determines whether a track format describes HDR video content or not. The
+// logic is based on isHdr() in libstagefright/Utils.cpp.
+bool VideoIsHdr(AMediaFormat* format) {
+    // If VUI signals HDR content, this internal flag is set by the extractor.
+    int32_t isHdr;
+    if (AMediaFormat_getInt32(format, "android._is-hdr", &isHdr)) {
+        return isHdr;
+    }
+
+    // If container supplied HDR static info without transfer set, assume HDR.
+    const char* hdrInfo;
+    int32_t transfer;
+    if ((AMediaFormat_getString(format, AMEDIAFORMAT_KEY_HDR_STATIC_INFO, &hdrInfo) ||
+         AMediaFormat_getString(format, "hdr10-plus-info", &hdrInfo)) &&
+        !AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_COLOR_TRANSFER, &transfer)) {
+        return true;
+    }
+
+    // Otherwise, check if an HDR transfer function is set.
+    if (AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_COLOR_TRANSFER, &transfer)) {
+        return transfer == COLOR_TRANSFER_ST2084 || transfer == COLOR_TRANSFER_HLG;
+    }
+
+    return false;
+}
+}  // namespace AMediaFormatUtils
diff --git a/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp b/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
index 038afd2..4405180 100644
--- a/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
+++ b/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
@@ -51,32 +51,6 @@
 // Default frame rate.
 static constexpr int32_t kDefaultFrameRate = 30;
 
-// Determines whether a track format describes HDR video content or not. The
-// logic is based on isHdr() in libstagefright/Utils.cpp.
-static bool isHdr(AMediaFormat* format) {
-    // If VUI signals HDR content, this internal flag is set by the extractor.
-    int32_t isHdr;
-    if (AMediaFormat_getInt32(format, "android._is-hdr", &isHdr)) {
-        return isHdr;
-    }
-
-    // If container supplied HDR static info without transfer set, assume HDR.
-    const char* hdrInfo;
-    int32_t transfer;
-    if ((AMediaFormat_getString(format, AMEDIAFORMAT_KEY_HDR_STATIC_INFO, &hdrInfo) ||
-         AMediaFormat_getString(format, "hdr10-plus-info", &hdrInfo)) &&
-        !AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_COLOR_TRANSFER, &transfer)) {
-        return true;
-    }
-
-    // Otherwise, check if an HDR transfer function is set.
-    if (AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_COLOR_TRANSFER, &transfer)) {
-        return transfer == COLOR_TRANSFER_ST2084 || transfer == COLOR_TRANSFER_HLG;
-    }
-
-    return false;
-}
-
 template <typename T>
 void VideoTrackTranscoder::BlockingQueue<T>::push(T const& value, bool front) {
     {
@@ -347,7 +321,7 @@
     }
 
     // Request decoder to convert HDR content to SDR.
-    const bool sourceIsHdr = isHdr(mSourceFormat.get());
+    const bool sourceIsHdr = VideoIsHdr(mSourceFormat.get());
     if (sourceIsHdr) {
         AMediaFormat_setInt32(decoderFormat.get(),
                               TBD_AMEDIACODEC_PARAMETER_KEY_COLOR_TRANSFER_REQUEST,
diff --git a/media/libmediatranscoding/transcoder/include/media/NdkCommon.h b/media/libmediatranscoding/transcoder/include/media/NdkCommon.h
index 73d880d..c5547c6 100644
--- a/media/libmediatranscoding/transcoder/include/media/NdkCommon.h
+++ b/media/libmediatranscoding/transcoder/include/media/NdkCommon.h
@@ -90,5 +90,7 @@
 bool SetDefaultFormatValueFloat(const char* key, AMediaFormat* format, float value);
 bool SetDefaultFormatValueInt32(const char* key, AMediaFormat* format, int32_t value);
 
+bool VideoIsHdr(AMediaFormat* format);
+
 }  // namespace AMediaFormatUtils
 #endif  // ANDROID_MEDIA_TRANSCODING_NDK_COMMON_H
diff --git a/media/libnblog/ReportPerformance.cpp b/media/libnblog/ReportPerformance.cpp
index aa678ba..4c78b01 100644
--- a/media/libnblog/ReportPerformance.cpp
+++ b/media/libnblog/ReportPerformance.cpp
@@ -92,8 +92,8 @@
         (*dataJson)["threadNum"] = item.first;
         root.append(*dataJson);
     }
-    Json::StyledWriter writer;
-    std::string rootStr = writer.write(root);
+    Json::StreamWriterBuilder factory;
+    std::string rootStr = Json::writeString(factory, root);
     write(fd, rootStr.c_str(), rootStr.size());
 }
 
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 489a4bd..930bc0f 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -337,6 +337,8 @@
         "android.hardware.cas.native@1.0",
         "android.hardware.drm@1.0",
         "android.hardware.media.omx@1.0",
+        "media_permission-aidl-cpp",
+        "libaudioclient_aidl_conversion",
     ],
 
     static_libs: [
@@ -349,6 +351,7 @@
         "libogg",
         "libwebm",
         "libstagefright_id3",
+        "media_permission-aidl-cpp",
     ],
 
     header_libs:[
@@ -363,6 +366,7 @@
         "libhidlmemory",
         "libmedia",
         "android.hidl.allocator@1.0",
+        "media_permission-aidl-cpp",
     ],
 
     export_include_dirs: [
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index 4bc861e..89fe56f 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -21,6 +21,8 @@
 #define LOG_TAG "AudioSource"
 #include <utils/Log.h>
 
+#include <binder/IPCThreadState.h>
+#include <media/AidlConversion.h>
 #include <media/AudioRecord.h>
 #include <media/stagefright/AudioSource.h>
 #include <media/stagefright/MediaBuffer.h>
@@ -32,6 +34,8 @@
 
 namespace android {
 
+using android::media::permission::Identity;
+
 static void AudioRecordCallbackFunction(int event, void *user, void *info) {
     AudioSource *source = (AudioSource *) user;
     switch (event) {
@@ -50,74 +54,100 @@
 }
 
 AudioSource::AudioSource(
+    const audio_attributes_t *attr, const Identity& identity,
+    uint32_t sampleRate, uint32_t channelCount, uint32_t outSampleRate,
+    audio_port_handle_t selectedDeviceId,
+    audio_microphone_direction_t selectedMicDirection,
+    float selectedMicFieldDimension)
+{
+  set(attr, identity, sampleRate, channelCount, outSampleRate, selectedDeviceId,
+      selectedMicDirection, selectedMicFieldDimension);
+}
+
+AudioSource::AudioSource(
         const audio_attributes_t *attr, const String16 &opPackageName,
         uint32_t sampleRate, uint32_t channelCount, uint32_t outSampleRate,
         uid_t uid, pid_t pid, audio_port_handle_t selectedDeviceId,
         audio_microphone_direction_t selectedMicDirection,
         float selectedMicFieldDimension)
-    : mStarted(false),
-      mSampleRate(sampleRate),
-      mOutSampleRate(outSampleRate > 0 ? outSampleRate : sampleRate),
-      mTrackMaxAmplitude(false),
-      mStartTimeUs(0),
-      mStopSystemTimeUs(-1),
-      mLastFrameTimestampUs(0),
-      mMaxAmplitude(0),
-      mPrevSampleTimeUs(0),
-      mInitialReadTimeUs(0),
-      mNumFramesReceived(0),
-      mNumFramesSkipped(0),
-      mNumFramesLost(0),
-      mNumClientOwnedBuffers(0),
-      mNoMoreFramesToRead(false) {
-    ALOGV("sampleRate: %u, outSampleRate: %u, channelCount: %u",
-            sampleRate, outSampleRate, channelCount);
-    CHECK(channelCount == 1 || channelCount == 2);
-    CHECK(sampleRate > 0);
+{
+  // TODO b/182392769: use identity util
+  Identity identity;
+  identity.packageName = VALUE_OR_FATAL(legacy2aidl_String16_string(opPackageName));
+  identity.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(uid));
+  identity.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(pid));
+  set(attr, identity, sampleRate, channelCount, outSampleRate, selectedDeviceId,
+      selectedMicDirection, selectedMicFieldDimension);
+}
 
-    size_t minFrameCount;
-    status_t status = AudioRecord::getMinFrameCount(&minFrameCount,
-                                           sampleRate,
-                                           AUDIO_FORMAT_PCM_16_BIT,
-                                           audio_channel_in_mask_from_count(channelCount));
-    if (status == OK) {
-        // make sure that the AudioRecord callback never returns more than the maximum
-        // buffer size
-        uint32_t frameCount = kMaxBufferSize / sizeof(int16_t) / channelCount;
+void AudioSource::set(
+   const audio_attributes_t *attr, const Identity& identity,
+        uint32_t sampleRate, uint32_t channelCount, uint32_t outSampleRate,
+        audio_port_handle_t selectedDeviceId,
+        audio_microphone_direction_t selectedMicDirection,
+        float selectedMicFieldDimension)
+{
+   mStarted = false;
+   mSampleRate = sampleRate;
+   mOutSampleRate = outSampleRate > 0 ? outSampleRate : sampleRate;
+   mTrackMaxAmplitude = false;
+   mStartTimeUs = 0;
+   mStopSystemTimeUs = -1;
+   mLastFrameTimestampUs = 0;
+   mMaxAmplitude = 0;
+   mPrevSampleTimeUs = 0;
+   mInitialReadTimeUs = 0;
+   mNumFramesReceived = 0;
+   mNumFramesSkipped = 0;
+   mNumFramesLost = 0;
+   mNumClientOwnedBuffers = 0;
+   mNoMoreFramesToRead = false;
+  ALOGV("sampleRate: %u, outSampleRate: %u, channelCount: %u",
+        sampleRate, outSampleRate, channelCount);
+  CHECK(channelCount == 1 || channelCount == 2);
+  CHECK(sampleRate > 0);
 
-        // make sure that the AudioRecord total buffer size is large enough
-        size_t bufCount = 2;
-        while ((bufCount * frameCount) < minFrameCount) {
-            bufCount++;
-        }
+  size_t minFrameCount;
+  status_t status = AudioRecord::getMinFrameCount(&minFrameCount,
+                                                  sampleRate,
+                                                  AUDIO_FORMAT_PCM_16_BIT,
+                                                  audio_channel_in_mask_from_count(channelCount));
+  if (status == OK) {
+    // make sure that the AudioRecord callback never returns more than the maximum
+    // buffer size
+    uint32_t frameCount = kMaxBufferSize / sizeof(int16_t) / channelCount;
 
-        mRecord = new AudioRecord(
-                    AUDIO_SOURCE_DEFAULT, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
-                    audio_channel_in_mask_from_count(channelCount),
-                    opPackageName,
-                    (size_t) (bufCount * frameCount),
-                    AudioRecordCallbackFunction,
-                    this,
-                    frameCount /*notificationFrames*/,
-                    AUDIO_SESSION_ALLOCATE,
-                    AudioRecord::TRANSFER_DEFAULT,
-                    AUDIO_INPUT_FLAG_NONE,
-                    uid,
-                    pid,
-                    attr,
-                    selectedDeviceId,
-                    selectedMicDirection,
-                    selectedMicFieldDimension);
-        // Set caller name so it can be logged in destructor.
-        // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_MEDIA
-        mRecord->setCallerName("media");
-        mInitCheck = mRecord->initCheck();
-        if (mInitCheck != OK) {
-            mRecord.clear();
-        }
-    } else {
-        mInitCheck = status;
+    // make sure that the AudioRecord total buffer size is large enough
+    size_t bufCount = 2;
+    while ((bufCount * frameCount) < minFrameCount) {
+      bufCount++;
     }
+
+    mRecord = new AudioRecord(
+        AUDIO_SOURCE_DEFAULT, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
+        audio_channel_in_mask_from_count(channelCount),
+        identity,
+        (size_t) (bufCount * frameCount),
+        AudioRecordCallbackFunction,
+        this,
+        frameCount /*notificationFrames*/,
+        AUDIO_SESSION_ALLOCATE,
+        AudioRecord::TRANSFER_DEFAULT,
+        AUDIO_INPUT_FLAG_NONE,
+        attr,
+        selectedDeviceId,
+        selectedMicDirection,
+        selectedMicFieldDimension);
+    // Set caller name so it can be logged in destructor.
+    // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_MEDIA
+    mRecord->setCallerName("media");
+    mInitCheck = mRecord->initCheck();
+    if (mInitCheck != OK) {
+      mRecord.clear();
+    }
+  } else {
+    mInitCheck = status;
+  }
 }
 
 AudioSource::~AudioSource() {
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index bcf418a..b1aa7a9 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -658,7 +658,7 @@
     mStartTimeUs = 0;
     mNumInputBuffers = 0;
     mEncoderFormat = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
-    mEncoderDataSpace = HAL_DATASPACE_V0_BT709;
+    mEncoderDataSpace = mBufferDataSpace = HAL_DATASPACE_V0_BT709;
 
     if (meta) {
         int64_t startTimeUs;
@@ -678,6 +678,7 @@
         }
         if (meta->findInt32(kKeyColorSpace, &mEncoderDataSpace)) {
             ALOGI("Using encoder data space: %#x", mEncoderDataSpace);
+            mBufferDataSpace = mEncoderDataSpace;
         }
     }
 
@@ -908,6 +909,11 @@
         (*buffer)->setObserver(this);
         (*buffer)->add_ref();
         (*buffer)->meta_data().setInt64(kKeyTime, frameTime);
+        if (mBufferDataSpace != mEncoderDataSpace) {
+            ALOGD("Data space updated to %x", mBufferDataSpace);
+            (*buffer)->meta_data().setInt32(kKeyColorSpace, mBufferDataSpace);
+            mEncoderDataSpace = mBufferDataSpace;
+        }
     }
     return OK;
 }
@@ -1039,6 +1045,7 @@
     // Find a available memory slot to store the buffer as VideoNativeMetadata.
     sp<IMemory> data = *mMemoryBases.begin();
     mMemoryBases.erase(mMemoryBases.begin());
+    mBufferDataSpace = buffer.mDataSpace;
 
     ssize_t offset;
     size_t size;
diff --git a/media/libstagefright/FrameDecoder.cpp b/media/libstagefright/FrameDecoder.cpp
index a78e6d2..01190b5 100644
--- a/media/libstagefright/FrameDecoder.cpp
+++ b/media/libstagefright/FrameDecoder.cpp
@@ -644,6 +644,10 @@
                 0,
                 dstBpp(),
                 mCaptureLayer != nullptr /*allocRotated*/);
+        if (frameMem == nullptr) {
+            return NO_MEMORY;
+        }
+
         mFrame = static_cast<VideoFrame*>(frameMem->unsecurePointer());
 
         setFrame(frameMem);
@@ -886,6 +890,11 @@
     if (mFrame == NULL) {
         sp<IMemory> frameMem = allocVideoFrame(
                 trackMeta(), mWidth, mHeight, mTileWidth, mTileHeight, dstBpp());
+
+        if (frameMem == nullptr) {
+            return NO_MEMORY;
+        }
+
         mFrame = static_cast<VideoFrame*>(frameMem->unsecurePointer());
 
         setFrame(frameMem);
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 447d599..76a5cab 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -4260,13 +4260,20 @@
 void MPEG4Writer::Track::writeColrBox() {
     ColorAspects aspects;
     memset(&aspects, 0, sizeof(aspects));
+    // Color metadata may have changed.
+    sp<MetaData> meta = mSource->getFormat();
     // TRICKY: using | instead of || because we want to execute all findInt32-s
-    if (mMeta->findInt32(kKeyColorPrimaries, (int32_t*)&aspects.mPrimaries)
-            | mMeta->findInt32(kKeyTransferFunction, (int32_t*)&aspects.mTransfer)
-            | mMeta->findInt32(kKeyColorMatrix, (int32_t*)&aspects.mMatrixCoeffs)
-            | mMeta->findInt32(kKeyColorRange, (int32_t*)&aspects.mRange)) {
+    if (meta->findInt32(kKeyColorPrimaries, (int32_t*)&aspects.mPrimaries)
+            | meta->findInt32(kKeyTransferFunction, (int32_t*)&aspects.mTransfer)
+            | meta->findInt32(kKeyColorMatrix, (int32_t*)&aspects.mMatrixCoeffs)
+            | meta->findInt32(kKeyColorRange, (int32_t*)&aspects.mRange)) {
         int32_t primaries, transfer, coeffs;
         bool fullRange;
+        ALOGV("primaries=%s transfer=%s matrix=%s range=%s",
+                asString(aspects.mPrimaries),
+                asString(aspects.mTransfer),
+                asString(aspects.mMatrixCoeffs),
+                asString(aspects.mRange));
         ColorUtils::convertCodecColorAspectsToIsoAspects(
                 aspects, &primaries, &transfer, &coeffs, &fullRange);
         mOwner->beginBox("colr");
@@ -4276,6 +4283,8 @@
         mOwner->writeInt16(coeffs);
         mOwner->writeInt8(int8_t(fullRange ? 0x80 : 0x0));
         mOwner->endBox(); // colr
+    } else {
+        ALOGV("no color information");
     }
 }
 
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 3e5f9ea..f80b22f 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -118,6 +118,11 @@
 static const char *kCodecNumLowLatencyModeOn = "android.media.mediacodec.low-latency.on";  /* 0..n */
 static const char *kCodecNumLowLatencyModeOff = "android.media.mediacodec.low-latency.off";  /* 0..n */
 static const char *kCodecFirstFrameIndexLowLatencyModeOn = "android.media.mediacodec.low-latency.first-frame";  /* 0..n */
+static const char *kCodecChannelCount = "android.media.mediacodec.channelCount";
+static const char *kCodecSampleRate = "android.media.mediacodec.sampleRate";
+static const char *kCodecVideoEncodedBytes = "android.media.mediacodec.vencode.bytes";
+static const char *kCodecVideoEncodedFrames = "android.media.mediacodec.vencode.frames";
+static const char *kCodecVideoEncodedDurationUs = "android.media.mediacodec.vencode.durationUs";
 
 // the kCodecRecent* fields appear only in getMetrics() results
 static const char *kCodecRecentLatencyMax = "android.media.mediacodec.recent.max";      /* in us */
@@ -695,6 +700,10 @@
       mHavePendingInputBuffers(false),
       mCpuBoostRequested(false),
       mLatencyUnknown(0),
+      mBytesEncoded(0),
+      mEarliestEncodedPtsUs(INT64_MAX),
+      mLatestEncodedPtsUs(INT64_MIN),
+      mFramesEncoded(0),
       mNumLowLatencyEnables(0),
       mNumLowLatencyDisables(0),
       mIsLowLatencyModeOn(false),
@@ -802,6 +811,18 @@
         mediametrics_setInt64(mMetricsHandle, kCodecLifetimeMs, lifetime);
     }
 
+    if (mBytesEncoded) {
+        Mutex::Autolock al(mOutputStatsLock);
+
+        mediametrics_setInt64(mMetricsHandle, kCodecVideoEncodedBytes, mBytesEncoded);
+        int64_t duration = 0;
+        if (mLatestEncodedPtsUs > mEarliestEncodedPtsUs) {
+            duration = mLatestEncodedPtsUs - mEarliestEncodedPtsUs;
+        }
+        mediametrics_setInt64(mMetricsHandle, kCodecVideoEncodedDurationUs, duration);
+        mediametrics_setInt64(mMetricsHandle, kCodecVideoEncodedFrames, mFramesEncoded);
+    }
+
     {
         Mutex::Autolock al(mLatencyLock);
         mediametrics_setInt64(mMetricsHandle, kCodecNumLowLatencyModeOn, mNumLowLatencyEnables);
@@ -1005,10 +1026,34 @@
 }
 
 // when we get a buffer back from the codec
-void MediaCodec::statsBufferReceived(int64_t presentationUs) {
+void MediaCodec::statsBufferReceived(int64_t presentationUs, const sp<MediaCodecBuffer> &buffer) {
 
     CHECK_NE(mState, UNINITIALIZED);
 
+    if (mIsVideo && (mFlags & kFlagIsEncoder)) {
+        int32_t flags = 0;
+        (void) buffer->meta()->findInt32("flags", &flags);
+
+        // some of these frames, we don't want to count
+        // standalone EOS.... has an invalid timestamp
+        if ((flags & (BUFFER_FLAG_CODECCONFIG|BUFFER_FLAG_EOS)) == 0) {
+            mBytesEncoded += buffer->size();
+            mFramesEncoded++;
+
+            Mutex::Autolock al(mOutputStatsLock);
+            int64_t timeUs = 0;
+            if (buffer->meta()->findInt64("timeUs", &timeUs)) {
+                if (timeUs > mLatestEncodedPtsUs) {
+                    mLatestEncodedPtsUs = timeUs;
+                }
+                // can't chain as an else-if or this never triggers
+                if (timeUs < mEarliestEncodedPtsUs) {
+                    mEarliestEncodedPtsUs = timeUs;
+                }
+            }
+        }
+    }
+
     // mutex access to mBuffersInFlight and other stats
     Mutex::Autolock al(mLatencyLock);
 
@@ -1064,7 +1109,7 @@
         return;
     }
 
-    // nowNs start our calculations
+    // now start our calculations
     const int64_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC);
     int64_t latencyUs = (nowNs - startdata.startedNs + 500) / 1000;
 
@@ -1337,6 +1382,17 @@
             ALOGE("Invalid size(s), width=%d, height=%d", mVideoWidth, mVideoHeight);
             return BAD_VALUE;
         }
+    } else {
+        if (mMetricsHandle != 0) {
+            int32_t channelCount;
+            if (format->findInt32(KEY_CHANNEL_COUNT, &channelCount)) {
+                mediametrics_setInt32(mMetricsHandle, kCodecChannelCount, channelCount);
+            }
+            int32_t sampleRate;
+            if (format->findInt32(KEY_SAMPLE_RATE, &sampleRate)) {
+                mediametrics_setInt32(mMetricsHandle, kCodecSampleRate, sampleRate);
+            }
+        }
     }
 
     updateLowLatency(format);
@@ -2183,14 +2239,15 @@
         int64_t timeUs;
         CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
 
-        statsBufferReceived(timeUs);
-
         response->setInt64("timeUs", timeUs);
 
         int32_t flags;
         CHECK(buffer->meta()->findInt32("flags", &flags));
 
         response->setInt32("flags", flags);
+
+        statsBufferReceived(timeUs, buffer);
+
         response->postReply(replyID);
     }
 
@@ -2281,6 +2338,8 @@
                                 }
                                 postPendingRepliesAndDeferredMessages(origin + ":dead");
                                 sendErrorResponse = false;
+                            } else if (!mReplyID) {
+                                sendErrorResponse = false;
                             }
                             break;
                         }
@@ -4337,13 +4396,13 @@
 
         msg->setInt64("timeUs", timeUs);
 
-        statsBufferReceived(timeUs);
-
         int32_t flags;
         CHECK(buffer->meta()->findInt32("flags", &flags));
 
         msg->setInt32("flags", flags);
 
+        statsBufferReceived(timeUs, buffer);
+
         msg->post();
     }
 }
diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp
index bc656a2..0f7df24 100644
--- a/media/libstagefright/MediaCodecSource.cpp
+++ b/media/libstagefright/MediaCodecSource.cpp
@@ -30,6 +30,7 @@
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/ALooper.h>
 #include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/ColorUtils.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaCodec.h>
 #include <media/stagefright/MediaCodecConstants.h>
@@ -768,6 +769,26 @@
             memcpy(inbuf->data(), mbuf->data(), size);
 
             if (mIsVideo) {
+                int32_t ds = 0;
+                if (mbuf->meta_data().findInt32(kKeyColorSpace, &ds)
+                        && ds != HAL_DATASPACE_UNKNOWN) {
+                    android_dataspace dataspace = static_cast<android_dataspace>(ds);
+                    ColorUtils::convertDataSpaceToV0(dataspace);
+                    ALOGD("Updating dataspace to %x", dataspace);
+                    int32_t standard = (int32_t(dataspace) & HAL_DATASPACE_STANDARD_MASK)
+                        >> HAL_DATASPACE_STANDARD_SHIFT;
+                    int32_t transfer = (int32_t(dataspace) & HAL_DATASPACE_TRANSFER_MASK)
+                        >> HAL_DATASPACE_TRANSFER_SHIFT;
+                    int32_t range = (int32_t(dataspace) & HAL_DATASPACE_RANGE_MASK)
+                        >> HAL_DATASPACE_RANGE_SHIFT;
+                    sp<AMessage> msg = new AMessage;
+                    msg->setInt32(KEY_COLOR_STANDARD, standard);
+                    msg->setInt32(KEY_COLOR_TRANSFER, transfer);
+                    msg->setInt32(KEY_COLOR_RANGE, range);
+                    msg->setInt32("android._dataspace", dataspace);
+                    mEncoder->setParameters(msg);
+                }
+
                 // video encoder will release MediaBuffer when done
                 // with underlying data.
                 inbuf->meta()->setObject("mediaBufferHolder", new MediaBufferHolder(mbuf));
diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp
index 7c981b3..d77845f 100644
--- a/media/libstagefright/MediaExtractorFactory.cpp
+++ b/media/libstagefright/MediaExtractorFactory.cpp
@@ -188,11 +188,11 @@
     // sanity check check struct version, uuid, name
     if (plugin->def.def_version != EXTRACTORDEF_VERSION_NDK_V1 &&
             plugin->def.def_version != EXTRACTORDEF_VERSION_NDK_V2) {
-        ALOGE("don't understand extractor format %u, ignoring.", plugin->def.def_version);
+        ALOGW("don't understand extractor format %u, ignoring.", plugin->def.def_version);
         return;
     }
     if (memcmp(&plugin->def.extractor_uuid, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) == 0) {
-        ALOGE("invalid UUID, ignoring");
+        ALOGW("invalid UUID, ignoring");
         return;
     }
     if (plugin->def.extractor_name == NULL || strlen(plugin->def.extractor_name) == 0) {
@@ -244,13 +244,17 @@
             void *libHandle = android_dlopen_ext(
                     libPath.string(),
                     RTLD_NOW | RTLD_LOCAL, dlextinfo);
-            CHECK(libHandle != nullptr)
-                    << "couldn't dlopen(" << libPath.string() << ") " << strerror(errno);
+            if (libHandle == nullptr) {
+                ALOGI("dlopen(%s) reported error %s", libPath.string(), strerror(errno));
+                continue;
+            }
 
             GetExtractorDef getDef =
                 (GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF");
-            CHECK(getDef != nullptr)
-                    << libPath.string() << " does not contain sniffer";
+            if (getDef == nullptr) {
+                ALOGI("no sniffer found in %s", libPath.string());
+                continue;
+            }
 
             ALOGV("registering sniffer for %s", libPath.string());
             RegisterExtractor(
@@ -258,7 +262,7 @@
         }
         closedir(libDir);
     } else {
-        ALOGE("couldn't opendir(%s)", libDirPath);
+        ALOGI("plugin directory not present (%s)", libDirPath);
     }
 }
 
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index f63740e..51d9730 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -1675,7 +1675,7 @@
     if (msg->findString("mime", &mime)) {
         meta->setCString(kKeyMIMEType, mime.c_str());
     } else {
-        ALOGE("did not find mime type");
+        ALOGI("did not find mime type");
         return BAD_VALUE;
     }
 
@@ -1725,7 +1725,7 @@
             meta->setInt32(kKeyWidth, width);
             meta->setInt32(kKeyHeight, height);
         } else {
-            ALOGE("did not find width and/or height");
+            ALOGI("did not find width and/or height");
             return BAD_VALUE;
         }
 
@@ -1814,7 +1814,7 @@
         int32_t numChannels, sampleRate;
         if (!msg->findInt32("channel-count", &numChannels) ||
                 !msg->findInt32("sample-rate", &sampleRate)) {
-            ALOGE("did not find channel-count and/or sample-rate");
+            ALOGI("did not find channel-count and/or sample-rate");
             return BAD_VALUE;
         }
         meta->setInt32(kKeyChannelCount, numChannels);
diff --git a/media/libstagefright/flac/dec/Android.bp b/media/libstagefright/flac/dec/Android.bp
index 225c930..665aae1 100644
--- a/media/libstagefright/flac/dec/Android.bp
+++ b/media/libstagefright/flac/dec/Android.bp
@@ -40,12 +40,12 @@
     },
 
     shared_libs: [
-        "libaudioutils",
         "liblog",
     ],
 
     static_libs: [
         "libFLAC",
+        "libaudioutils", // needed for 'float_from_i32'
     ],
 
     export_static_lib_headers: [
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index f242b19..6bb7b37 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -1078,6 +1078,17 @@
     return OK;
 }
 
+status_t AMessage::removeEntryByName(const char *name) {
+    if (name == nullptr) {
+        return BAD_VALUE;
+    }
+    size_t index = findEntryByName(name);
+    if (index >= mNumItems) {
+        return BAD_INDEX;
+    }
+    return removeEntryAt(index);
+}
+
 void AMessage::setItem(const char *name, const ItemData &item) {
     if (item.used()) {
         Item *it = allocateItem(name);
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/AMessage.h b/media/libstagefright/foundation/include/media/stagefright/foundation/AMessage.h
index 31e58ba..98d6147 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/AMessage.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/AMessage.h
@@ -261,6 +261,17 @@
      */
     status_t removeEntryAt(size_t index);
 
+    /**
+     * Removes an entry based on name.
+     *
+     * \param name  name of the entry
+     *
+     * \retval OK the entry was removed successfully
+     * \retval BAD_VALUE name is invalid (null)
+     * \retval BAD_INDEX name not found
+     */
+    status_t removeEntryByName(const char *name);
+
 protected:
     virtual ~AMessage();
 
diff --git a/media/libstagefright/foundation/tests/AMessage_test.cpp b/media/libstagefright/foundation/tests/AMessage_test.cpp
new file mode 100644
index 0000000..2b11326
--- /dev/null
+++ b/media/libstagefright/foundation/tests/AMessage_test.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AData_test"
+
+#include <gtest/gtest.h>
+#include <utils/RefBase.h>
+
+#include <media/stagefright/foundation/AMessage.h>
+
+using namespace android;
+
+class AMessageTest : public ::testing::Test {
+};
+
+
+TEST(AMessage_tests, item_manipulation) {
+  sp<AMessage> m1 = new AMessage();
+
+  m1->setInt32("value", 2);
+  m1->setInt32("bar", 3);
+
+  int32_t i32;
+  EXPECT_TRUE(m1->findInt32("value", &i32));
+  EXPECT_EQ(2, i32);
+
+  EXPECT_TRUE(m1->findInt32("bar", &i32));
+  EXPECT_EQ(3, i32);
+
+
+  m1->setInt64("big", INT64_MAX);
+  m1->setInt64("smaller", INT64_MAX - 2);
+  m1->setInt64("smallest", 257);
+
+  int64_t i64;
+  EXPECT_TRUE(m1->findInt64("big", &i64));
+  EXPECT_EQ(INT64_MAX, i64);
+
+  EXPECT_TRUE(m1->findInt64("smaller", &i64));
+  EXPECT_EQ(INT64_MAX - 2, i64);
+
+  m1->setSize("size1", 257);
+  m1->setSize("size2", 1023);
+
+  size_t sizing;
+  EXPECT_TRUE(m1->findSize("size2", &sizing));
+  EXPECT_EQ(1023, sizing);
+  EXPECT_TRUE(m1->findSize("size1", &sizing));
+  EXPECT_EQ(257, sizing);
+
+  m1->setDouble("precise", 10.5);
+  m1->setDouble("small", 0.125);
+
+  double d;
+  EXPECT_TRUE(m1->findDouble("precise", &d));
+  EXPECT_EQ(10.5, d);
+
+  EXPECT_TRUE(m1->findDouble("small", &d));
+  EXPECT_EQ(0.125, d);
+
+  // should be unchanged from the top of the test
+  EXPECT_TRUE(m1->findInt32("bar", &i32));
+  EXPECT_EQ(3, i32);
+
+  EXPECT_FALSE(m1->findInt32("nonesuch", &i32));
+  EXPECT_FALSE(m1->findInt64("nonesuch2", &i64));
+  // types disagree, not found
+  EXPECT_FALSE(m1->findInt32("big", &i32));
+  EXPECT_FALSE(m1->findInt32("precise", &i32));
+
+  // integral types should come back true
+  EXPECT_TRUE(m1->findAsInt64("big", &i64));
+  EXPECT_EQ(INT64_MAX, i64);
+  EXPECT_TRUE(m1->findAsInt64("bar", &i64));
+  EXPECT_EQ(3, i64);
+  EXPECT_FALSE(m1->findAsInt64("precise", &i64));
+
+  // recovers ints, size, and floating point values
+  float value;
+  EXPECT_TRUE(m1->findAsFloat("value", &value));
+  EXPECT_EQ(2, value);
+  EXPECT_TRUE(m1->findAsFloat("smallest", &value));
+  EXPECT_EQ(257, value);
+  EXPECT_TRUE(m1->findAsFloat("size2", &value));
+  EXPECT_EQ(1023, value);
+  EXPECT_TRUE(m1->findAsFloat("precise", &value));
+  EXPECT_EQ(10.5, value);
+  EXPECT_TRUE(m1->findAsFloat("small", &value));
+  EXPECT_EQ(0.125, value);
+
+
+  // need to handle still:
+  // strings
+  // Object
+  // Buffer
+  // Message (nested)
+  //
+
+  // removal
+  m1->setInt32("shortlived", 2);
+  m1->setInt32("alittlelonger", 2);
+  EXPECT_EQ(OK, m1->removeEntryByName("shortlived"));
+  EXPECT_EQ(BAD_VALUE, m1->removeEntryByName(nullptr));
+  EXPECT_EQ(BAD_INDEX, m1->removeEntryByName("themythicalnonesuch"));
+  EXPECT_FALSE(m1->findInt32("shortlived", &i32));
+  EXPECT_TRUE(m1->findInt32("alittlelonger", &i32));
+
+  EXPECT_NE(OK, m1->removeEntryByName("notpresent"));
+
+}
+
diff --git a/media/libstagefright/foundation/tests/Android.bp b/media/libstagefright/foundation/tests/Android.bp
index 715b57a..e50742e 100644
--- a/media/libstagefright/foundation/tests/Android.bp
+++ b/media/libstagefright/foundation/tests/Android.bp
@@ -30,6 +30,7 @@
 
     srcs: [
         "AData_test.cpp",
+        "AMessage_test.cpp",
         "Base64_test.cpp",
         "Flagged_test.cpp",
         "TypeTraits_test.cpp",
diff --git a/media/libstagefright/include/media/stagefright/AudioSource.h b/media/libstagefright/include/media/stagefright/AudioSource.h
index 451aa57..d1dcdb5 100644
--- a/media/libstagefright/include/media/stagefright/AudioSource.h
+++ b/media/libstagefright/include/media/stagefright/AudioSource.h
@@ -37,16 +37,27 @@
     // Note that the "channels" parameter _is_ the number of channels,
     // _not_ a bitmask of audio_channels_t constants.
     AudioSource(
-            const audio_attributes_t *attr,
-            const String16 &opPackageName,
-            uint32_t sampleRate,
-            uint32_t channels,
-            uint32_t outSampleRate = 0,
-            uid_t uid = -1,
-            pid_t pid = -1,
-            audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
-            audio_microphone_direction_t selectedMicDirection = MIC_DIRECTION_UNSPECIFIED,
-            float selectedMicFieldDimension = MIC_FIELD_DIMENSION_NORMAL);
+        const audio_attributes_t *attr,
+        const media::permission::Identity& identity,
+        uint32_t sampleRate,
+        uint32_t channels,
+        uint32_t outSampleRate = 0,
+        audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
+        audio_microphone_direction_t selectedMicDirection = MIC_DIRECTION_UNSPECIFIED,
+        float selectedMicFieldDimension = MIC_FIELD_DIMENSION_NORMAL);
+
+    // Legacy constructor kept for vendor dependencies
+    AudioSource(
+        const audio_attributes_t *attr,
+        const String16 &opPackageName,
+        uint32_t sampleRate,
+        uint32_t channels,
+        uint32_t outSampleRate = 0,
+        uid_t uid = -1,
+        pid_t pid = -1,
+        audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
+        audio_microphone_direction_t selectedMicDirection = MIC_DIRECTION_UNSPECIFIED,
+        float selectedMicFieldDimension = MIC_FIELD_DIMENSION_NORMAL);
 
     status_t initCheck() const;
 
@@ -131,6 +142,16 @@
 
     AudioSource(const AudioSource &);
     AudioSource &operator=(const AudioSource &);
+
+    void set(
+        const audio_attributes_t *attr,
+        const media::permission::Identity& identity,
+        uint32_t sampleRate,
+        uint32_t channels,
+        uint32_t outSampleRate = 0,
+        audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
+        audio_microphone_direction_t selectedMicDirection = MIC_DIRECTION_UNSPECIFIED,
+        float selectedMicFieldDimension = MIC_FIELD_DIMENSION_NORMAL);
 };
 
 }  // namespace android
diff --git a/media/libstagefright/include/media/stagefright/CameraSource.h b/media/libstagefright/include/media/stagefright/CameraSource.h
index 16e7d89..e8770ed 100644
--- a/media/libstagefright/include/media/stagefright/CameraSource.h
+++ b/media/libstagefright/include/media/stagefright/CameraSource.h
@@ -159,6 +159,7 @@
     int32_t  mColorFormat;
     int32_t  mEncoderFormat;
     int32_t  mEncoderDataSpace;
+    int32_t  mBufferDataSpace;
     status_t mInitCheck;
 
     sp<Camera>   mCamera;
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index a28d479..5f64686 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -528,6 +528,14 @@
     std::deque<BufferFlightTiming_t> mBuffersInFlight;
     Mutex mLatencyLock;
     int64_t mLatencyUnknown;    // buffers for which we couldn't calculate latency
+
+    Mutex mOutputStatsLock;
+    int64_t mBytesEncoded = 0;
+    int64_t mEarliestEncodedPtsUs = INT64_MAX;
+    int64_t mLatestEncodedPtsUs = INT64_MIN;
+    int32_t mFramesEncoded = 0;
+
+
     int64_t mNumLowLatencyEnables;  // how many times low latency mode is enabled
     int64_t mNumLowLatencyDisables;  // how many times low latency mode is disabled
     bool mIsLowLatencyModeOn;  // is low latency mode on currently
@@ -544,7 +552,7 @@
     sp<BatteryChecker> mBatteryChecker;
 
     void statsBufferSent(int64_t presentationUs);
-    void statsBufferReceived(int64_t presentationUs);
+    void statsBufferReceived(int64_t presentationUs, const sp<MediaCodecBuffer> &buffer);
 
     enum {
         // the default shape of our latency histogram buckets
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
index 619cb44..8fb4db2 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
@@ -794,7 +794,7 @@
 constexpr char KEY_PCM_ENCODING[] = "pcm-encoding";
 constexpr char KEY_PIXEL_ASPECT_RATIO_HEIGHT[] = "sar-height";
 constexpr char KEY_PIXEL_ASPECT_RATIO_WIDTH[] = "sar-width";
-constexpr char KEY_PREPEND_HEADERS_TO_SYNC_FRAMES[] = "prepend-sps-pps-to-idr-frames";
+constexpr char KEY_PREPEND_HEADER_TO_SYNC_FRAMES[] = "prepend-sps-pps-to-idr-frames";
 constexpr char KEY_PRIORITY[] = "priority";
 constexpr char KEY_PROFILE[] = "profile";
 constexpr char KEY_PUSH_BLANK_BUFFERS_ON_STOP[] = "push-blank-buffers-on-shutdown";
diff --git a/media/libstagefright/include/media/stagefright/MediaErrors.h b/media/libstagefright/include/media/stagefright/MediaErrors.h
index cfd5608..d1df2ca 100644
--- a/media/libstagefright/include/media/stagefright/MediaErrors.h
+++ b/media/libstagefright/include/media/stagefright/MediaErrors.h
@@ -94,12 +94,13 @@
     ERROR_DRM_PROVISIONING_CERTIFICATE       = DRM_ERROR_BASE - 31,
     ERROR_DRM_PROVISIONING_CONFIG            = DRM_ERROR_BASE - 32,
     ERROR_DRM_PROVISIONING_PARSE             = DRM_ERROR_BASE - 33,
-    ERROR_DRM_PROVISIONING_RETRY             = DRM_ERROR_BASE - 34,
-    ERROR_DRM_SECURE_STOP_RELEASE            = DRM_ERROR_BASE - 35,
-    ERROR_DRM_STORAGE_READ                   = DRM_ERROR_BASE - 36,
-    ERROR_DRM_STORAGE_WRITE                  = DRM_ERROR_BASE - 37,
-    ERROR_DRM_ZERO_SUBSAMPLES                = DRM_ERROR_BASE - 38,
-    ERROR_DRM_LAST_USED_ERRORCODE            = DRM_ERROR_BASE - 38,
+    ERROR_DRM_PROVISIONING_REQUEST_REJECTED  = DRM_ERROR_BASE - 34,
+    ERROR_DRM_PROVISIONING_RETRY             = DRM_ERROR_BASE - 35,
+    ERROR_DRM_SECURE_STOP_RELEASE            = DRM_ERROR_BASE - 36,
+    ERROR_DRM_STORAGE_READ                   = DRM_ERROR_BASE - 37,
+    ERROR_DRM_STORAGE_WRITE                  = DRM_ERROR_BASE - 38,
+    ERROR_DRM_ZERO_SUBSAMPLES                = DRM_ERROR_BASE - 39,
+    ERROR_DRM_LAST_USED_ERRORCODE            = ERROR_DRM_ZERO_SUBSAMPLES,
 
     ERROR_DRM_VENDOR_MAX                     = DRM_ERROR_BASE - 500,
     ERROR_DRM_VENDOR_MIN                     = DRM_ERROR_BASE - 999,
@@ -202,6 +203,7 @@
         STATUS_CASE(ERROR_DRM_PROVISIONING_CERTIFICATE);
         STATUS_CASE(ERROR_DRM_PROVISIONING_CONFIG);
         STATUS_CASE(ERROR_DRM_PROVISIONING_PARSE);
+        STATUS_CASE(ERROR_DRM_PROVISIONING_REQUEST_REJECTED);
         STATUS_CASE(ERROR_DRM_PROVISIONING_RETRY);
         STATUS_CASE(ERROR_DRM_SECURE_STOP_RELEASE);
         STATUS_CASE(ERROR_DRM_STORAGE_READ);
diff --git a/media/libstagefright/tests/fuzzers/Android.bp b/media/libstagefright/tests/fuzzers/Android.bp
index 65e74e6..0097830 100644
--- a/media/libstagefright/tests/fuzzers/Android.bp
+++ b/media/libstagefright/tests/fuzzers/Android.bp
@@ -30,6 +30,7 @@
         "libgui",
         "libbinder",
         "liblog",
+        "media_permission-aidl-cpp",
     ],
     include_dirs: [
         "frameworks/av/media/libstagefright",
diff --git a/media/libstagefright/tests/fuzzers/WriterFuzzer.cpp b/media/libstagefright/tests/fuzzers/WriterFuzzer.cpp
index 03e9b43..969c6e1 100644
--- a/media/libstagefright/tests/fuzzers/WriterFuzzer.cpp
+++ b/media/libstagefright/tests/fuzzers/WriterFuzzer.cpp
@@ -17,6 +17,7 @@
 //          dylan.katz@leviathansecurity.com
 
 #include <android-base/file.h>
+#include <android/media/permission/Identity.h>
 #include <ctype.h>
 #include <media/mediarecorder.h>
 #include <media/stagefright/MPEG4Writer.h>
@@ -39,6 +40,8 @@
 
 namespace android {
 
+using media::permission::Identity;
+
 std::string getFourCC(FuzzedDataProvider *fdp) {
     std::string fourCC = fdp->ConsumeRandomLengthString(4);
     // Replace any existing nulls
@@ -163,9 +166,11 @@
     StandardWriters writerType = dataProvider.ConsumeEnum<StandardWriters>();
     sp<MediaWriter> writer = createWriter(tf.fd, writerType, fileMeta);
 
-    std::string packageName = dataProvider.ConsumeRandomLengthString(kMaxPackageNameLen);
-
-    sp<MediaRecorder> mr = new MediaRecorder(String16(packageName.c_str()));
+    Identity i;
+    i.packageName = dataProvider.ConsumeRandomLengthString(kMaxPackageNameLen);
+    i.uid = dataProvider.ConsumeIntegral<int32_t>();
+    i.pid = dataProvider.ConsumeIntegral<int32_t>();
+    sp<MediaRecorder> mr = new MediaRecorder(i);
     writer->setListener(mr);
 
     uint8_t baseOpLen = operations.size();
diff --git a/media/libstagefright/tests/mediacodec/MediaCodecTest.cpp b/media/libstagefright/tests/mediacodec/MediaCodecTest.cpp
index 6facbd8..06e36ad 100644
--- a/media/libstagefright/tests/mediacodec/MediaCodecTest.cpp
+++ b/media/libstagefright/tests/mediacodec/MediaCodecTest.cpp
@@ -285,10 +285,11 @@
     // 1) Client thread calls stop(); MediaCodec looper thread calls
     //    initiateShutdown(); shutdown is being handled at the component thread.
     // 2) Error occurred, but the shutdown operation is still being done.
-    // 3) MediaCodec looper thread handles the error.
-    // 4) Client releases the codec upon the error; previous shutdown is still
+    // 3) Another error occurred during the shutdown operation.
+    // 4) MediaCodec looper thread handles the error.
+    // 5) Client releases the codec upon the error; previous shutdown is still
     //    going on.
-    // 5) Component thread completes shutdown and posts onStopCompleted();
+    // 6) Component thread completes shutdown and posts onStopCompleted();
     //    Shutdown from release also completes.
 
     static const AString kCodecName{"test.codec"};
@@ -317,6 +318,9 @@
                 });
             ON_CALL(*mockCodec, initiateShutdown(true))
                 .WillByDefault([mockCodec](bool) {
+                    // 2)
+                    mockCodec->callback()->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+                    // 3)
                     mockCodec->callback()->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
                 });
             ON_CALL(*mockCodec, initiateShutdown(false))
@@ -339,6 +343,8 @@
     codec->start();
     // stop() will fail because of the error
     EXPECT_NE(OK, codec->stop());
+    // sleep here so that the looper thread can handle all the errors.
+    std::this_thread::sleep_for(std::chrono::milliseconds(100));
     // upon receiving the error, client tries to release the codec.
     codec->release();
     looper->stop();
diff --git a/media/libstagefright/webm/Android.bp b/media/libstagefright/webm/Android.bp
index 68752cd..3ceacfe 100644
--- a/media/libstagefright/webm/Android.bp
+++ b/media/libstagefright/webm/Android.bp
@@ -40,6 +40,7 @@
         "libstagefright_foundation",
         "libutils",
         "liblog",
+        "media_permission-aidl-cpp",
     ],
 
     header_libs: [
diff --git a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
index dbdb43c..5a9760d 100644
--- a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
+++ b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
@@ -346,6 +346,7 @@
         status_t addAlias(const char **attrs);
         status_t addFeature(const char **attrs);
         status_t addLimit(const char **attrs);
+        status_t addMapping(const char **attrs);
         status_t addQuirk(const char **attrs, const char *prefix = nullptr);
         status_t addSetting(const char **attrs, const char *prefix = nullptr);
         status_t enterMediaCodec(const char **attrs, bool encoder);
@@ -428,7 +429,7 @@
         if (findFileInDirs(searchDirs, fileName, &path)) {
             err = parseXmlPath(path);
         } else {
-            ALOGD("Cannot find %s", path.c_str());
+            ALOGD("Cannot find %s in search path", fileName.c_str());
         }
         res = combineStatus(res, err);
     }
@@ -741,13 +742,16 @@
         {
             // ignore limits and features specified outside of type
             if (!mState->inType()
-                    && (strEq(name, "Limit") || strEq(name, "Feature") || strEq(name, "Variant"))) {
+                    && (strEq(name, "Limit") || strEq(name, "Feature")
+                        || strEq(name, "Variant") || strEq(name, "Mapping"))) {
                 PLOGD("ignoring %s specified outside of a Type", name);
                 return;
             } else if (strEq(name, "Limit")) {
                 err = addLimit(attrs);
             } else if (strEq(name, "Feature")) {
                 err = addFeature(attrs);
+            } else if (strEq(name, "Mapping")) {
+                err = addMapping(attrs);
             } else if (strEq(name, "Variant") && section != SECTION_VARIANT) {
                 err = limitVariants(attrs);
                 mState->enterSection(err == OK ? SECTION_VARIANT : SECTION_UNKNOWN);
@@ -981,7 +985,9 @@
     TypeMap::iterator typeIt;
     if (codecIt == mData->mCodecMap.end()) { // New codec name
         if (updating) {
-            return { NAME_NOT_FOUND, "MediaCodec: cannot update non-existing codec" };
+            std::string msg = "MediaCodec: cannot update non-existing codec: ";
+            msg = msg + name;
+            return { NAME_NOT_FOUND, msg };
         }
         // Create a new codec in mCodecMap
         codecIt = mData->mCodecMap.insert(Codec(name, CodecProperties())).first;
@@ -994,19 +1000,25 @@
         codecIt->second.order = mData->mCodecMap.size();
     } else { // Existing codec name
         if (!updating) {
-            return { ALREADY_EXISTS, "MediaCodec: cannot add existing codec" };
+            std::string msg = "MediaCodec: cannot add existing codec: ";
+            msg = msg + name;
+            return { ALREADY_EXISTS, msg };
         }
         if (type != nullptr) {
             typeIt = codecIt->second.typeMap.find(type);
             if (typeIt == codecIt->second.typeMap.end()) {
-                return { NAME_NOT_FOUND, "MediaCodec: cannot update non-existing type for codec" };
+                std::string msg = "MediaCodec: cannot update non-existing type for codec: ";
+                msg = msg + name;
+                return { NAME_NOT_FOUND, msg };
             }
         } else {
             // This should happen only when the codec has at most one type.
             typeIt = codecIt->second.typeMap.begin();
             if (typeIt == codecIt->second.typeMap.end()
                     || codecIt->second.typeMap.size() != 1) {
-                return { BAD_VALUE, "MediaCodec: cannot update codec without type specified" };
+                std::string msg = "MediaCodec: cannot update codec without type specified: ";
+                msg = msg + name;
+                return { BAD_VALUE, msg };
             }
         }
     }
@@ -1386,6 +1398,53 @@
     return OK;
 }
 
+status_t MediaCodecsXmlParser::Impl::Parser::addMapping(const char **attrs) {
+    CHECK(mState->inType());
+    size_t i = 0;
+    const char *a_name = nullptr;
+    const char *a_value = nullptr;
+    const char *a_kind = nullptr;
+
+    while (attrs[i] != nullptr) {
+        CHECK((i & 1) == 0);
+        if (attrs[i + 1] == nullptr) {
+            PLOGD("Mapping: attribute '%s' is null", attrs[i]);
+            return BAD_VALUE;
+        }
+
+        if (strEq(attrs[i], "name")) {
+            a_name = attrs[++i];
+        } else if (strEq(attrs[i], "kind")) {
+            a_kind = attrs[++i];
+        } else if (strEq(attrs[i], "value")) {
+            a_value = attrs[++i];
+        } else {
+            PLOGD("Mapping: ignoring unrecognized attribute '%s'", attrs[i]);
+            ++i;
+        }
+        ++i;
+    }
+
+    // Every mapping must have all 3 fields
+    if (a_name == nullptr) {
+        PLOGD("Mapping with no 'name' attribute");
+        return BAD_VALUE;
+    }
+
+    if (a_kind == nullptr) {
+        PLOGD("Mapping with no 'kind' attribute");
+        return BAD_VALUE;
+    }
+
+    if (a_value == nullptr) {
+        PLOGD("Mapping with no 'value' attribute");
+        return BAD_VALUE;
+    }
+
+    mState->addDetail(std::string("mapping-") + a_kind + "-" + a_name, a_value);
+    return OK;
+}
+
 status_t MediaCodecsXmlParser::Impl::Parser::addAlias(const char **attrs) {
     CHECK(mState->inCodec());
     size_t i = 0;
diff --git a/media/libstagefright/xmlparser/api/current.txt b/media/libstagefright/xmlparser/api/current.txt
index 16c8af8..6f55dc0 100644
--- a/media/libstagefright/xmlparser/api/current.txt
+++ b/media/libstagefright/xmlparser/api/current.txt
@@ -65,6 +65,16 @@
     method public void set_default(String);
   }
 
+  public class Mapping {
+    ctor public Mapping();
+    method public String getKind();
+    method public String getName();
+    method public String getValue();
+    method public void setKind(String);
+    method public void setName(String);
+    method public void setValue(String);
+  }
+
   public class MediaCodec {
     ctor public MediaCodec();
     method public java.util.List<media.codecs.Alias> getAlias_optional();
@@ -73,6 +83,7 @@
     method public String getEnabled();
     method public java.util.List<media.codecs.Feature> getFeature_optional();
     method public java.util.List<media.codecs.Limit> getLimit_optional();
+    method public java.util.List<media.codecs.Mapping> getMapping_optional();
     method public String getName();
     method public java.util.List<media.codecs.Quirk> getQuirk_optional();
     method public String getRank();
diff --git a/media/libstagefright/xmlparser/media_codecs.xsd b/media/libstagefright/xmlparser/media_codecs.xsd
index 3b5681f..30974f6 100644
--- a/media/libstagefright/xmlparser/media_codecs.xsd
+++ b/media/libstagefright/xmlparser/media_codecs.xsd
@@ -63,6 +63,7 @@
             <xs:element name="Alias" type="Alias" minOccurs="0" maxOccurs="unbounded"/>
             <xs:element name="Limit" type="Limit" minOccurs="0" maxOccurs="unbounded"/>
             <xs:element name="Feature" type="Feature" minOccurs="0" maxOccurs="unbounded"/>
+            <xs:element name="Mapping" type="Mapping" minOccurs="0" maxOccurs="unbounded"/>
             <xs:element name="Variant" type="Variant" minOccurs="0" maxOccurs="unbounded"/>
         </xs:choice>
         <xs:attribute name="name" type="xs:string"/>
@@ -122,6 +123,11 @@
         <xs:attribute name="enabled" type="xs:string"/>
         <xs:attribute name="update" type="xs:string"/>
     </xs:complexType>
+    <xs:complexType name="Mapping">
+        <xs:attribute name="name" type="xs:string"/>
+        <xs:attribute name="kind" type="xs:string"/>
+        <xs:attribute name="value" type="xs:string"/>
+    </xs:complexType>
     <xs:complexType name="Include">
         <xs:attribute name="href" type="xs:string"/>
     </xs:complexType>
diff --git a/media/libstagefright/xmlparser/test/XMLParserTest.cpp b/media/libstagefright/xmlparser/test/XMLParserTest.cpp
index 9ddd374..c411c8d 100644
--- a/media/libstagefright/xmlparser/test/XMLParserTest.cpp
+++ b/media/libstagefright/xmlparser/test/XMLParserTest.cpp
@@ -127,6 +127,18 @@
     setCodecProperties("test8.encoder", true, 8, {}, {}, {}, "audio/opus",
                        {pair<string, string>("max-limit1", "limit1Max")}, {}, "");
 
+    setCodecProperties("test9.encoder", true, 9, {}, {}, {}, "video/avc",
+           {
+                   pair<string, string>("mapping-sure-before", "after"),
+           },
+           {}, "");
+
+    setCodecProperties("test10.encoder", true, 10, {}, {}, {}, "video/hevc",
+           {
+                   pair<string, string>("mapping-fire-from", "to"),
+           },
+           {}, "");
+
     setRoleProperties("audio_decoder.mp3", false, 1, "audio/mpeg", "test1.decoder",
                       {pair<string, string>("attribute::disabled", "present"),
                        pair<string, string>("rank", "4")});
@@ -162,6 +174,12 @@
     setRoleProperties("audio_encoder.opus", true, 8, "audio/opus", "test8.encoder",
                       {pair<string, string>("max-limit1", "limit1Max")});
 
+    setRoleProperties("video_encoder.avc", true, 9, "video/avc", "test9.encoder",
+                       {pair<string, string>("mapping-sure-before", "after")});
+
+    setRoleProperties("video_encoder.hevc", true, 10, "video/hevc", "test10.encoder",
+                       { pair<string, string>("mapping-fire-from", "to")});
+
     setServiceAttribute(
             {pair<string, string>("domain-telephony", "0"), pair<string, string>("domain-tv", "0"),
              pair<string, string>("setting2", "0"), pair<string, string>("variant-variant1", "0")});
diff --git a/media/libstagefright/xmlparser/test/testdata/media_codecs_unit_test.xml b/media/libstagefright/xmlparser/test/testdata/media_codecs_unit_test.xml
index a7299d3..c8913e5 100644
--- a/media/libstagefright/xmlparser/test/testdata/media_codecs_unit_test.xml
+++ b/media/libstagefright/xmlparser/test/testdata/media_codecs_unit_test.xml
@@ -76,5 +76,12 @@
         <MediaCodec name="test8.encoder" type="audio/opus">
             <Limit name="limit1" max="limit1Max" />
         </MediaCodec>
+        <!-- entry for testing Mapping -->
+        <MediaCodec name="test9.encoder" type="video/avc" >
+            <Mapping kind="sure" name="before" value="after"/>
+        </MediaCodec>
+        <MediaCodec name="test10.encoder" type="video/hevc" >
+            <Mapping kind="fire" name="from" value="to"/>
+        </MediaCodec>
     </Encoders>
 </Included>
diff --git a/media/ndk/TEST_MAPPING b/media/ndk/TEST_MAPPING
index 1a81538..e420812 100644
--- a/media/ndk/TEST_MAPPING
+++ b/media/ndk/TEST_MAPPING
@@ -1,6 +1,7 @@
 // mappings for frameworks/av/media/ndk
 {
   "presubmit": [
-    { "name": "AImageReaderWindowHandleTest" }
+    { "name": "AImageReaderWindowHandleTest" },
+    { "name": "libmediandk_test" }
   ]
 }
diff --git a/media/ndk/tests/Android.bp b/media/ndk/tests/Android.bp
new file mode 100644
index 0000000..984b3ee
--- /dev/null
+++ b/media/ndk/tests/Android.bp
@@ -0,0 +1,41 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Headers module is in frameworks/av/Android.bp because modules are not allowed
+// to refer to headers in parent directories and the headers live in
+// frameworks/av/include.
+
+package {
+    default_applicable_licenses: ["frameworks_av_media_ndk_license"],
+}
+
+cc_test {
+    name: "libmediandk_test",
+    test_suites: ["device-tests"],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+
+    shared_libs: [
+        "liblog",
+        "libmediandk",
+        "libutils",
+    ],
+
+    srcs: [
+        "NdkMediaFormat_test.cpp",
+    ],
+}
diff --git a/media/ndk/tests/NdkMediaFormat_test.cpp b/media/ndk/tests/NdkMediaFormat_test.cpp
new file mode 100644
index 0000000..668d0a4
--- /dev/null
+++ b/media/ndk/tests/NdkMediaFormat_test.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NdkMediaFormat_test"
+
+#include <gtest/gtest.h>
+#include <utils/RefBase.h>
+
+#include <media/NdkMediaFormat.h>
+
+namespace android {
+
+class NdkMediaFormatTest : public ::testing::Test {
+};
+
+
+TEST(NdkMediaFormat_tests, test_create) {
+
+   AMediaFormat *fmt1 = AMediaFormat_new();
+   AMediaFormat *fmt2 = AMediaFormat_new();
+
+   EXPECT_NE(fmt1, fmt2);
+   EXPECT_NE(fmt1, nullptr);
+   EXPECT_NE(fmt2, nullptr);
+
+   AMediaFormat_delete(fmt1);
+   AMediaFormat_delete(fmt2);
+}
+
+TEST(NdkMediaFormat_tests, test_int32) {
+   AMediaFormat *fmt1 = AMediaFormat_new();
+   int32_t i32;
+   int64_t i64;
+   AMediaFormat_setInt32(fmt1, "five", 5);
+
+   EXPECT_TRUE(AMediaFormat_getInt32(fmt1, "five", &i32));
+   EXPECT_FALSE(AMediaFormat_getInt64(fmt1, "five", &i64));
+   EXPECT_EQ(i32, 5);
+
+   AMediaFormat_delete(fmt1);
+}
+
+TEST(NdkMediaFormat_tests, test_int64) {
+   AMediaFormat *fmt1 = AMediaFormat_new();
+   int64_t i64;
+   AMediaFormat_setInt64(fmt1, "verylarge", INT64_MAX);
+
+   EXPECT_TRUE(AMediaFormat_getInt64(fmt1, "verylarge", &i64));
+   EXPECT_EQ(i64, INT64_MAX);
+
+   // return unchanged if not found
+   i64 = -1;
+   EXPECT_FALSE(AMediaFormat_getInt64(fmt1, "five", &i64));
+   EXPECT_EQ(i64, -1);
+
+   AMediaFormat_delete(fmt1);
+}
+
+TEST(NdkMediaFormat_tests, test_size) {
+   AMediaFormat *fmt1 = AMediaFormat_new();
+
+   size_t size = -15;
+   AMediaFormat_setSize(fmt1, "small", 1);
+   AMediaFormat_setSize(fmt1, "medium", 10);
+   AMediaFormat_setSize(fmt1, "large", 100);
+   EXPECT_TRUE(AMediaFormat_getSize(fmt1, "medium", &size));
+   EXPECT_EQ(size, 10);
+
+   AMediaFormat_delete(fmt1);
+}
+
+TEST(NdkMediaFormat_tests, test_float) {
+   AMediaFormat *fmt1 = AMediaFormat_new();
+   float f;
+   AMediaFormat_setFloat(fmt1, "boat", 1.5);
+   AMediaFormat_setFloat(fmt1, "ship", 0.5);
+   EXPECT_TRUE(AMediaFormat_getFloat(fmt1, "boat", &f));
+   EXPECT_EQ(f, 1.5);
+   AMediaFormat_delete(fmt1);
+}
+
+TEST(NdkMediaFormat_tests, test_double) {
+   AMediaFormat *fmt1 = AMediaFormat_new();
+   double d;
+   AMediaFormat_setDouble(fmt1, "trouble", 100.5);
+   AMediaFormat_setDouble(fmt1, "dip", 0.5);
+   EXPECT_TRUE(AMediaFormat_getDouble(fmt1, "trouble", &d));
+   EXPECT_EQ(d, 100.5);
+   AMediaFormat_delete(fmt1);
+}
+
+TEST(NdkMediaFormat_tests, test_string) {
+   AMediaFormat *fmt1 = AMediaFormat_new();
+
+   const char *content = "This is my test string";
+   const char *out = nullptr;
+   AMediaFormat_setString(fmt1, "stringtheory", content);
+   EXPECT_TRUE(AMediaFormat_getString(fmt1, "stringtheory", &out));
+   EXPECT_NE(out, nullptr);
+   EXPECT_EQ(strcmp(out,content), 0);
+
+   AMediaFormat_delete(fmt1);
+}
+
+
+TEST(NdkMediaFormat_tests, test_clear) {
+   AMediaFormat *fmt1 = AMediaFormat_new();
+
+   int32_t i32;
+   AMediaFormat_setInt32(fmt1, "five", 5);
+   size_t size = -15;
+   AMediaFormat_setSize(fmt1, "medium", 10);
+   float f;
+   AMediaFormat_setFloat(fmt1, "boat", 1.5);
+
+   AMediaFormat_clear(fmt1);
+   EXPECT_FALSE(AMediaFormat_getInt32(fmt1, "five", &i32));
+   EXPECT_FALSE(AMediaFormat_getSize(fmt1, "medium", &size));
+   EXPECT_FALSE(AMediaFormat_getFloat(fmt1, "boat", &f));
+
+   AMediaFormat_delete(fmt1);
+}
+
+TEST(NdkMediaFormat_tests, test_copy) {
+   AMediaFormat *fmt1 = AMediaFormat_new();
+   AMediaFormat *fmt2 = AMediaFormat_new();
+
+   double d;
+   int32_t i32;
+
+   // test copy functionality (NB: we cleared everything just above here)
+   AMediaFormat_setDouble(fmt1, "trouble", 100.5);
+   EXPECT_TRUE(AMediaFormat_getDouble(fmt1, "trouble", &d));
+   EXPECT_FALSE(AMediaFormat_getDouble(fmt2, "trouble", &d));
+
+   EXPECT_EQ(AMEDIA_OK, AMediaFormat_copy(fmt2, fmt1));
+
+   EXPECT_TRUE(AMediaFormat_getDouble(fmt2, "trouble", &d));
+   EXPECT_EQ(d, 100.5);
+
+   AMediaFormat *fmt3 = nullptr;
+   EXPECT_NE(AMEDIA_OK, AMediaFormat_copy(fmt3, fmt1));
+   EXPECT_NE(AMEDIA_OK, AMediaFormat_copy(fmt1, fmt3));
+
+   // we should lose an entry when we copy over it
+   AMediaFormat_setInt32(fmt2, "vanishing", 50);
+   EXPECT_FALSE(AMediaFormat_getInt32(fmt1, "vanishing", &i32));
+   EXPECT_TRUE(AMediaFormat_getInt32(fmt2, "vanishing", &i32));
+   EXPECT_EQ(AMEDIA_OK, AMediaFormat_copy(fmt2, fmt1));
+   EXPECT_FALSE(AMediaFormat_getInt32(fmt2, "vanishing", &i32));
+
+   AMediaFormat_delete(fmt1);
+   AMediaFormat_delete(fmt2);
+}
+
+TEST(NdkMediaFormat_tests, test_buffer) {
+   AMediaFormat *fmt1 = AMediaFormat_new();
+
+   typedef struct blockomem {
+        int leading;
+        int filled[100];
+        int trailing;
+   } block_t;
+   block_t buf = {};
+   buf.leading = 1;
+   buf.trailing = 2;
+   void *data;
+   size_t bsize;
+
+   AMediaFormat_setBuffer(fmt1, "mybuffer", &buf, sizeof(buf));
+   EXPECT_TRUE(AMediaFormat_getBuffer(fmt1, "mybuffer", &data, &bsize));
+   EXPECT_NE(&buf, data);
+   EXPECT_EQ(sizeof(buf), bsize);
+   block_t *bufp = (block_t*) data;
+   EXPECT_EQ(bufp->leading, buf.leading);
+   EXPECT_EQ(bufp->trailing, buf.trailing);
+   EXPECT_EQ(0, memcmp(&buf, data, bsize));
+
+   AMediaFormat_delete(fmt1);
+}
+
+} // namespace android
diff --git a/media/tests/SampleVideoEncoder/README.md b/media/tests/SampleVideoEncoder/README.md
new file mode 100644
index 0000000..074c939
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/README.md
@@ -0,0 +1,42 @@
+# B-Frames Encoding App
+
+This is a sample android application for encoding AVC/HEVC streams with B-Frames enabled. It uses MediaRecorder APIs to record B-frames enabled video from camera2 input and MediaCodec APIs to encode reference test vector using input surface.
+
+This page describes how to get started with the Encoder App.
+
+
+# Getting Started
+
+This app uses the Gradle build system as well as Soong Build System.
+
+To build this project using Gradle build, use the "gradlew build" command or use "Import Project" in Android Studio.
+
+To build the app using Soong Build System, run the following command:
+```
+mmm frameworks/av/media/tests/SampleVideoEncoder/
+```
+
+The apk is generated at the following location:
+```
+out\target\product\sargo\testcases\SampleVideoEncoder\arm64\SampleVideoEncoder.apk
+```
+
+Command to install the apk:
+```
+adb install SampleVideoEncoder.apk
+```
+
+Command to launch the app:
+```
+adb shell am start -n "com.android.media.samplevideoencoder/com.android.media.samplevideoencoder.MainActivity"
+```
+
+After installing the app, a TextureView showing camera preview is dispalyed on one third of the screen. It also features checkboxes to select either avc/hevc and hw/sw codecs. It also has an option to select either MediaRecorder APIs or MediaCodec, along with the 'Start' button to start/stop recording.
+
+
+# Ouput
+
+The muxed ouptput video is saved in the app data at:
+```
+/storage/emulated/0/Android/data/com.android.media.samplevideoencoder/files/
+```
diff --git a/media/tests/SampleVideoEncoder/app/Android.bp b/media/tests/SampleVideoEncoder/app/Android.bp
new file mode 100644
index 0000000..35fe0d8
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/Android.bp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+android_app {
+    name: "SampleVideoEncoder",
+
+    manifest: "src/main/AndroidManifest.xml",
+
+    srcs: ["src/**/*.java"],
+
+    sdk_version: "current",
+    min_sdk_version: "24", // N
+
+    resource_dirs: [
+        "src/main/res",
+    ],
+
+    static_libs: [
+        "androidx.annotation_annotation",
+        "androidx.appcompat_appcompat",
+        "androidx-constraintlayout_constraintlayout",
+    ],
+
+    javacflags: [
+        "-Xlint:deprecation",
+        "-Xlint:unchecked",
+    ],
+}
diff --git a/media/tests/SampleVideoEncoder/app/build.gradle b/media/tests/SampleVideoEncoder/app/build.gradle
new file mode 100644
index 0000000..cc54981
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/build.gradle
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion 30
+    buildToolsVersion "30.0.2"
+
+    defaultConfig {
+        applicationId "com.android.media.samplevideoencoder"
+        minSdkVersion 24
+        targetSdkVersion 30
+        versionCode 1
+        versionName "1.0"
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+}
+
+dependencies {
+    implementation fileTree(dir: "libs", include: ["*.jar"])
+    implementation 'androidx.appcompat:appcompat:1.2.0'
+    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
+    testImplementation 'junit:junit:4.13.1'
+    androidTestImplementation 'androidx.test:runner:1.3.0'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+}
\ No newline at end of file
diff --git a/media/tests/SampleVideoEncoder/app/src/main/AndroidManifest.xml b/media/tests/SampleVideoEncoder/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..ed668bb
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.media.samplevideoencoder">
+
+    <uses-permission android:name="android.permission.CAMERA"/>
+    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+
+    <application
+        android:configChanges="orientation"
+        android:screenOrientation="portrait"
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:roundIcon="@mipmap/ic_launcher_round"
+        android:supportsRtl="true"
+        android:theme="@style/AppTheme">
+        <activity android:name="com.android.media.samplevideoencoder.MainActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
\ No newline at end of file
diff --git a/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/AutoFitTextureView.java b/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/AutoFitTextureView.java
new file mode 100644
index 0000000..a3ea4c7
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/AutoFitTextureView.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 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 com.android.media.samplevideoencoder;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.TextureView;
+
+public class AutoFitTextureView extends TextureView {
+
+    public AutoFitTextureView(Context context) {
+        this(context, null);
+    }
+
+    public AutoFitTextureView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public AutoFitTextureView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public void setAspectRatio(int width, int height) {
+        if (width < 0 || height < 0) {
+            throw new IllegalArgumentException("Size cannot be negative.");
+        }
+        requestLayout();
+    }
+}
diff --git a/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MainActivity.java b/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MainActivity.java
new file mode 100644
index 0000000..33e81bb
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MainActivity.java
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) 2020 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 com.android.media.samplevideoencoder;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+
+import android.Manifest;
+import android.app.Activity;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.graphics.SurfaceTexture;
+import android.media.MediaCodecInfo;
+import android.media.MediaFormat;
+import android.media.MediaRecorder;
+
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.view.Surface;
+import android.view.View;
+import android.view.TextureView;
+import android.widget.Button;
+import android.widget.CheckBox;
+
+import java.io.File;
+import java.io.IOException;
+
+import android.util.Log;
+import android.util.Size;
+import android.widget.RadioGroup;
+import android.widget.Toast;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import static java.lang.Boolean.FALSE;
+import static java.lang.Boolean.TRUE;
+
+public class MainActivity extends AppCompatActivity
+        implements View.OnClickListener, ActivityCompat.OnRequestPermissionsResultCallback {
+
+    private static final String TAG = "SampleVideoEncoder";
+    private static final String[] RECORD_PERMISSIONS =
+            {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO};
+    private static final int REQUEST_RECORD_PERMISSIONS = 1;
+    private final Semaphore mCameraOpenCloseLock = new Semaphore(1);
+    private static final int VIDEO_BITRATE = 8000000 /* 8 Mbps */;
+    private static final int VIDEO_FRAMERATE = 30;
+
+    private String mMime = MediaFormat.MIMETYPE_VIDEO_AVC;
+    private String mOutputVideoPath = null;
+
+    private final boolean mIsFrontCamera = true;
+    private boolean mIsCodecSoftware = false;
+    private boolean mIsMediaRecorder = true;
+    private boolean mIsRecording;
+
+    private AutoFitTextureView mTextureView;
+    private CameraDevice mCameraDevice;
+    private CameraCaptureSession mPreviewSession;
+    private CaptureRequest.Builder mPreviewBuilder;
+    private MediaRecorder mMediaRecorder;
+    private Size mVideoSize;
+    private Size mPreviewSize;
+
+    private Handler mBackgroundHandler;
+    private HandlerThread mBackgroundThread;
+
+    private Button mStartButton;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        final RadioGroup radioGroup_mime = findViewById(R.id.radio_group_mime);
+        radioGroup_mime.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(RadioGroup group, int checkedId) {
+                if (checkedId == R.id.avc) {
+                    mMime = MediaFormat.MIMETYPE_VIDEO_AVC;
+                } else {
+                    mMime = MediaFormat.MIMETYPE_VIDEO_HEVC;
+                }
+            }
+        });
+
+        final RadioGroup radioGroup_codec = findViewById(R.id.radio_group_codec);
+        radioGroup_codec.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(RadioGroup group, int checkedId) {
+                mIsCodecSoftware = checkedId == R.id.sw;
+            }
+        });
+
+        final CheckBox checkBox_mr = findViewById(R.id.checkBox_media_recorder);
+        final CheckBox checkBox_mc = findViewById(R.id.checkBox_media_codec);
+        mTextureView = findViewById(R.id.texture);
+        checkBox_mr.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                boolean checked = ((CheckBox) v).isChecked();
+                if (checked) {
+                    checkBox_mc.setChecked(false);
+                    mIsMediaRecorder = TRUE;
+                    for (int i = 0; i < radioGroup_codec.getChildCount(); i++) {
+                        radioGroup_codec.getChildAt(i).setEnabled(false);
+                    }
+                }
+            }
+        });
+        checkBox_mc.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                boolean checked = ((CheckBox) v).isChecked();
+                if (checked) {
+                    checkBox_mr.setChecked(false);
+                    mIsMediaRecorder = FALSE;
+                    for (int i = 0; i < radioGroup_codec.getChildCount(); i++) {
+                        radioGroup_codec.getChildAt(i).setEnabled(true);
+                    }
+                }
+            }
+        });
+        mStartButton = findViewById(R.id.start_button);
+        mStartButton.setOnClickListener(this);
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (v.getId() == R.id.start_button) {
+            if (mIsMediaRecorder) {
+                if (mIsRecording) {
+                    stopRecordingVideo();
+                } else {
+                    mStartButton.setEnabled(false);
+                    startRecordingVideo();
+                }
+            } else {
+                mStartButton.setEnabled(false);
+                mOutputVideoPath = getVideoPath(MainActivity.this);
+                MediaCodecSurfaceAsync codecAsyncTask = new MediaCodecSurfaceAsync(this);
+                codecAsyncTask.execute(
+                        "Encoding reference test vector with MediaCodec APIs using surface");
+            }
+        }
+    }
+
+    private static class MediaCodecSurfaceAsync extends AsyncTask<String, String, Integer> {
+
+        private final WeakReference<MainActivity> activityReference;
+
+        MediaCodecSurfaceAsync(MainActivity context) {
+            activityReference = new WeakReference<>(context);
+        }
+
+        @Override
+        protected Integer doInBackground(String... strings) {
+            MainActivity mainActivity = activityReference.get();
+            int resId = R.raw.crowd_1920x1080_25fps_4000kbps_h265;
+            int encodingStatus = 1;
+            MediaCodecSurfaceEncoder codecSurfaceEncoder =
+                    new MediaCodecSurfaceEncoder(mainActivity.getApplicationContext(), resId,
+                            mainActivity.mMime, mainActivity.mIsCodecSoftware,
+                            mainActivity.mOutputVideoPath);
+            try {
+                encodingStatus = codecSurfaceEncoder.startEncodingSurface();
+            } catch (IOException | InterruptedException e) {
+                e.printStackTrace();
+            }
+            return encodingStatus;
+        }
+
+        @Override
+        protected void onPostExecute(Integer encodingStatus) {
+            MainActivity mainActivity = activityReference.get();
+            mainActivity.mStartButton.setEnabled(true);
+            if (encodingStatus == 0) {
+                Toast.makeText(mainActivity.getApplicationContext(), "Encoding Completed",
+                        Toast.LENGTH_SHORT).show();
+            } else {
+                Toast.makeText(mainActivity.getApplicationContext(),
+                        "Error occurred while " + "encoding", Toast.LENGTH_SHORT).show();
+            }
+            mainActivity.mOutputVideoPath = null;
+            super.onPostExecute(encodingStatus);
+        }
+    }
+
+    private final TextureView.SurfaceTextureListener mSurfaceTextureListener =
+            new TextureView.SurfaceTextureListener() {
+
+                @Override
+                public void onSurfaceTextureAvailable(SurfaceTexture surface, int width,
+                                                      int height) {
+                    openCamera(width, height);
+                }
+
+                @Override
+                public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width,
+                                                        int height) {
+                    configureTransform(width, height);
+                    Log.v(TAG, "Keeping camera preview size fixed");
+                }
+
+                @Override
+                public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+                    return true;
+                }
+
+                @Override
+                public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+                }
+            };
+
+
+    private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
+
+        @Override
+        public void onOpened(CameraDevice cameraDevice) {
+            mCameraDevice = cameraDevice;
+            startPreview();
+            mCameraOpenCloseLock.release();
+        }
+
+        @Override
+        public void onDisconnected(CameraDevice cameraDevice) {
+            mCameraOpenCloseLock.release();
+            cameraDevice.close();
+            mCameraDevice = null;
+        }
+
+        @Override
+        public void onError(CameraDevice cameraDevice, int error) {
+            mCameraOpenCloseLock.release();
+            cameraDevice.close();
+            mCameraDevice = null;
+            Activity activity = MainActivity.this;
+            activity.finish();
+        }
+    };
+
+    private boolean shouldShowRequestPermissionRationale(String[] recordPermissions) {
+        for (String permission : recordPermissions) {
+            if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void requestRecordPermissions() {
+        if (!shouldShowRequestPermissionRationale(RECORD_PERMISSIONS)) {
+            ActivityCompat.requestPermissions(this, RECORD_PERMISSIONS, REQUEST_RECORD_PERMISSIONS);
+        }
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, String[] permissions,
+                                           int[] grantResults) {
+        if (requestCode == REQUEST_RECORD_PERMISSIONS) {
+            if (grantResults.length == RECORD_PERMISSIONS.length) {
+                for (int result : grantResults) {
+                    if (result != PackageManager.PERMISSION_GRANTED) {
+                        Log.e(TAG, "Permission is not granted");
+                        break;
+                    }
+                }
+            }
+        } else {
+            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+        }
+    }
+
+    @SuppressWarnings("MissingPermission")
+    private void openCamera(int width, int height) {
+        if (!hasPermissionGranted(RECORD_PERMISSIONS)) {
+            Log.e(TAG, "Camera does not have permission to record video");
+            requestRecordPermissions();
+            return;
+        }
+        final Activity activity = MainActivity.this;
+        if (activity == null || activity.isFinishing()) {
+            Log.e(TAG, "Activity not found");
+            return;
+        }
+        CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
+        try {
+            Log.v(TAG, "Acquire Camera");
+            if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
+                throw new RuntimeException("Timed out waiting to lock camera opening");
+            }
+            Log.d(TAG, "Camera Acquired");
+
+            String cameraId = manager.getCameraIdList()[0];
+            if (mIsFrontCamera) {
+                cameraId = manager.getCameraIdList()[1];
+            }
+
+            CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
+            StreamConfigurationMap map =
+                    characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+            mVideoSize = chooseVideoSize(map.getOutputSizes(MediaRecorder.class));
+            mPreviewSize =
+                    chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), width, height,
+                            mVideoSize);
+            mTextureView.setAspectRatio(mPreviewSize.getHeight(), mPreviewSize.getWidth());
+            configureTransform(width, height);
+            mMediaRecorder = new MediaRecorder();
+            manager.openCamera(cameraId, mStateCallback, null);
+        } catch (InterruptedException | CameraAccessException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void closeCamera() {
+        try {
+            mCameraOpenCloseLock.acquire();
+            closePreviewSession();
+            if (null != mCameraDevice) {
+                mCameraDevice.close();
+                mCameraDevice = null;
+            }
+            if (null != mMediaRecorder) {
+                mMediaRecorder.release();
+                mMediaRecorder = null;
+            }
+        } catch (InterruptedException e) {
+            throw new RuntimeException("Interrupted while trying to lock camera closing.");
+        } finally {
+            mCameraOpenCloseLock.release();
+        }
+    }
+
+    private static Size chooseVideoSize(Size[] choices) {
+        for (Size size : choices) {
+            if (size.getWidth() == size.getHeight() * 16 / 9 && size.getWidth() <= 1920) {
+                return size;
+            }
+        }
+        Log.e(TAG, "Couldn't find any suitable video size");
+        return choices[choices.length - 1];
+    }
+
+    private static Size chooseOptimalSize(Size[] choices, int width, int height, Size aspectRatio) {
+        List<Size> bigEnough = new ArrayList<>();
+        int w = aspectRatio.getWidth();
+        int h = aspectRatio.getHeight();
+        for (Size option : choices) {
+            if (option.getHeight() == option.getWidth() * h / w && option.getWidth() >= width &&
+                    option.getHeight() >= height) {
+                bigEnough.add(option);
+            }
+        }
+
+        // Pick the smallest of those, assuming we found any
+        if (bigEnough.size() > 0) {
+            return Collections.min(bigEnough, new CompareSizesByArea());
+        } else {
+            Log.e(TAG, "Couldn't find any suitable preview size");
+            return choices[0];
+        }
+    }
+
+    private boolean hasPermissionGranted(String[] recordPermissions) {
+        for (String permission : recordPermissions) {
+            if (ActivityCompat.checkSelfPermission(MainActivity.this, permission) !=
+                    PackageManager.PERMISSION_GRANTED) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        startBackgroundThread();
+        if (mTextureView.isAvailable()) {
+            openCamera(mTextureView.getWidth(), mTextureView.getHeight());
+        } else {
+            mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
+        }
+    }
+
+    @Override
+    public void onPause() {
+        closeCamera();
+        stopBackgroundThread();
+        super.onPause();
+    }
+
+    private void startBackgroundThread() {
+        mBackgroundThread = new HandlerThread("CameraBackground");
+        mBackgroundThread.start();
+        mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
+    }
+
+    private void stopBackgroundThread() {
+        mBackgroundThread.quitSafely();
+        try {
+            mBackgroundThread.join();
+            mBackgroundThread = null;
+            mBackgroundHandler = null;
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void startRecordingVideo() {
+        if (null == mCameraDevice || !mTextureView.isAvailable() || null == mPreviewSize) {
+            Toast.makeText(MainActivity.this, "Cannot start recording.", Toast.LENGTH_SHORT).show();
+            Log.e(TAG, "Cannot start recording.");
+            return;
+        }
+        try {
+            closePreviewSession();
+            setUpMediaRecorder();
+            SurfaceTexture texture = mTextureView.getSurfaceTexture();
+            assert texture != null;
+            texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
+            mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
+            List<Surface> surfaces = new ArrayList<>();
+
+            // Set up Surface for the camera preview
+            Surface previewSurface = new Surface(texture);
+            surfaces.add(previewSurface);
+            mPreviewBuilder.addTarget(previewSurface);
+
+            // Set up Surface for the MediaRecorder
+            Surface recorderSurface = mMediaRecorder.getSurface();
+            surfaces.add(recorderSurface);
+            mPreviewBuilder.addTarget(recorderSurface);
+
+            //Start a capture session
+            mCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
+
+                @Override
+                public void onConfigured(CameraCaptureSession session) {
+                    mPreviewSession = session;
+                    updatePreview();
+                    MainActivity.this.runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            mIsRecording = true;
+                            mMediaRecorder.start();
+                            mStartButton.setText(R.string.stop);
+                            mStartButton.setEnabled(true);
+                        }
+                    });
+                }
+
+                @Override
+                public void onConfigureFailed(CameraCaptureSession session) {
+                    Log.e(TAG, "Failed to configure. Cannot start Recording");
+                }
+            }, mBackgroundHandler);
+        } catch (CameraAccessException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void setUpMediaRecorder() {
+        final Activity activity = MainActivity.this;
+        if (activity == null) {
+            Toast.makeText(MainActivity.this, "Error occurred while setting up the MediaRecorder",
+                    Toast.LENGTH_SHORT).show();
+            Log.e(TAG, "Error occurred while setting up the MediaRecorder");
+            return;
+        }
+        try {
+            mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+            mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
+            mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
+        } catch (IllegalStateException e) {
+            e.printStackTrace();
+        }
+        if (mOutputVideoPath == null) {
+            mOutputVideoPath = getVideoPath(MainActivity.this);
+        }
+        mMediaRecorder.setOutputFile(mOutputVideoPath);
+        mMediaRecorder.setVideoEncodingBitRate(VIDEO_BITRATE);
+        mMediaRecorder.setVideoFrameRate(VIDEO_FRAMERATE);
+        mMediaRecorder.setVideoSize(mVideoSize.getWidth(), mVideoSize.getHeight());
+        mMediaRecorder.setOrientationHint(270);
+        if (mMime.equals(MediaFormat.MIMETYPE_VIDEO_HEVC)) {
+            mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.HEVC);
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+                mMediaRecorder.setVideoEncodingProfileLevel(
+                        MediaCodecInfo.CodecProfileLevel.HEVCProfileMain,
+                        MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel4);
+            }
+        } else {
+            mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+                mMediaRecorder.setVideoEncodingProfileLevel(
+                        MediaCodecInfo.CodecProfileLevel.AVCProfileMain,
+                        MediaCodecInfo.CodecProfileLevel.AVCLevel4);
+            }
+        }
+        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
+        try {
+            mMediaRecorder.prepare();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private String getVideoPath(Activity activity) {
+        File dir = activity.getApplicationContext().getExternalFilesDir(null);
+        if (dir == null) {
+            Log.e(TAG, "Cannot get external directory path to save output video");
+            return null;
+        }
+        String videoPath = dir.getAbsolutePath() + "/Video-" + System.currentTimeMillis() + ".mp4";
+        Log.d(TAG, "Output video is saved at: " + videoPath);
+        return videoPath;
+    }
+
+    private void closePreviewSession() {
+        if (mPreviewSession != null) {
+            mPreviewSession.close();
+            mPreviewSession = null;
+        }
+    }
+
+    private void stopRecordingVideo() {
+        mIsRecording = false;
+        mStartButton.setText(R.string.start);
+        mMediaRecorder.stop();
+        mMediaRecorder.reset();
+        Toast.makeText(MainActivity.this, "Recording Finished", Toast.LENGTH_SHORT).show();
+        mOutputVideoPath = null;
+        startPreview();
+    }
+
+    private void startPreview() {
+        if (null == mCameraDevice || !mTextureView.isAvailable() || null == mPreviewSize) {
+            return;
+        }
+        try {
+            closePreviewSession();
+            SurfaceTexture texture = mTextureView.getSurfaceTexture();
+            assert texture != null;
+            texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
+            mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+            Surface previewSurface = new Surface(texture);
+            mPreviewBuilder.addTarget(previewSurface);
+
+            mCameraDevice.createCaptureSession(Collections.singletonList(previewSurface),
+                    new CameraCaptureSession.StateCallback() {
+
+                        @Override
+                        public void onConfigured(CameraCaptureSession session) {
+                            mPreviewSession = session;
+                            updatePreview();
+                        }
+
+                        @Override
+                        public void onConfigureFailed(CameraCaptureSession session) {
+                            Toast.makeText(MainActivity.this,
+                                    "Configure Failed; Cannot start " + "preview",
+                                    Toast.LENGTH_SHORT).show();
+                            Log.e(TAG, "Configure failed; Cannot start preview");
+                        }
+                    }, mBackgroundHandler);
+        } catch (CameraAccessException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void updatePreview() {
+        if (mCameraDevice == null) {
+            Toast.makeText(MainActivity.this, "Camera not found; Cannot update " + "preview",
+                    Toast.LENGTH_SHORT).show();
+            Log.e(TAG, "Camera not found; Cannot update preview");
+            return;
+        }
+        try {
+            mPreviewBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
+            HandlerThread thread = new HandlerThread("Camera preview");
+            thread.start();
+            mPreviewSession.setRepeatingRequest(mPreviewBuilder.build(), null, mBackgroundHandler);
+        } catch (CameraAccessException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void configureTransform(int viewWidth, int viewHeight) {
+        Activity activity = MainActivity.this;
+        if (null == mTextureView || null == mPreviewSize || null == activity) {
+            return;
+        }
+        Matrix matrix = new Matrix();
+        RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
+        RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());
+        float centerX = viewRect.centerX();
+        float centerY = viewRect.centerY();
+        bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
+        matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
+        float scale = Math.max((float) viewHeight / mPreviewSize.getHeight(),
+                (float) viewWidth / mPreviewSize.getWidth());
+        matrix.postScale(scale, scale, centerX, centerY);
+        mTextureView.setTransform(matrix);
+    }
+
+    static class CompareSizesByArea implements Comparator<Size> {
+        @Override
+        public int compare(Size lhs, Size rhs) {
+            return Long.signum((long) lhs.getWidth() * lhs.getHeight() -
+                    (long) rhs.getWidth() * rhs.getHeight());
+        }
+    }
+}
diff --git a/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MediaCodecBase.java b/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MediaCodecBase.java
new file mode 100644
index 0000000..88ce73b
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MediaCodecBase.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2020 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 com.android.media.samplevideoencoder;
+
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
+import android.media.MediaFormat;
+import android.os.Build;
+import android.util.Log;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+class CodecAsyncHandler extends MediaCodec.Callback {
+    private static final String TAG = CodecAsyncHandler.class.getSimpleName();
+    private final Lock mLock = new ReentrantLock();
+    private final Condition mCondition = mLock.newCondition();
+    private final LinkedList<Pair<Integer, MediaCodec.BufferInfo>> mCbInputQueue;
+    private final LinkedList<Pair<Integer, MediaCodec.BufferInfo>> mCbOutputQueue;
+    private volatile boolean mSignalledError;
+
+    CodecAsyncHandler() {
+        mCbInputQueue = new LinkedList<>();
+        mCbOutputQueue = new LinkedList<>();
+        mSignalledError = false;
+    }
+
+    void clearQueues() {
+        mLock.lock();
+        mCbInputQueue.clear();
+        mCbOutputQueue.clear();
+        mLock.unlock();
+    }
+
+    void resetContext() {
+        clearQueues();
+        mSignalledError = false;
+    }
+
+    @Override
+    public void onInputBufferAvailable(MediaCodec codec, int bufferIndex) {
+        mLock.lock();
+        mCbInputQueue.add(new Pair<>(bufferIndex, (MediaCodec.BufferInfo) null));
+        mCondition.signalAll();
+        mLock.unlock();
+    }
+
+    @Override
+    public void onOutputBufferAvailable(MediaCodec codec, int bufferIndex,
+                                        MediaCodec.BufferInfo info) {
+        mLock.lock();
+        mCbOutputQueue.add(new Pair<>(bufferIndex, info));
+        mCondition.signalAll();
+        mLock.unlock();
+    }
+
+    @Override
+    public void onError(MediaCodec codec, MediaCodec.CodecException e) {
+        mLock.lock();
+        mSignalledError = true;
+        mCondition.signalAll();
+        mLock.unlock();
+        Log.e(TAG, "Received media codec error : " + e.getMessage());
+    }
+
+    @Override
+    public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
+        Log.i(TAG, "Output format changed: " + format.toString());
+    }
+
+    void setCallBack(MediaCodec codec, boolean isCodecInAsyncMode) {
+        if (isCodecInAsyncMode) {
+            codec.setCallback(this);
+        }
+    }
+
+    Pair<Integer, MediaCodec.BufferInfo> getOutput() throws InterruptedException {
+        Pair<Integer, MediaCodec.BufferInfo> element = null;
+        mLock.lock();
+        while (!mSignalledError) {
+            if (mCbOutputQueue.isEmpty()) {
+                mCondition.await();
+            } else {
+                element = mCbOutputQueue.remove(0);
+                break;
+            }
+        }
+        mLock.unlock();
+        return element;
+    }
+
+    Pair<Integer, MediaCodec.BufferInfo> getWork() throws InterruptedException {
+        Pair<Integer, MediaCodec.BufferInfo> element = null;
+        mLock.lock();
+        while (!mSignalledError) {
+            if (mCbInputQueue.isEmpty() && mCbOutputQueue.isEmpty()) {
+                mCondition.await();
+            } else {
+                if (!mCbOutputQueue.isEmpty()) {
+                    element = mCbOutputQueue.remove(0);
+                    break;
+                }
+                if (!mCbInputQueue.isEmpty()) {
+                    element = mCbInputQueue.remove(0);
+                    break;
+                }
+            }
+        }
+        mLock.unlock();
+        return element;
+    }
+
+    boolean hasSeenError() {
+        return mSignalledError;
+    }
+}
+
+abstract public class MediaCodecBase {
+    static ArrayList<String> selectCodecs(String mime, ArrayList<MediaFormat> formats,
+                                          String[] features, boolean isEncoder,
+                                          boolean isSoftware) {
+
+        MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+        MediaCodecInfo[] codecInfos = codecList.getCodecInfos();
+        ArrayList<String> listOfCodecs = new ArrayList<>();
+        for (MediaCodecInfo codecInfo : codecInfos) {
+            if (isEncoder) {
+                if (!codecInfo.isEncoder()) continue;
+            } else {
+                if (codecInfo.isEncoder()) continue;
+            }
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && codecInfo.isAlias()) continue;
+            String[] types = codecInfo.getSupportedTypes();
+            for (String type : types) {
+                if (type.equalsIgnoreCase(mime)) {
+                    boolean isOk = true;
+                    MediaCodecInfo.CodecCapabilities codecCapabilities =
+                            codecInfo.getCapabilitiesForType(type);
+                    if (formats != null) {
+                        for (MediaFormat format : formats) {
+                            if (!codecCapabilities.isFormatSupported(format)) {
+                                isOk = false;
+                                break;
+                            }
+                        }
+                    }
+                    if (features != null) {
+                        for (String feature : features) {
+                            if (!codecCapabilities.isFeatureSupported(feature)) {
+                                isOk = false;
+                                break;
+                            }
+                        }
+                    }
+                    if (isSoftware) {
+                        if (codecInfo.getName().contains("software") ||
+                                codecInfo.getName().contains("android") ||
+                                codecInfo.getName().contains("google")) {
+                            if (isOk) listOfCodecs.add(codecInfo.getName());
+                        }
+                    } else {
+                        if (codecInfo.getName().contains("software") ||
+                                codecInfo.getName().contains("android") ||
+                                codecInfo.getName().contains("google")) {
+                            continue;
+                        } else {
+                            if (isOk) listOfCodecs.add(codecInfo.getName());
+                        }
+                    }
+                }
+            }
+        }
+        return listOfCodecs;
+    }
+}
diff --git a/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MediaCodecSurfaceEncoder.java b/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MediaCodecSurfaceEncoder.java
new file mode 100644
index 0000000..146a475
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MediaCodecSurfaceEncoder.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2020 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 com.android.media.samplevideoencoder;
+
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.media.MediaMuxer;
+import android.os.Build;
+import android.util.Log;
+import android.util.Pair;
+import android.view.Surface;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+public class MediaCodecSurfaceEncoder {
+    private static final String TAG = MediaCodecSurfaceEncoder.class.getSimpleName();
+
+    private static final boolean DEBUG = false;
+    private static final int VIDEO_BITRATE = 8000000  /*8 Mbps*/;
+    private static final int VIDEO_FRAMERATE = 30;
+    private final Context mActivityContext;
+    private final int mResID;
+    private final int mMaxBFrames;
+    private final String mMime;
+    private final String mOutputPath;
+    private int mTrackID = -1;
+
+    private Surface mSurface;
+    private MediaExtractor mExtractor;
+    private MediaCodec mDecoder;
+    private MediaCodec mEncoder;
+    private MediaMuxer mMuxer;
+
+    private final boolean mIsCodecSoftware;
+    private boolean mSawDecInputEOS;
+    private boolean mSawDecOutputEOS;
+    private boolean mSawEncOutputEOS;
+    private int mDecOutputCount;
+    private int mEncOutputCount;
+
+    private final CodecAsyncHandler mAsyncHandleEncoder = new CodecAsyncHandler();
+    private final CodecAsyncHandler mAsyncHandleDecoder = new CodecAsyncHandler();
+
+    public MediaCodecSurfaceEncoder(Context context, int resId, String mime, boolean isSoftware,
+                                    String outputPath, int maxBFrames) {
+        mActivityContext = context;
+        mResID = resId;
+        mMime = mime;
+        mIsCodecSoftware = isSoftware;
+        mOutputPath = outputPath;
+        mMaxBFrames = maxBFrames;
+    }
+
+    public MediaCodecSurfaceEncoder(Context context, int resId, String mime, boolean isSoftware,
+                                    String outputPath) {
+        // Default value of MediaFormat.KEY_MAX_B_FRAMES is set to 1, if not passed as a parameter.
+        this(context, resId, mime, isSoftware, outputPath, 1);
+    }
+
+    public int startEncodingSurface() throws IOException, InterruptedException {
+        MediaFormat decoderFormat = setUpSource();
+        if (decoderFormat == null) {
+            return -1;
+        }
+
+        String decoderMime = decoderFormat.getString(MediaFormat.KEY_MIME);
+        ArrayList<String> listOfDeocders =
+                MediaCodecBase.selectCodecs(decoderMime, null, null, false, mIsCodecSoftware);
+        if (listOfDeocders.isEmpty()) {
+            Log.e(TAG, "No suitable decoder found for mime: " + decoderMime);
+            return -1;
+        }
+        mDecoder = MediaCodec.createByCodecName(listOfDeocders.get(0));
+
+        MediaFormat encoderFormat = setUpEncoderFormat(decoderFormat);
+        ArrayList<String> listOfEncoders =
+                MediaCodecBase.selectCodecs(mMime, null, null, true, mIsCodecSoftware);
+        if (listOfEncoders.isEmpty()) {
+            Log.e(TAG, "No suitable encoder found for mime: " + mMime);
+            return -1;
+        }
+
+        boolean muxOutput = true;
+        for (String encoder : listOfEncoders) {
+            mEncoder = MediaCodec.createByCodecName(encoder);
+            mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
+            if (muxOutput) {
+                int muxerFormat = MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4;
+                mMuxer = new MediaMuxer(mOutputPath, muxerFormat);
+            }
+            configureCodec(decoderFormat, encoderFormat);
+            mEncoder.start();
+            mDecoder.start();
+            doWork(Integer.MAX_VALUE);
+            queueEOS();
+            waitForAllEncoderOutputs();
+            if (muxOutput) {
+                if (mTrackID != -1) {
+                    mMuxer.stop();
+                    mTrackID = -1;
+                }
+                if (mMuxer != null) {
+                    mMuxer.release();
+                    mMuxer = null;
+                }
+            }
+            mDecoder.reset();
+            mEncoder.reset();
+            mSurface.release();
+            mSurface = null;
+        }
+
+        mEncoder.release();
+        mDecoder.release();
+        mExtractor.release();
+        return 0;
+    }
+
+    private MediaFormat setUpSource() throws IOException {
+        mExtractor = new MediaExtractor();
+        AssetFileDescriptor fd = mActivityContext.getResources().openRawResourceFd(mResID);
+        mExtractor.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
+        for (int trackID = 0; trackID < mExtractor.getTrackCount(); trackID++) {
+            MediaFormat format = mExtractor.getTrackFormat(trackID);
+            String mime = format.getString(MediaFormat.KEY_MIME);
+            if (mime.startsWith("video/")) {
+                mExtractor.selectTrack(trackID);
+                format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+                        MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
+                return format;
+            }
+        }
+        mExtractor.release();
+        return null;
+    }
+
+    private MediaFormat setUpEncoderFormat(MediaFormat decoderFormat) {
+        MediaFormat encoderFormat = new MediaFormat();
+        encoderFormat.setString(MediaFormat.KEY_MIME, mMime);
+        encoderFormat
+                .setInteger(MediaFormat.KEY_WIDTH, decoderFormat.getInteger(MediaFormat.KEY_WIDTH));
+        encoderFormat.setInteger(MediaFormat.KEY_HEIGHT,
+                decoderFormat.getInteger(MediaFormat.KEY_HEIGHT));
+        encoderFormat.setInteger(MediaFormat.KEY_FRAME_RATE, VIDEO_FRAMERATE);
+        encoderFormat.setInteger(MediaFormat.KEY_BIT_RATE, VIDEO_BITRATE);
+        encoderFormat.setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, 1.0f);
+        encoderFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+                MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
+        if (mMime.equals(MediaFormat.MIMETYPE_VIDEO_HEVC)) {
+            encoderFormat.setInteger(MediaFormat.KEY_PROFILE,
+                    MediaCodecInfo.CodecProfileLevel.HEVCProfileMain);
+            encoderFormat.setInteger(MediaFormat.KEY_LEVEL,
+                    MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel4);
+        } else {
+            encoderFormat.setInteger(MediaFormat.KEY_PROFILE,
+                    MediaCodecInfo.CodecProfileLevel.AVCProfileMain);
+            encoderFormat
+                    .setInteger(MediaFormat.KEY_LEVEL, MediaCodecInfo.CodecProfileLevel.AVCLevel4);
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+            encoderFormat.setInteger(MediaFormat.KEY_MAX_B_FRAMES, mMaxBFrames);
+        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            encoderFormat.setInteger(MediaFormat.KEY_LATENCY, 1);
+        }
+        return encoderFormat;
+    }
+
+    private void resetContext() {
+        mAsyncHandleDecoder.resetContext();
+        mAsyncHandleEncoder.resetContext();
+        mSawDecInputEOS = false;
+        mSawDecOutputEOS = false;
+        mSawEncOutputEOS = false;
+        mDecOutputCount = 0;
+        mEncOutputCount = 0;
+    }
+
+    private void configureCodec(MediaFormat decFormat, MediaFormat encFormat) {
+        resetContext();
+        mAsyncHandleEncoder.setCallBack(mEncoder, true);
+        mEncoder.configure(encFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+        mSurface = mEncoder.createInputSurface();
+        if (!mSurface.isValid()) {
+            Log.e(TAG, "Surface is not valid");
+            return;
+        }
+        mAsyncHandleDecoder.setCallBack(mDecoder, true);
+        mDecoder.configure(decFormat, mSurface, null, 0);
+        Log.d(TAG, "Codec configured");
+        if (DEBUG) {
+            Log.d(TAG, "Encoder Output format: " + mEncoder.getOutputFormat());
+        }
+    }
+
+    private void dequeueDecoderOutput(int bufferIndex, MediaCodec.BufferInfo info) {
+        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+            mSawDecOutputEOS = true;
+        }
+        if (DEBUG) {
+            Log.d(TAG,
+                    "output: id: " + bufferIndex + " flags: " + info.flags + " size: " + info.size +
+                            " timestamp: " + info.presentationTimeUs);
+        }
+        if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+            mDecOutputCount++;
+        }
+        mDecoder.releaseOutputBuffer(bufferIndex, mSurface != null);
+    }
+
+    private void enqueueDecoderInput(int bufferIndex) {
+        ByteBuffer inputBuffer = mDecoder.getInputBuffer(bufferIndex);
+        int size = mExtractor.readSampleData(inputBuffer, 0);
+        if (size < 0) {
+            enqueueDecoderEOS(bufferIndex);
+        } else {
+            long pts = mExtractor.getSampleTime();
+            int extractorFlags = mExtractor.getSampleFlags();
+            int codecFlags = 0;
+            if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
+                codecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
+            }
+            if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) {
+                codecFlags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME;
+            }
+            if (!mExtractor.advance()) {
+                codecFlags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+                mSawDecInputEOS = true;
+            }
+            if (DEBUG) {
+                Log.d(TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts +
+                        " flags: " + codecFlags);
+            }
+            mDecoder.queueInputBuffer(bufferIndex, 0, size, pts, codecFlags);
+        }
+    }
+
+    private void doWork(int frameLimit) throws InterruptedException {
+        int frameCount = 0;
+        while (!hasSeenError() && !mSawDecInputEOS && frameCount < frameLimit) {
+            Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandleDecoder.getWork();
+            if (element != null) {
+                int bufferID = element.first;
+                MediaCodec.BufferInfo info = element.second;
+                if (info != null) {
+                    // <id, info> corresponds to output callback.
+                    dequeueDecoderOutput(bufferID, info);
+                } else {
+                    // <id, null> corresponds to input callback.
+                    enqueueDecoderInput(bufferID);
+                    frameCount++;
+                }
+            }
+            // check decoder EOS
+            if (mSawDecOutputEOS) mEncoder.signalEndOfInputStream();
+            // encoder output
+            if (mDecOutputCount - mEncOutputCount > mMaxBFrames) {
+                tryEncoderOutput();
+            }
+        }
+    }
+
+    private void queueEOS() throws InterruptedException {
+        while (!mAsyncHandleDecoder.hasSeenError() && !mSawDecInputEOS) {
+            Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandleDecoder.getWork();
+            if (element != null) {
+                int bufferID = element.first;
+                MediaCodec.BufferInfo info = element.second;
+                if (info != null) {
+                    dequeueDecoderOutput(bufferID, info);
+                } else {
+                    enqueueDecoderEOS(element.first);
+                }
+            }
+        }
+
+        while (!hasSeenError() && !mSawDecOutputEOS) {
+            Pair<Integer, MediaCodec.BufferInfo> decOp = mAsyncHandleDecoder.getOutput();
+            if (decOp != null) dequeueDecoderOutput(decOp.first, decOp.second);
+            if (mSawDecOutputEOS) mEncoder.signalEndOfInputStream();
+            if (mDecOutputCount - mEncOutputCount > mMaxBFrames) {
+                tryEncoderOutput();
+            }
+        }
+    }
+
+    private void tryEncoderOutput() throws InterruptedException {
+        if (!hasSeenError() && !mSawEncOutputEOS) {
+            Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandleEncoder.getOutput();
+            if (element != null) {
+                dequeueEncoderOutput(element.first, element.second);
+            }
+        }
+    }
+
+    private void waitForAllEncoderOutputs() throws InterruptedException {
+        while (!hasSeenError() && !mSawEncOutputEOS) {
+            tryEncoderOutput();
+        }
+    }
+
+    private void enqueueDecoderEOS(int bufferIndex) {
+        if (!mSawDecInputEOS) {
+            mDecoder.queueInputBuffer(bufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
+            mSawDecInputEOS = true;
+            Log.d(TAG, "Queued End of Stream");
+        }
+    }
+
+    private void dequeueEncoderOutput(int bufferIndex, MediaCodec.BufferInfo info) {
+        if (DEBUG) {
+            Log.d(TAG, "encoder output: id: " + bufferIndex + " flags: " + info.flags + " size: " +
+                    info.size + " timestamp: " + info.presentationTimeUs);
+        }
+        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+            mSawEncOutputEOS = true;
+        }
+        if (info.size > 0) {
+            ByteBuffer buf = mEncoder.getOutputBuffer(bufferIndex);
+            if (mMuxer != null) {
+                if (mTrackID == -1) {
+                    mTrackID = mMuxer.addTrack(mEncoder.getOutputFormat());
+                    mMuxer.start();
+                }
+                mMuxer.writeSampleData(mTrackID, buf, info);
+            }
+            if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+                mEncOutputCount++;
+            }
+        }
+        mEncoder.releaseOutputBuffer(bufferIndex, false);
+    }
+
+    private boolean hasSeenError() {
+        return mAsyncHandleDecoder.hasSeenError() || mAsyncHandleEncoder.hasSeenError();
+    }
+}
diff --git a/media/tests/SampleVideoEncoder/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/media/tests/SampleVideoEncoder/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+    <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
+        <aapt:attr name="android:fillColor">
+            <gradient
+                android:endX="85.84757"
+                android:endY="92.4963"
+                android:startX="42.9492"
+                android:startY="49.59793"
+                android:type="linear">
+                <item
+                    android:color="#44000000"
+                    android:offset="0.0" />
+                <item
+                    android:color="#00000000"
+                    android:offset="1.0" />
+            </gradient>
+        </aapt:attr>
+    </path>
+    <path
+        android:fillColor="#FFFFFF"
+        android:fillType="nonZero"
+        android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
+        android:strokeWidth="1"
+        android:strokeColor="#00000000" />
+</vector>
\ No newline at end of file
diff --git a/media/tests/SampleVideoEncoder/app/src/main/res/drawable/ic_launcher_background.xml b/media/tests/SampleVideoEncoder/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+    <path
+        android:fillColor="#3DDC84"
+        android:pathData="M0,0h108v108h-108z" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M9,0L9,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,0L19,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,0L29,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,0L39,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,0L49,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,0L59,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,0L69,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,0L79,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M89,0L89,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M99,0L99,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,9L108,9"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,19L108,19"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,29L108,29"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,39L108,39"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,49L108,49"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,59L108,59"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,69L108,69"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,79L108,79"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,89L108,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,99L108,99"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,29L89,29"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,39L89,39"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,49L89,49"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,59L89,59"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,69L89,69"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,79L89,79"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,19L29,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,19L39,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,19L49,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,19L59,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,19L69,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,19L79,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+</vector>
diff --git a/media/tests/SampleVideoEncoder/app/src/main/res/layout/activity_main.xml b/media/tests/SampleVideoEncoder/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..164e02a
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_gravity="center"
+    tools:context="com.android.media.samplevideoencoder.MainActivity">
+
+    <com.android.media.samplevideoencoder.AutoFitTextureView
+        android:id="@+id/texture"
+        android:layout_width="wrap_content"
+        android:layout_height="300dp"
+        android:layout_alignParentStart="true"
+        android:layout_alignParentTop="true"
+        android:layout_marginBottom="16dp"
+        android:gravity="center"
+        app:layout_constraintBottom_toTopOf="@+id/checkBox_media_recorder"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <CheckBox
+        android:id="@+id/checkBox_media_recorder"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="90dp"
+        android:layout_marginTop="10dp"
+        android:fontFamily="sans-serif-medium"
+        android:text="@string/media_recorder"
+        android:textAppearance="@style/TextAppearance.AppCompat.Large"
+        android:textStyle="normal"
+        android:checked="true"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/texture"/>
+
+    <CheckBox
+        android:id="@+id/checkBox_media_codec"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="90dp"
+        android:fontFamily="sans-serif-medium"
+        android:text="@string/media_codec"
+        android:textAppearance="@style/TextAppearance.AppCompat.Large"
+        android:textStyle="normal"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/checkBox_media_recorder" />
+
+    <RadioGroup
+        android:id="@+id/radio_group_mime"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="40dp"
+        android:layout_marginTop="10dp"
+        android:orientation="vertical"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintBottom_toTopOf="@+id/frameLayout2"
+        app:layout_constraintTop_toBottomOf="@+id/checkBox_media_codec">
+
+        <RadioButton
+            android:id="@+id/avc"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:checked="true"
+            android:text="@string/avc" />
+
+        <RadioButton
+            android:id="@+id/hevc"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/hevc" />
+    </RadioGroup>
+
+    <RadioGroup
+        android:id="@+id/radio_group_codec"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="10dp"
+        android:layout_marginEnd="40dp"
+        android:orientation="vertical"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/checkBox_media_codec">
+
+        <RadioButton
+            android:id="@+id/hw"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:checked="true"
+            android:enabled="false"
+            android:text="@string/hardware" />
+
+        <RadioButton
+            android:id="@+id/sw"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:enabled="false"
+            android:text="@string/software" />
+    </RadioGroup>
+
+    <FrameLayout
+        android:id="@+id/frameLayout2"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/radio_group_mime"
+        android:layout_alignParentStart="true"
+        android:layout_alignParentBottom="true"
+        android:layout_marginTop="10dp"
+        android:background="@color/colorPrimary"
+        app:layout_constraintTop_toBottomOf="@+id/radio_group_mime"
+        tools:layout_editor_absoluteX="80dp">
+
+        <Button
+            android:id="@+id/start_button"
+            android:layout_width="108dp"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:text="@string/start_button"
+            tools:layout_editor_absoluteX="155dp"
+            tools:layout_editor_absoluteY="455dp" />
+
+    </FrameLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/media/tests/SampleVideoEncoder/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/media/tests/SampleVideoEncoder/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
diff --git a/media/tests/SampleVideoEncoder/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/media/tests/SampleVideoEncoder/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
diff --git a/media/tests/SampleVideoEncoder/app/src/main/res/raw/crowd_1920x1080_25fps_4000kbps_h265.mp4 b/media/tests/SampleVideoEncoder/app/src/main/res/raw/crowd_1920x1080_25fps_4000kbps_h265.mp4
new file mode 100644
index 0000000..6204008
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/res/raw/crowd_1920x1080_25fps_4000kbps_h265.mp4
Binary files differ
diff --git a/media/tests/SampleVideoEncoder/app/src/main/res/values/colors.xml b/media/tests/SampleVideoEncoder/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..4faecfa
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="colorPrimary">#6200EE</color>
+    <color name="colorPrimaryDark">#3700B3</color>
+    <color name="colorAccent">#03DAC5</color>
+</resources>
\ No newline at end of file
diff --git a/media/tests/SampleVideoEncoder/app/src/main/res/values/strings.xml b/media/tests/SampleVideoEncoder/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..f825a7f
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/res/values/strings.xml
@@ -0,0 +1,13 @@
+<resources>
+    <string name="app_name">SampleVideoEncoder</string>
+    <string name="media_recorder">MediaRecorder</string>
+    <string name="media_codec">MediaCodec</string>
+    <string name="start_button">Start</string>
+    <string name="stop">Stop</string>
+    <string name="start">Start</string>
+    <string name="avc">AVC</string>
+    <string name="hevc">HEVC</string>
+    <string name="hardware">H/W</string>
+    <string name="software">S/W</string>
+
+</resources>
\ No newline at end of file
diff --git a/media/tests/SampleVideoEncoder/app/src/main/res/values/styles.xml b/media/tests/SampleVideoEncoder/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..fac9291
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/res/values/styles.xml
@@ -0,0 +1,10 @@
+<resources>
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+        <!-- Customize your theme here. -->
+        <item name="colorPrimary">@color/colorPrimary</item>
+        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+        <item name="colorAccent">@color/colorAccent</item>
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/media/tests/SampleVideoEncoder/build.gradle b/media/tests/SampleVideoEncoder/build.gradle
new file mode 100644
index 0000000..4ca0c7e
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/build.gradle
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+buildscript {
+    repositories {
+        google()
+        jcenter()
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:4.1.1'
+    }
+}
+
+allprojects {
+    repositories {
+        google()
+        jcenter()
+    }
+}
\ No newline at end of file
diff --git a/media/tests/SampleVideoEncoder/gradle.properties b/media/tests/SampleVideoEncoder/gradle.properties
new file mode 100644
index 0000000..5ae443b
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/gradle.properties
@@ -0,0 +1,4 @@
+# Project-wide Gradle settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/media/tests/SampleVideoEncoder/gradle/wrapper/gradle-wrapper.jar b/media/tests/SampleVideoEncoder/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..f6b961f
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/media/tests/SampleVideoEncoder/gradle/wrapper/gradle-wrapper.properties b/media/tests/SampleVideoEncoder/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a9a12eb
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Dec 16 10:06:45 IST 2020
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
diff --git a/media/tests/SampleVideoEncoder/gradlew b/media/tests/SampleVideoEncoder/gradlew
new file mode 100644
index 0000000..cccdd3d
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/media/tests/SampleVideoEncoder/gradlew.bat b/media/tests/SampleVideoEncoder/gradlew.bat
new file mode 100644
index 0000000..f955316
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/media/tests/SampleVideoEncoder/settings.gradle b/media/tests/SampleVideoEncoder/settings.gradle
new file mode 100644
index 0000000..4d3c3a5
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/settings.gradle
@@ -0,0 +1,2 @@
+include ':app'
+rootProject.name = "SampleVideoEncoder"
\ No newline at end of file
diff --git a/media/utils/Android.bp b/media/utils/Android.bp
index ec77bd0..26cdc3a 100644
--- a/media/utils/Android.bp
+++ b/media/utils/Android.bp
@@ -39,6 +39,7 @@
         "libc_malloc_debug_backtrace",
     ],
     shared_libs: [
+        "libaudioclient_aidl_conversion",
         "libaudioutils", // for clock.h
         "libbinder",
         "libcutils",
@@ -47,6 +48,7 @@
         "libhidlbase",
         "android.hardware.graphics.bufferqueue@1.0",
         "android.hidl.token@1.0-utils",
+        "media_permission-aidl-cpp",
     ],
 
     logtags: ["EventLogTags.logtags"],
@@ -62,6 +64,10 @@
         "libmedia_headers",
     ],
 
+    export_shared_lib_headers: [
+        "media_permission-aidl-cpp"
+    ],
+
     include_dirs: [
         // For DEBUGGER_SIGNAL
         "system/core/debuggerd/include",
diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp
index 2c8a452..e2e1043 100644
--- a/media/utils/ServiceUtilities.cpp
+++ b/media/utils/ServiceUtilities.cpp
@@ -23,6 +23,9 @@
 #include <binder/PermissionCache.h>
 #include "mediautils/ServiceUtilities.h"
 #include <system/audio-hal-enums.h>
+#include <media/AidlConversion.h>
+#include <media/AidlConversionUtil.h>
+#include <android/media/permission/Identity.h>
 
 #include <iterator>
 #include <algorithm>
@@ -37,14 +40,16 @@
 
 namespace android {
 
+using media::permission::Identity;
+
 static const String16 sAndroidPermissionRecordAudio("android.permission.RECORD_AUDIO");
 static const String16 sModifyPhoneState("android.permission.MODIFY_PHONE_STATE");
 static const String16 sModifyAudioRouting("android.permission.MODIFY_AUDIO_ROUTING");
 
 static String16 resolveCallingPackage(PermissionController& permissionController,
-        const String16& opPackageName, uid_t uid) {
-    if (opPackageName.size() > 0) {
-        return opPackageName;
+        const std::optional<String16> opPackageName, uid_t uid) {
+    if (opPackageName.has_value() && opPackageName.value().size() > 0) {
+        return opPackageName.value();
     }
     // In some cases the calling code has no access to the package it runs under.
     // For example, code using the wilhelm framework's OpenSL-ES APIs. In this
@@ -57,7 +62,7 @@
     permissionController.getPackagesForUid(uid, packages);
     if (packages.isEmpty()) {
         ALOGE("No packages for uid %d", uid);
-        return opPackageName; // empty string
+        return String16();
     }
     return packages[0];
 }
@@ -74,47 +79,52 @@
   }
 }
 
-static bool checkRecordingInternal(const String16& opPackageName, pid_t pid,
-        uid_t uid, bool start, audio_source_t source) {
+static bool checkRecordingInternal(const Identity& identity, const String16& msg,
+        bool start, audio_source_t source) {
     // Okay to not track in app ops as audio server or media server is us and if
     // device is rooted security model is considered compromised.
     // system_server loses its RECORD_AUDIO permission when a secondary
     // user is active, but it is a core system service so let it through.
     // TODO(b/141210120): UserManager.DISALLOW_RECORD_AUDIO should not affect system user 0
+    uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
     if (isAudioServerOrMediaServerOrSystemServerOrRootUid(uid)) return true;
 
     // We specify a pid and uid here as mediaserver (aka MediaRecorder or StageFrightRecorder)
     // may open a record track on behalf of a client.  Note that pid may be a tid.
     // IMPORTANT: DON'T USE PermissionCache - RUNTIME PERMISSIONS CHANGE.
     PermissionController permissionController;
-    const bool ok = permissionController.checkPermission(sAndroidPermissionRecordAudio, pid, uid);
+    const bool ok = permissionController.checkPermission(sAndroidPermissionRecordAudio,
+            identity.pid, identity.uid);
     if (!ok) {
         ALOGE("Request requires %s", String8(sAndroidPermissionRecordAudio).c_str());
         return false;
     }
 
     String16 resolvedOpPackageName = resolveCallingPackage(
-            permissionController, opPackageName, uid);
+            permissionController, VALUE_OR_FATAL(aidl2legacy_string_view_String16(
+                identity.packageName.value_or(""))), uid);
     if (resolvedOpPackageName.size() == 0) {
         return false;
     }
 
+
     AppOpsManager appOps;
     const int32_t op = getOpForSource(source);
     if (start) {
-        if (int32_t mode = appOps.startOpNoThrow(
-                        op, uid, resolvedOpPackageName, /*startIfModeDefault*/ false);
-                mode != AppOpsManager::MODE_ALLOWED) {
+        if (int32_t mode = appOps.startOpNoThrow(op, identity.uid,
+            resolvedOpPackageName, /*startIfModeDefault*/ false,
+            VALUE_OR_FATAL(aidl2legacy_optional_string_view_optional_String16(
+                identity.attributionTag)), msg) != AppOpsManager::MODE_ALLOWED) {
             ALOGE("Request start for \"%s\" (uid %d) denied by app op: %d, mode: %d",
-                    String8(resolvedOpPackageName).c_str(), uid, op, mode);
+                String8(resolvedOpPackageName).c_str(), identity.uid, op, mode);
             return false;
         }
     } else {
         // Always use OP_RECORD_AUDIO for checks at creation time.
-        if (int32_t mode = appOps.checkOp(op, uid, resolvedOpPackageName);
-                mode != AppOpsManager::MODE_ALLOWED) {
+        if (int32_t mode = appOps.checkOp(op, uid,
+            resolvedOpPackageName) != AppOpsManager::MODE_ALLOWED) {
             ALOGE("Request check for \"%s\" (uid %d) denied by app op: %d, mode: %d",
-                    String8(resolvedOpPackageName).c_str(), uid, op, mode);
+                String8(resolvedOpPackageName).c_str(), identity.uid, op, mode);
             return false;
         }
     }
@@ -122,22 +132,25 @@
     return true;
 }
 
-bool recordingAllowed(const String16& opPackageName, pid_t pid, uid_t uid) {
-    return checkRecordingInternal(opPackageName, pid, uid, /*start*/ false, AUDIO_SOURCE_DEFAULT);
+bool recordingAllowed(const Identity& identity) {
+    return checkRecordingInternal(identity, String16(), /*start*/ false, AUDIO_SOURCE_DEFAULT);
 }
 
-bool startRecording(const String16& opPackageName, pid_t pid, uid_t uid, audio_source_t source) {
-     return checkRecordingInternal(opPackageName, pid, uid, /*start*/ true, source);
+bool startRecording(const Identity& identity, const String16& msg, audio_source_t source) {
+     return checkRecordingInternal(identity, msg, /*start*/ true, source);
 }
 
-void finishRecording(const String16& opPackageName, uid_t uid, audio_source_t source) {
+void finishRecording(const Identity& identity, audio_source_t source) {
     // Okay to not track in app ops as audio server is us and if
     // device is rooted security model is considered compromised.
+    uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
     if (isAudioServerOrRootUid(uid)) return;
 
     PermissionController permissionController;
     String16 resolvedOpPackageName = resolveCallingPackage(
-            permissionController, opPackageName, uid);
+            permissionController,
+            VALUE_OR_FATAL(aidl2legacy_string_view_String16(identity.packageName.value_or(""))),
+            VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid)));
     if (resolvedOpPackageName.size() == 0) {
         return;
     }
@@ -145,10 +158,14 @@
     AppOpsManager appOps;
 
     const int32_t op = getOpForSource(source);
-    appOps.finishOp(op, uid, resolvedOpPackageName);
+    appOps.finishOp(op, identity.uid, resolvedOpPackageName,
+        VALUE_OR_FATAL(aidl2legacy_optional_string_view_optional_String16(
+            identity.attributionTag)));
 }
 
-bool captureAudioOutputAllowed(pid_t pid, uid_t uid) {
+bool captureAudioOutputAllowed(const Identity& identity) {
+    uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
+    pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(identity.pid));
     if (isAudioServerOrRootUid(uid)) return true;
     static const String16 sCaptureAudioOutput("android.permission.CAPTURE_AUDIO_OUTPUT");
     bool ok = PermissionCache::checkPermission(sCaptureAudioOutput, pid, uid);
@@ -156,7 +173,9 @@
     return ok;
 }
 
-bool captureMediaOutputAllowed(pid_t pid, uid_t uid) {
+bool captureMediaOutputAllowed(const Identity& identity) {
+    uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
+    pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(identity.pid));
     if (isAudioServerOrRootUid(uid)) return true;
     static const String16 sCaptureMediaOutput("android.permission.CAPTURE_MEDIA_OUTPUT");
     bool ok = PermissionCache::checkPermission(sCaptureMediaOutput, pid, uid);
@@ -164,7 +183,9 @@
     return ok;
 }
 
-bool captureTunerAudioInputAllowed(pid_t pid, uid_t uid) {
+bool captureTunerAudioInputAllowed(const Identity& identity) {
+    uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
+    pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(identity.pid));
     if (isAudioServerOrRootUid(uid)) return true;
     static const String16 sCaptureTunerAudioInput("android.permission.CAPTURE_TUNER_AUDIO_INPUT");
     bool ok = PermissionCache::checkPermission(sCaptureTunerAudioInput, pid, uid);
@@ -172,7 +193,9 @@
     return ok;
 }
 
-bool captureVoiceCommunicationOutputAllowed(pid_t pid, uid_t uid) {
+bool captureVoiceCommunicationOutputAllowed(const Identity& identity) {
+    uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
+    uid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(identity.pid));
     if (isAudioServerOrRootUid(uid)) return true;
     static const String16 sCaptureVoiceCommOutput(
         "android.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT");
@@ -181,9 +204,11 @@
     return ok;
 }
 
-bool captureHotwordAllowed(const String16& opPackageName, pid_t pid, uid_t uid) {
+bool captureHotwordAllowed(const Identity& identity) {
+    uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
+    uid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(identity.pid));
     // CAPTURE_AUDIO_HOTWORD permission implies RECORD_AUDIO permission
-    bool ok = recordingAllowed(opPackageName, pid, uid);
+    bool ok = recordingAllowed(identity);
 
     if (ok) {
         static const String16 sCaptureHotwordAllowed("android.permission.CAPTURE_AUDIO_HOTWORD");
@@ -205,11 +230,12 @@
 }
 
 bool modifyAudioRoutingAllowed() {
-    return modifyAudioRoutingAllowed(
-        IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid());
+    return modifyAudioRoutingAllowed(getCallingIdentity());
 }
 
-bool modifyAudioRoutingAllowed(pid_t pid, uid_t uid) {
+bool modifyAudioRoutingAllowed(const Identity& identity) {
+    uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
+    pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(identity.pid));
     if (isAudioServerUid(IPCThreadState::self()->getCallingUid())) return true;
     // IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
     bool ok = PermissionCache::checkPermission(sModifyAudioRouting, pid, uid);
@@ -219,11 +245,12 @@
 }
 
 bool modifyDefaultAudioEffectsAllowed() {
-    return modifyDefaultAudioEffectsAllowed(
-        IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid());
+    return modifyDefaultAudioEffectsAllowed(getCallingIdentity());
 }
 
-bool modifyDefaultAudioEffectsAllowed(pid_t pid, uid_t uid) {
+bool modifyDefaultAudioEffectsAllowed(const Identity& identity) {
+    uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
+    pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(identity.pid));
     if (isAudioServerUid(IPCThreadState::self()->getCallingUid())) return true;
 
     static const String16 sModifyDefaultAudioEffectsAllowed(
@@ -244,14 +271,18 @@
     return ok;
 }
 
-bool modifyPhoneStateAllowed(pid_t pid, uid_t uid) {
+bool modifyPhoneStateAllowed(const Identity& identity) {
+    uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
+    pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(identity.pid));
     bool ok = PermissionCache::checkPermission(sModifyPhoneState, pid, uid);
     ALOGE_IF(!ok, "Request requires %s", String8(sModifyPhoneState).c_str());
     return ok;
 }
 
 // privileged behavior needed by Dialer, Settings, SetupWizard and CellBroadcastReceiver
-bool bypassInterruptionPolicyAllowed(pid_t pid, uid_t uid) {
+bool bypassInterruptionPolicyAllowed(const Identity& identity) {
+    uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
+    pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(identity.pid));
     static const String16 sWriteSecureSettings("android.permission.WRITE_SECURE_SETTINGS");
     bool ok = PermissionCache::checkPermission(sModifyPhoneState, pid, uid)
         || PermissionCache::checkPermission(sWriteSecureSettings, pid, uid)
@@ -261,6 +292,13 @@
     return ok;
 }
 
+Identity getCallingIdentity() {
+  Identity identity = Identity();
+  identity.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(IPCThreadState::self()->getCallingPid()));
+  identity.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(IPCThreadState::self()->getCallingUid()));
+  return identity;
+}
+
 status_t checkIMemory(const sp<IMemory>& iMemory)
 {
     if (iMemory == 0) {
diff --git a/media/utils/TimeCheck.cpp b/media/utils/TimeCheck.cpp
index 59d74de..819e146 100644
--- a/media/utils/TimeCheck.cpp
+++ b/media/utils/TimeCheck.cpp
@@ -39,10 +39,9 @@
     static std::atomic<int> curAudioHalPids = 0;
 
     if (update) {
-        audioHalPids[(curAudioHalPids + 1) % kNumAudioHalPidsVectors] = *pids;
-        curAudioHalPids++;
+        audioHalPids[(curAudioHalPids++ + 1) % kNumAudioHalPidsVectors] = *pids;
     } else {
-        *pids = audioHalPids[curAudioHalPids];
+        *pids = audioHalPids[curAudioHalPids % kNumAudioHalPidsVectors];
     }
 }
 
diff --git a/media/utils/fuzzers/Android.bp b/media/utils/fuzzers/Android.bp
index 80882b2..187ef7c 100644
--- a/media/utils/fuzzers/Android.bp
+++ b/media/utils/fuzzers/Android.bp
@@ -15,6 +15,7 @@
         "liblog",
         "libmediautils",
         "libutils",
+        "media_permission-aidl-cpp",
     ],
 
     cflags: [
diff --git a/media/utils/fuzzers/ServiceUtilitiesFuzz.cpp b/media/utils/fuzzers/ServiceUtilitiesFuzz.cpp
index f4c815c..2f9e780 100644
--- a/media/utils/fuzzers/ServiceUtilitiesFuzz.cpp
+++ b/media/utils/fuzzers/ServiceUtilitiesFuzz.cpp
@@ -19,12 +19,15 @@
 #include <functional>
 #include  <type_traits>
 
+#include <android/media/permission/Identity.h>
 #include "fuzzer/FuzzedDataProvider.h"
 #include "mediautils/ServiceUtilities.h"
 
 static constexpr int kMaxOperations = 50;
 static constexpr int kMaxStringLen = 256;
 
+using android::media::permission::Identity;
+
 const std::vector<std::function<void(FuzzedDataProvider*, android::MediaPackageManager)>>
     operations = {
         [](FuzzedDataProvider* data_provider, android::MediaPackageManager pm) -> void {
@@ -43,27 +46,33 @@
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
     FuzzedDataProvider data_provider(data, size);
-    uid_t uid = data_provider.ConsumeIntegral<uid_t>();
-    pid_t pid = data_provider.ConsumeIntegral<pid_t>();
+    int32_t uid = data_provider.ConsumeIntegral<int32_t>();
+    int32_t pid = data_provider.ConsumeIntegral<int32_t>();
     audio_source_t source = static_cast<audio_source_t>(data_provider
         .ConsumeIntegral<std::underlying_type_t<audio_source_t>>());
 
+    std::string packageNameStr = data_provider.ConsumeRandomLengthString(kMaxStringLen);
+    std::string msgStr = data_provider.ConsumeRandomLengthString(kMaxStringLen);
+    android::String16 msgStr16(packageNameStr.c_str());
+    Identity identity;
+    identity.packageName = packageNameStr;
+    identity.uid = uid;
+    identity.pid = pid;
+
     // There is not state here, and order is not significant,
     // so we can simply call all of the target functions
     android::isServiceUid(uid);
     android::isAudioServerUid(uid);
     android::isAudioServerOrSystemServerUid(uid);
     android::isAudioServerOrMediaServerUid(uid);
-    std::string packageNameStr = data_provider.ConsumeRandomLengthString(kMaxStringLen);
-    android::String16 opPackageName(packageNameStr.c_str());
-    android::recordingAllowed(opPackageName, pid, uid);
-    android::startRecording(opPackageName, pid, uid, source);
-    android::finishRecording(opPackageName, uid, source);
-    android::captureAudioOutputAllowed(pid, uid);
-    android::captureMediaOutputAllowed(pid, uid);
-    android::captureHotwordAllowed(opPackageName, pid, uid);
-    android::modifyPhoneStateAllowed(uid, pid);
-    android::bypassInterruptionPolicyAllowed(uid, pid);
+    android::recordingAllowed(identity);
+    android::startRecording(identity, msgStr16, source);
+    android::finishRecording(identity, source);
+    android::captureAudioOutputAllowed(identity);
+    android::captureMediaOutputAllowed(identity);
+    android::captureHotwordAllowed(identity);
+    android::modifyPhoneStateAllowed(identity);
+    android::bypassInterruptionPolicyAllowed(identity);
     android::settingsAllowed();
     android::modifyAudioRoutingAllowed();
     android::modifyDefaultAudioEffectsAllowed();
diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h
index 03965db..9a3c6fb 100644
--- a/media/utils/include/mediautils/ServiceUtilities.h
+++ b/media/utils/include/mediautils/ServiceUtilities.h
@@ -25,6 +25,7 @@
 #include <cutils/multiuser.h>
 #include <private/android_filesystem_config.h>
 #include <system/audio-hal-enums.h>
+#include <android/media/permission/Identity.h>
 
 #include <map>
 #include <optional>
@@ -79,22 +80,25 @@
     }
 }
 
-bool recordingAllowed(const String16& opPackageName, pid_t pid, uid_t uid);
-bool startRecording(const String16& opPackageName, pid_t pid, uid_t uid, audio_source_t source);
-void finishRecording(const String16& opPackageName, uid_t uid, audio_source_t source);
-bool captureAudioOutputAllowed(pid_t pid, uid_t uid);
-bool captureMediaOutputAllowed(pid_t pid, uid_t uid);
-bool captureTunerAudioInputAllowed(pid_t pid, uid_t uid);
-bool captureVoiceCommunicationOutputAllowed(pid_t pid, uid_t uid);
-bool captureHotwordAllowed(const String16& opPackageName, pid_t pid, uid_t uid);
+bool recordingAllowed(const media::permission::Identity& identity);
+bool startRecording(const media::permission::Identity& identity,
+    const String16& msg, audio_source_t source);
+void finishRecording(const media::permission::Identity& identity, audio_source_t source);
+bool captureAudioOutputAllowed(const media::permission::Identity& identity);
+bool captureMediaOutputAllowed(const media::permission::Identity& identity);
+bool captureTunerAudioInputAllowed(const media::permission::Identity& identity);
+bool captureVoiceCommunicationOutputAllowed(const media::permission::Identity& identity);
+bool captureHotwordAllowed(const media::permission::Identity& identity);
 bool settingsAllowed();
 bool modifyAudioRoutingAllowed();
-bool modifyAudioRoutingAllowed(pid_t pid, uid_t uid);
+bool modifyAudioRoutingAllowed(const media::permission::Identity& identity);
 bool modifyDefaultAudioEffectsAllowed();
-bool modifyDefaultAudioEffectsAllowed(pid_t pid, uid_t uid);
+bool modifyDefaultAudioEffectsAllowed(const media::permission::Identity& identity);
 bool dumpAllowed();
-bool modifyPhoneStateAllowed(pid_t pid, uid_t uid);
-bool bypassInterruptionPolicyAllowed(pid_t pid, uid_t uid);
+bool modifyPhoneStateAllowed(const media::permission::Identity& identity);
+bool bypassInterruptionPolicyAllowed(const media::permission::Identity& identity);
+
+media::permission::Identity getCallingIdentity();
 
 status_t checkIMemory(const sp<IMemory>& iMemory);
 
diff --git a/services/audioflinger/Android.bp b/services/audioflinger/Android.bp
index 518ef9a..2294c49 100644
--- a/services/audioflinger/Android.bp
+++ b/services/audioflinger/Android.bp
@@ -80,6 +80,7 @@
         "libmedia_helper",
         "libshmemcompat",
         "libvibrator",
+        "media_permission-aidl-cpp",
     ],
 
     static_libs: [
@@ -93,6 +94,10 @@
         "libmedia_headers",
     ],
 
+    export_shared_lib_headers: [
+        "media_permission-aidl-cpp",
+    ],
+
     cflags: [
         "-DSTATE_QUEUE_INSTANTIATIONS=\"StateQueueInstantiations.cpp\"",
         "-fvisibility=hidden",
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index ae9fc64..6678287 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -85,15 +85,6 @@
 
 #include "TypedLogger.h"
 
-#define VALUE_OR_FATAL(result)                   \
-    ({                                           \
-       auto _tmp = (result);                     \
-       LOG_ALWAYS_FATAL_IF(!_tmp.ok(),           \
-                           "Failed result (%d)", \
-                           _tmp.error());        \
-       std::move(_tmp.value());                  \
-     })
-
 // ----------------------------------------------------------------------------
 
 // Note: the following macro is used for extremely verbose logging message.  In
@@ -112,6 +103,7 @@
 namespace android {
 
 using media::IEffectClient;
+using media::permission::Identity;
 
 static const char kDeadlockedString[] = "AudioFlinger may be deadlocked\n";
 static const char kHardwareLockedString[] = "Hardware lock is taken\n";
@@ -364,7 +356,7 @@
 
         ret = AudioSystem::getOutputForAttr(&localAttr, &io,
                                             actualSessionId,
-                                            &streamType, client.clientPid, client.clientUid,
+                                            &streamType, client.identity,
                                             &fullConfig,
                                             (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ |
                                                     AUDIO_OUTPUT_FLAG_DIRECT),
@@ -375,9 +367,7 @@
         ret = AudioSystem::getInputForAttr(&localAttr, &io,
                                               RECORD_RIID_INVALID,
                                               actualSessionId,
-                                              client.clientPid,
-                                              client.clientUid,
-                                              client.packageName,
+                                              client.identity,
                                               config,
                                               AUDIO_INPUT_FLAG_MMAP_NOIRQ, deviceId, &portId);
     }
@@ -781,27 +771,33 @@
     audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
     std::vector<audio_io_handle_t> secondaryOutputs;
 
-    bool updatePid = (input.clientInfo.clientPid == -1);
+    // TODO b/182392553: refactor or make clearer
+    pid_t clientPid =
+        VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_pid_t(input.clientInfo.identity.pid));
+    bool updatePid = (clientPid == (pid_t)-1);
     const uid_t callingUid = IPCThreadState::self()->getCallingUid();
-    uid_t clientUid = input.clientInfo.clientUid;
+    uid_t clientUid =
+        VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_uid_t(input.clientInfo.identity.uid));
     audio_io_handle_t effectThreadId = AUDIO_IO_HANDLE_NONE;
     std::vector<int> effectIds;
     audio_attributes_t localAttr = input.attr;
 
+    Identity adjIdentity = input.clientInfo.identity;
     if (!isAudioServerOrMediaServerUid(callingUid)) {
         ALOGW_IF(clientUid != callingUid,
                 "%s uid %d tried to pass itself off as %d",
                 __FUNCTION__, callingUid, clientUid);
+        adjIdentity.uid = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(callingUid));
         clientUid = callingUid;
         updatePid = true;
     }
-    pid_t clientPid = input.clientInfo.clientPid;
     const pid_t callingPid = IPCThreadState::self()->getCallingPid();
     if (updatePid) {
-        ALOGW_IF(clientPid != -1 && clientPid != callingPid,
+        ALOGW_IF(clientPid != (pid_t)-1 && clientPid != callingPid,
                  "%s uid %d pid %d tried to pass itself off as pid %d",
                  __func__, callingUid, callingPid, clientPid);
         clientPid = callingPid;
+        adjIdentity.pid = VALUE_OR_RETURN_STATUS(legacy2aidl_pid_t_int32_t(callingPid));
     }
 
     audio_session_t sessionId = input.sessionId;
@@ -816,7 +812,7 @@
     output.outputId = AUDIO_IO_HANDLE_NONE;
     output.selectedDeviceId = input.selectedDeviceId;
     lStatus = AudioSystem::getOutputForAttr(&localAttr, &output.outputId, sessionId, &streamType,
-                                            clientPid, clientUid, &input.config, input.flags,
+                                            adjIdentity, &input.config, input.flags,
                                             &output.selectedDeviceId, &portId, &secondaryOutputs);
 
     if (lStatus != NO_ERROR || output.outputId == AUDIO_IO_HANDLE_NONE) {
@@ -881,9 +877,8 @@
                                       &output.frameCount, &output.notificationFrameCount,
                                       input.notificationsPerBuffer, input.speed,
                                       input.sharedBuffer, sessionId, &output.flags,
-                                      callingPid, input.clientInfo.clientTid, clientUid,
-                                      &lStatus, portId, input.audioTrackCallback,
-                                      input.opPackageName);
+                                      callingPid, adjIdentity, input.clientInfo.clientTid,
+                                      &lStatus, portId, input.audioTrackCallback);
         LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (track == 0));
         // we don't abort yet if lStatus != NO_ERROR; there is still work to be done regardless
 
@@ -2041,23 +2036,25 @@
     output.buffers.clear();
     output.inputId = AUDIO_IO_HANDLE_NONE;
 
-    bool updatePid = (input.clientInfo.clientPid == -1);
+    // TODO b/182392553: refactor or clean up
+    Identity adjIdentity = input.clientInfo.identity;
+    bool updatePid = (adjIdentity.pid == -1);
     const uid_t callingUid = IPCThreadState::self()->getCallingUid();
-    uid_t clientUid = input.clientInfo.clientUid;
+    const uid_t currentUid = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(adjIdentity.uid));
     if (!isAudioServerOrMediaServerUid(callingUid)) {
-        ALOGW_IF(clientUid != callingUid,
+        ALOGW_IF(currentUid != callingUid,
                 "%s uid %d tried to pass itself off as %d",
-                __FUNCTION__, callingUid, clientUid);
-        clientUid = callingUid;
+                __FUNCTION__, callingUid, currentUid);
+        adjIdentity.uid = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(callingUid));
         updatePid = true;
     }
-    pid_t clientPid = input.clientInfo.clientPid;
     const pid_t callingPid = IPCThreadState::self()->getCallingPid();
+    const pid_t currentPid = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_pid_t(adjIdentity.pid));
     if (updatePid) {
-        ALOGW_IF(clientPid != -1 && clientPid != callingPid,
+        ALOGW_IF(currentPid != (pid_t)-1 && currentPid != callingPid,
                  "%s uid %d pid %d tried to pass itself off as pid %d",
-                 __func__, callingUid, callingPid, clientPid);
-        clientPid = callingPid;
+                 __func__, callingUid, callingPid, currentPid);
+        adjIdentity.pid = VALUE_OR_RETURN_STATUS(legacy2aidl_pid_t_int32_t(callingPid));
     }
 
     // we don't yet support anything other than linear PCM
@@ -2085,7 +2082,7 @@
     output.selectedDeviceId = input.selectedDeviceId;
     output.flags = input.flags;
 
-    client = registerPid(clientPid);
+    client = registerPid(VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(adjIdentity.pid)));
 
     // Not a conventional loop, but a retry loop for at most two iterations total.
     // Try first maybe with FAST flag then try again without FAST flag if that fails.
@@ -2105,9 +2102,7 @@
                                       input.riid,
                                       sessionId,
                                     // FIXME compare to AudioTrack
-                                      clientPid,
-                                      clientUid,
-                                      input.opPackageName,
+                                      adjIdentity,
                                       &input.config,
                                       output.flags, &output.selectedDeviceId, &portId);
     if (lStatus != NO_ERROR) {
@@ -2134,10 +2129,9 @@
                                                   input.config.format, input.config.channel_mask,
                                                   &output.frameCount, sessionId,
                                                   &output.notificationFrameCount,
-                                                  callingPid, clientUid, &output.flags,
+                                                  callingPid, adjIdentity, &output.flags,
                                                   input.clientInfo.clientTid,
-                                                  &lStatus, portId,
-                                                  input.opPackageName);
+                                                  &lStatus, portId);
         LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (recordTrack == 0));
 
         // lStatus == BAD_TYPE means FAST flag was rejected: request a new input from
@@ -3530,9 +3524,7 @@
     const int32_t priority = request.priority;
     const AudioDeviceTypeAddr device = VALUE_OR_RETURN_STATUS(
             aidl2legacy_AudioDeviceTypeAddress(request.device));
-    const String16 opPackageName = VALUE_OR_RETURN_STATUS(
-            aidl2legacy_string_view_String16(request.opPackageName));
-    pid_t pid = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_pid_t(request.pid));
+    Identity adjIdentity = request.identity;
     const audio_session_t sessionId = VALUE_OR_RETURN_STATUS(
             aidl2legacy_int32_t_audio_session_t(request.sessionId));
     audio_io_handle_t io = VALUE_OR_RETURN_STATUS(
@@ -3548,17 +3540,21 @@
 
     status_t lStatus = NO_ERROR;
 
+    // TODO b/182392553: refactor or make clearer
     const uid_t callingUid = IPCThreadState::self()->getCallingUid();
-    if (pid == -1 || !isAudioServerOrMediaServerUid(callingUid)) {
+    adjIdentity.uid = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(callingUid));
+    pid_t currentPid = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_pid_t(adjIdentity.pid));
+    if (currentPid == -1 || !isAudioServerOrMediaServerUid(callingUid)) {
         const pid_t callingPid = IPCThreadState::self()->getCallingPid();
-        ALOGW_IF(pid != -1 && pid != callingPid,
+        ALOGW_IF(currentPid != -1 && currentPid != callingPid,
                  "%s uid %d pid %d tried to pass itself off as pid %d",
-                 __func__, callingUid, callingPid, pid);
-        pid = callingPid;
+                 __func__, callingUid, callingPid, currentPid);
+        adjIdentity.pid = VALUE_OR_RETURN_STATUS(legacy2aidl_pid_t_int32_t(callingPid));
+        currentPid = callingPid;
     }
 
     ALOGV("createEffect pid %d, effectClient %p, priority %d, sessionId %d, io %d, factory %p",
-          pid, effectClient.get(), priority, sessionId, io, mEffectsFactoryHal.get());
+          adjIdentity.pid, effectClient.get(), priority, sessionId, io, mEffectsFactoryHal.get());
 
     if (mEffectsFactoryHal == 0) {
         ALOGE("%s: no effects factory hal", __func__);
@@ -3586,7 +3582,7 @@
             goto Exit;
         }
     } else if (sessionId == AUDIO_SESSION_DEVICE) {
-        if (!modifyDefaultAudioEffectsAllowed(pid, callingUid)) {
+        if (!modifyDefaultAudioEffectsAllowed(adjIdentity)) {
             ALOGE("%s: device effect permission denied for uid %d", __func__, callingUid);
             lStatus = PERMISSION_DENIED;
             goto Exit;
@@ -3631,7 +3627,7 @@
         // check recording permission for visualizer
         if ((memcmp(&descOut.type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0) &&
             // TODO: Do we need to start/stop op - i.e. is there recording being performed?
-            !recordingAllowed(opPackageName, pid, callingUid)) {
+            !recordingAllowed(adjIdentity)) {
             lStatus = PERMISSION_DENIED;
             goto Exit;
         }
@@ -3657,7 +3653,7 @@
         Mutex::Autolock _l(mLock);
 
         if (sessionId == AUDIO_SESSION_DEVICE) {
-            sp<Client> client = registerPid(pid);
+            sp<Client> client = registerPid(currentPid);
             ALOGV("%s device type %#x address %s", __func__, device.mType, device.getAddress());
             handle = mDeviceEffectManager.createEffect_l(
                     &descOut, device, client, effectClient, mPatchPanel.patches_l(),
@@ -3761,7 +3757,7 @@
             }
         }
 
-        sp<Client> client = registerPid(pid);
+        sp<Client> client = registerPid(currentPid);
 
         // create effect on selected output thread
         bool pinned = !audio_is_global_session(sessionId) && isSessionAcquired_l(sessionId);
diff --git a/services/audioflinger/MmapTracks.h b/services/audioflinger/MmapTracks.h
index b83f6b5..ba868d7 100644
--- a/services/audioflinger/MmapTracks.h
+++ b/services/audioflinger/MmapTracks.h
@@ -29,8 +29,7 @@
                             audio_channel_mask_t channelMask,
                             audio_session_t sessionId,
                             bool isOut,
-                            uid_t uid,
-                            pid_t pid,
+                            const media::permission::Identity& identity,
                             pid_t creatorPid,
                             audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE);
     virtual             ~MmapTrack();
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index c70d6f9..a5b3077 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -15,6 +15,8 @@
 ** limitations under the License.
 */
 
+#include <android/media/permission/Identity.h>
+
 #ifndef INCLUDING_FROM_AUDIOFLINGER_H
     #error This header file should only be included from AudioFlinger.h
 #endif
@@ -26,11 +28,13 @@
     bool hasOpPlayAudio() const;
 
     static sp<OpPlayAudioMonitor> createIfNeeded(
-            uid_t uid, const audio_attributes_t& attr, int id, audio_stream_type_t streamType,
-            const std::string& opPackageName);
+            const android::media::permission::Identity& identity,
+            const audio_attributes_t& attr, int id,
+            audio_stream_type_t streamType);
 
 private:
-    OpPlayAudioMonitor(uid_t uid, audio_usage_t usage, int id, const String16& opPackageName);
+    OpPlayAudioMonitor(const android::media::permission::Identity& identity,
+        audio_usage_t usage, int id);
     void onFirstRef() override;
     static void getPackagesForUid(uid_t uid, Vector<String16>& packages);
 
@@ -50,10 +54,9 @@
     void checkPlayAudioForUsage();
 
     std::atomic_bool mHasOpPlayAudio;
-    const uid_t mUid;
+    const android::media::permission::Identity mIdentity;
     const int32_t mUsage; // on purpose not audio_usage_t because always checked in appOps as int32_t
     const int mId; // for logging purposes only
-    const String16 mOpPackageName;
 };
 
 // playback track
@@ -72,14 +75,13 @@
                                 const sp<IMemory>& sharedBuffer,
                                 audio_session_t sessionId,
                                 pid_t creatorPid,
-                                uid_t uid,
+                                const media::permission::Identity& identity,
                                 audio_output_flags_t flags,
                                 track_type type,
                                 audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE,
                                 /** default behaviour is to start when there are as many frames
                                   * ready as possible (aka. Buffer is full). */
-                                size_t frameCountToBeReady = SIZE_MAX,
-                                const std::string opPackageName = "");
+                                size_t frameCountToBeReady = SIZE_MAX);
     virtual             ~Track();
     virtual status_t    initCheck() const;
 
@@ -336,7 +338,7 @@
                                 audio_format_t format,
                                 audio_channel_mask_t channelMask,
                                 size_t frameCount,
-                                uid_t uid);
+                                android::media::permission::Identity& identity);
     virtual             ~OutputTrack();
 
     virtual status_t    start(AudioSystem::sync_event_t event =
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index d87239d..4d03441 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -26,10 +26,10 @@
     bool hasOpRecordAudio() const;
 
     static sp<OpRecordAudioMonitor> createIfNeeded
-        (uid_t uid, const audio_attributes_t& attr, const String16& opPackageName);
+        (const media::permission::Identity& identity, const audio_attributes_t& attr);
 
 private:
-    OpRecordAudioMonitor(uid_t uid, const String16& opPackageName);
+    explicit OpRecordAudioMonitor(const media::permission::Identity& identity);
     void onFirstRef() override;
 
     AppOpsManager mAppOpsManager;
@@ -49,8 +49,7 @@
     void checkRecordAudio();
 
     std::atomic_bool mHasOpRecordAudio;
-    const uid_t mUid;
-    const String16 mPackage;
+    const media::permission::Identity mIdentity;
 };
 
 // record track
@@ -67,10 +66,9 @@
                                 size_t bufferSize,
                                 audio_session_t sessionId,
                                 pid_t creatorPid,
-                                uid_t uid,
+                                const media::permission::Identity& identity,
                                 audio_input_flags_t flags,
                                 track_type type,
-                                const String16& opPackageName,
                                 audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE);
     virtual             ~RecordTrack();
     virtual status_t    initCheck() const;
@@ -149,7 +147,7 @@
 
             // used to enforce OP_RECORD_AUDIO
             uid_t                              mUid;
-            String16                           mOpPackageName;
+            media::permission::Identity        mIdentity;
             sp<OpRecordAudioMonitor>           mOpRecordAudioMonitor;
 };
 
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index eaf0d10..90f7a61 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -108,6 +108,7 @@
 
 // TODO: Move these macro/inlines to a header file.
 #define max(a, b) ((a) > (b) ? (a) : (b))
+
 template <typename T>
 static inline T min(const T& a, const T& b)
 {
@@ -117,6 +118,7 @@
 namespace android {
 
 using media::IEffectClient;
+using media::permission::Identity;
 
 // retry counts for buffer fill timeout
 // 50 * ~20msecs = 1 second
@@ -2076,12 +2078,11 @@
         audio_session_t sessionId,
         audio_output_flags_t *flags,
         pid_t creatorPid,
+        const Identity& identity,
         pid_t tid,
-        uid_t uid,
         status_t *status,
         audio_port_handle_t portId,
-        const sp<media::IAudioTrackCallback>& callback,
-        const std::string& opPackageName)
+        const sp<media::IAudioTrackCallback>& callback)
 {
     size_t frameCount = *pFrameCount;
     size_t notificationFrameCount = *pNotificationFrameCount;
@@ -2172,8 +2173,8 @@
                 "sampleRate=%u mSampleRate=%u "
                 "hasFastMixer=%d tid=%d fastTrackAvailMask=%#x",
                 sharedBuffer.get(), frameCount, mFrameCount, format, mFormat,
-                audio_is_linear_pcm(format),
-                channelMask, sampleRate, mSampleRate, hasFastMixer(), tid, mFastTrackAvailMask);
+                audio_is_linear_pcm(format), channelMask, sampleRate,
+                mSampleRate, hasFastMixer(), tid, mFastTrackAvailMask);
         *flags = (audio_output_flags_t)(*flags & ~AUDIO_OUTPUT_FLAG_FAST);
       }
     }
@@ -2372,8 +2373,8 @@
         track = new Track(this, client, streamType, attr, sampleRate, format,
                           channelMask, frameCount,
                           nullptr /* buffer */, (size_t)0 /* bufferSize */, sharedBuffer,
-                          sessionId, creatorPid, uid, trackFlags, TrackBase::TYPE_DEFAULT, portId,
-                          SIZE_MAX /*frameCountToBeReady*/, opPackageName);
+                          sessionId, creatorPid, identity, trackFlags, TrackBase::TYPE_DEFAULT,
+                          portId, SIZE_MAX /*frameCountToBeReady*/);
 
         lStatus = track != 0 ? track->initCheck() : (status_t) NO_MEMORY;
         if (lStatus != NO_ERROR) {
@@ -6812,13 +6813,19 @@
     // from different OutputTracks and their associated MixerThreads (e.g. one may
     // nearly empty and the other may be dropping data).
 
+    // TODO b/182392769: use identity util, move to server edge
+    Identity identity = Identity();
+    identity.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(
+        IPCThreadState::self()->getCallingUid()));
+    identity.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(
+      IPCThreadState::self()->getCallingPid()));
     sp<OutputTrack> outputTrack = new OutputTrack(thread,
                                             this,
                                             mSampleRate,
                                             mFormat,
                                             mChannelMask,
                                             frameCount,
-                                            IPCThreadState::self()->getCallingUid());
+                                            identity);
     status_t status = outputTrack != 0 ? outputTrack->initCheck() : (status_t) NO_MEMORY;
     if (status != NO_ERROR) {
         ALOGE("addOutputTrack() initCheck failed %d", status);
@@ -7730,12 +7737,11 @@
         audio_session_t sessionId,
         size_t *pNotificationFrameCount,
         pid_t creatorPid,
-        uid_t uid,
+        const Identity& identity,
         audio_input_flags_t *flags,
         pid_t tid,
         status_t *status,
-        audio_port_handle_t portId,
-        const String16& opPackageName)
+        audio_port_handle_t portId)
 {
     size_t frameCount = *pFrameCount;
     size_t notificationFrameCount = *pNotificationFrameCount;
@@ -7868,8 +7874,8 @@
 
         track = new RecordTrack(this, client, attr, sampleRate,
                       format, channelMask, frameCount,
-                      nullptr /* buffer */, (size_t)0 /* bufferSize */, sessionId, creatorPid, uid,
-                      *flags, TrackBase::TYPE_DEFAULT, opPackageName, portId);
+                      nullptr /* buffer */, (size_t)0 /* bufferSize */, sessionId, creatorPid,
+                      identity, *flags, TrackBase::TYPE_DEFAULT, portId);
 
         lStatus = track->initCheck();
         if (lStatus != NO_ERROR) {
@@ -8884,7 +8890,7 @@
                                          audio_port_handle_t *handle)
 {
     ALOGV("%s clientUid %d mStandby %d mPortId %d *handle %d", __FUNCTION__,
-          client.clientUid, mStandby, mPortId, *handle);
+          client.identity.uid, mStandby, mPortId, *handle);
     if (mHalStream == 0) {
         return NO_INIT;
     }
@@ -8916,8 +8922,7 @@
         ret = AudioSystem::getOutputForAttr(&mAttr, &io,
                                             mSessionId,
                                             &stream,
-                                            client.clientPid,
-                                            client.clientUid,
+                                            client.identity,
                                             &config,
                                             flags,
                                             &deviceId,
@@ -8934,9 +8939,7 @@
         ret = AudioSystem::getInputForAttr(&mAttr, &io,
                                               RECORD_RIID_INVALID,
                                               mSessionId,
-                                              client.clientPid,
-                                              client.clientUid,
-                                              client.packageName,
+                                              client.identity,
                                               &config,
                                               AUDIO_INPUT_FLAG_MMAP_NOIRQ,
                                               &deviceId,
@@ -8976,16 +8979,15 @@
 
     // Given that MmapThread::mAttr is mutable, should a MmapTrack have attributes ?
     sp<MmapTrack> track = new MmapTrack(this, attr == nullptr ? mAttr : *attr, mSampleRate, mFormat,
-                                        mChannelMask, mSessionId, isOutput(), client.clientUid,
-                                        client.clientPid, IPCThreadState::self()->getCallingPid(),
-                                        portId);
+                                        mChannelMask, mSessionId, isOutput(), client.identity,
+                                        IPCThreadState::self()->getCallingPid(), portId);
 
     if (isOutput()) {
         // force volume update when a new track is added
         mHalVolFloat = -1.0f;
     } else if (!track->isSilenced_l()) {
         for (const sp<MmapTrack> &t : mActiveTracks) {
-            if (t->isSilenced_l() && t->uid() != client.clientUid)
+            if (t->isSilenced_l() && t->uid() != client.identity.uid)
                 t->invalidate();
         }
     }
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index c974252..5aa5169 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -882,12 +882,11 @@
                                 audio_session_t sessionId,
                                 audio_output_flags_t *flags,
                                 pid_t creatorPid,
+                                const media::permission::Identity& identity,
                                 pid_t tid,
-                                uid_t uid,
                                 status_t *status /*non-NULL*/,
                                 audio_port_handle_t portId,
-                                const sp<media::IAudioTrackCallback>& callback,
-                                const std::string& opPackageName);
+                                const sp<media::IAudioTrackCallback>& callback);
 
                 AudioStreamOut* getOutput() const;
                 AudioStreamOut* clearOutput();
@@ -1642,12 +1641,11 @@
                     audio_session_t sessionId,
                     size_t *pNotificationFrameCount,
                     pid_t creatorPid,
-                    uid_t uid,
+                    const media::permission::Identity& identity,
                     audio_input_flags_t *flags,
                     pid_t tid,
                     status_t *status /*non-NULL*/,
-                    audio_port_handle_t portId,
-                    const String16& opPackageName);
+                    audio_port_handle_t portId);
 
             status_t    start(RecordTrack* recordTrack,
                               AudioSystem::sync_event_t event,
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 5454778..a86102f 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -65,6 +65,7 @@
 
 using ::android::aidl_utils::binderStatusFromStatusT;
 using binder::Status;
+using media::permission::Identity;
 using media::VolumeShaper;
 // ----------------------------------------------------------------------------
 //      TrackBase
@@ -237,6 +238,13 @@
     }
 }
 
+// TODO b/182392769: use identity util
+Identity audioServerIdentity() {
+   Identity i = Identity();
+   i.uid = AID_AUDIOSERVER;
+   return i;
+}
+
 status_t AudioFlinger::ThreadBase::TrackBase::initCheck() const
 {
     status_t status;
@@ -489,10 +497,11 @@
 // static
 sp<AudioFlinger::PlaybackThread::OpPlayAudioMonitor>
 AudioFlinger::PlaybackThread::OpPlayAudioMonitor::createIfNeeded(
-            uid_t uid, const audio_attributes_t& attr, int id, audio_stream_type_t streamType,
-            const std::string& opPackageName)
+            const Identity& identity, const audio_attributes_t& attr, int id,
+            audio_stream_type_t streamType)
 {
     Vector <String16> packages;
+    uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
     getPackagesForUid(uid, packages);
     if (isServiceUid(uid)) {
         if (packages.isEmpty()) {
@@ -515,31 +524,36 @@
         return nullptr;
     }
 
-    String16 opPackageNameStr(opPackageName.c_str());
-    if (opPackageName.empty()) {
+    // TODO b/182392769: use identity util
+    std::optional<std::string> opPackageNameStr = identity.packageName;
+    if (!identity.packageName.has_value()) {
         // If no package name is provided by the client, use the first associated with the uid
         if (!packages.isEmpty()) {
-            opPackageNameStr = packages[0];
+            opPackageNameStr =
+                VALUE_OR_FATAL(legacy2aidl_String16_string(packages[0]));
         }
     } else {
         // If the provided package name is invalid, we force app ops denial by clearing the package
         // name passed to OpPlayAudioMonitor
+        String16 opPackageLegacy = VALUE_OR_FATAL(
+            aidl2legacy_string_view_String16(opPackageNameStr.value_or("")));
         if (std::find_if(packages.begin(), packages.end(),
-                [&opPackageNameStr](const auto& package) {
-                return opPackageNameStr == package; }) == packages.end()) {
+                [&opPackageLegacy](const auto& package) {
+                return opPackageLegacy == package; }) == packages.end()) {
             ALOGW("The package name(%s) provided does not correspond to the uid %d, "
-                  "force muting the track", opPackageName.c_str(), uid);
-            // Set package name as an empty string so that hasOpPlayAudio will always return false.
-            opPackageNameStr = String16("");
+                  "force muting the track", opPackageNameStr.value().c_str(), uid);
+            // Set null package name so hasOpPlayAudio will always return false.
+            opPackageNameStr = std::optional<std::string>();
         }
     }
-    return new OpPlayAudioMonitor(uid, attr.usage, id, opPackageNameStr);
+    Identity adjIdentity = identity;
+    adjIdentity.packageName = opPackageNameStr;
+    return new OpPlayAudioMonitor(adjIdentity, attr.usage, id);
 }
 
 AudioFlinger::PlaybackThread::OpPlayAudioMonitor::OpPlayAudioMonitor(
-        uid_t uid, audio_usage_t usage, int id, const String16& opPackageName)
-        : mHasOpPlayAudio(true), mUid(uid), mUsage((int32_t) usage), mId(id),
-          mOpPackageName(opPackageName)
+        const Identity& identity, audio_usage_t usage, int id)
+        : mHasOpPlayAudio(true), mIdentity(identity), mUsage((int32_t) usage), mId(id)
 {
 }
 
@@ -554,9 +568,11 @@
 void AudioFlinger::PlaybackThread::OpPlayAudioMonitor::onFirstRef()
 {
     checkPlayAudioForUsage();
-    if (mOpPackageName.size() != 0) {
+    if (mIdentity.packageName.has_value()) {
         mOpCallback = new PlayAudioOpCallback(this);
-        mAppOpsManager.startWatchingMode(AppOpsManager::OP_PLAY_AUDIO, mOpPackageName, mOpCallback);
+        mAppOpsManager.startWatchingMode(AppOpsManager::OP_PLAY_AUDIO,
+            VALUE_OR_FATAL(aidl2legacy_string_view_String16(mIdentity.packageName.value_or("")))
+            , mOpCallback);
     }
 }
 
@@ -569,11 +585,14 @@
 // - not called from PlayAudioOpCallback because the callback is not installed in this case
 void AudioFlinger::PlaybackThread::OpPlayAudioMonitor::checkPlayAudioForUsage()
 {
-    if (mOpPackageName.size() == 0) {
+    if (!mIdentity.packageName.has_value()) {
         mHasOpPlayAudio.store(false);
     } else {
+        uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(mIdentity.uid));
+        String16 packageName = VALUE_OR_FATAL(
+            aidl2legacy_string_view_String16(mIdentity.packageName.value_or("")));
         bool hasIt = mAppOpsManager.checkAudioOpNoThrow(AppOpsManager::OP_PLAY_AUDIO,
-                    mUsage, mUid, mOpPackageName) == AppOpsManager::MODE_ALLOWED;
+                    mUsage, uid, packageName) == AppOpsManager::MODE_ALLOWED;
         ALOGD("OpPlayAudio: track:%d usage:%d %smuted", mId, mUsage, hasIt ? "not " : "");
         mHasOpPlayAudio.store(hasIt);
     }
@@ -623,12 +642,11 @@
             const sp<IMemory>& sharedBuffer,
             audio_session_t sessionId,
             pid_t creatorPid,
-            uid_t uid,
+            const Identity& identity,
             audio_output_flags_t flags,
             track_type type,
             audio_port_handle_t portId,
-            size_t frameCountToBeReady,
-            const std::string opPackageName)
+            size_t frameCountToBeReady)
     :   TrackBase(thread, client, attr, sampleRate, format, channelMask, frameCount,
                   // TODO: Using unsecurePointer() has some associated security pitfalls
                   //       (see declaration for details).
@@ -636,7 +654,8 @@
                   //       issue (e.g. by copying).
                   (sharedBuffer != 0) ? sharedBuffer->unsecurePointer() : buffer,
                   (sharedBuffer != 0) ? sharedBuffer->size() : bufferSize,
-                  sessionId, creatorPid, uid, true /*isOut*/,
+                  sessionId, creatorPid,
+                  VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid)), true /*isOut*/,
                   (type == TYPE_PATCH) ? ( buffer == NULL ? ALLOC_LOCAL : ALLOC_NONE) : ALLOC_CBLK,
                   type,
                   portId,
@@ -651,8 +670,8 @@
     mPresentationCompleteFrames(0),
     mFrameMap(16 /* sink-frame-to-track-frame map memory */),
     mVolumeHandler(new media::VolumeHandler(sampleRate)),
-    mOpPlayAudioMonitor(OpPlayAudioMonitor::createIfNeeded(
-            uid, attr, id(), streamType, opPackageName)),
+    mOpPlayAudioMonitor(OpPlayAudioMonitor::createIfNeeded(identity, attr, id(),
+        streamType)),
     // mSinkTimestamp
     mFrameCountToBeReady(frameCountToBeReady),
     mFastIndex(-1),
@@ -674,6 +693,7 @@
         return;
     }
 
+    uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
     if (!thread->isTrackAllowed_l(channelMask, format, sessionId, uid)) {
         ALOGE("%s(%d): no more tracks available", __func__, mId);
         releaseCblk(); // this makes the track invalid.
@@ -718,8 +738,10 @@
         // HapticGenerator effect, which will generate haptic data, on the track. In that case,
         // external vibration is always created for all tracks attached to haptic playback thread.
         mAudioVibrationController = new AudioVibrationController(this);
+        std::string packageName = identity.packageName.has_value() ?
+            identity.packageName.value() : "";
         mExternalVibration = new os::ExternalVibration(
-                mUid, opPackageName, mAttr, mAudioVibrationController);
+                mUid, packageName, mAttr, mAudioVibrationController);
     }
 
     // Once this item is logged by the server, the client can add properties.
@@ -1821,12 +1843,12 @@
             audio_format_t format,
             audio_channel_mask_t channelMask,
             size_t frameCount,
-            uid_t uid)
+            Identity& identity)
     :   Track(playbackThread, NULL, AUDIO_STREAM_PATCH,
               audio_attributes_t{} /* currently unused for output track */,
               sampleRate, format, channelMask, frameCount,
               nullptr /* buffer */, (size_t)0 /* bufferSize */, nullptr /* sharedBuffer */,
-              AUDIO_SESSION_NONE, getpid(), uid, AUDIO_OUTPUT_FLAG_NONE,
+              AUDIO_SESSION_NONE, getpid(), identity, AUDIO_OUTPUT_FLAG_NONE,
               TYPE_OUTPUT),
     mActive(false), mSourceThread(sourceThread)
 {
@@ -2056,8 +2078,8 @@
               audio_attributes_t{} /* currently unused for patch track */,
               sampleRate, format, channelMask, frameCount,
               buffer, bufferSize, nullptr /* sharedBuffer */,
-              AUDIO_SESSION_NONE, getpid(), AID_AUDIOSERVER, flags, TYPE_PATCH,
-              AUDIO_PORT_HANDLE_NONE, frameCountToBeReady),
+              AUDIO_SESSION_NONE, getpid(), audioServerIdentity(), flags,
+              TYPE_PATCH, AUDIO_PORT_HANDLE_NONE, frameCountToBeReady),
         PatchTrackBase(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, true, true),
                        *playbackThread, timeout)
 {
@@ -2194,41 +2216,44 @@
 // static
 sp<AudioFlinger::RecordThread::OpRecordAudioMonitor>
 AudioFlinger::RecordThread::OpRecordAudioMonitor::createIfNeeded(
-            uid_t uid, const audio_attributes_t& attr, const String16& opPackageName)
+            const Identity& identity, const audio_attributes_t& attr)
 {
-    if (isServiceUid(uid)) {
-        ALOGV("not silencing record for service uid:%d pack:%s",
-                uid, String8(opPackageName).string());
+    if (isServiceUid(identity.uid)) {
+        ALOGV("not silencing record for service %s",
+                identity.toString().c_str());
         return nullptr;
     }
 
     // Capturing from FM TUNER output is not controlled by OP_RECORD_AUDIO
     // because it does not affect users privacy as does capturing from an actual microphone.
     if (attr.source == AUDIO_SOURCE_FM_TUNER) {
-        ALOGV("not muting FM TUNER capture for uid %d", uid);
+        ALOGV("not muting FM TUNER capture for uid %d", identity.uid);
         return nullptr;
     }
 
-    if (opPackageName.size() == 0) {
+    if (!identity.packageName.has_value() || identity.packageName.value().size() == 0) {
         Vector<String16> packages;
         // no package name, happens with SL ES clients
         // query package manager to find one
         PermissionController permissionController;
-        permissionController.getPackagesForUid(uid, packages);
+        permissionController.getPackagesForUid(identity.uid, packages);
         if (packages.isEmpty()) {
             return nullptr;
         } else {
-            ALOGV("using pack:%s for uid:%d", String8(packages[0]).string(), uid);
-            return new OpRecordAudioMonitor(uid, packages[0]);
+            Identity adjIdentity = identity;
+            adjIdentity.packageName =
+                VALUE_OR_FATAL(legacy2aidl_String16_string(packages[0]));
+            ALOGV("using identity:%s", adjIdentity.toString().c_str());
+            return new OpRecordAudioMonitor(adjIdentity);
         }
     }
 
-    return new OpRecordAudioMonitor(uid, opPackageName);
+    return new OpRecordAudioMonitor(identity);
 }
 
 AudioFlinger::RecordThread::OpRecordAudioMonitor::OpRecordAudioMonitor(
-        uid_t uid, const String16& opPackageName)
-        : mHasOpRecordAudio(true), mUid(uid), mPackage(opPackageName)
+        const Identity& identity)
+        : mHasOpRecordAudio(true), mIdentity(identity)
 {
 }
 
@@ -2244,8 +2269,10 @@
 {
     checkRecordAudio();
     mOpCallback = new RecordAudioOpCallback(this);
-    ALOGV("start watching OP_RECORD_AUDIO for pack:%s", String8(mPackage).string());
-    mAppOpsManager.startWatchingMode(AppOpsManager::OP_RECORD_AUDIO, mPackage, mOpCallback);
+    ALOGV("start watching OP_RECORD_AUDIO for %s", mIdentity.toString().c_str());
+    mAppOpsManager.startWatchingMode(AppOpsManager::OP_RECORD_AUDIO,
+        VALUE_OR_FATAL(aidl2legacy_string_view_String16(mIdentity.packageName.value_or(""))),
+        mOpCallback);
 }
 
 bool AudioFlinger::RecordThread::OpRecordAudioMonitor::hasOpRecordAudio() const {
@@ -2260,14 +2287,17 @@
 // - not called from RecordAudioOpCallback because the callback is not installed in this case
 void AudioFlinger::RecordThread::OpRecordAudioMonitor::checkRecordAudio()
 {
+
     const int32_t mode = mAppOpsManager.checkOp(AppOpsManager::OP_RECORD_AUDIO,
-            mUid, mPackage);
+            mIdentity.uid, VALUE_OR_FATAL(aidl2legacy_string_view_String16(
+                mIdentity.packageName.value_or(""))));
     const bool hasIt =  (mode == AppOpsManager::MODE_ALLOWED);
     // verbose logging only log when appOp changed
     ALOGI_IF(hasIt != mHasOpRecordAudio.load(),
-            "OP_RECORD_AUDIO missing, %ssilencing record uid%d pack:%s",
-            hasIt ? "un" : "", mUid, String8(mPackage).string());
+            "OP_RECORD_AUDIO missing, %ssilencing record %s",
+            hasIt ? "un" : "", mIdentity.toString().c_str());
     mHasOpRecordAudio.store(hasIt);
+
 }
 
 AudioFlinger::RecordThread::OpRecordAudioMonitor::RecordAudioOpCallback::RecordAudioOpCallback(
@@ -2361,14 +2391,15 @@
             size_t bufferSize,
             audio_session_t sessionId,
             pid_t creatorPid,
-            uid_t uid,
+            const Identity& identity,
             audio_input_flags_t flags,
             track_type type,
-            const String16& opPackageName,
             audio_port_handle_t portId)
     :   TrackBase(thread, client, attr, sampleRate, format,
                   channelMask, frameCount, buffer, bufferSize, sessionId,
-                  creatorPid, uid, false /*isOut*/,
+                  creatorPid,
+                  VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid)),
+                  false /*isOut*/,
                   (type == TYPE_DEFAULT) ?
                           ((flags & AUDIO_INPUT_FLAG_FAST) ? ALLOC_PIPE : ALLOC_CBLK) :
                           ((buffer == NULL) ? ALLOC_LOCAL : ALLOC_NONE),
@@ -2380,7 +2411,7 @@
         mRecordBufferConverter(NULL),
         mFlags(flags),
         mSilenced(false),
-        mOpRecordAudioMonitor(OpRecordAudioMonitor::createIfNeeded(uid, attr, opPackageName))
+        mOpRecordAudioMonitor(OpRecordAudioMonitor::createIfNeeded(mIdentity, attr))
 {
     if (mCblk == NULL) {
         return;
@@ -2421,7 +2452,9 @@
 #endif
 
     // Once this item is logged by the server, the client can add properties.
-    mTrackMetrics.logConstructor(creatorPid, uid, id());
+    pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mIdentity.pid));
+    uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(mIdentity.uid));
+    mTrackMetrics.logConstructor(pid, uid, id());
 }
 
 AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
@@ -2692,11 +2725,12 @@
     :   RecordTrack(recordThread, NULL,
                 audio_attributes_t{} /* currently unused for patch track */,
                 sampleRate, format, channelMask, frameCount,
-                buffer, bufferSize, AUDIO_SESSION_NONE, getpid(), AID_AUDIOSERVER,
-                flags, TYPE_PATCH, String16()),
+                buffer, bufferSize, AUDIO_SESSION_NONE, getpid(),
+                audioServerIdentity(), flags, TYPE_PATCH),
         PatchTrackBase(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, false, true),
                        *recordThread, timeout)
 {
+    mIdentity.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(getpid()));
     ALOGV("%s(%d): sampleRate %d mPeerTimeout %d.%03d sec",
                                       __func__, mId, sampleRate,
                                       (int)mPeerTimeout.tv_sec,
@@ -2970,21 +3004,24 @@
         audio_channel_mask_t channelMask,
         audio_session_t sessionId,
         bool isOut,
-        uid_t uid,
-        pid_t pid,
+        const Identity& identity,
         pid_t creatorPid,
         audio_port_handle_t portId)
     :   TrackBase(thread, NULL, attr, sampleRate, format,
                   channelMask, (size_t)0 /* frameCount */,
                   nullptr /* buffer */, (size_t)0 /* bufferSize */,
-                  sessionId, creatorPid, uid, isOut,
+                  sessionId, creatorPid,
+                  VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid)),
+                  isOut,
                   ALLOC_NONE,
                   TYPE_DEFAULT, portId,
                   std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_MMAP) + std::to_string(portId)),
-        mPid(pid), mSilenced(false), mSilencedNotified(false)
+        mPid(VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.pid))),
+            mSilenced(false), mSilencedNotified(false)
 {
     // Once this item is logged by the server, the client can add properties.
-    mTrackMetrics.logConstructor(creatorPid, uid, id());
+    mTrackMetrics.logConstructor(creatorPid,
+        VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid)), id());
 }
 
 AudioFlinger::MmapThread::MmapTrack::~MmapTrack()
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 9132086..5f052a5 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -21,6 +21,7 @@
 #include <media/AudioSystem.h>
 #include <media/AudioPolicy.h>
 #include <media/DeviceDescriptorBase.h>
+#include <android/media/permission/Identity.h>
 #include <utils/String8.h>
 
 namespace android {
@@ -122,7 +123,7 @@
                                         audio_io_handle_t *output,
                                         audio_session_t session,
                                         audio_stream_type_t *stream,
-                                        uid_t uid,
+                                        const media::permission::Identity& identity,
                                         const audio_config_t *config,
                                         audio_output_flags_t *flags,
                                         audio_port_handle_t *selectedDeviceId,
@@ -141,7 +142,7 @@
                                      audio_io_handle_t *input,
                                      audio_unique_id_t riid,
                                      audio_session_t session,
-                                     uid_t uid,
+                                     const media::permission::Identity& identity,
                                      const audio_config_base_t *config,
                                      audio_input_flags_t flags,
                                      audio_port_handle_t *selectedDeviceId,
@@ -272,8 +273,11 @@
 
     virtual status_t getSurroundFormats(unsigned int *numSurroundFormats,
                                         audio_format_t *surroundFormats,
-                                        bool *surroundFormatsEnabled,
-                                        bool reported) = 0;
+                                        bool *surroundFormatsEnabled) = 0;
+
+    virtual status_t getReportedSurroundFormats(unsigned int *numSurroundFormats,
+                                                audio_format_t *surroundFormats) = 0;
+
     virtual status_t setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled) = 0;
 
     virtual bool     isHapticPlaybackSupported() = 0;
diff --git a/services/audiopolicy/common/managerdefinitions/include/Serializer.h b/services/audiopolicy/common/managerdefinitions/include/Serializer.h
index 48c4147..b70c595 100644
--- a/services/audiopolicy/common/managerdefinitions/include/Serializer.h
+++ b/services/audiopolicy/common/managerdefinitions/include/Serializer.h
@@ -21,5 +21,9 @@
 namespace android {
 
 status_t deserializeAudioPolicyFile(const char *fileName, AudioPolicyConfig *config);
+// In VTS mode all vendor extensions are ignored. This is done because
+// VTS tests are built using AOSP code and thus can not use vendor overlays
+// of system libraries.
+status_t deserializeAudioPolicyFileForVts(const char *fileName, AudioPolicyConfig *config);
 
 } // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
index 129f6f6..562c213 100644
--- a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
@@ -20,8 +20,8 @@
 #include <memory>
 #include <string>
 #include <utility>
+#include <variant>
 
-#include <hidl/Status.h>
 #include <libxml/parser.h>
 #include <libxml/xinclude.h>
 #include <media/convert.h>
@@ -36,12 +36,14 @@
 
 namespace {
 
-// TODO(mnaganov): Consider finding an alternative for using HIDL code.
-using hardware::Return;
-using hardware::Status;
-using hardware::Void;
 using utilities::convertTo;
 
+static inline bool maybeVendorExtension(const std::string& s) {
+    // Only checks whether the string starts with the "vendor prefix".
+    static const std::string vendorPrefix = "VX_";
+    return s.size() > vendorPrefix.size() && s.substr(0, vendorPrefix.size()) == vendorPrefix;
+}
+
 template<typename E, typename C>
 struct AndroidCollectionTraits {
     typedef sp<E> Element;
@@ -187,7 +189,7 @@
 
 struct GlobalConfigTraits
 {
-    typedef void Element;
+    typedef std::monostate Element;
 
     static constexpr const char *tag = "globalConfiguration";
 
@@ -203,7 +205,7 @@
 
 struct SurroundSoundTraits
 {
-    typedef void Element;
+    typedef std::monostate Element;
 
     static constexpr const char *tag = "surroundSound";
 
@@ -226,14 +228,15 @@
 class PolicySerializer
 {
 public:
-    status_t deserialize(const char *configFile, AudioPolicyConfig *config);
+    status_t deserialize(const char *configFile, AudioPolicyConfig *config,
+            bool ignoreVendorExtensions = false);
 
     template <class Trait>
     status_t deserializeCollection(const xmlNode *cur,
             typename Trait::Collection *collection,
             typename Trait::PtrSerializingCtx serializingContext);
     template <class Trait>
-    Return<typename Trait::Element> deserialize(const xmlNode *cur,
+    std::variant<status_t, typename Trait::Element> deserialize(const xmlNode *cur,
             typename Trait::PtrSerializingCtx serializingContext);
 
 private:
@@ -242,6 +245,7 @@
 
     typedef AudioPolicyConfig Element;
 
+    bool mIgnoreVendorExtensions = false;
     std::string mChannelMasksSeparator = ",";
     std::string mSamplingRatesSeparator = ",";
     std::string mFlagsSeparator = "|";
@@ -307,14 +311,17 @@
         }
         for (; child != NULL; child = child->next) {
             if (!xmlStrcmp(child->name, reinterpret_cast<const xmlChar*>(Trait::tag))) {
-                auto element = deserialize<Trait>(child, serializingContext);
-                if (element.isOk()) {
-                    status_t status = Trait::addElementToCollection(element, collection);
+                auto maybeElement = deserialize<Trait>(child, serializingContext);
+                if (maybeElement.index() == 1) {
+                    status_t status = Trait::addElementToCollection(
+                            std::get<1>(maybeElement), collection);
                     if (status != NO_ERROR) {
                         ALOGE("%s: could not add element to %s collection", __func__,
                             Trait::collectionTag);
                         return status;
                     }
+                } else if (mIgnoreVendorExtensions && std::get<status_t>(maybeElement) == NO_INIT) {
+                    // Skip a vendor extension element.
                 } else {
                     return BAD_VALUE;
                 }
@@ -328,8 +335,8 @@
 }
 
 template<>
-Return<AudioGainTraits::Element> PolicySerializer::deserialize<AudioGainTraits>(const xmlNode *cur,
-        AudioGainTraits::PtrSerializingCtx /*serializingContext*/)
+std::variant<status_t, AudioGainTraits::Element> PolicySerializer::deserialize<AudioGainTraits>(
+        const xmlNode *cur, AudioGainTraits::PtrSerializingCtx /*serializingContext*/)
 {
     using Attributes = AudioGainTraits::Attributes;
 
@@ -393,12 +400,13 @@
     if (gain->getMode() != 0) {
         return gain;
     } else {
-        return Status::fromStatusT(BAD_VALUE);
+        return BAD_VALUE;
     }
 }
 
 template<>
-Return<AudioProfileTraits::Element> PolicySerializer::deserialize<AudioProfileTraits>(
+std::variant<status_t, AudioProfileTraits::Element>
+PolicySerializer::deserialize<AudioProfileTraits>(
         const xmlNode *cur, AudioProfileTraits::PtrSerializingCtx /*serializingContext*/)
 {
     using Attributes = AudioProfileTraits::Attributes;
@@ -407,6 +415,10 @@
     std::string format = getXmlAttribute(cur, Attributes::format);
     std::string channels = getXmlAttribute(cur, Attributes::channelMasks);
 
+    if (mIgnoreVendorExtensions && maybeVendorExtension(format)) {
+        ALOGI("%s: vendor extension format \"%s\" skipped", __func__, format.c_str());
+        return NO_INIT;
+    }
     AudioProfileTraits::Element profile = new AudioProfile(formatFromString(format, gDynamicFormat),
             channelMasksFromString(channels, mChannelMasksSeparator.c_str()),
             samplingRatesFromString(samplingRates, mSamplingRatesSeparator.c_str()));
@@ -419,21 +431,21 @@
 }
 
 template<>
-Return<MixPortTraits::Element> PolicySerializer::deserialize<MixPortTraits>(const xmlNode *child,
-        MixPortTraits::PtrSerializingCtx /*serializingContext*/)
+std::variant<status_t, MixPortTraits::Element> PolicySerializer::deserialize<MixPortTraits>(
+        const xmlNode *child, MixPortTraits::PtrSerializingCtx /*serializingContext*/)
 {
     using Attributes = MixPortTraits::Attributes;
 
     std::string name = getXmlAttribute(child, Attributes::name);
     if (name.empty()) {
         ALOGE("%s: No %s found", __func__, Attributes::name);
-        return Status::fromStatusT(BAD_VALUE);
+        return BAD_VALUE;
     }
     ALOGV("%s: %s %s=%s", __func__, MixPortTraits::tag, Attributes::name, name.c_str());
     std::string role = getXmlAttribute(child, Attributes::role);
     if (role.empty()) {
         ALOGE("%s: No %s found", __func__, Attributes::role);
-        return Status::fromStatusT(BAD_VALUE);
+        return BAD_VALUE;
     }
     ALOGV("%s: Role=%s", __func__, role.c_str());
     audio_port_role_t portRole = (role == Attributes::roleSource) ?
@@ -444,7 +456,7 @@
     AudioProfileTraits::Collection profiles;
     status_t status = deserializeCollection<AudioProfileTraits>(child, &profiles, NULL);
     if (status != NO_ERROR) {
-        return Status::fromStatusT(status);
+        return status;
     }
     if (profiles.empty()) {
         profiles.add(AudioProfile::createFullDynamic(gDynamicFormat));
@@ -476,7 +488,7 @@
     AudioGainTraits::Collection gains;
     status = deserializeCollection<AudioGainTraits>(child, &gains, NULL);
     if (status != NO_ERROR) {
-        return Status::fromStatusT(status);
+        return status;
     }
     mixPort->setGains(gains);
 
@@ -484,7 +496,7 @@
 }
 
 template<>
-Return<DevicePortTraits::Element> PolicySerializer::deserialize<DevicePortTraits>(
+std::variant<status_t, DevicePortTraits::Element> PolicySerializer::deserialize<DevicePortTraits>(
         const xmlNode *cur, DevicePortTraits::PtrSerializingCtx /*serializingContext*/)
 {
     using Attributes = DevicePortTraits::Attributes;
@@ -493,30 +505,34 @@
     std::string name = getXmlAttribute(cur, Attributes::tagName);
     if (name.empty()) {
         ALOGE("%s: No %s found", __func__, Attributes::tagName);
-        return Status::fromStatusT(BAD_VALUE);
+        return BAD_VALUE;
     }
     ALOGV("%s: %s %s=%s", __func__, tag, Attributes::tagName, name.c_str());
     std::string typeName = getXmlAttribute(cur, Attributes::type);
     if (typeName.empty()) {
         ALOGE("%s: no type for %s", __func__, name.c_str());
-        return Status::fromStatusT(BAD_VALUE);
+        return BAD_VALUE;
     }
     ALOGV("%s: %s %s=%s", __func__, tag, Attributes::type, typeName.c_str());
     std::string role = getXmlAttribute(cur, Attributes::role);
     if (role.empty()) {
         ALOGE("%s: No %s found", __func__, Attributes::role);
-        return Status::fromStatusT(BAD_VALUE);
+        return BAD_VALUE;
     }
     ALOGV("%s: %s %s=%s", __func__, tag, Attributes::role, role.c_str());
     audio_port_role_t portRole = (role == Attributes::roleSource) ?
                 AUDIO_PORT_ROLE_SOURCE : AUDIO_PORT_ROLE_SINK;
 
+    if (mIgnoreVendorExtensions && maybeVendorExtension(typeName)) {
+        ALOGI("%s: vendor extension device type \"%s\" skipped", __func__, typeName.c_str());
+        return NO_INIT;
+    }
     audio_devices_t type = AUDIO_DEVICE_NONE;
     if (!DeviceConverter::fromString(typeName, type) ||
             (!audio_is_input_device(type) && portRole == AUDIO_PORT_ROLE_SOURCE) ||
             (!audio_is_output_devices(type) && portRole == AUDIO_PORT_ROLE_SINK)) {
         ALOGW("%s: bad type %08x", __func__, type);
-        return Status::fromStatusT(BAD_VALUE);
+        return BAD_VALUE;
     }
     std::string encodedFormatsLiteral = getXmlAttribute(cur, Attributes::encodedFormats);
     ALOGV("%s: %s %s=%s", __func__, tag, Attributes::encodedFormats, encodedFormatsLiteral.c_str());
@@ -531,7 +547,7 @@
     AudioProfileTraits::Collection profiles;
     status_t status = deserializeCollection<AudioProfileTraits>(cur, &profiles, NULL);
     if (status != NO_ERROR) {
-        return Status::fromStatusT(status);
+        return status;
     }
     if (profiles.empty()) {
         profiles.add(AudioProfile::createFullDynamic(gDynamicFormat));
@@ -544,7 +560,7 @@
     // Deserialize AudioGain children
     status = deserializeCollection<AudioGainTraits>(cur, &deviceDesc->mGains, NULL);
     if (status != NO_ERROR) {
-        return Status::fromStatusT(status);
+        return status;
     }
     ALOGV("%s: adding device tag %s type %08x address %s", __func__,
           deviceDesc->getName().c_str(), type, deviceDesc->address().c_str());
@@ -552,7 +568,7 @@
 }
 
 template<>
-Return<RouteTraits::Element> PolicySerializer::deserialize<RouteTraits>(
+std::variant<status_t, RouteTraits::Element> PolicySerializer::deserialize<RouteTraits>(
         const xmlNode *cur, RouteTraits::PtrSerializingCtx ctx)
 {
     using Attributes = RouteTraits::Attributes;
@@ -560,7 +576,7 @@
     std::string type = getXmlAttribute(cur, Attributes::type);
     if (type.empty()) {
         ALOGE("%s: No %s found", __func__, Attributes::type);
-        return Status::fromStatusT(BAD_VALUE);
+        return BAD_VALUE;
     }
     audio_route_type_t routeType = (type == Attributes::typeMix) ?
                 AUDIO_ROUTE_MIX : AUDIO_ROUTE_MUX;
@@ -571,20 +587,24 @@
     std::string sinkAttr = getXmlAttribute(cur, Attributes::sink);
     if (sinkAttr.empty()) {
         ALOGE("%s: No %s found", __func__, Attributes::sink);
-        return Status::fromStatusT(BAD_VALUE);
+        return BAD_VALUE;
     }
     // Convert Sink name to port pointer
     sp<PolicyAudioPort> sink = ctx->findPortByTagName(sinkAttr);
-    if (sink == NULL) {
+    if (sink == NULL && !mIgnoreVendorExtensions) {
         ALOGE("%s: no sink found with name=%s", __func__, sinkAttr.c_str());
-        return Status::fromStatusT(BAD_VALUE);
+        return BAD_VALUE;
+    } else if (sink == NULL) {
+        ALOGW("Skipping route to sink \"%s\" as it likely has vendor extension type",
+                sinkAttr.c_str());
+        return NO_INIT;
     }
     route->setSink(sink);
 
     std::string sourcesAttr = getXmlAttribute(cur, Attributes::sources);
     if (sourcesAttr.empty()) {
         ALOGE("%s: No %s found", __func__, Attributes::sources);
-        return Status::fromStatusT(BAD_VALUE);
+        return BAD_VALUE;
     }
     // Tokenize and Convert Sources name to port pointer
     PolicyAudioPortVector sources;
@@ -594,11 +614,15 @@
     while (devTag != NULL) {
         if (strlen(devTag) != 0) {
             sp<PolicyAudioPort> source = ctx->findPortByTagName(devTag);
-            if (source == NULL) {
+            if (source == NULL && !mIgnoreVendorExtensions) {
                 ALOGE("%s: no source found with name=%s", __func__, devTag);
-                return Status::fromStatusT(BAD_VALUE);
+                return BAD_VALUE;
+            } else if (source == NULL) {
+                ALOGW("Skipping route source \"%s\" as it likely has vendor extension type",
+                        devTag);
+            } else {
+                sources.add(source);
             }
-            sources.add(source);
         }
         devTag = strtok(NULL, ",");
     }
@@ -613,7 +637,7 @@
 }
 
 template<>
-Return<ModuleTraits::Element> PolicySerializer::deserialize<ModuleTraits>(
+std::variant<status_t, ModuleTraits::Element> PolicySerializer::deserialize<ModuleTraits>(
         const xmlNode *cur, ModuleTraits::PtrSerializingCtx ctx)
 {
     using Attributes = ModuleTraits::Attributes;
@@ -625,7 +649,7 @@
     std::string name = getXmlAttribute(cur, Attributes::name);
     if (name.empty()) {
         ALOGE("%s: No %s found", __func__, Attributes::name);
-        return Status::fromStatusT(BAD_VALUE);
+        return BAD_VALUE;
     }
     uint32_t versionMajor = 0, versionMinor = 0;
     std::string versionLiteral = getXmlAttribute(cur, Attributes::version);
@@ -643,21 +667,21 @@
     MixPortTraits::Collection mixPorts;
     status_t status = deserializeCollection<MixPortTraits>(cur, &mixPorts, NULL);
     if (status != NO_ERROR) {
-        return Status::fromStatusT(status);
+        return status;
     }
     module->setProfiles(mixPorts);
 
     DevicePortTraits::Collection devicePorts;
     status = deserializeCollection<DevicePortTraits>(cur, &devicePorts, NULL);
     if (status != NO_ERROR) {
-        return Status::fromStatusT(status);
+        return status;
     }
     module->setDeclaredDevices(devicePorts);
 
     RouteTraits::Collection routes;
     status = deserializeCollection<RouteTraits>(cur, &routes, module.get());
     if (status != NO_ERROR) {
-        return Status::fromStatusT(status);
+        return status;
     }
     module->setRoutes(routes);
 
@@ -677,6 +701,12 @@
                         sp<DeviceDescriptor> device = module->getDeclaredDevices().
                                 getDeviceFromTagName(std::string(reinterpret_cast<const char*>(
                                                         attachedDevice.get())));
+                        if (device == nullptr && mIgnoreVendorExtensions) {
+                            ALOGW("Skipped attached device \"%s\" because it likely uses a vendor"
+                                    "extension type",
+                                    reinterpret_cast<const char*>(attachedDevice.get()));
+                            continue;
+                        }
                         ctx->addDevice(device);
                     }
                 }
@@ -703,7 +733,8 @@
 }
 
 template<>
-Return<GlobalConfigTraits::Element> PolicySerializer::deserialize<GlobalConfigTraits>(
+std::variant<status_t, GlobalConfigTraits::Element>
+PolicySerializer::deserialize<GlobalConfigTraits>(
         const xmlNode *root, GlobalConfigTraits::PtrSerializingCtx config)
 {
     using Attributes = GlobalConfigTraits::Attributes;
@@ -725,14 +756,15 @@
             if (!engineLibrarySuffix.empty()) {
                 config->setEngineLibraryNameSuffix(engineLibrarySuffix);
             }
-            return Void();
+            return NO_ERROR;
         }
     }
-    return Void();
+    return NO_ERROR;
 }
 
 template<>
-Return<SurroundSoundTraits::Element> PolicySerializer::deserialize<SurroundSoundTraits>(
+std::variant<status_t, SurroundSoundTraits::Element>
+PolicySerializer::deserialize<SurroundSoundTraits>(
         const xmlNode *root, SurroundSoundTraits::PtrSerializingCtx config)
 {
     config->setDefaultSurroundFormats();
@@ -745,14 +777,15 @@
             if (status == NO_ERROR) {
                 config->setSurroundFormats(formats);
             }
-            return Void();
+            return NO_ERROR;
         }
     }
-    return Void();
+    return NO_ERROR;
 }
 
 template<>
-Return<SurroundSoundFormatTraits::Element> PolicySerializer::deserialize<SurroundSoundFormatTraits>(
+std::variant<status_t, SurroundSoundFormatTraits::Element>
+PolicySerializer::deserialize<SurroundSoundFormatTraits>(
         const xmlNode *cur, SurroundSoundFormatTraits::PtrSerializingCtx /*serializingContext*/)
 {
     using Attributes = SurroundSoundFormatTraits::Attributes;
@@ -760,12 +793,16 @@
     std::string formatLiteral = getXmlAttribute(cur, Attributes::name);
     if (formatLiteral.empty()) {
         ALOGE("%s: No %s found for a surround format", __func__, Attributes::name);
-        return Status::fromStatusT(BAD_VALUE);
+        return BAD_VALUE;
+    }
+    if (mIgnoreVendorExtensions && maybeVendorExtension(formatLiteral)) {
+        ALOGI("%s: vendor extension format \"%s\" skipped", __func__, formatLiteral.c_str());
+        return NO_INIT;
     }
     audio_format_t format = formatFromString(formatLiteral);
     if (format == AUDIO_FORMAT_DEFAULT) {
         ALOGE("%s: Unrecognized format %s", __func__, formatLiteral.c_str());
-        return Status::fromStatusT(BAD_VALUE);
+        return BAD_VALUE;
     }
     SurroundSoundFormatTraits::Element pair = std::make_pair(
             format, SurroundSoundFormatTraits::Collection::mapped_type{});
@@ -777,14 +814,16 @@
         auto result = pair.second.insert(subformat);
         if (!result.second) {
             ALOGE("%s: could not add subformat %x to collection", __func__, subformat);
-            return Status::fromStatusT(BAD_VALUE);
+            return BAD_VALUE;
         }
     }
     return pair;
 }
 
-status_t PolicySerializer::deserialize(const char *configFile, AudioPolicyConfig *config)
+status_t PolicySerializer::deserialize(const char *configFile, AudioPolicyConfig *config,
+                                       bool ignoreVendorExtensions)
 {
+    mIgnoreVendorExtensions = ignoreVendorExtensions;
     auto doc = make_xmlUnique(xmlParseFile(configFile));
     if (doc == nullptr) {
         ALOGE("%s: Could not parse %s document.", __func__, configFile);
@@ -845,4 +884,12 @@
     return status;
 }
 
+status_t deserializeAudioPolicyFileForVts(const char *fileName, AudioPolicyConfig *config)
+{
+    PolicySerializer serializer;
+    status_t status = serializer.deserialize(fileName, config, true /*ignoreVendorExtensions*/);
+    if (status != OK) config->clear();
+    return status;
+}
+
 } // namespace android
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index d038ce5..380bf6b 100644
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -142,47 +142,19 @@
     return EngineBase::setForceUse(usage, config);
 }
 
-DeviceVector Engine::getDevicesForStrategyInt(legacy_strategy strategy,
-                                              DeviceVector availableOutputDevices,
-                                              DeviceVector availableInputDevices,
-                                              const SwAudioOutputCollection &outputs) const
+void Engine::filterOutputDevicesForStrategy(legacy_strategy strategy,
+                                            DeviceVector& availableOutputDevices,
+                                            const DeviceVector availableInputDevices,
+                                            const SwAudioOutputCollection &outputs) const
 {
-    DeviceVector devices;
-
     switch (strategy) {
-
-    case STRATEGY_TRANSMITTED_THROUGH_SPEAKER:
-        devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER);
-        break;
-
-    case STRATEGY_SONIFICATION_RESPECTFUL:
-        if (isInCall() || outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_VOICE_CALL))) {
-            devices = getDevicesForStrategyInt(
-                    STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs);
-        } else {
-            bool media_active_locally =
-                    outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_MUSIC),
-                                            SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)
-                    || outputs.isActiveLocally(
-                        toVolumeSource(AUDIO_STREAM_ACCESSIBILITY),
-                        SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY);
+    case STRATEGY_SONIFICATION_RESPECTFUL: {
+        if (!(isInCall() || outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_VOICE_CALL)))) {
             // routing is same as media without the "remote" device
             availableOutputDevices.remove(availableOutputDevices.getDevicesFromType(
                     AUDIO_DEVICE_OUT_REMOTE_SUBMIX));
-            devices = getDevicesForStrategyInt(STRATEGY_MEDIA,
-                    availableOutputDevices,
-                    availableInputDevices, outputs);
-            // if no media is playing on the device, check for mandatory use of "safe" speaker
-            // when media would have played on speaker, and the safe speaker path is available
-            if (!media_active_locally) {
-                devices.replaceDevicesByType(
-                        AUDIO_DEVICE_OUT_SPEAKER,
-                        availableOutputDevices.getDevicesFromType(
-                                AUDIO_DEVICE_OUT_SPEAKER_SAFE));
-            }
         }
-        break;
-
+        } break;
     case STRATEGY_DTMF:
     case STRATEGY_PHONE: {
         // Force use of only devices on primary output if:
@@ -214,6 +186,64 @@
                 availableOutputDevices = availPrimaryOutputDevices;
             }
         }
+        } break;
+    case STRATEGY_ACCESSIBILITY: {
+        // do not route accessibility prompts to a digital output currently configured with a
+        // compressed format as they would likely not be mixed and dropped.
+        for (size_t i = 0; i < outputs.size(); i++) {
+            sp<AudioOutputDescriptor> desc = outputs.valueAt(i);
+            if (desc->isActive() && !audio_is_linear_pcm(desc->getFormat())) {
+                availableOutputDevices.remove(desc->devices().getDevicesFromTypes({
+                        AUDIO_DEVICE_OUT_HDMI, AUDIO_DEVICE_OUT_SPDIF,
+                        AUDIO_DEVICE_OUT_HDMI_ARC}));
+            }
+        }
+        } break;
+    default:
+        break;
+    }
+}
+
+DeviceVector Engine::getDevicesForStrategyInt(legacy_strategy strategy,
+                                              DeviceVector availableOutputDevices,
+                                              DeviceVector availableInputDevices,
+                                              const SwAudioOutputCollection &outputs) const
+{
+    DeviceVector devices;
+
+    switch (strategy) {
+
+    case STRATEGY_TRANSMITTED_THROUGH_SPEAKER:
+        devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER);
+        break;
+
+    case STRATEGY_SONIFICATION_RESPECTFUL:
+        if (isInCall() || outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_VOICE_CALL))) {
+            devices = getDevicesForStrategyInt(
+                    STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs);
+        } else {
+            bool media_active_locally =
+                    outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_MUSIC),
+                                            SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)
+                    || outputs.isActiveLocally(
+                        toVolumeSource(AUDIO_STREAM_ACCESSIBILITY),
+                        SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY);
+            devices = getDevicesForStrategyInt(STRATEGY_MEDIA,
+                    availableOutputDevices,
+                    availableInputDevices, outputs);
+            // if no media is playing on the device, check for mandatory use of "safe" speaker
+            // when media would have played on speaker, and the safe speaker path is available
+            if (!media_active_locally) {
+                devices.replaceDevicesByType(
+                        AUDIO_DEVICE_OUT_SPEAKER,
+                        availableOutputDevices.getDevicesFromType(
+                                AUDIO_DEVICE_OUT_SPEAKER_SAFE));
+            }
+        }
+        break;
+
+    case STRATEGY_DTMF:
+    case STRATEGY_PHONE: {
         devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_HEARING_AID);
         if (!devices.isEmpty()) break;
         devices = availableOutputDevices.getFirstDevicesFromTypes({
@@ -286,16 +316,6 @@
 
     case STRATEGY_ACCESSIBILITY:
         if (strategy == STRATEGY_ACCESSIBILITY) {
-            // do not route accessibility prompts to a digital output currently configured with a
-            // compressed format as they would likely not be mixed and dropped.
-            for (size_t i = 0; i < outputs.size(); i++) {
-                sp<AudioOutputDescriptor> desc = outputs.valueAt(i);
-                if (desc->isActive() && !audio_is_linear_pcm(desc->getFormat())) {
-                    availableOutputDevices.remove(desc->devices().getDevicesFromTypes({
-                            AUDIO_DEVICE_OUT_HDMI, AUDIO_DEVICE_OUT_SPDIF,
-                            AUDIO_DEVICE_OUT_HDMI_ARC}));
-                }
-            }
             if (outputs.isActive(toVolumeSource(AUDIO_STREAM_RING)) ||
                     outputs.isActive(toVolumeSource(AUDIO_STREAM_ALARM))) {
                 return getDevicesForStrategyInt(
@@ -634,11 +654,18 @@
     auto legacyStrategy = mLegacyStrategyMap.find(strategy) != end(mLegacyStrategyMap) ?
                           mLegacyStrategyMap.at(strategy) : STRATEGY_NONE;
 
-    // When not in call, STRATEGY_PHONE and STRATEGY_DTMF follow STRATEGY_MEDIA
-    if (!isInCall() && (legacyStrategy == STRATEGY_PHONE || legacyStrategy == STRATEGY_DTMF)) {
+    // When not in call, STRATEGY_DTMF follows STRATEGY_MEDIA
+    if (!isInCall() && legacyStrategy == STRATEGY_DTMF) {
         legacyStrategy = STRATEGY_MEDIA;
         strategy = getProductStrategyFromLegacy(STRATEGY_MEDIA);
     }
+
+    DeviceVector availableInputDevices = getApmObserver()->getAvailableInputDevices();
+    const SwAudioOutputCollection& outputs = getApmObserver()->getOutputs();
+
+    filterOutputDevicesForStrategy(legacyStrategy, availableOutputDevices,
+                                   availableInputDevices, outputs);
+
     // check if this strategy has a preferred device that is available,
     // if yes, give priority to it.
     DeviceVector preferredAvailableDevVec =
@@ -647,9 +674,6 @@
         return preferredAvailableDevVec;
     }
 
-    DeviceVector availableInputDevices = getApmObserver()->getAvailableInputDevices();
-    const SwAudioOutputCollection& outputs = getApmObserver()->getOutputs();
-
     return getDevicesForStrategyInt(legacyStrategy,
                                     availableOutputDevices,
                                     availableInputDevices, outputs);
diff --git a/services/audiopolicy/enginedefault/src/Engine.h b/services/audiopolicy/enginedefault/src/Engine.h
index 6214fe7..6dc6cd0 100644
--- a/services/audiopolicy/enginedefault/src/Engine.h
+++ b/services/audiopolicy/enginedefault/src/Engine.h
@@ -74,6 +74,11 @@
 
     status_t setDefaultDevice(audio_devices_t device);
 
+    void filterOutputDevicesForStrategy(legacy_strategy strategy,
+                                            DeviceVector& availableOutputDevices,
+                                            const DeviceVector availableInputDevices,
+                                            const SwAudioOutputCollection &outputs) const;
+
     DeviceVector getDevicesForStrategyInt(legacy_strategy strategy,
                                           DeviceVector availableOutputDevices,
                                           DeviceVector availableInputDevices,
diff --git a/services/audiopolicy/fuzzer/Android.bp b/services/audiopolicy/fuzzer/Android.bp
index 5dbcebd..38bdedc 100644
--- a/services/audiopolicy/fuzzer/Android.bp
+++ b/services/audiopolicy/fuzzer/Android.bp
@@ -50,6 +50,7 @@
         "libbinder",
         "libaudiopolicy",
         "libaudiopolicymanagerdefault",
+        "media_permission-aidl-cpp",
     ],
     static_libs: [
         "android.hardware.audio.common@7.0-enums",
diff --git a/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp b/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp
index 692ce08..1177b95 100644
--- a/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp
+++ b/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp
@@ -26,6 +26,7 @@
 
 #include <Serializer.h>
 #include <android-base/file.h>
+#include <android/media/permission/Identity.h>
 #include <libxml/parser.h>
 #include <libxml/xinclude.h>
 #include <media/AudioPolicy.h>
@@ -46,6 +47,8 @@
 using namespace ::android::audio::policy::configuration::V7_0;
 }
 
+using media::permission::Identity;
+
 static const std::vector<audio_format_t> kAudioFormats = [] {
     std::vector<audio_format_t> result;
     for (const auto enumVal : xsdc_enum_range<xsd::AudioFormat>{}) {
@@ -246,7 +249,10 @@
     *portId = AUDIO_PORT_HANDLE_NONE;
     AudioPolicyInterface::output_type_t outputType;
 
-    if (mManager->getOutputForAttr(&attr, output, AUDIO_SESSION_NONE, &stream, 0 /*uid*/, &config,
+    // TODO b/182392769: use identity util
+    Identity i;
+    i.uid = 0;
+    if (mManager->getOutputForAttr(&attr, output, AUDIO_SESSION_NONE, &stream, i, &config,
                                    &flags, selectedDeviceId, portId, {}, &outputType) != OK) {
         return false;
     }
@@ -270,7 +276,9 @@
     *portId = AUDIO_PORT_HANDLE_NONE;
     AudioPolicyInterface::input_type_t inputType;
 
-    if (mManager->getInputForAttr(&attr, &input, riid, AUDIO_SESSION_NONE, 0 /*uid*/, &config,
+    Identity i;
+    i.uid = 0;
+    if (mManager->getInputForAttr(&attr, &input, riid, AUDIO_SESSION_NONE, i, &config,
                                   flags, selectedDeviceId, &inputType, portId) != OK) {
         return false;
     }
diff --git a/services/audiopolicy/managerdefault/Android.bp b/services/audiopolicy/managerdefault/Android.bp
index 5572beb..b111db4 100644
--- a/services/audiopolicy/managerdefault/Android.bp
+++ b/services/audiopolicy/managerdefault/Android.bp
@@ -34,6 +34,8 @@
         // a dependency on it in the device makefile. There will be no build time
         // conflict with libaudiopolicyenginedefault.
         "libaudiopolicyenginedefault",
+        "media_permission-aidl-cpp",
+        "libaudioclient_aidl_conversion",
     ],
 
     header_libs: [
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 75f0c1b..2b9f8d7 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -52,6 +52,8 @@
 
 namespace android {
 
+using media::permission::Identity;
+
 //FIXME: workaround for truncated touch sounds
 // to be removed when the problem is handled by system UI
 #define TOUCH_SOUND_FIXED_DELAY_MS 100
@@ -261,11 +263,7 @@
         } else {
             checkCloseOutputs();
         }
-
-        if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
-            DeviceVector newDevices = getNewOutputDevices(mPrimaryOutput, false /*fromCache*/);
-            updateCallRouting(newDevices);
-        }
+        (void)updateCallRouting(false /*fromCache*/);
         std::vector<audio_io_handle_t> outputsToReopen;
         const DeviceVector msdOutDevices = getMsdAudioOutDevices();
         const DeviceVector activeMediaDevices =
@@ -382,10 +380,7 @@
         // getDeviceForStrategy() cache
         updateDevicesAndOutputs();
 
-        if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
-            DeviceVector newDevices = getNewOutputDevices(mPrimaryOutput, false /*fromCache*/);
-            updateCallRouting(newDevices);
-        }
+        (void)updateCallRouting(false /*fromCache*/);
         // Reconnect Audio Source
         for (const auto &strategy : mEngine->getOrderedProductStrategies()) {
             auto attributes = mEngine->getAllAttributesForProductStrategy(strategy).front();
@@ -550,23 +545,58 @@
     return status;
 }
 
-uint32_t AudioPolicyManager::updateCallRouting(const DeviceVector &rxDevices, uint32_t delayMs)
+DeviceVector AudioPolicyManager::selectBestRxSinkDevicesForCall(bool fromCache)
+{
+    DeviceVector rxSinkdevices{};
+    rxSinkdevices = mEngine->getOutputDevicesForAttributes(
+                attributes_initializer(AUDIO_USAGE_VOICE_COMMUNICATION), nullptr, fromCache);
+    if (!rxSinkdevices.isEmpty() && mAvailableOutputDevices.contains(rxSinkdevices.itemAt(0))) {
+        auto rxSinkDevice = rxSinkdevices.itemAt(0);
+        auto telephonyRxModule = mHwModules.getModuleForDeviceType(
+                    AUDIO_DEVICE_IN_TELEPHONY_RX, AUDIO_FORMAT_DEFAULT);
+        // retrieve Rx Source device descriptor
+        sp<DeviceDescriptor> rxSourceDevice = mAvailableInputDevices.getDevice(
+                    AUDIO_DEVICE_IN_TELEPHONY_RX, String8(), AUDIO_FORMAT_DEFAULT);
+
+        // RX Telephony and Rx sink devices are declared by Primary Audio HAL
+        if (isPrimaryModule(telephonyRxModule) && (telephonyRxModule->getHalVersionMajor() >= 3) &&
+                telephonyRxModule->supportsPatch(rxSourceDevice, rxSinkDevice)) {
+            ALOGW("%s() device %s using HW Bridge", __func__, rxSinkDevice->toString().c_str());
+            return DeviceVector(rxSinkDevice);
+        }
+    }
+    // Note that despite the fact that getNewOutputDevices() is called on the primary output,
+    // the device returned is not necessarily reachable via this output
+    // (filter later by setOutputDevices())
+    return getNewOutputDevices(mPrimaryOutput, fromCache);
+}
+
+status_t AudioPolicyManager::updateCallRouting(bool fromCache, uint32_t delayMs, uint32_t *waitMs)
+{
+    if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
+        DeviceVector rxDevices = selectBestRxSinkDevicesForCall(fromCache);
+        return updateCallRoutingInternal(rxDevices, delayMs, waitMs);
+    }
+    return INVALID_OPERATION;
+}
+
+status_t AudioPolicyManager::updateCallRoutingInternal(
+        const DeviceVector &rxDevices, uint32_t delayMs, uint32_t *waitMs)
 {
     bool createTxPatch = false;
     bool createRxPatch = false;
     uint32_t muteWaitMs = 0;
-
     if(!hasPrimaryOutput() ||
             mPrimaryOutput->devices().onlyContainsDevicesWithType(AUDIO_DEVICE_OUT_STUB)) {
-        return muteWaitMs;
+        return INVALID_OPERATION;
     }
-    ALOG_ASSERT(!rxDevices.isEmpty(), "updateCallRouting() no selected output device");
+    ALOG_ASSERT(!rxDevices.isEmpty(), "%s() no selected output device", __func__);
 
     audio_attributes_t attr = { .source = AUDIO_SOURCE_VOICE_COMMUNICATION };
     auto txSourceDevice = mEngine->getInputDeviceForAttributes(attr);
-    ALOG_ASSERT(txSourceDevice != 0, "updateCallRouting() input selected device not available");
+    ALOG_ASSERT(txSourceDevice != 0, "%s() input selected device not available", __func__);
 
-    ALOGV("updateCallRouting device rxDevice %s txDevice %s",
+    ALOGV("%s device rxDevice %s txDevice %s", __func__,
           rxDevices.itemAt(0)->toString().c_str(), txSourceDevice->toString().c_str());
 
     disconnectTelephonyRxAudioSource();
@@ -595,8 +625,8 @@
             (telephonyRxModule->getHalVersionMajor() >= 3)) {
         if (rxSourceDevice == 0 || txSinkDevice == 0) {
             // RX / TX Telephony device(s) is(are) not currently available
-            ALOGE("updateCallRouting() no telephony Tx and/or RX device");
-            return muteWaitMs;
+            ALOGE("%s() no telephony Tx and/or RX device", __func__);
+            return INVALID_OPERATION;
         }
         // createAudioPatchInternal now supports both HW / SW bridging
         createRxPatch = true;
@@ -634,8 +664,10 @@
         }
         mCallTxPatch = createTelephonyPatch(false /*isRx*/, txSourceDevice, delayMs);
     }
-
-    return muteWaitMs;
+    if (waitMs != nullptr) {
+        *waitMs = muteWaitMs;
+    }
+    return NO_ERROR;
 }
 
 sp<AudioPatch> AudioPolicyManager::createTelephonyPatch(
@@ -753,25 +785,22 @@
     }
 
     if (hasPrimaryOutput()) {
-        // Note that despite the fact that getNewOutputDevices() is called on the primary output,
-        // the device returned is not necessarily reachable via this output
-        DeviceVector rxDevices = getNewOutputDevices(mPrimaryOutput, false /*fromCache*/);
-        // force routing command to audio hardware when ending call
-        // even if no device change is needed
-        if (isStateInCall(oldState) && rxDevices.isEmpty()) {
-            rxDevices = mPrimaryOutput->devices();
-        }
-
         if (state == AUDIO_MODE_IN_CALL) {
-            updateCallRouting(rxDevices, delayMs);
-        } else if (oldState == AUDIO_MODE_IN_CALL) {
-            disconnectTelephonyRxAudioSource();
-            if (mCallTxPatch != 0) {
-                releaseAudioPatchInternal(mCallTxPatch->getHandle());
-                mCallTxPatch.clear();
-            }
-            setOutputDevices(mPrimaryOutput, rxDevices, force, 0);
+            (void)updateCallRouting(false /*fromCache*/, delayMs);
         } else {
+            DeviceVector rxDevices = getNewOutputDevices(mPrimaryOutput, false /*fromCache*/);
+            // force routing command to audio hardware when ending call
+            // even if no device change is needed
+            if (isStateInCall(oldState) && rxDevices.isEmpty()) {
+                rxDevices = mPrimaryOutput->devices();
+            }
+            if (oldState == AUDIO_MODE_IN_CALL) {
+                disconnectTelephonyRxAudioSource();
+                if (mCallTxPatch != 0) {
+                    releaseAudioPatchInternal(mCallTxPatch->getHandle());
+                    mCallTxPatch.clear();
+                }
+            }
             setOutputDevices(mPrimaryOutput, rxDevices, force, 0);
         }
     }
@@ -1103,7 +1132,7 @@
                                               audio_io_handle_t *output,
                                               audio_session_t session,
                                               audio_stream_type_t *stream,
-                                              uid_t uid,
+                                              const Identity& identity,
                                               const audio_config_t *config,
                                               audio_output_flags_t *flags,
                                               audio_port_handle_t *selectedDeviceId,
@@ -1115,6 +1144,8 @@
     if (*portId != AUDIO_PORT_HANDLE_NONE) {
         return INVALID_OPERATION;
     }
+    const uid_t uid = VALUE_OR_RETURN_STATUS(
+        aidl2legacy_int32_t_uid_t(identity.uid));
     const audio_port_handle_t requestedPortId = *selectedDeviceId;
     audio_attributes_t resultAttr;
     bool isRequestedDeviceForExclusiveUse = false;
@@ -2083,7 +2114,7 @@
                                              audio_io_handle_t *input,
                                              audio_unique_id_t riid,
                                              audio_session_t session,
-                                             uid_t uid,
+                                             const Identity& identity,
                                              const audio_config_base_t *config,
                                              audio_input_flags_t flags,
                                              audio_port_handle_t *selectedDeviceId,
@@ -2102,6 +2133,7 @@
     sp<AudioInputDescriptor> inputDesc;
     sp<RecordClientDescriptor> clientDesc;
     audio_port_handle_t requestedDeviceId = *selectedDeviceId;
+    uid_t uid = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_uid_t(identity.uid));
     bool isSoundTrigger;
 
     // The supplied portId must be AUDIO_PORT_HANDLE_NONE
@@ -3300,9 +3332,7 @@
 void AudioPolicyManager::updateCallAndOutputRouting(bool forceVolumeReeval, uint32_t delayMs)
 {
     uint32_t waitMs = 0;
-    if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
-        DeviceVector newDevices = getNewOutputDevices(mPrimaryOutput, true /*fromCache*/);
-        waitMs = updateCallRouting(newDevices, delayMs);
+    if (updateCallRouting(true /*fromCache*/, delayMs, &waitMs) == NO_ERROR) {
         // Only apply special touch sound delay once
         delayMs = 0;
     }
@@ -4499,68 +4529,28 @@
 
 status_t AudioPolicyManager::getSurroundFormats(unsigned int *numSurroundFormats,
                                                 audio_format_t *surroundFormats,
-                                                bool *surroundFormatsEnabled,
-                                                bool reported)
+                                                bool *surroundFormatsEnabled)
 {
-    if (numSurroundFormats == NULL || (*numSurroundFormats != 0 &&
-            (surroundFormats == NULL || surroundFormatsEnabled == NULL))) {
+    if (numSurroundFormats == nullptr || (*numSurroundFormats != 0 &&
+            (surroundFormats == nullptr || surroundFormatsEnabled == nullptr))) {
         return BAD_VALUE;
     }
-    ALOGV("%s() numSurroundFormats %d surroundFormats %p surroundFormatsEnabled %p reported %d",
-            __func__, *numSurroundFormats, surroundFormats, surroundFormatsEnabled, reported);
+    ALOGV("%s() numSurroundFormats %d surroundFormats %p surroundFormatsEnabled %p",
+            __func__, *numSurroundFormats, surroundFormats, surroundFormatsEnabled);
 
     size_t formatsWritten = 0;
     size_t formatsMax = *numSurroundFormats;
-    std::unordered_set<audio_format_t> formats; // Uses primary surround formats only
-    if (reported) {
-        // Return formats from all device profiles that have already been resolved by
-        // checkOutputsForDevice().
-        for (size_t i = 0; i < mAvailableOutputDevices.size(); i++) {
-            sp<DeviceDescriptor> device = mAvailableOutputDevices[i];
-            audio_devices_t deviceType = device->type();
-            // Enabling/disabling formats are applied to only HDMI devices. So, this function
-            // returns formats reported by HDMI devices.
-            if (deviceType != AUDIO_DEVICE_OUT_HDMI) {
-                continue;
-            }
-            // Formats reported by sink devices
-            std::unordered_set<audio_format_t> formatset;
-            if (auto it = mReportedFormatsMap.find(device); it != mReportedFormatsMap.end()) {
-                formatset.insert(it->second.begin(), it->second.end());
-            }
 
-            // Formats hard-coded in the in policy configuration file (if any).
-            FormatVector encodedFormats = device->encodedFormats();
-            formatset.insert(encodedFormats.begin(), encodedFormats.end());
-            // Filter the formats which are supported by the vendor hardware.
-            for (auto it = formatset.begin(); it != formatset.end(); ++it) {
-                if (mConfig.getSurroundFormats().count(*it) != 0) {
-                    formats.insert(*it);
-                } else {
-                    for (const auto& pair : mConfig.getSurroundFormats()) {
-                        if (pair.second.count(*it) != 0) {
-                            formats.insert(pair.first);
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-    } else {
-        for (const auto& pair : mConfig.getSurroundFormats()) {
-            formats.insert(pair.first);
-        }
-    }
-    *numSurroundFormats = formats.size();
+    *numSurroundFormats = mConfig.getSurroundFormats().size();
     audio_policy_forced_cfg_t forceUse = mEngine->getForceUse(
             AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND);
-    for (const auto& format: formats) {
+    for (const auto& format: mConfig.getSurroundFormats()) {
         if (formatsWritten < formatsMax) {
-            surroundFormats[formatsWritten] = format;
+            surroundFormats[formatsWritten] = format.first;
             bool formatEnabled = true;
             switch (forceUse) {
                 case AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL:
-                    formatEnabled = mManualSurroundFormats.count(format) != 0;
+                    formatEnabled = mManualSurroundFormats.count(format.first) != 0;
                     break;
                 case AUDIO_POLICY_FORCE_ENCODED_SURROUND_NEVER:
                     formatEnabled = false;
@@ -4574,6 +4564,60 @@
     return NO_ERROR;
 }
 
+status_t AudioPolicyManager::getReportedSurroundFormats(unsigned int *numSurroundFormats,
+                                                        audio_format_t *surroundFormats) {
+    if (numSurroundFormats == nullptr || (*numSurroundFormats != 0 && surroundFormats == nullptr)) {
+        return BAD_VALUE;
+    }
+    ALOGV("%s() numSurroundFormats %d surroundFormats %p",
+            __func__, *numSurroundFormats, surroundFormats);
+
+    size_t formatsWritten = 0;
+    size_t formatsMax = *numSurroundFormats;
+    std::unordered_set<audio_format_t> formats; // Uses primary surround formats only
+
+    // Return formats from all device profiles that have already been resolved by
+    // checkOutputsForDevice().
+    for (size_t i = 0; i < mAvailableOutputDevices.size(); i++) {
+        sp<DeviceDescriptor> device = mAvailableOutputDevices[i];
+        audio_devices_t deviceType = device->type();
+        // Enabling/disabling formats are applied to only HDMI devices. So, this function
+        // returns formats reported by HDMI devices.
+        if (deviceType != AUDIO_DEVICE_OUT_HDMI) {
+            continue;
+        }
+        // Formats reported by sink devices
+        std::unordered_set<audio_format_t> formatset;
+        if (auto it = mReportedFormatsMap.find(device); it != mReportedFormatsMap.end()) {
+            formatset.insert(it->second.begin(), it->second.end());
+        }
+
+        // Formats hard-coded in the in policy configuration file (if any).
+        FormatVector encodedFormats = device->encodedFormats();
+        formatset.insert(encodedFormats.begin(), encodedFormats.end());
+        // Filter the formats which are supported by the vendor hardware.
+        for (auto it = formatset.begin(); it != formatset.end(); ++it) {
+            if (mConfig.getSurroundFormats().count(*it) != 0) {
+                formats.insert(*it);
+            } else {
+                for (const auto& pair : mConfig.getSurroundFormats()) {
+                    if (pair.second.count(*it) != 0) {
+                        formats.insert(pair.first);
+                        break;
+                    }
+                }
+            }
+        }
+    }
+    *numSurroundFormats = formats.size();
+    for (const auto& format: formats) {
+        if (formatsWritten < formatsMax) {
+            surroundFormats[formatsWritten++] = format;
+        }
+    }
+    return NO_ERROR;
+}
+
 status_t AudioPolicyManager::setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled)
 {
     ALOGV("%s() format 0x%X enabled %d", __func__, audioFormat, enabled);
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index bf73f75..f5dd20c 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -116,7 +116,7 @@
                                   audio_io_handle_t *output,
                                   audio_session_t session,
                                   audio_stream_type_t *stream,
-                                  uid_t uid,
+                                  const media::permission::Identity& identity,
                                   const audio_config_t *config,
                                   audio_output_flags_t *flags,
                                   audio_port_handle_t *selectedDeviceId,
@@ -130,7 +130,7 @@
                                          audio_io_handle_t *input,
                                          audio_unique_id_t riid,
                                          audio_session_t session,
-                                         uid_t uid,
+                                         const media::permission::Identity& identity,
                                          const audio_config_base_t *config,
                                          audio_input_flags_t flags,
                                          audio_port_handle_t *selectedDeviceId,
@@ -313,8 +313,9 @@
 
         virtual status_t getSurroundFormats(unsigned int *numSurroundFormats,
                                             audio_format_t *surroundFormats,
-                                            bool *surroundFormatsEnabled,
-                                            bool reported);
+                                            bool *surroundFormatsEnabled);
+        virtual status_t getReportedSurroundFormats(unsigned int *numSurroundFormats,
+                                                    audio_format_t *surroundFormats);
         virtual status_t setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled);
 
         virtual status_t getHwOffloadEncodingFormatsSupportedForA2DP(
@@ -735,9 +736,22 @@
                     String8(devices.itemAt(0)->address().c_str()) : String8("");
         }
 
-        uint32_t updateCallRouting(const DeviceVector &rxDevices, uint32_t delayMs = 0);
+        status_t updateCallRouting(
+                bool fromCache, uint32_t delayMs = 0, uint32_t *waitMs = nullptr);
+        status_t updateCallRoutingInternal(
+                const DeviceVector &rxDevices, uint32_t delayMs, uint32_t *waitMs);
         sp<AudioPatch> createTelephonyPatch(bool isRx, const sp<DeviceDescriptor> &device,
                                             uint32_t delayMs);
+        /**
+         * @brief selectBestRxSinkDevicesForCall: if the primary module host both Telephony Rx/Tx
+         * devices, and it declares also supporting a HW bridge between the Telephony Rx and the
+         * given sink device for Voice Call audio attributes, select this device in prio.
+         * Otherwise, getNewOutputDevices() is called on the primary output to select sink device.
+         * @param fromCache true to prevent engine reconsidering all product strategies and retrieve
+         * from engine cache.
+         * @return vector of devices, empty if none is found.
+         */
+        DeviceVector selectBestRxSinkDevicesForCall(bool fromCache);
         bool isDeviceOfModule(const sp<DeviceDescriptor>& devDesc, const char *moduleId) const;
 
         status_t startSource(const sp<SwAudioOutputDescriptor>& outputDesc,
diff --git a/services/audiopolicy/service/Android.bp b/services/audiopolicy/service/Android.bp
index 439f9f0..d5ba756 100644
--- a/services/audiopolicy/service/Android.bp
+++ b/services/audiopolicy/service/Android.bp
@@ -44,10 +44,12 @@
         "audiopolicy-aidl-cpp",
         "audiopolicy-types-aidl-cpp",
         "capture_state_listener-aidl-cpp",
+        "media_permission-aidl-cpp",
     ],
 
     static_libs: [
         "libaudiopolicycomponents",
+        "media_permission-aidl-cpp",
     ],
 
     header_libs: [
@@ -66,5 +68,6 @@
 
     export_shared_lib_headers: [
         "libsensorprivacy",
+        "media_permission-aidl-cpp",
     ],
 }
diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp
index b738633..8426a77 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.cpp
+++ b/services/audiopolicy/service/AudioPolicyEffects.cpp
@@ -35,6 +35,8 @@
 
 namespace android {
 
+using media::permission::Identity;
+
 // ----------------------------------------------------------------------------
 // AudioPolicyEffects Implementation
 // ----------------------------------------------------------------------------
@@ -121,7 +123,9 @@
         Vector <EffectDesc *> effects = mInputSources.valueAt(index)->mEffects;
         for (size_t i = 0; i < effects.size(); i++) {
             EffectDesc *effect = effects[i];
-            sp<AudioEffect> fx = new AudioEffect(String16("android"));
+            Identity identity;
+            identity.packageName = "android";
+            sp<AudioEffect> fx = new AudioEffect(identity);
             fx->set(NULL, &effect->mUuid, -1, 0, 0, audioSession, input);
             status_t status = fx->initCheck();
             if (status != NO_ERROR && status != ALREADY_EXISTS) {
@@ -270,7 +274,9 @@
         Vector <EffectDesc *> effects = mOutputStreams.valueAt(index)->mEffects;
         for (size_t i = 0; i < effects.size(); i++) {
             EffectDesc *effect = effects[i];
-            sp<AudioEffect> fx = new AudioEffect(String16("android"));
+            Identity identity;
+            identity.packageName = "android";
+            sp<AudioEffect> fx = new AudioEffect(identity);
             fx->set(NULL, &effect->mUuid, 0, 0, 0, audioSession, output);
             status_t status = fx->initCheck();
             if (status != NO_ERROR && status != ALREADY_EXISTS) {
@@ -970,7 +976,9 @@
     for (const auto& deviceEffectsIter : mDeviceEffects) {
         const auto& deviceEffects =  deviceEffectsIter.second;
         for (const auto& effectDesc : deviceEffects->mEffectDescriptors->mEffects) {
-            auto fx = std::make_unique<AudioEffect>(String16("android"));
+            Identity identity;
+            identity.packageName = "android";
+            sp<AudioEffect> fx = new AudioEffect(identity);
             fx->set(EFFECT_UUID_NULL, &effectDesc->mUuid, 0, nullptr,
                     nullptr, AUDIO_SESSION_DEVICE, AUDIO_IO_HANDLE_NONE,
                     AudioDeviceTypeAddr{deviceEffects->getDeviceType(),
@@ -987,7 +995,7 @@
             ALOGV("%s(): create Fx %s added on port type=%d address=%s", __func__,
                   effectDesc->mName, deviceEffects->getDeviceType(),
                   deviceEffects->getDeviceAddress().c_str());
-            deviceEffects->mEffects.push_back(std::move(fx));
+            deviceEffects->mEffects.push_back(fx);
         }
     }
 }
diff --git a/services/audiopolicy/service/AudioPolicyEffects.h b/services/audiopolicy/service/AudioPolicyEffects.h
index 81c728d..13d5d0c 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.h
+++ b/services/audiopolicy/service/AudioPolicyEffects.h
@@ -207,7 +207,7 @@
             mDeviceType(device), mDeviceAddress(address) {}
         /*virtual*/ ~DeviceEffects() = default;
 
-        std::vector<std::unique_ptr<AudioEffect>> mEffects;
+        std::vector< sp<AudioEffect> > mEffects;
         audio_devices_t getDeviceType() const { return mDeviceType; }
         std::string getDeviceAddress() const { return mDeviceAddress; }
         const std::unique_ptr<EffectDescVector> mEffectDescriptors;
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 07122cc..639fa58 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -25,6 +25,7 @@
 #include <media/MediaMetricsItem.h>
 #include <media/PolicyAidlConversion.h>
 #include <utils/Log.h>
+#include <android/media/permission/Identity.h>
 
 #define VALUE_OR_RETURN_BINDER_STATUS(x) \
     ({ auto _tmp = (x); \
@@ -42,6 +43,7 @@
 namespace android {
 using binder::Status;
 using aidl_utils::binderStatusFromStatusT;
+using media::permission::Identity;
 
 const std::vector<audio_usage_t>& SYSTEM_USAGES = {
     AUDIO_USAGE_CALL_ASSISTANT,
@@ -62,15 +64,15 @@
 }
 
 status_t AudioPolicyService::validateUsage(audio_usage_t usage) {
-     return validateUsage(usage, IPCThreadState::self()->getCallingPid(),
-        IPCThreadState::self()->getCallingUid());
+     return validateUsage(usage, getCallingIdentity());
 }
 
-status_t AudioPolicyService::validateUsage(audio_usage_t usage, pid_t pid, uid_t uid) {
+status_t AudioPolicyService::validateUsage(audio_usage_t usage, const Identity& identity) {
     if (isSystemUsage(usage)) {
         if (isSupportedSystemUsage(usage)) {
-            if (!modifyAudioRoutingAllowed(pid, uid)) {
-                ALOGE("permission denied: modify audio routing not allowed for uid %d", uid);
+            if (!modifyAudioRoutingAllowed(identity)) {
+                ALOGE(("permission denied: modify audio routing not allowed "
+                       "for identity %s"), identity.toString().c_str());
                 return PERMISSION_DENIED;
             }
         } else {
@@ -276,8 +278,7 @@
 
 Status AudioPolicyService::getOutputForAttr(const media::AudioAttributesInternal& attrAidl,
                                             int32_t sessionAidl,
-                                            int32_t pidAidl,
-                                            int32_t uidAidl,
+                                            const Identity& identity,
                                             const media::AudioConfig& configAidl,
                                             int32_t flagsAidl,
                                             int32_t selectedDeviceIdAidl,
@@ -288,8 +289,6 @@
     audio_session_t session = VALUE_OR_RETURN_BINDER_STATUS(
             aidl2legacy_int32_t_audio_session_t(sessionAidl));
     audio_stream_type_t stream = AUDIO_STREAM_DEFAULT;
-    pid_t pid = VALUE_OR_RETURN_BINDER_STATUS(aidl2legacy_int32_t_pid_t(pidAidl));
-    uid_t uid = VALUE_OR_RETURN_BINDER_STATUS(aidl2legacy_int32_t_uid_t(uidAidl));
     audio_config_t config = VALUE_OR_RETURN_BINDER_STATUS(
             aidl2legacy_AudioConfig_audio_config_t(configAidl));
     audio_output_flags_t flags = VALUE_OR_RETURN_BINDER_STATUS(
@@ -307,22 +306,28 @@
 
     RETURN_IF_BINDER_ERROR(
             binderStatusFromStatusT(AudioValidator::validateAudioAttributes(attr, "68953950")));
-    RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(validateUsage(attr.usage, pid, uid)));
+    RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(validateUsage(attr.usage, identity)));
 
     ALOGV("%s()", __func__);
     Mutex::Autolock _l(mLock);
 
+    // TODO b/182392553: refactor or remove
+    Identity adjIdentity = identity;
     const uid_t callingUid = IPCThreadState::self()->getCallingUid();
-    if (!isAudioServerOrMediaServerUid(callingUid) || uid == (uid_t)-1) {
-        ALOGW_IF(uid != (uid_t)-1 && uid != callingUid,
-                "%s uid %d tried to pass itself off as %d", __func__, callingUid, uid);
-        uid = callingUid;
+    if (!isAudioServerOrMediaServerUid(callingUid) || identity.uid == -1) {
+        int32_t callingUidAidl = VALUE_OR_RETURN_BINDER_STATUS(
+            legacy2aidl_uid_t_int32_t(callingUid));
+        ALOGW_IF(identity.uid != -1 && identity.uid != callingUidAidl,
+                "%s uid %d tried to pass itself off as %d", __func__,
+                callingUidAidl, identity.uid);
+        adjIdentity.uid = callingUidAidl;
     }
-    if (!mPackageManager.allowPlaybackCapture(uid)) {
+    if (!mPackageManager.allowPlaybackCapture(VALUE_OR_RETURN_BINDER_STATUS(
+        aidl2legacy_int32_t_uid_t(adjIdentity.uid)))) {
         attr.flags = static_cast<audio_flags_mask_t>(attr.flags | AUDIO_FLAG_NO_MEDIA_PROJECTION);
     }
     if (((attr.flags & (AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY|AUDIO_FLAG_BYPASS_MUTE)) != 0)
-            && !bypassInterruptionPolicyAllowed(pid, uid)) {
+            && !bypassInterruptionPolicyAllowed(identity)) {
         attr.flags = static_cast<audio_flags_mask_t>(
                 attr.flags & ~(AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY|AUDIO_FLAG_BYPASS_MUTE));
     }
@@ -330,7 +335,7 @@
     AudioPolicyInterface::output_type_t outputType;
     status_t result = mAudioPolicyManager->getOutputForAttr(&attr, &output, session,
                                                             &stream,
-                                                            uid,
+                                                            adjIdentity,
                                                             &config,
                                                             &flags, &selectedDeviceId, &portId,
                                                             &secondaryOutputs,
@@ -343,16 +348,16 @@
         case AudioPolicyInterface::API_OUTPUT_LEGACY:
             break;
         case AudioPolicyInterface::API_OUTPUT_TELEPHONY_TX:
-            if (!modifyPhoneStateAllowed(pid, uid)) {
+            if (!modifyPhoneStateAllowed(adjIdentity)) {
                 ALOGE("%s() permission denied: modify phone state not allowed for uid %d",
-                    __func__, uid);
+                    __func__, adjIdentity.uid);
                 result = PERMISSION_DENIED;
             }
             break;
         case AudioPolicyInterface::API_OUT_MIX_PLAYBACK:
-            if (!modifyAudioRoutingAllowed(pid, uid)) {
+            if (!modifyAudioRoutingAllowed(adjIdentity)) {
                 ALOGE("%s() permission denied: modify audio routing not allowed for uid %d",
-                    __func__, uid);
+                    __func__, adjIdentity.uid);
                 result = PERMISSION_DENIED;
             }
             break;
@@ -365,8 +370,8 @@
 
     if (result == NO_ERROR) {
         sp<AudioPlaybackClient> client =
-                new AudioPlaybackClient(attr, output, uid, pid, session, portId, selectedDeviceId,
-                                        stream);
+                new AudioPlaybackClient(attr, output, adjIdentity, session,
+                    portId, selectedDeviceId, stream);
         mAudioPlaybackClients.add(portId, client);
 
         _aidl_return->output = VALUE_OR_RETURN_BINDER_STATUS(
@@ -502,9 +507,7 @@
                                            int32_t inputAidl,
                                            int32_t riidAidl,
                                            int32_t sessionAidl,
-                                           int32_t pidAidl,
-                                           int32_t uidAidl,
-                                           const std::string& opPackageNameAidl,
+                                           const Identity& identity,
                                            const media::AudioConfigBase& configAidl,
                                            int32_t flagsAidl,
                                            int32_t selectedDeviceIdAidl,
@@ -517,10 +520,6 @@
             aidl2legacy_int32_t_audio_unique_id_t(riidAidl));
     audio_session_t session = VALUE_OR_RETURN_BINDER_STATUS(
             aidl2legacy_int32_t_audio_session_t(sessionAidl));
-    pid_t pid = VALUE_OR_RETURN_BINDER_STATUS(aidl2legacy_int32_t_pid_t(pidAidl));
-    uid_t uid = VALUE_OR_RETURN_BINDER_STATUS(aidl2legacy_int32_t_uid_t(uidAidl));
-    String16 opPackageName = VALUE_OR_RETURN_BINDER_STATUS(
-            aidl2legacy_string_view_String16(opPackageNameAidl));
     audio_config_base_t config = VALUE_OR_RETURN_BINDER_STATUS(
             aidl2legacy_AudioConfigBase_audio_config_base_t(configAidl));
     audio_input_flags_t flags = VALUE_OR_RETURN_BINDER_STATUS(
@@ -536,7 +535,6 @@
 
     RETURN_IF_BINDER_ERROR(
             binderStatusFromStatusT(AudioValidator::validateAudioAttributes(attr, "68953950")));
-    RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(validateUsage(attr.usage, pid, uid)));
 
     audio_source_t inputSource = attr.source;
     if (inputSource == AUDIO_SOURCE_DEFAULT) {
@@ -552,34 +550,42 @@
         return binderStatusFromStatusT(BAD_VALUE);
     }
 
-    bool updatePid = (pid == -1);
-    const uid_t callingUid = IPCThreadState::self()->getCallingUid();
+    // Make sure identity represents the current caller
+    Identity adjIdentity = identity;
+    // TODO b/182392553: refactor or remove
+    bool updatePid = (identity.pid == -1);
+    const uid_t callingUid =IPCThreadState::self()->getCallingUid();
+    const uid_t currentUid = VALUE_OR_RETURN_BINDER_STATUS(aidl2legacy_int32_t_uid_t(identity.uid));
     if (!isAudioServerOrMediaServerUid(callingUid)) {
-        ALOGW_IF(uid != (uid_t)-1 && uid != callingUid,
-                "%s uid %d tried to pass itself off as %d", __FUNCTION__, callingUid, uid);
-        uid = callingUid;
+        ALOGW_IF(currentUid != (uid_t)-1 && currentUid != callingUid,
+                "%s uid %d tried to pass itself off as %d", __FUNCTION__, callingUid,
+                currentUid);
+        adjIdentity.uid = VALUE_OR_RETURN_BINDER_STATUS(legacy2aidl_uid_t_int32_t(callingUid));
         updatePid = true;
     }
 
     if (updatePid) {
-        const pid_t callingPid = IPCThreadState::self()->getCallingPid();
-        ALOGW_IF(pid != (pid_t)-1 && pid != callingPid,
+        const int32_t callingPid = VALUE_OR_RETURN_BINDER_STATUS(legacy2aidl_pid_t_int32_t(
+            IPCThreadState::self()->getCallingPid()));
+        ALOGW_IF(identity.pid != -1 && identity.pid != callingPid,
                  "%s uid %d pid %d tried to pass itself off as pid %d",
-                 __func__, callingUid, callingPid, pid);
-        pid = callingPid;
+                 __func__, adjIdentity.uid, callingPid, identity.pid);
+        adjIdentity.pid = callingPid;
     }
 
+    RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(validateUsage(attr.usage, adjIdentity)));
+
     // check calling permissions.
     // Capturing from FM_TUNER source is controlled by captureTunerAudioInputAllowed() and
     // captureAudioOutputAllowed() (deprecated) as this does not affect users privacy
     // as does capturing from an actual microphone.
-    if (!(recordingAllowed(opPackageName, pid, uid) || attr.source == AUDIO_SOURCE_FM_TUNER)) {
-        ALOGE("%s permission denied: recording not allowed for uid %d pid %d",
-                __func__, uid, pid);
+    if (!(recordingAllowed(adjIdentity) || attr.source == AUDIO_SOURCE_FM_TUNER)) {
+        ALOGE("%s permission denied: recording not allowed for %s",
+                __func__, adjIdentity.toString().c_str());
         return binderStatusFromStatusT(PERMISSION_DENIED);
     }
 
-    bool canCaptureOutput = captureAudioOutputAllowed(pid, uid);
+    bool canCaptureOutput = captureAudioOutputAllowed(adjIdentity);
     if ((inputSource == AUDIO_SOURCE_VOICE_UPLINK ||
         inputSource == AUDIO_SOURCE_VOICE_DOWNLINK ||
         inputSource == AUDIO_SOURCE_VOICE_CALL ||
@@ -589,12 +595,12 @@
     }
 
     if (inputSource == AUDIO_SOURCE_FM_TUNER
-        && !captureTunerAudioInputAllowed(pid, uid)
+        && !captureTunerAudioInputAllowed(adjIdentity)
         && !canCaptureOutput) {
         return binderStatusFromStatusT(PERMISSION_DENIED);
     }
 
-    bool canCaptureHotword = captureHotwordAllowed(opPackageName, pid, uid);
+    bool canCaptureHotword = captureHotwordAllowed(adjIdentity);
     if ((inputSource == AUDIO_SOURCE_HOTWORD) && !canCaptureHotword) {
         return binderStatusFromStatusT(PERMISSION_DENIED);
     }
@@ -602,7 +608,7 @@
     if (((flags & AUDIO_INPUT_FLAG_HW_HOTWORD) != 0)
             && !canCaptureHotword) {
         ALOGE("%s: permission denied: hotword mode not allowed"
-              " for uid %d pid %d", __func__, uid, pid);
+              " for uid %d pid %d", __func__, adjIdentity.uid, adjIdentity.pid);
         return binderStatusFromStatusT(PERMISSION_DENIED);
     }
 
@@ -615,10 +621,11 @@
         {
             AutoCallerClear acc;
             // the audio_in_acoustics_t parameter is ignored by get_input()
-            status = mAudioPolicyManager->getInputForAttr(&attr, &input, riid, session, uid,
-                                                          &config,
+            status = mAudioPolicyManager->getInputForAttr(&attr, &input, riid, session,
+                                                          adjIdentity, &config,
                                                           flags, &selectedDeviceId,
                                                           &inputType, &portId);
+
         }
         audioPolicyEffects = mAudioPolicyEffects;
 
@@ -639,7 +646,7 @@
                 }
                 break;
             case AudioPolicyInterface::API_INPUT_MIX_EXT_POLICY_REROUTE:
-                if (!modifyAudioRoutingAllowed(pid, uid)) {
+                if (!modifyAudioRoutingAllowed(adjIdentity)) {
                     ALOGE("getInputForAttr() permission denied: modify audio routing not allowed");
                     status = PERMISSION_DENIED;
                 }
@@ -659,8 +666,8 @@
             return binderStatusFromStatusT(status);
         }
 
-        sp<AudioRecordClient> client = new AudioRecordClient(attr, input, uid, pid, session, portId,
-                                                             selectedDeviceId, opPackageName,
+        sp<AudioRecordClient> client = new AudioRecordClient(attr, input, session, portId,
+                                                             selectedDeviceId, adjIdentity,
                                                              canCaptureOutput, canCaptureHotword);
         mAudioRecordClients.add(portId, client);
     }
@@ -711,12 +718,15 @@
         client = mAudioRecordClients.valueAt(index);
     }
 
+    std::stringstream msg;
+    msg << "Audio recording on session " << client->session;
+
     // check calling permissions
-    if (!(startRecording(client->opPackageName, client->pid, client->uid,
-            client->attributes.source)
+    if (!(startRecording(client->identity, String16(msg.str().c_str()),
+        client->attributes.source)
             || client->attributes.source == AUDIO_SOURCE_FM_TUNER)) {
-        ALOGE("%s permission denied: recording not allowed for uid %d pid %d",
-                __func__, client->uid, client->pid);
+        ALOGE("%s permission denied: recording not allowed for identity %s",
+                __func__, client->identity.toString().c_str());
         return binderStatusFromStatusT(PERMISSION_DENIED);
     }
 
@@ -760,11 +770,13 @@
             item->setCString(kAudioPolicyRqstSrc,
                              toString(client->attributes.source).c_str());
             item->setInt32(kAudioPolicyRqstSession, client->session);
-            if (client->opPackageName.size() != 0) {
+            if (client->identity.packageName.has_value() &&
+                client->identity.packageName.value().size() != 0) {
                 item->setCString(kAudioPolicyRqstPkg,
-                                 std::string(String8(client->opPackageName).string()).c_str());
+                    client->identity.packageName.value().c_str());
             } else {
-                item->setCString(kAudioPolicyRqstPkg, std::to_string(client->uid).c_str());
+                item->setCString(kAudioPolicyRqstPkg,
+                    std::to_string(client->identity.uid).c_str());
             }
             item->setCString(
                     kAudioPolicyRqstDevice, getDeviceTypeStrForPortId(client->deviceId).c_str());
@@ -780,12 +792,13 @@
                     item->setCString(kAudioPolicyActiveSrc,
                                      toString(other->attributes.source).c_str());
                     item->setInt32(kAudioPolicyActiveSession, other->session);
-                    if (other->opPackageName.size() != 0) {
+                    if (other->identity.packageName.has_value() &&
+                        other->identity.packageName.value().size() != 0) {
                         item->setCString(kAudioPolicyActivePkg,
-                             std::string(String8(other->opPackageName).string()).c_str());
+                            other->identity.packageName.value().c_str());
                     } else {
-                        item->setCString(kAudioPolicyRqstPkg,
-                                         std::to_string(other->uid).c_str());
+                        item->setCString(kAudioPolicyRqstPkg, std::to_string(
+                            other->identity.uid).c_str());
                     }
                     item->setCString(kAudioPolicyActiveDevice,
                                      getDeviceTypeStrForPortId(other->deviceId).c_str());
@@ -801,8 +814,7 @@
         client->active = false;
         client->startTimeNs = 0;
         updateUidStates_l();
-        finishRecording(client->opPackageName, client->uid,
-                        client->attributes.source);
+        finishRecording(client->identity, client->attributes.source);
     }
 
     return binderStatusFromStatusT(status);
@@ -831,8 +843,7 @@
     updateUidStates_l();
 
     // finish the recording app op
-    finishRecording(client->opPackageName, client->uid,
-                    client->attributes.source);
+    finishRecording(client->identity, client->attributes.source);
     AutoCallerClear acc;
     return binderStatusFromStatusT(mAudioPolicyManager->stopInput(portId));
 }
@@ -1629,15 +1640,15 @@
     bool needCaptureMediaOutput = std::any_of(mixes.begin(), mixes.end(), [](auto& mix) {
             return mix.mAllowPrivilegedMediaPlaybackCapture; });
 
-    const uid_t callingUid = IPCThreadState::self()->getCallingUid();
-    const pid_t callingPid = IPCThreadState::self()->getCallingPid();
+    const Identity identity = getCallingIdentity();
 
-    if (needCaptureMediaOutput && !captureMediaOutputAllowed(callingPid, callingUid)) {
+
+    if (needCaptureMediaOutput && !captureMediaOutputAllowed(identity)) {
         return binderStatusFromStatusT(PERMISSION_DENIED);
     }
 
     if (needCaptureVoiceCommunicationOutput &&
-        !captureVoiceCommunicationOutputAllowed(callingPid, callingUid)) {
+        !captureVoiceCommunicationOutputAllowed(identity)) {
         return binderStatusFromStatusT(PERMISSION_DENIED);
     }
 
@@ -1801,8 +1812,7 @@
     return Status::ok();
 }
 
-Status AudioPolicyService::getSurroundFormats(
-        bool reported, media::Int* count,
+Status AudioPolicyService::getSurroundFormats(media::Int* count,
         std::vector<media::audio::common::AudioFormat>* formats,
         std::vector<bool>* formatsEnabled) {
     unsigned int numSurroundFormats = VALUE_OR_RETURN_BINDER_STATUS(
@@ -1821,7 +1831,7 @@
     AutoCallerClear acc;
     RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
             mAudioPolicyManager->getSurroundFormats(&numSurroundFormats, surroundFormats.get(),
-                                                    surroundFormatsEnabled.get(), reported)));
+                                                    surroundFormatsEnabled.get())));
     numSurroundFormatsReq = std::min(numSurroundFormats, numSurroundFormatsReq);
     RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
             convertRange(surroundFormats.get(), surroundFormats.get() + numSurroundFormatsReq,
@@ -1834,6 +1844,32 @@
     return Status::ok();
 }
 
+Status AudioPolicyService::getReportedSurroundFormats(
+        media::Int* count, std::vector<media::audio::common::AudioFormat>* formats) {
+    unsigned int numSurroundFormats = VALUE_OR_RETURN_BINDER_STATUS(
+            convertIntegral<unsigned int>(count->value));
+    if (numSurroundFormats > MAX_ITEMS_PER_LIST) {
+        numSurroundFormats = MAX_ITEMS_PER_LIST;
+    }
+    unsigned int numSurroundFormatsReq = numSurroundFormats;
+    std::unique_ptr<audio_format_t[]>surroundFormats(new audio_format_t[numSurroundFormats]);
+
+    if (mAudioPolicyManager == NULL) {
+        return binderStatusFromStatusT(NO_INIT);
+    }
+    Mutex::Autolock _l(mLock);
+    AutoCallerClear acc;
+    RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+            mAudioPolicyManager->getReportedSurroundFormats(
+                    &numSurroundFormats, surroundFormats.get())));
+    numSurroundFormatsReq = std::min(numSurroundFormats, numSurroundFormatsReq);
+    RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+            convertRange(surroundFormats.get(), surroundFormats.get() + numSurroundFormatsReq,
+                         std::back_inserter(*formats), legacy2aidl_audio_format_t_AudioFormat)));
+    count->value = VALUE_OR_RETURN_BINDER_STATUS(convertIntegral<uint32_t>(numSurroundFormats));
+    return Status::ok();
+}
+
 Status AudioPolicyService::getHwOffloadEncodingFormatsSupportedForA2DP(
         std::vector<media::audio::common::AudioFormat>* _aidl_return) {
     std::vector<audio_format_t> formats;
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 90ad81e..4ffa9cc 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -595,23 +595,24 @@
 
     for (size_t i =0; i < mAudioRecordClients.size(); i++) {
         sp<AudioRecordClient> current = mAudioRecordClients[i];
+        uid_t currentUid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(current->identity.uid));
         if (!current->active || (!isVirtualSource(current->attributes.source)
-                && isUserSensorPrivacyEnabledForUid(current->uid))) {
+                && isUserSensorPrivacyEnabledForUid(currentUid))) {
             continue;
         }
 
-        app_state_t appState = apmStatFromAmState(mUidPolicy->getUidState(current->uid));
+        app_state_t appState = apmStatFromAmState(mUidPolicy->getUidState(currentUid));
         // clients which app is in IDLE state are not eligible for top active or
         // latest active
         if (appState == APP_STATE_IDLE) {
             continue;
         }
 
-        bool isAccessibility = mUidPolicy->isA11yUid(current->uid);
+        bool isAccessibility = mUidPolicy->isA11yUid(currentUid);
         // Clients capturing for Accessibility services or virtual sources are not considered
         // for top or latest active to avoid masking regular clients started before
         if (!isAccessibility && !isVirtualSource(current->attributes.source)) {
-            bool isAssistant = mUidPolicy->isAssistantUid(current->uid);
+            bool isAssistant = mUidPolicy->isAssistantUid(currentUid);
             bool isPrivacySensitive =
                     (current->attributes.flags & AUDIO_FLAG_CAPTURE_PRIVATE) != 0;
 
@@ -639,9 +640,11 @@
                     // if audio mode is IN_COMMUNICATION, make sure the audio mode owner
                     // is marked latest sensitive active even if another app qualifies.
                     if (current->startTimeNs > latestSensitiveStartNs
-                            || (isInCommunication && current->uid == mPhoneStateOwnerUid)) {
+                            || (isInCommunication && currentUid == mPhoneStateOwnerUid)) {
                         if (!isInCommunication || latestSensitiveActiveOrComm == nullptr
-                                || latestSensitiveActiveOrComm->uid != mPhoneStateOwnerUid) {
+                                || VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(
+                                    latestSensitiveActiveOrComm->identity.uid))
+                                        != mPhoneStateOwnerUid) {
                             latestSensitiveActiveOrComm = current;
                             latestSensitiveStartNs = current->startTimeNs;
                         }
@@ -658,7 +661,7 @@
         if (current->attributes.source != AUDIO_SOURCE_HOTWORD) {
             onlyHotwordActive = false;
         }
-        if (current->uid == mPhoneStateOwnerUid) {
+        if (currentUid == mPhoneStateOwnerUid) {
             isPhoneStateOwnerActive = true;
         }
     }
@@ -674,7 +677,9 @@
     } else if (latestSensitiveActiveOrComm != nullptr) {
         // if audio mode is IN_COMMUNICATION, favor audio mode owner over an app with
         // foreground UI in case both are capturing with privacy sensitive flag.
-        if (isInCommunication && latestSensitiveActiveOrComm->uid == mPhoneStateOwnerUid) {
+        uid_t latestActiveUid = VALUE_OR_FATAL(
+            aidl2legacy_int32_t_uid_t(latestSensitiveActiveOrComm->identity.uid));
+        if (isInCommunication && latestActiveUid == mPhoneStateOwnerUid) {
             topSensitiveActive = latestSensitiveActiveOrComm;
             topSensitiveStartNs = latestSensitiveStartNs;
         }
@@ -692,20 +697,25 @@
 
     for (size_t i =0; i < mAudioRecordClients.size(); i++) {
         sp<AudioRecordClient> current = mAudioRecordClients[i];
+        uid_t currentUid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(
+            current->identity.uid));
         if (!current->active) {
             continue;
         }
 
         audio_source_t source = current->attributes.source;
-        bool isTopOrLatestActive = topActive == nullptr ? false : current->uid == topActive->uid;
-        bool isTopOrLatestSensitive = topSensitiveActive == nullptr ?
-                                 false : current->uid == topSensitiveActive->uid;
+        bool isTopOrLatestActive = topActive == nullptr ? false :
+            current->identity.uid == topActive->identity.uid;
+        bool isTopOrLatestSensitive = topSensitiveActive == nullptr ? false :
+            current->identity.uid == topSensitiveActive->identity.uid;
 
         auto canCaptureIfInCallOrCommunication = [&](const auto &recordClient) REQUIRES(mLock) {
+            uid_t recordUid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(
+                recordClient->identity.uid));
             bool canCaptureCall = recordClient->canCaptureOutput;
             bool canCaptureCommunication = recordClient->canCaptureOutput
                 || !isPhoneStateOwnerActive
-                || recordClient->uid == mPhoneStateOwnerUid;
+                || recordUid == mPhoneStateOwnerUid;
             return !(isInCall && !canCaptureCall)
                 && !(isInCommunication && !canCaptureCommunication);
         };
@@ -724,10 +734,10 @@
         if (isVirtualSource(source)) {
             // Allow capture for virtual (remote submix, call audio TX or RX...) sources
             allowCapture = true;
-        } else if (isUserSensorPrivacyEnabledForUid(current->uid)) {
+        } else if (isUserSensorPrivacyEnabledForUid(currentUid)) {
             // If sensor privacy is enabled, don't allow capture
             allowCapture = false;
-        } else if (mUidPolicy->isAssistantUid(current->uid)) {
+        } else if (mUidPolicy->isAssistantUid(currentUid)) {
             // For assistant allow capture if:
             //     An accessibility service is on TOP or a RTT call is active
             //            AND the source is VOICE_RECOGNITION or HOTWORD
@@ -747,7 +757,7 @@
                     allowCapture = true;
                 }
             }
-        } else if (mUidPolicy->isA11yUid(current->uid)) {
+        } else if (mUidPolicy->isA11yUid(currentUid)) {
             // For accessibility service allow capture if:
             //     The assistant is not on TOP
             //         AND there is no active privacy sensitive capture or call
@@ -773,7 +783,7 @@
                     && canCaptureIfInCallOrCommunication(current)) {
                 allowCapture = true;
             }
-        } else if (mUidPolicy->isCurrentImeUid(current->uid)) {
+        } else if (mUidPolicy->isCurrentImeUid(currentUid)) {
             // For current InputMethodService allow capture if:
             //     A RTT call is active AND the source is VOICE_RECOGNITION
             if (rttCallActive && source == AUDIO_SOURCE_VOICE_RECOGNITION) {
@@ -781,7 +791,7 @@
             }
         }
         setAppState_l(current->portId,
-                      allowCapture ? apmStatFromAmState(mUidPolicy->getUidState(current->uid)) :
+                      allowCapture ? apmStatFromAmState(mUidPolicy->getUidState(currentUid)) :
                                 APP_STATE_IDLE);
     }
 }
@@ -925,6 +935,7 @@
         case TRANSACTION_registerPolicyMixes:
         case TRANSACTION_setMasterMono:
         case TRANSACTION_getSurroundFormats:
+        case TRANSACTION_getReportedSurroundFormats:
         case TRANSACTION_setSurroundFormatEnabled:
         case TRANSACTION_setAssistantUid:
         case TRANSACTION_setA11yServicesUids:
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index a11b2cc..145ba06 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -38,6 +38,8 @@
 #include "CaptureStateNotifier.h"
 #include <AudioPolicyInterface.h>
 #include <android/hardware/BnSensorPrivacyListener.h>
+#include <android/media/permission/Identity.h>
+
 #include <unordered_map>
 
 namespace android {
@@ -79,15 +81,16 @@
                                media::AudioPolicyForcedConfig* _aidl_return) override;
     binder::Status getOutput(media::AudioStreamType stream, int32_t* _aidl_return) override;
     binder::Status getOutputForAttr(const media::AudioAttributesInternal& attr, int32_t session,
-                                    int32_t pid, int32_t uid, const media::AudioConfig& config,
+                                    const media::permission::Identity &identity,
+                                    const media::AudioConfig& config,
                                     int32_t flags, int32_t selectedDeviceId,
                                     media::GetOutputForAttrResponse* _aidl_return) override;
     binder::Status startOutput(int32_t portId) override;
     binder::Status stopOutput(int32_t portId) override;
     binder::Status releaseOutput(int32_t portId) override;
     binder::Status getInputForAttr(const media::AudioAttributesInternal& attr, int32_t input,
-                                   int32_t riid, int32_t session, int32_t pid, int32_t uid,
-                                   const std::string& opPackageName,
+                                   int32_t riid, int32_t session,
+                                   const media::permission::Identity &identity,
                                    const media::AudioConfigBase& config, int32_t flags,
                                    int32_t selectedDeviceId,
                                    media::GetInputForAttrResponse* _aidl_return) override;
@@ -182,9 +185,11 @@
     binder::Status getMasterMono(bool* _aidl_return) override;
     binder::Status getStreamVolumeDB(media::AudioStreamType stream, int32_t index, int32_t device,
                                      float* _aidl_return) override;
-    binder::Status getSurroundFormats(bool reported, media::Int* count,
+    binder::Status getSurroundFormats(media::Int* count,
                                       std::vector<media::audio::common::AudioFormat>* formats,
                                       std::vector<bool>* formatsEnabled) override;
+    binder::Status getReportedSurroundFormats(
+            media::Int* count, std::vector<media::audio::common::AudioFormat>* formats) override;
     binder::Status getHwOffloadEncodingFormatsSupportedForA2DP(
             std::vector<media::audio::common::AudioFormat>* _aidl_return) override;
     binder::Status setSurroundFormatEnabled(media::audio::common::AudioFormat audioFormat,
@@ -339,7 +344,7 @@
 
     bool isSupportedSystemUsage(audio_usage_t usage);
     status_t validateUsage(audio_usage_t usage);
-    status_t validateUsage(audio_usage_t usage, pid_t pid, uid_t uid);
+    status_t validateUsage(audio_usage_t usage, const media::permission::Identity& identity);
 
     bool isUserSensorPrivacyEnabledForUid(uid_t uid);
 
@@ -784,18 +789,18 @@
     class AudioClient : public virtual RefBase {
     public:
                 AudioClient(const audio_attributes_t attributes,
-                            const audio_io_handle_t io, uid_t uid, pid_t pid,
+                            const audio_io_handle_t io,
+                            const media::permission::Identity& identity,
                             const audio_session_t session,  audio_port_handle_t portId,
                             const audio_port_handle_t deviceId) :
-                                attributes(attributes), io(io), uid(uid), pid(pid),
+                                attributes(attributes), io(io), identity(identity),
                                 session(session), portId(portId), deviceId(deviceId), active(false) {}
                 ~AudioClient() override = default;
 
 
         const audio_attributes_t attributes; // source, flags ...
         const audio_io_handle_t io;          // audio HAL stream IO handle
-        const uid_t uid;                     // client UID
-        const pid_t pid;                     // client PID
+        const media::permission::Identity& identity; //client identity
         const audio_session_t session;       // audio session ID
         const audio_port_handle_t portId;
         const audio_port_handle_t deviceId;  // selected input device port ID
@@ -808,16 +813,17 @@
     class AudioRecordClient : public AudioClient {
     public:
                 AudioRecordClient(const audio_attributes_t attributes,
-                          const audio_io_handle_t io, uid_t uid, pid_t pid,
+                          const audio_io_handle_t io,
                           const audio_session_t session, audio_port_handle_t portId,
-                          const audio_port_handle_t deviceId, const String16& opPackageName,
+                          const audio_port_handle_t deviceId,
+                          const media::permission::Identity& identity,
                           bool canCaptureOutput, bool canCaptureHotword) :
-                    AudioClient(attributes, io, uid, pid, session, portId, deviceId),
-                    opPackageName(opPackageName), startTimeNs(0),
+                    AudioClient(attributes, io, identity,
+                        session, portId, deviceId), identity(identity), startTimeNs(0),
                     canCaptureOutput(canCaptureOutput), canCaptureHotword(canCaptureHotword) {}
                 ~AudioRecordClient() override = default;
 
-        const String16 opPackageName;        // client package name
+        const media::permission::Identity identity;        // identity of client
         nsecs_t startTimeNs;
         const bool canCaptureOutput;
         const bool canCaptureHotword;
@@ -829,10 +835,11 @@
     class AudioPlaybackClient : public AudioClient {
     public:
                 AudioPlaybackClient(const audio_attributes_t attributes,
-                      const audio_io_handle_t io, uid_t uid, pid_t pid,
+                      const audio_io_handle_t io, media::permission::Identity identity,
                             const audio_session_t session, audio_port_handle_t portId,
                             audio_port_handle_t deviceId, audio_stream_type_t stream) :
-                    AudioClient(attributes, io, uid, pid, session, portId, deviceId), stream(stream) {}
+                    AudioClient(attributes, io, identity, session, portId,
+                        deviceId), stream(stream) {}
                 ~AudioPlaybackClient() override = default;
 
         const audio_stream_type_t stream;
diff --git a/services/audiopolicy/tests/Android.bp b/services/audiopolicy/tests/Android.bp
index d10fcb9..f480210 100644
--- a/services/audiopolicy/tests/Android.bp
+++ b/services/audiopolicy/tests/Android.bp
@@ -25,6 +25,7 @@
         "libmedia_helper",
         "libutils",
         "libxml2",
+        "media_permission-aidl-cpp",
     ],
 
     static_libs: [
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index a8ede90..8f12ecf 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -25,6 +25,7 @@
 #define LOG_TAG "APM_Test"
 #include <Serializer.h>
 #include <android-base/file.h>
+#include <android/media/permission/Identity.h>
 #include <media/AudioPolicy.h>
 #include <media/PatchBuilder.h>
 #include <media/RecordingActivityTracker.h>
@@ -39,6 +40,7 @@
 
 using namespace android;
 using testing::UnorderedElementsAre;
+using media::permission::Identity;
 
 TEST(AudioPolicyManagerTestInit, EngineFailure) {
     AudioPolicyTestClient client;
@@ -214,8 +216,11 @@
     if (!portId) portId = &localPortId;
     *portId = AUDIO_PORT_HANDLE_NONE;
     AudioPolicyInterface::output_type_t outputType;
+    // TODO b/182392769: use identity util
+    Identity i = Identity();
+    i.uid = 0;
     ASSERT_EQ(OK, mManager->getOutputForAttr(
-                    &attr, output, AUDIO_SESSION_NONE, &stream, 0 /*uid*/, &config, &flags,
+                    &attr, output, AUDIO_SESSION_NONE, &stream, i, &config, &flags,
                     selectedDeviceId, portId, {}, &outputType));
     ASSERT_NE(AUDIO_PORT_HANDLE_NONE, *portId);
     ASSERT_NE(AUDIO_IO_HANDLE_NONE, *output);
@@ -239,8 +244,11 @@
     if (!portId) portId = &localPortId;
     *portId = AUDIO_PORT_HANDLE_NONE;
     AudioPolicyInterface::input_type_t inputType;
+    // TODO b/182392769: use identity util
+    Identity i = Identity();
+    i.uid = 0;
     ASSERT_EQ(OK, mManager->getInputForAttr(
-            &attr, &input, riid, AUDIO_SESSION_NONE, 0 /*uid*/, &config, flags,
+            &attr, &input, riid, AUDIO_SESSION_NONE, i, &config, flags,
             selectedDeviceId, &inputType, portId));
     ASSERT_NE(AUDIO_PORT_HANDLE_NONE, *portId);
 }
@@ -797,7 +805,8 @@
 protected:
     void SetUp() override;
     std::string getConfigFile() override { return sTvConfig; }
-    std::map<audio_format_t, bool> getSurroundFormatsHelper(bool reported);
+    std::map<audio_format_t, bool> getSurroundFormatsHelper();
+    std::vector<audio_format_t> getReportedSurroundFormatsHelper();
     std::unordered_set<audio_format_t> getFormatsFromPorts();
     AudioPolicyManagerTestClient* getClient() override {
         return new AudioPolicyManagerTestClientForHdmi;
@@ -828,12 +837,12 @@
 }
 
 std::map<audio_format_t, bool>
-        AudioPolicyManagerTestForHdmi::getSurroundFormatsHelper(bool reported) {
+        AudioPolicyManagerTestForHdmi::getSurroundFormatsHelper() {
     unsigned int numSurroundFormats = 0;
     std::map<audio_format_t, bool> surroundFormatsMap;
     status_t ret = mManager->getSurroundFormats(
             &numSurroundFormats, nullptr /* surroundFormats */,
-            nullptr /* surroundFormatsEnabled */, reported);
+            nullptr /* surroundFormatsEnabled */);
     EXPECT_EQ(NO_ERROR, ret);
     if (ret != NO_ERROR) {
         return surroundFormatsMap;
@@ -843,7 +852,7 @@
     bool surroundFormatsEnabled[numSurroundFormats];
     memset(surroundFormatsEnabled, 0, sizeof(bool) * numSurroundFormats);
     ret = mManager->getSurroundFormats(
-            &numSurroundFormats, surroundFormats, surroundFormatsEnabled, reported);
+            &numSurroundFormats, surroundFormats, surroundFormatsEnabled);
     EXPECT_EQ(NO_ERROR, ret);
     if (ret != NO_ERROR) {
         return surroundFormatsMap;
@@ -854,6 +863,28 @@
     return surroundFormatsMap;
 }
 
+std::vector<audio_format_t> AudioPolicyManagerTestForHdmi::getReportedSurroundFormatsHelper() {
+    unsigned int numSurroundFormats = 0;
+    std::vector<audio_format_t>  surroundFormatsVector;
+    status_t ret = mManager->getReportedSurroundFormats(
+            &numSurroundFormats, nullptr /* surroundFormats */);
+    EXPECT_EQ(NO_ERROR, ret);
+    if (ret != NO_ERROR) {
+        return surroundFormatsVector;
+    }
+    audio_format_t surroundFormats[numSurroundFormats];
+    memset(surroundFormats, 0, sizeof(audio_format_t) * numSurroundFormats);
+    ret = mManager->getReportedSurroundFormats(&numSurroundFormats, surroundFormats);
+    EXPECT_EQ(NO_ERROR, ret);
+    if (ret != NO_ERROR) {
+        return surroundFormatsVector;
+    }
+    for (const auto &surroundFormat : surroundFormats) {
+        surroundFormatsVector.push_back(surroundFormat);
+    }
+    return surroundFormatsVector;
+}
+
 std::unordered_set<audio_format_t>
         AudioPolicyManagerTestForHdmi::getFormatsFromPorts() {
     uint32_t numPorts = 0;
@@ -884,7 +915,7 @@
 TEST_F(AudioPolicyManagerTestForHdmi, GetSurroundFormatsReturnsSupportedFormats) {
     mManager->setForceUse(
             AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND, AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS);
-    auto surroundFormats = getSurroundFormatsHelper(false /*reported*/);
+    auto surroundFormats = getSurroundFormatsHelper();
     ASSERT_EQ(1, surroundFormats.count(AUDIO_FORMAT_E_AC3));
 }
 
@@ -896,19 +927,19 @@
     status_t ret =
             mManager->setSurroundFormatEnabled(AUDIO_FORMAT_E_AC3, false /*enabled*/);
     ASSERT_EQ(NO_ERROR, ret);
-    auto surroundFormats = getSurroundFormatsHelper(false /*reported*/);
+    auto surroundFormats = getSurroundFormatsHelper();
     ASSERT_EQ(1, surroundFormats.count(AUDIO_FORMAT_E_AC3));
     ASSERT_FALSE(surroundFormats[AUDIO_FORMAT_E_AC3]);
 
     ret = mManager->setSurroundFormatEnabled(AUDIO_FORMAT_E_AC3, true /*enabled*/);
     ASSERT_EQ(NO_ERROR, ret);
-    surroundFormats = getSurroundFormatsHelper(false /*reported*/);
+    surroundFormats = getSurroundFormatsHelper();
     ASSERT_EQ(1, surroundFormats.count(AUDIO_FORMAT_E_AC3));
     ASSERT_TRUE(surroundFormats[AUDIO_FORMAT_E_AC3]);
 
     ret = mManager->setSurroundFormatEnabled(AUDIO_FORMAT_E_AC3, false /*enabled*/);
     ASSERT_EQ(NO_ERROR, ret);
-    surroundFormats = getSurroundFormatsHelper(false /*reported*/);
+    surroundFormats = getSurroundFormatsHelper();
     ASSERT_EQ(1, surroundFormats.count(AUDIO_FORMAT_E_AC3));
     ASSERT_FALSE(surroundFormats[AUDIO_FORMAT_E_AC3]);
 }
@@ -931,8 +962,8 @@
         GetReportedSurroundFormatsReturnsHdmiReportedFormats) {
     mManager->setForceUse(
             AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND, AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS);
-    auto surroundFormats = getSurroundFormatsHelper(true /*reported*/);
-    ASSERT_EQ(1, surroundFormats.count(AUDIO_FORMAT_E_AC3));
+    auto surroundFormats = getReportedSurroundFormatsHelper();
+    ASSERT_EQ(1, std::count(surroundFormats.begin(), surroundFormats.end(), AUDIO_FORMAT_E_AC3));
 }
 
 TEST_F(AudioPolicyManagerTestForHdmi,
@@ -942,13 +973,13 @@
 
     status_t ret = mManager->setSurroundFormatEnabled(AUDIO_FORMAT_E_AC3, false /*enabled*/);
     ASSERT_EQ(NO_ERROR, ret);
-    auto surroundFormats = getSurroundFormatsHelper(true /*reported*/);
-    ASSERT_EQ(1, surroundFormats.count(AUDIO_FORMAT_E_AC3));
+    auto surroundFormats = getReportedSurroundFormatsHelper();
+    ASSERT_EQ(1, std::count(surroundFormats.begin(), surroundFormats.end(), AUDIO_FORMAT_E_AC3));
 
     ret = mManager->setSurroundFormatEnabled(AUDIO_FORMAT_E_AC3, true /*enabled*/);
     ASSERT_EQ(NO_ERROR, ret);
-    surroundFormats = getSurroundFormatsHelper(true /*reported*/);
-    ASSERT_EQ(1, surroundFormats.count(AUDIO_FORMAT_E_AC3));
+    surroundFormats = getReportedSurroundFormatsHelper();
+    ASSERT_EQ(1, std::count(surroundFormats.begin(), surroundFormats.end(), AUDIO_FORMAT_E_AC3));
 }
 
 class AudioPolicyManagerTestDPNoRemoteSubmixModule : public AudioPolicyManagerTestDynamicPolicy {
diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp
index e9f95cb..a0b35a8 100644
--- a/services/camera/libcameraservice/Android.bp
+++ b/services/camera/libcameraservice/Android.bp
@@ -143,15 +143,19 @@
         "android.hardware.camera.provider@2.4",
         "android.hardware.camera.provider@2.5",
         "android.hardware.camera.provider@2.6",
+        "android.hardware.camera.provider@2.7",
         "android.hardware.camera.device@3.2",
         "android.hardware.camera.device@3.3",
         "android.hardware.camera.device@3.4",
         "android.hardware.camera.device@3.5",
-        "android.hardware.camera.device@3.6"
+        "android.hardware.camera.device@3.6",
+        "android.hardware.camera.device@3.7",
+        "media_permission-aidl-cpp",
     ],
 
     static_libs: [
         "libbinderthreadstateutils",
+        "media_permission-aidl-cpp",
     ],
 
     export_shared_lib_headers: [
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 1234dfd..706197e 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -805,6 +805,7 @@
         case CAMERA_DEVICE_API_VERSION_3_4:
         case CAMERA_DEVICE_API_VERSION_3_5:
         case CAMERA_DEVICE_API_VERSION_3_6:
+        case CAMERA_DEVICE_API_VERSION_3_7:
             if (effectiveApiLevel == API_1) { // Camera1 API route
                 sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
                 *client = new Camera2Client(cameraService, tmp, packageName, featureId,
@@ -2271,6 +2272,7 @@
         case CAMERA_DEVICE_API_VERSION_3_4:
         case CAMERA_DEVICE_API_VERSION_3_5:
         case CAMERA_DEVICE_API_VERSION_3_6:
+        case CAMERA_DEVICE_API_VERSION_3_7:
             ALOGV("%s: Camera id %s uses HAL3.2 or newer, supports api1/api2 directly",
                     __FUNCTION__, id.string());
             *isSupported = true;
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index e062c14..8164df0 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -29,7 +29,6 @@
 
 #include "Parameters.h"
 #include "system/camera.h"
-#include "hardware/camera_common.h"
 #include <android/hardware/ICamera.h>
 #include <media/MediaProfiles.h>
 #include <media/mediarecorder.h>
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
index 0701b6f..9fdc727 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
@@ -237,7 +237,7 @@
     if (mInputStreamId == NO_STREAM) {
         res = device->createInputStream(params.fastInfo.maxZslSize.width,
             params.fastInfo.maxZslSize.height, HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
-            &mInputStreamId);
+            /*isMultiResolution*/false, &mInputStreamId);
         if (res != OK) {
             ALOGE("%s: Camera %d: Can't create input stream: "
                     "%s (%d)", __FUNCTION__, client->getCameraId(),
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index d47014e..8cccbb1 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -584,7 +584,7 @@
         ALOGE("%s: %s", __FUNCTION__, msg.string());
         return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
     }
-    hardware::camera::device::V3_4::StreamConfiguration streamConfiguration;
+    hardware::camera::device::V3_7::StreamConfiguration streamConfiguration;
     bool earlyExit = false;
     metadataGetter getMetadata = [this](const String8 &id) {return mDevice->infoPhysical(id);};
     std::vector<std::string> physicalCameraIds;
@@ -738,6 +738,7 @@
     bool isShared = outputConfiguration.isShared();
     String8 physicalCameraId = String8(outputConfiguration.getPhysicalCameraId());
     bool deferredConsumerOnly = deferredConsumer && numBufferProducers == 0;
+    bool isMultiResolution = outputConfiguration.isMultiResolution();
 
     res = SessionConfigurationUtils::checkSurfaceType(numBufferProducers, deferredConsumer,
             outputConfiguration.getSurfaceType());
@@ -809,7 +810,7 @@
                 streamInfo.height, streamInfo.format,
                 static_cast<camera_stream_rotation_t>(outputConfiguration.getRotation()),
                 &streamId, physicalCameraId, &surfaceIds, outputConfiguration.getSurfaceSetID(),
-                isShared);
+                isShared, isMultiResolution);
         if (err == OK) {
             mCompositeStreamMap.add(IInterface::asBinder(surfaces[0]->getIGraphicBufferProducer()),
                     compositeStream);
@@ -819,7 +820,7 @@
                 streamInfo.height, streamInfo.format, streamInfo.dataSpace,
                 static_cast<camera_stream_rotation_t>(outputConfiguration.getRotation()),
                 &streamId, physicalCameraId, &surfaceIds, outputConfiguration.getSurfaceSetID(),
-                isShared);
+                isShared, isMultiResolution);
     }
 
     if (err != OK) {
@@ -888,7 +889,7 @@
             static_cast<camera_stream_rotation_t>(outputConfiguration.getRotation()),
             &streamId, physicalCameraId, &surfaceIds,
             outputConfiguration.getSurfaceSetID(), isShared,
-            consumerUsage);
+            outputConfiguration.isMultiResolution(), consumerUsage);
 
     if (err != OK) {
         res = STATUS_ERROR_FMT(CameraService::ERROR_INVALID_OPERATION,
@@ -943,12 +944,13 @@
 }
 
 binder::Status CameraDeviceClient::createInputStream(
-        int width, int height, int format,
+        int width, int height, int format, bool isMultiResolution,
         /*out*/
         int32_t* newStreamId) {
 
     ATRACE_CALL();
-    ALOGV("%s (w = %d, h = %d, f = 0x%x)", __FUNCTION__, width, height, format);
+    ALOGV("%s (w = %d, h = %d, f = 0x%x, isMultiResolution %d)", __FUNCTION__,
+            width, height, format, isMultiResolution);
 
     binder::Status res;
     if (!(res = checkPidStatus(__FUNCTION__)).isOk()) return res;
@@ -967,7 +969,7 @@
     }
 
     int streamId = -1;
-    status_t err = mDevice->createInputStream(width, height, format, &streamId);
+    status_t err = mDevice->createInputStream(width, height, format, isMultiResolution, &streamId);
     if (err == OK) {
         mInputStream.configured = true;
         mInputStream.width = width;
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 5588285..9f7a4af 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -115,6 +115,7 @@
 
     // Create an input stream of width, height, and format.
     virtual binder::Status createInputStream(int width, int height, int format,
+            bool isMultiResolution,
             /*out*/
             int32_t* newStreamId = NULL) override;
 
diff --git a/services/camera/libcameraservice/api2/CompositeStream.cpp b/services/camera/libcameraservice/api2/CompositeStream.cpp
index 2f8ca6b..515b7f2 100644
--- a/services/camera/libcameraservice/api2/CompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/CompositeStream.cpp
@@ -47,7 +47,7 @@
 status_t CompositeStream::createStream(const std::vector<sp<Surface>>& consumers,
         bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
         camera_stream_rotation_t rotation, int * id, const String8& physicalCameraId,
-        std::vector<int> * surfaceIds, int streamSetId, bool isShared) {
+        std::vector<int> * surfaceIds, int streamSetId, bool isShared, bool isMultiResolution) {
     if (hasDeferredConsumer) {
         ALOGE("%s: Deferred consumers not supported in case of composite streams!",
                 __FUNCTION__);
@@ -66,6 +66,12 @@
         return BAD_VALUE;
     }
 
+    if (isMultiResolution) {
+        ALOGE("%s: Multi-resolution output not supported in case of composite streams!",
+                __FUNCTION__);
+        return BAD_VALUE;
+    }
+
     return createInternalStreams(consumers, hasDeferredConsumer, width, height, format, rotation, id,
             physicalCameraId, surfaceIds, streamSetId, isShared);
 }
diff --git a/services/camera/libcameraservice/api2/CompositeStream.h b/services/camera/libcameraservice/api2/CompositeStream.h
index 2a934df..1bf137a 100644
--- a/services/camera/libcameraservice/api2/CompositeStream.h
+++ b/services/camera/libcameraservice/api2/CompositeStream.h
@@ -44,7 +44,7 @@
     status_t createStream(const std::vector<sp<Surface>>& consumers,
             bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
             camera_stream_rotation_t rotation, int *id, const String8& physicalCameraId,
-            std::vector<int> *surfaceIds, int streamSetId, bool isShared);
+            std::vector<int> *surfaceIds, int streamSetId, bool isShared, bool isMultiResolution);
 
     status_t deleteStream();
 
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 1be46d6..5acbb99 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -166,7 +166,8 @@
             const String8& physicalCameraId,
             std::vector<int> *surfaceIds = nullptr,
             int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
-            bool isShared = false, uint64_t consumerUsage = 0) = 0;
+            bool isShared = false, bool isMultiResolution = false,
+            uint64_t consumerUsage = 0) = 0;
 
     /**
      * Create an output stream of the requested size, format, rotation and
@@ -181,7 +182,8 @@
             const String8& physicalCameraId,
             std::vector<int> *surfaceIds = nullptr,
             int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
-            bool isShared = false, uint64_t consumerUsage = 0) = 0;
+            bool isShared = false, bool isMultiResolution = false,
+            uint64_t consumerUsage = 0) = 0;
 
     /**
      * Create an input stream of width, height, and format.
@@ -189,7 +191,7 @@
      * Return value is the stream ID if non-negative and an error if negative.
      */
     virtual status_t createInputStream(uint32_t width, uint32_t height,
-            int32_t format, /*out*/ int32_t *id) = 0;
+            int32_t format, bool multiResolution, /*out*/ int32_t *id) = 0;
 
     struct StreamInfo {
         uint32_t width;
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index e9dcb01..dfe2409 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -20,7 +20,7 @@
 
 #include "CameraProviderManager.h"
 
-#include <android/hardware/camera/device/3.5/ICameraDevice.h>
+#include <android/hardware/camera/device/3.7/ICameraDevice.h>
 
 #include <algorithm>
 #include <chrono>
@@ -28,7 +28,6 @@
 #include <dlfcn.h>
 #include <future>
 #include <inttypes.h>
-#include <hardware/camera_common.h>
 #include <android/hidl/manager/1.2/IServiceManager.h>
 #include <hidl/ServiceManagement.h>
 #include <functional>
@@ -49,7 +48,7 @@
 using namespace ::android::hardware::camera::common::V1_0;
 using std::literals::chrono_literals::operator""s;
 using hardware::camera2::utils::CameraIdAndSessionConfiguration;
-using hardware::camera::provider::V2_6::CameraIdAndStreamCombination;
+using hardware::camera::provider::V2_7::CameraIdAndStreamCombination;
 
 namespace {
 const bool kEnableLazyHal(property_get_bool("ro.camera.enableLazyHal", false));
@@ -267,7 +266,7 @@
 }
 
 status_t CameraProviderManager::isSessionConfigurationSupported(const std::string& id,
-        const hardware::camera::device::V3_4::StreamConfiguration &configuration,
+        const hardware::camera::device::V3_7::StreamConfiguration &configuration,
         bool *status /*out*/) const {
     std::lock_guard<std::mutex> lock(mInterfaceMutex);
     auto deviceInfo = findDeviceInfoLocked(id);
@@ -1302,6 +1301,14 @@
                 mMinorVersion = 5;
             }
         }
+    } else {
+        auto cast2_7 = provider::V2_7::ICameraProvider::castFrom(interface);
+        if (cast2_7.isOk()) {
+            sp<provider::V2_7::ICameraProvider> interface2_7 = cast2_7;
+            if (interface2_7 != nullptr) {
+                mMinorVersion = 7;
+            }
+        }
     }
 
     // cameraDeviceStatusChange callbacks may be called (and causing new devices added)
@@ -1973,38 +1980,67 @@
             // TODO: This might be some other problem
             return INVALID_OPERATION;
         }
-        auto castResult = provider::V2_6::ICameraProvider::castFrom(interface);
-        if (castResult.isOk()) {
-            sp<provider::V2_6::ICameraProvider> interface_2_6 = castResult;
-            if (interface_2_6 != nullptr) {
-                Status callStatus;
-                auto cb =
-                        [&isSupported, &callStatus](Status s, bool supported) {
-                              callStatus = s;
-                              *isSupported = supported; };
+        auto castResult2_6 = provider::V2_6::ICameraProvider::castFrom(interface);
+        auto castResult2_7 = provider::V2_7::ICameraProvider::castFrom(interface);
+        Status callStatus;
+        auto cb =
+                [&isSupported, &callStatus](Status s, bool supported) {
+                      callStatus = s;
+                      *isSupported = supported; };
 
-                auto ret =  interface_2_6->isConcurrentStreamCombinationSupported(
-                            halCameraIdsAndStreamCombinations, cb);
-                if (ret.isOk()) {
-                    switch (callStatus) {
-                        case Status::OK:
-                            // Expected case, do nothing.
-                            res = OK;
-                            break;
-                        case Status::METHOD_NOT_SUPPORTED:
-                            res = INVALID_OPERATION;
-                            break;
-                        default:
-                            ALOGE("%s: Session configuration query failed: %d", __FUNCTION__,
-                                      callStatus);
-                            res = UNKNOWN_ERROR;
-                    }
-                } else {
-                    ALOGE("%s: Unexpected binder error: %s", __FUNCTION__, ret.description().c_str());
-                    res = UNKNOWN_ERROR;
-                }
-                return res;
+        ::android::hardware::Return<void> ret;
+        sp<provider::V2_7::ICameraProvider> interface_2_7;
+        sp<provider::V2_6::ICameraProvider> interface_2_6;
+        if (mMinorVersion >= 7 && castResult2_7.isOk()) {
+            interface_2_7 = castResult2_7;
+            if (interface_2_7 != nullptr) {
+                ret = interface_2_7->isConcurrentStreamCombinationSupported_2_7(
+                        halCameraIdsAndStreamCombinations, cb);
             }
+        } else if (mMinorVersion == 6 && castResult2_6.isOk()) {
+            interface_2_6 = castResult2_6;
+            if (interface_2_6 != nullptr) {
+                hardware::hidl_vec<provider::V2_6::CameraIdAndStreamCombination>
+                        halCameraIdsAndStreamCombinations_2_6;
+                size_t numStreams = halCameraIdsAndStreamCombinations.size();
+                halCameraIdsAndStreamCombinations_2_6.resize(numStreams);
+                for (size_t i = 0; i < numStreams; i++) {
+                    auto const& combination = halCameraIdsAndStreamCombinations[i];
+                    halCameraIdsAndStreamCombinations_2_6[i].cameraId = combination.cameraId;
+                    bool success =
+                            SessionConfigurationUtils::convertHALStreamCombinationFromV37ToV34(
+                            halCameraIdsAndStreamCombinations_2_6[i].streamConfiguration,
+                            combination.streamConfiguration);
+                    if (!success) {
+                        *isSupported = false;
+                        return OK;
+                    }
+                }
+                ret = interface_2_6->isConcurrentStreamCombinationSupported(
+                        halCameraIdsAndStreamCombinations_2_6, cb);
+            }
+        }
+
+        if (interface_2_7 != nullptr || interface_2_6 != nullptr) {
+            if (ret.isOk()) {
+                switch (callStatus) {
+                    case Status::OK:
+                        // Expected case, do nothing.
+                        res = OK;
+                        break;
+                    case Status::METHOD_NOT_SUPPORTED:
+                        res = INVALID_OPERATION;
+                        break;
+                    default:
+                        ALOGE("%s: Session configuration query failed: %d", __FUNCTION__,
+                                  callStatus);
+                        res = UNKNOWN_ERROR;
+                }
+            } else {
+                ALOGE("%s: Unexpected binder error: %s", __FUNCTION__, ret.description().c_str());
+                res = UNKNOWN_ERROR;
+            }
+            return res;
         }
     }
     // unsupported operation
@@ -2374,7 +2410,7 @@
 }
 
 status_t CameraProviderManager::ProviderInfo::DeviceInfo3::isSessionConfigurationSupported(
-        const hardware::camera::device::V3_4::StreamConfiguration &configuration,
+        const hardware::camera::device::V3_7::StreamConfiguration &configuration,
         bool *status /*out*/) {
 
     const sp<CameraProviderManager::ProviderInfo::DeviceInfo3::InterfaceT> interface =
@@ -2382,19 +2418,37 @@
     if (interface == nullptr) {
         return DEAD_OBJECT;
     }
-    auto castResult = device::V3_5::ICameraDevice::castFrom(interface);
-    sp<hardware::camera::device::V3_5::ICameraDevice> interface_3_5 = castResult;
-    if (interface_3_5 == nullptr) {
-        return INVALID_OPERATION;
-    }
+    auto castResult_3_5 = device::V3_5::ICameraDevice::castFrom(interface);
+    sp<hardware::camera::device::V3_5::ICameraDevice> interface_3_5 = castResult_3_5;
+    auto castResult_3_7 = device::V3_7::ICameraDevice::castFrom(interface);
+    sp<hardware::camera::device::V3_7::ICameraDevice> interface_3_7 = castResult_3_7;
 
     status_t res;
     Status callStatus;
-    auto ret =  interface_3_5->isStreamCombinationSupported(configuration,
+    ::android::hardware::Return<void> ret;
+    if (interface_3_7 != nullptr) {
+        ret = interface_3_7->isStreamCombinationSupported_3_7(configuration,
             [&callStatus, &status] (Status s, bool combStatus) {
                 callStatus = s;
                 *status = combStatus;
             });
+    } else if (interface_3_5 != nullptr) {
+        hardware::camera::device::V3_4::StreamConfiguration configuration_3_4;
+        bool success = SessionConfigurationUtils::convertHALStreamCombinationFromV37ToV34(
+                configuration_3_4, configuration);
+        if (!success) {
+            *status = false;
+            return OK;
+        }
+
+        ret = interface_3_5->isStreamCombinationSupported(configuration_3_4,
+            [&callStatus, &status] (Status s, bool combStatus) {
+                callStatus = s;
+                *status = combStatus;
+            });
+    } else {
+        return INVALID_OPERATION;
+    }
     if (ret.isOk()) {
         switch (callStatus) {
             case Status::OK:
@@ -2769,7 +2823,7 @@
     bool shouldExit = false;
     status_t res = OK;
     for (auto &cameraIdAndSessionConfig : cameraIdsAndSessionConfigs) {
-        hardware::camera::device::V3_4::StreamConfiguration streamConfiguration;
+        hardware::camera::device::V3_7::StreamConfiguration streamConfiguration;
         CameraMetadata deviceInfo;
         res = getCameraCharacteristicsLocked(cameraIdAndSessionConfig.mCameraId, &deviceInfo);
         if (res != OK) {
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index 8727e7f..fa9cc1c 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -33,7 +33,8 @@
 #include <android/hardware/camera/provider/2.5/ICameraProvider.h>
 #include <android/hardware/camera/provider/2.6/ICameraProviderCallback.h>
 #include <android/hardware/camera/provider/2.6/ICameraProvider.h>
-#include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
+#include <android/hardware/camera/provider/2.7/ICameraProvider.h>
+#include <android/hardware/camera/device/3.7/types.h>
 #include <android/hidl/manager/1.0/IServiceNotification.h>
 #include <camera/VendorTagDescriptor.h>
 
@@ -78,6 +79,16 @@
    HIDDEN_SECURE_CAMERA
 };
 
+#define CAMERA_DEVICE_API_VERSION_1_0 HARDWARE_DEVICE_API_VERSION(1, 0)
+#define CAMERA_DEVICE_API_VERSION_3_0 HARDWARE_DEVICE_API_VERSION(3, 0)
+#define CAMERA_DEVICE_API_VERSION_3_1 HARDWARE_DEVICE_API_VERSION(3, 1)
+#define CAMERA_DEVICE_API_VERSION_3_2 HARDWARE_DEVICE_API_VERSION(3, 2)
+#define CAMERA_DEVICE_API_VERSION_3_3 HARDWARE_DEVICE_API_VERSION(3, 3)
+#define CAMERA_DEVICE_API_VERSION_3_4 HARDWARE_DEVICE_API_VERSION(3, 4)
+#define CAMERA_DEVICE_API_VERSION_3_5 HARDWARE_DEVICE_API_VERSION(3, 5)
+#define CAMERA_DEVICE_API_VERSION_3_6 HARDWARE_DEVICE_API_VERSION(3, 6)
+#define CAMERA_DEVICE_API_VERSION_3_7 HARDWARE_DEVICE_API_VERSION(3, 7)
+
 /**
  * A manager for all camera providers available on an Android device.
  *
@@ -227,7 +238,7 @@
      * Check for device support of specific stream combination.
      */
     status_t isSessionConfigurationSupported(const std::string& id,
-            const hardware::camera::device::V3_4::StreamConfiguration &configuration,
+            const hardware::camera::device::V3_7::StreamConfiguration &configuration,
             bool *status /*out*/) const;
 
     /**
@@ -430,7 +441,7 @@
          */
         status_t isConcurrentSessionConfigurationSupported(
                 const hardware::hidl_vec<
-                        hardware::camera::provider::V2_6::CameraIdAndStreamCombination>
+                        hardware::camera::provider::V2_7::CameraIdAndStreamCombination>
                                 &halCameraIdsAndStreamCombinations,
                 bool *isSupported);
 
@@ -470,7 +481,7 @@
             }
 
             virtual status_t isSessionConfigurationSupported(
-                    const hardware::camera::device::V3_4::StreamConfiguration &/*configuration*/,
+                    const hardware::camera::device::V3_7::StreamConfiguration &/*configuration*/,
                     bool * /*status*/) {
                 return INVALID_OPERATION;
             }
@@ -529,7 +540,7 @@
             virtual status_t getPhysicalCameraCharacteristics(const std::string& physicalCameraId,
                     CameraMetadata *characteristics) const override;
             virtual status_t isSessionConfigurationSupported(
-                    const hardware::camera::device::V3_4::StreamConfiguration &configuration,
+                    const hardware::camera::device::V3_7::StreamConfiguration &configuration,
                     bool *status /*out*/)
                     override;
 
@@ -684,7 +695,7 @@
     status_t convertToHALStreamCombinationAndCameraIdsLocked(
               const std::vector<hardware::camera2::utils::CameraIdAndSessionConfiguration>
                       &cameraIdsAndSessionConfigs,
-              hardware::hidl_vec<hardware::camera::provider::V2_6::CameraIdAndStreamCombination>
+              hardware::hidl_vec<hardware::camera::provider::V2_7::CameraIdAndStreamCombination>
                       *halCameraIdsAndStreamCombinations,
               bool *earlyExit);
 };
diff --git a/services/camera/libcameraservice/device3/Camera3BufferManager.cpp b/services/camera/libcameraservice/device3/Camera3BufferManager.cpp
index d6bf83e..a556200 100644
--- a/services/camera/libcameraservice/device3/Camera3BufferManager.cpp
+++ b/services/camera/libcameraservice/device3/Camera3BufferManager.cpp
@@ -40,16 +40,17 @@
     ATRACE_CALL();
 
     int streamId = streamInfo.streamId;
-    int streamSetId = streamInfo.streamSetId;
+    StreamSetKey streamSetKey = {streamInfo.streamSetId, streamInfo.isMultiRes};
 
-    if (streamId == CAMERA3_STREAM_ID_INVALID || streamSetId == CAMERA3_STREAM_SET_ID_INVALID) {
+    if (streamId == CAMERA3_STREAM_ID_INVALID ||
+            streamSetKey.id == CAMERA3_STREAM_SET_ID_INVALID) {
         ALOGE("%s: Stream id (%d) or stream set id (%d) is invalid",
-                __FUNCTION__, streamId, streamSetId);
+                __FUNCTION__, streamId, streamSetKey.id);
         return BAD_VALUE;
     }
     if (streamInfo.totalBufferCount > kMaxBufferCount || streamInfo.totalBufferCount == 0) {
         ALOGE("%s: Stream id (%d) with stream set id (%d) total buffer count %zu is invalid",
-                __FUNCTION__, streamId, streamSetId, streamInfo.totalBufferCount);
+                __FUNCTION__, streamId, streamSetKey.id, streamInfo.totalBufferCount);
         return BAD_VALUE;
     }
     if (!streamInfo.isConfigured) {
@@ -75,7 +76,8 @@
     for (size_t i = 0; i < mStreamSetMap.size(); i++) {
         ssize_t streamIdx = mStreamSetMap[i].streamInfoMap.indexOfKey(streamId);
         if (streamIdx != NAME_NOT_FOUND &&
-            mStreamSetMap[i].streamInfoMap[streamIdx].streamSetId != streamInfo.streamSetId) {
+            mStreamSetMap[i].streamInfoMap[streamIdx].streamSetId != streamInfo.streamSetId &&
+            mStreamSetMap[i].streamInfoMap[streamIdx].isMultiRes != streamInfo.isMultiRes) {
             ALOGE("%s: It is illegal to register the same stream id with different stream set",
                     __FUNCTION__);
             return BAD_VALUE;
@@ -83,20 +85,20 @@
     }
     // Check if there is an existing stream set registered; if not, create one; otherwise, add this
     // stream info to the existing stream set entry.
-    ssize_t setIdx = mStreamSetMap.indexOfKey(streamSetId);
+    ssize_t setIdx = mStreamSetMap.indexOfKey(streamSetKey);
     if (setIdx == NAME_NOT_FOUND) {
-        ALOGV("%s: stream set %d is not registered to stream set map yet, create it.",
-                __FUNCTION__, streamSetId);
+        ALOGV("%s: stream set %d(%d) is not registered to stream set map yet, create it.",
+                __FUNCTION__, streamSetKey.id, streamSetKey.isMultiRes);
         // Create stream info map, then add to mStreamsetMap.
         StreamSet newStreamSet;
-        setIdx = mStreamSetMap.add(streamSetId, newStreamSet);
+        setIdx = mStreamSetMap.add(streamSetKey, newStreamSet);
     }
     // Update stream set map and water mark.
     StreamSet& currentStreamSet = mStreamSetMap.editValueAt(setIdx);
     ssize_t streamIdx = currentStreamSet.streamInfoMap.indexOfKey(streamId);
     if (streamIdx != NAME_NOT_FOUND) {
-        ALOGW("%s: stream %d was already registered with stream set %d",
-                __FUNCTION__, streamId, streamSetId);
+        ALOGW("%s: stream %d was already registered with stream set %d(%d)",
+                __FUNCTION__, streamId, streamSetKey.id, streamSetKey.isMultiRes);
         return OK;
     }
     currentStreamSet.streamInfoMap.add(streamId, streamInfo);
@@ -113,21 +115,22 @@
     return OK;
 }
 
-status_t Camera3BufferManager::unregisterStream(int streamId, int streamSetId) {
+status_t Camera3BufferManager::unregisterStream(int streamId, int streamSetId, bool isMultiRes) {
     ATRACE_CALL();
 
     Mutex::Autolock l(mLock);
-    ALOGV("%s: unregister stream %d with stream set %d", __FUNCTION__,
-            streamId, streamSetId);
+    ALOGV("%s: unregister stream %d with stream set %d(%d)", __FUNCTION__,
+            streamId, streamSetId, isMultiRes);
 
-    if (!checkIfStreamRegisteredLocked(streamId, streamSetId)){
-        ALOGE("%s: stream %d with set id %d wasn't properly registered to this buffer manager!",
-                __FUNCTION__, streamId, streamSetId);
+    StreamSetKey streamSetKey = {streamSetId, isMultiRes};
+    if (!checkIfStreamRegisteredLocked(streamId, streamSetKey)){
+        ALOGE("%s: stream %d with set %d(%d) wasn't properly registered to this"
+                " buffer manager!", __FUNCTION__, streamId, streamSetId, isMultiRes);
         return BAD_VALUE;
     }
 
     // De-list all the buffers associated with this stream first.
-    StreamSet& currentSet = mStreamSetMap.editValueFor(streamSetId);
+    StreamSet& currentSet = mStreamSetMap.editValueFor(streamSetKey);
     BufferCountMap& handOutBufferCounts = currentSet.handoutBufferCountMap;
     BufferCountMap& attachedBufferCounts = currentSet.attachedBufferCountMap;
     InfoMap& infoMap = currentSet.streamInfoMap;
@@ -150,26 +153,28 @@
 
     // Remove this stream set if all its streams have been removed.
     if (handOutBufferCounts.size() == 0 && infoMap.size() == 0) {
-        mStreamSetMap.removeItem(streamSetId);
+        mStreamSetMap.removeItem(streamSetKey);
     }
 
     return OK;
 }
 
-void Camera3BufferManager::notifyBufferRemoved(int streamId, int streamSetId) {
+void Camera3BufferManager::notifyBufferRemoved(int streamId, int streamSetId, bool isMultiRes) {
     Mutex::Autolock l(mLock);
-    StreamSet &streamSet = mStreamSetMap.editValueFor(streamSetId);
+    StreamSetKey streamSetKey = {streamSetId, isMultiRes};
+    StreamSet &streamSet = mStreamSetMap.editValueFor(streamSetKey);
     size_t& attachedBufferCount =
             streamSet.attachedBufferCountMap.editValueFor(streamId);
     attachedBufferCount--;
 }
 
 status_t Camera3BufferManager::checkAndFreeBufferOnOtherStreamsLocked(
-        int streamId, int streamSetId) {
+        int streamId, StreamSetKey streamSetKey) {
     StreamId firstOtherStreamId = CAMERA3_STREAM_ID_INVALID;
-    StreamSet &streamSet = mStreamSetMap.editValueFor(streamSetId);
+    StreamSet &streamSet = mStreamSetMap.editValueFor(streamSetKey);
     if (streamSet.streamInfoMap.size() == 1) {
-        ALOGV("StreamSet %d has no other stream available to free", streamSetId);
+        ALOGV("StreamSet %d(%d) has no other stream available to free",
+                streamSetKey.id, streamSetKey.isMultiRes);
         return OK;
     }
 
@@ -190,7 +195,8 @@
         firstOtherStreamId = CAMERA3_STREAM_ID_INVALID;
     }
     if (firstOtherStreamId == CAMERA3_STREAM_ID_INVALID || !freeBufferIsAttached) {
-        ALOGV("StreamSet %d has no buffer available to free", streamSetId);
+        ALOGV("StreamSet %d(%d) has no buffer available to free",
+                streamSetKey.id, streamSetKey.isMultiRes);
         return OK;
     }
 
@@ -237,20 +243,21 @@
 }
 
 status_t Camera3BufferManager::getBufferForStream(int streamId, int streamSetId,
-        sp<GraphicBuffer>* gb, int* fenceFd, bool noFreeBufferAtConsumer) {
+        bool isMultiRes, sp<GraphicBuffer>* gb, int* fenceFd, bool noFreeBufferAtConsumer) {
     ATRACE_CALL();
 
     Mutex::Autolock l(mLock);
-    ALOGV("%s: get buffer for stream %d with stream set %d", __FUNCTION__,
-            streamId, streamSetId);
+    ALOGV("%s: get buffer for stream %d with stream set %d(%d)", __FUNCTION__,
+            streamId, streamSetId, isMultiRes);
 
-    if (!checkIfStreamRegisteredLocked(streamId, streamSetId)) {
-        ALOGE("%s: stream %d is not registered with stream set %d yet!!!",
-                __FUNCTION__, streamId, streamSetId);
+    StreamSetKey streamSetKey = {streamSetId, isMultiRes};
+    if (!checkIfStreamRegisteredLocked(streamId, streamSetKey)) {
+        ALOGE("%s: stream %d is not registered with stream set %d(%d) yet!!!",
+                __FUNCTION__, streamId, streamSetId, isMultiRes);
         return BAD_VALUE;
     }
 
-    StreamSet &streamSet = mStreamSetMap.editValueFor(streamSetId);
+    StreamSet &streamSet = mStreamSetMap.editValueFor(streamSetKey);
     BufferCountMap& handOutBufferCounts = streamSet.handoutBufferCountMap;
     size_t& bufferCount = handOutBufferCounts.editValueFor(streamId);
     BufferCountMap& attachedBufferCounts = streamSet.attachedBufferCountMap;
@@ -272,7 +279,8 @@
         bufferCount++;
         return ALREADY_EXISTS;
     }
-    ALOGV("Stream %d set %d: Get buffer for stream: Allocate new", streamId, streamSetId);
+    ALOGV("Stream %d set %d(%d): Get buffer for stream: Allocate new",
+            streamId, streamSetId, isMultiRes);
 
     if (mGrallocVersion < HARDWARE_DEVICE_API_VERSION(1,0)) {
         const StreamInfo& info = streamSet.streamInfoMap.valueFor(streamId);
@@ -313,13 +321,13 @@
         // in returnBufferForStream() if we want to free buffer more quickly.
         // TODO: probably should find out all the inactive stream IDs, and free the firstly found
         // buffers for them.
-        res = checkAndFreeBufferOnOtherStreamsLocked(streamId, streamSetId);
+        res = checkAndFreeBufferOnOtherStreamsLocked(streamId, streamSetKey);
         if (res != OK) {
             return res;
         }
         // Since we just allocated one new buffer above, try free one more buffer from other streams
         // to prevent total buffer count from growing
-        res = checkAndFreeBufferOnOtherStreamsLocked(streamId, streamSetId);
+        res = checkAndFreeBufferOnOtherStreamsLocked(streamId, streamSetKey);
         if (res != OK) {
             return res;
         }
@@ -332,7 +340,7 @@
 }
 
 status_t Camera3BufferManager::onBufferReleased(
-        int streamId, int streamSetId, bool* shouldFreeBuffer) {
+        int streamId, int streamSetId, bool isMultiRes, bool* shouldFreeBuffer) {
     ATRACE_CALL();
 
     if (shouldFreeBuffer == nullptr) {
@@ -341,22 +349,24 @@
     }
 
     Mutex::Autolock l(mLock);
-    ALOGV("Stream %d set %d: Buffer released", streamId, streamSetId);
+    ALOGV("Stream %d set %d(%d): Buffer released", streamId, streamSetId, isMultiRes);
     *shouldFreeBuffer = false;
 
-    if (!checkIfStreamRegisteredLocked(streamId, streamSetId)){
+    StreamSetKey streamSetKey = {streamSetId, isMultiRes};
+    if (!checkIfStreamRegisteredLocked(streamId, streamSetKey)){
         ALOGV("%s: signaling buffer release for an already unregistered stream "
-                "(stream %d with set id %d)", __FUNCTION__, streamId, streamSetId);
+                "(stream %d with set id %d(%d))", __FUNCTION__, streamId, streamSetId,
+                isMultiRes);
         return OK;
     }
 
     if (mGrallocVersion < HARDWARE_DEVICE_API_VERSION(1,0)) {
-        StreamSet& streamSet = mStreamSetMap.editValueFor(streamSetId);
+        StreamSet& streamSet = mStreamSetMap.editValueFor(streamSetKey);
         BufferCountMap& handOutBufferCounts = streamSet.handoutBufferCountMap;
         size_t& bufferCount = handOutBufferCounts.editValueFor(streamId);
         bufferCount--;
-        ALOGV("%s: Stream %d set %d: Buffer count now %zu", __FUNCTION__, streamId, streamSetId,
-                bufferCount);
+        ALOGV("%s: Stream %d set %d(%d): Buffer count now %zu", __FUNCTION__, streamId,
+                streamSetId, isMultiRes, bufferCount);
 
         size_t totalAllocatedBufferCount = 0;
         size_t totalHandOutBufferCount = 0;
@@ -371,8 +381,9 @@
             // BufferManager got more than enough buffers, so decrease watermark
             // to trigger more buffers free operation.
             streamSet.allocatedBufferWaterMark = newWaterMark;
-            ALOGV("%s: Stream %d set %d: watermark--; now %zu",
-                    __FUNCTION__, streamId, streamSetId, streamSet.allocatedBufferWaterMark);
+            ALOGV("%s: Stream %d set %d(%d): watermark--; now %zu",
+                    __FUNCTION__, streamId, streamSetId, isMultiRes,
+                    streamSet.allocatedBufferWaterMark);
         }
 
         size_t attachedBufferCount = streamSet.attachedBufferCountMap.valueFor(streamId);
@@ -395,20 +406,22 @@
     return OK;
 }
 
-status_t Camera3BufferManager::onBuffersRemoved(int streamId, int streamSetId, size_t count) {
+status_t Camera3BufferManager::onBuffersRemoved(int streamId, int streamSetId,
+        bool isMultiRes, size_t count) {
     ATRACE_CALL();
     Mutex::Autolock l(mLock);
 
-    ALOGV("Stream %d set %d: Buffer removed", streamId, streamSetId);
+    ALOGV("Stream %d set %d(%d): Buffer removed", streamId, streamSetId, isMultiRes);
 
-    if (!checkIfStreamRegisteredLocked(streamId, streamSetId)){
+    StreamSetKey streamSetKey = {streamSetId, isMultiRes};
+    if (!checkIfStreamRegisteredLocked(streamId, streamSetKey)){
         ALOGV("%s: signaling buffer removal for an already unregistered stream "
-                "(stream %d with set id %d)", __FUNCTION__, streamId, streamSetId);
+                "(stream %d with set id %d(%d))", __FUNCTION__, streamId, streamSetId, isMultiRes);
         return OK;
     }
 
     if (mGrallocVersion < HARDWARE_DEVICE_API_VERSION(1,0)) {
-        StreamSet& streamSet = mStreamSetMap.editValueFor(streamSetId);
+        StreamSet& streamSet = mStreamSetMap.editValueFor(streamSetKey);
         BufferCountMap& handOutBufferCounts = streamSet.handoutBufferCountMap;
         size_t& totalHandoutCount = handOutBufferCounts.editValueFor(streamId);
         BufferCountMap& attachedBufferCounts = streamSet.attachedBufferCountMap;
@@ -427,8 +440,9 @@
 
         totalHandoutCount -= count;
         totalAttachedCount -= count;
-        ALOGV("%s: Stream %d set %d: Buffer count now %zu, attached buffer count now %zu",
-                __FUNCTION__, streamId, streamSetId, totalHandoutCount, totalAttachedCount);
+        ALOGV("%s: Stream %d set %d(%d): Buffer count now %zu, attached buffer count now %zu",
+                __FUNCTION__, streamId, streamSetId, isMultiRes, totalHandoutCount,
+                totalAttachedCount);
     } else {
         // TODO: implement gralloc V1 support
         return BAD_VALUE;
@@ -444,7 +458,8 @@
     String8 lines;
     lines.appendFormat("      Total stream sets: %zu\n", mStreamSetMap.size());
     for (size_t i = 0; i < mStreamSetMap.size(); i++) {
-        lines.appendFormat("        Stream set %d has below streams:\n", mStreamSetMap.keyAt(i));
+        lines.appendFormat("        Stream set %d(%d) has below streams:\n",
+                mStreamSetMap.keyAt(i).id, mStreamSetMap.keyAt(i).isMultiRes);
         for (size_t j = 0; j < mStreamSetMap[i].streamInfoMap.size(); j++) {
             lines.appendFormat("          Stream %d\n", mStreamSetMap[i].streamInfoMap[j].streamId);
         }
@@ -470,11 +485,12 @@
     write(fd, lines.string(), lines.size());
 }
 
-bool Camera3BufferManager::checkIfStreamRegisteredLocked(int streamId, int streamSetId) const {
-    ssize_t setIdx = mStreamSetMap.indexOfKey(streamSetId);
+bool Camera3BufferManager::checkIfStreamRegisteredLocked(int streamId,
+        StreamSetKey streamSetKey) const {
+    ssize_t setIdx = mStreamSetMap.indexOfKey(streamSetKey);
     if (setIdx == NAME_NOT_FOUND) {
-        ALOGV("%s: stream set %d is not registered to stream set map yet!",
-                __FUNCTION__, streamSetId);
+        ALOGV("%s: stream set %d(%d) is not registered to stream set map yet!",
+                __FUNCTION__, streamSetKey.id, streamSetKey.isMultiRes);
         return false;
     }
 
@@ -486,9 +502,10 @@
 
     size_t bufferWaterMark = mStreamSetMap[setIdx].maxAllowedBufferCount;
     if (bufferWaterMark == 0 || bufferWaterMark > kMaxBufferCount) {
-        ALOGW("%s: stream %d with stream set %d is not registered correctly to stream set map,"
+        ALOGW("%s: stream %d with stream set %d(%d) is not registered correctly to stream set map,"
                 " as the water mark (%zu) is wrong!",
-                __FUNCTION__, streamId, streamSetId, bufferWaterMark);
+                __FUNCTION__, streamId, streamSetKey.id, streamSetKey.isMultiRes,
+                bufferWaterMark);
         return false;
     }
 
diff --git a/services/camera/libcameraservice/device3/Camera3BufferManager.h b/services/camera/libcameraservice/device3/Camera3BufferManager.h
index f0de1c1..64aaa230 100644
--- a/services/camera/libcameraservice/device3/Camera3BufferManager.h
+++ b/services/camera/libcameraservice/device3/Camera3BufferManager.h
@@ -99,7 +99,7 @@
      *             combination doesn't match what was registered, or this stream wasn't registered
      *             to this buffer manager before.
      */
-    status_t unregisterStream(int streamId, int streamSetId);
+    status_t unregisterStream(int streamId, int streamSetId, bool isMultiRes);
 
     /**
      * This method obtains a buffer for a stream from this buffer manager.
@@ -127,8 +127,8 @@
      *  NO_MEMORY: Unable to allocate a buffer for this stream at this time.
      */
     status_t getBufferForStream(
-            int streamId, int streamSetId, sp<GraphicBuffer>* gb, int* fenceFd,
-            bool noFreeBufferAtConsumer = false);
+            int streamId, int streamSetId, bool isMultiRes, sp<GraphicBuffer>* gb,
+            int* fenceFd, bool noFreeBufferAtConsumer = false);
 
     /**
      * This method notifies the manager that a buffer has been released by the consumer.
@@ -153,7 +153,8 @@
      *             combination doesn't match what was registered, or this stream wasn't registered
      *             to this buffer manager before, or shouldFreeBuffer is null/
      */
-    status_t onBufferReleased(int streamId, int streamSetId, /*out*/bool* shouldFreeBuffer);
+    status_t onBufferReleased(int streamId, int streamSetId, bool isMultiRes,
+                              /*out*/bool* shouldFreeBuffer);
 
     /**
      * This method notifies the manager that certain buffers has been removed from the
@@ -171,13 +172,13 @@
      *             to this buffer manager before, or the removed buffer count is larger than
      *             current total handoutCount or attachedCount.
      */
-    status_t onBuffersRemoved(int streamId, int streamSetId, size_t count);
+    status_t onBuffersRemoved(int streamId, int streamSetId, bool isMultiRes, size_t count);
 
     /**
      * This method notifiers the manager that a buffer is freed from the buffer queue, usually
      * because onBufferReleased signals the caller to free a buffer via the shouldFreeBuffer flag.
      */
-    void notifyBufferRemoved(int streamId, int streamSetId);
+    void notifyBufferRemoved(int streamId, int streamSetId, bool isMultiRes);
 
     /**
      * Dump the buffer manager statistics.
@@ -292,8 +293,20 @@
     /**
      * Stream set map managed by this buffer manager.
      */
-    typedef int StreamSetId;
-    KeyedVector<StreamSetId, StreamSet> mStreamSetMap;
+    struct StreamSetKey {
+        // The stream set ID
+        int id;
+        // Whether this stream set is for multi-resolution output streams. It's
+        // valid for 2 stream sets to have the same stream set ID if: one is for
+        // multi-resolution output stream, and the other one is not.
+        bool isMultiRes;
+
+        inline bool operator<(const StreamSetKey& other) const {
+            return (isMultiRes < other.isMultiRes) ||
+                    ((isMultiRes == other.isMultiRes) && (id < other.id));
+        }
+    };
+    KeyedVector<StreamSetKey, StreamSet> mStreamSetMap;
     KeyedVector<StreamId, wp<Camera3OutputStream>> mStreamMap;
 
     // TODO: There is no easy way to query the Gralloc version in this code yet, we have different
@@ -304,13 +317,13 @@
      * Check if this stream was successfully registered already. This method needs to be called with
      * mLock held.
      */
-    bool checkIfStreamRegisteredLocked(int streamId, int streamSetId) const;
+    bool checkIfStreamRegisteredLocked(int streamId, StreamSetKey streamSetKey) const;
 
     /**
      * Check if other streams in the stream set has extra buffer available to be freed, and
      * free one if so.
      */
-    status_t checkAndFreeBufferOnOtherStreamsLocked(int streamId, int streamSetId);
+    status_t checkAndFreeBufferOnOtherStreamsLocked(int streamId, StreamSetKey streamSetKey);
 };
 
 } // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 73a133f..35a06d8 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -313,6 +313,7 @@
     mFakeStreamId = NO_STREAM;
     mNeedConfig = true;
     mPauseStateNotify = false;
+    mIsInputStreamMultiResolution = false;
 
     // Measure the clock domain offset between camera and video/hw_composer
     camera_metadata_entry timestampSource =
@@ -481,7 +482,7 @@
     return gotLock;
 }
 
-Camera3Device::Size Camera3Device::getMaxJpegResolution() const {
+camera3::Size Camera3Device::getMaxJpegResolution() const {
     int32_t maxJpegWidth = 0, maxJpegHeight = 0;
     const int STREAM_CONFIGURATION_SIZE = 4;
     const int STREAM_FORMAT_OFFSET = 0;
@@ -492,7 +493,7 @@
             mDeviceInfo.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
     if (availableStreamConfigs.count == 0 ||
             availableStreamConfigs.count % STREAM_CONFIGURATION_SIZE != 0) {
-        return Size(0, 0);
+        return camera3::Size(0, 0);
     }
 
     // Get max jpeg size (area-wise).
@@ -509,7 +510,7 @@
         }
     }
 
-    return Size(maxJpegWidth, maxJpegHeight);
+    return camera3::Size(maxJpegWidth, maxJpegHeight);
 }
 
 nsecs_t Camera3Device::getMonoToBoottimeOffset() {
@@ -603,7 +604,7 @@
 
 ssize_t Camera3Device::getJpegBufferSize(uint32_t width, uint32_t height) const {
     // Get max jpeg size (area-wise).
-    Size maxJpegResolution = getMaxJpegResolution();
+    camera3::Size maxJpegResolution = getMaxJpegResolution();
     if (maxJpegResolution.width == 0) {
         ALOGE("%s: Camera %s: Can't find valid available jpeg sizes in static metadata!",
                 __FUNCTION__, mId.string());
@@ -1252,7 +1253,7 @@
 }
 
 status_t Camera3Device::createInputStream(
-        uint32_t width, uint32_t height, int format, int *id) {
+        uint32_t width, uint32_t height, int format, bool isMultiResolution, int *id) {
     ATRACE_CALL();
     Mutex::Autolock il(mInterfaceLock);
     nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
@@ -1299,6 +1300,7 @@
     newStream->setStatusTracker(mStatusTracker);
 
     mInputStream = newStream;
+    mIsInputStreamMultiResolution = isMultiResolution;
 
     *id = mNextStreamId++;
 
@@ -1323,7 +1325,8 @@
             uint32_t width, uint32_t height, int format,
             android_dataspace dataSpace, camera_stream_rotation_t rotation, int *id,
             const String8& physicalCameraId,
-            std::vector<int> *surfaceIds, int streamSetId, bool isShared, uint64_t consumerUsage) {
+            std::vector<int> *surfaceIds, int streamSetId, bool isShared,
+            bool isMultiResolution, uint64_t consumerUsage) {
     ATRACE_CALL();
 
     if (consumer == nullptr) {
@@ -1336,23 +1339,24 @@
 
     return createStream(consumers, /*hasDeferredConsumer*/ false, width, height,
             format, dataSpace, rotation, id, physicalCameraId, surfaceIds, streamSetId,
-            isShared, consumerUsage);
+            isShared, isMultiResolution, consumerUsage);
 }
 
 status_t Camera3Device::createStream(const std::vector<sp<Surface>>& consumers,
         bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
         android_dataspace dataSpace, camera_stream_rotation_t rotation, int *id,
         const String8& physicalCameraId,
-        std::vector<int> *surfaceIds, int streamSetId, bool isShared, uint64_t consumerUsage) {
+        std::vector<int> *surfaceIds, int streamSetId, bool isShared, bool isMultiResolution,
+        uint64_t consumerUsage) {
     ATRACE_CALL();
 
     Mutex::Autolock il(mInterfaceLock);
     nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
     Mutex::Autolock l(mLock);
     ALOGV("Camera %s: Creating new stream %d: %d x %d, format %d, dataspace %d rotation %d"
-            " consumer usage %" PRIu64 ", isShared %d, physicalCameraId %s", mId.string(),
-            mNextStreamId, width, height, format, dataSpace, rotation, consumerUsage, isShared,
-            physicalCameraId.string());
+            " consumer usage %" PRIu64 ", isShared %d, physicalCameraId %s, isMultiResolution %d",
+            mId.string(), mNextStreamId, width, height, format, dataSpace, rotation,
+            consumerUsage, isShared, physicalCameraId.string(), isMultiResolution);
 
     status_t res;
     bool wasActive = false;
@@ -1414,7 +1418,7 @@
         }
         newStream = new Camera3OutputStream(mNextStreamId, consumers[0],
                 width, height, blobBufferSize, format, dataSpace, rotation,
-                mTimestampOffset, physicalCameraId, streamSetId);
+                mTimestampOffset, physicalCameraId, streamSetId, isMultiResolution);
     } else if (format == HAL_PIXEL_FORMAT_RAW_OPAQUE) {
         ssize_t rawOpaqueBufferSize = getRawOpaqueBufferSize(width, height);
         if (rawOpaqueBufferSize <= 0) {
@@ -1423,20 +1427,19 @@
         }
         newStream = new Camera3OutputStream(mNextStreamId, consumers[0],
                 width, height, rawOpaqueBufferSize, format, dataSpace, rotation,
-                mTimestampOffset, physicalCameraId, streamSetId);
+                mTimestampOffset, physicalCameraId, streamSetId, isMultiResolution);
     } else if (isShared) {
         newStream = new Camera3SharedOutputStream(mNextStreamId, consumers,
                 width, height, format, consumerUsage, dataSpace, rotation,
-                mTimestampOffset, physicalCameraId, streamSetId,
-                mUseHalBufManager);
+                mTimestampOffset, physicalCameraId, streamSetId, mUseHalBufManager);
     } else if (consumers.size() == 0 && hasDeferredConsumer) {
         newStream = new Camera3OutputStream(mNextStreamId,
                 width, height, format, consumerUsage, dataSpace, rotation,
-                mTimestampOffset, physicalCameraId, streamSetId);
+                mTimestampOffset, physicalCameraId, streamSetId, isMultiResolution);
     } else {
         newStream = new Camera3OutputStream(mNextStreamId, consumers[0],
                 width, height, format, dataSpace, rotation,
-                mTimestampOffset, physicalCameraId, streamSetId);
+                mTimestampOffset, physicalCameraId, streamSetId, isMultiResolution);
     }
 
     size_t consumerCount = consumers.size();
@@ -2549,8 +2552,9 @@
     if (mInputStream != NULL && notifyRequestThread) {
         while (true) {
             camera_stream_buffer_t inputBuffer;
+            camera3::Size inputBufferSize;
             status_t res = mInputStream->getInputBuffer(&inputBuffer,
-                    /*respectHalLimit*/ false);
+                    &inputBufferSize, /*respectHalLimit*/ false);
             if (res != OK) {
                 // Exhausted acquiring all input buffers.
                 break;
@@ -2587,6 +2591,7 @@
     camera_stream_configuration config;
     config.operation_mode = mOperatingMode;
     config.num_streams = (mInputStream != NULL) + mOutputStreams.size();
+    config.input_is_multi_resolution = false;
 
     Vector<camera3::camera_stream_t*> streams;
     streams.setCapacity(config.num_streams);
@@ -2602,6 +2607,8 @@
             return INVALID_OPERATION;
         }
         streams.add(inputStream);
+
+        config.input_is_multi_resolution = mIsInputStreamMultiResolution;
     }
 
     for (size_t i = 0; i < mOutputStreams.size(); i++) {
@@ -2999,6 +3006,10 @@
         mSupportOfflineProcessing(supportOfflineProcessing) {
     // Check with hardware service manager if we can downcast these interfaces
     // Somewhat expensive, so cache the results at startup
+    auto castResult_3_7 = device::V3_7::ICameraDeviceSession::castFrom(mHidlSession);
+    if (castResult_3_7.isOk()) {
+        mHidlSession_3_7 = castResult_3_7;
+    }
     auto castResult_3_6 = device::V3_6::ICameraDeviceSession::castFrom(mHidlSession);
     if (castResult_3_6.isOk()) {
         mHidlSession_3_6 = castResult_3_6;
@@ -3032,6 +3043,7 @@
 }
 
 void Camera3Device::HalInterface::clear() {
+    mHidlSession_3_7.clear();
     mHidlSession_3_6.clear();
     mHidlSession_3_5.clear();
     mHidlSession_3_4.clear();
@@ -3158,15 +3170,23 @@
     if (!valid()) return INVALID_OPERATION;
     status_t res = OK;
 
+    if (config->input_is_multi_resolution && mHidlSession_3_7 == nullptr) {
+        ALOGE("%s: Camera device doesn't support multi-resolution input stream", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
     // Convert stream config to HIDL
     std::set<int> activeStreams;
     device::V3_2::StreamConfiguration requestedConfiguration3_2;
     device::V3_4::StreamConfiguration requestedConfiguration3_4;
+    device::V3_7::StreamConfiguration requestedConfiguration3_7;
     requestedConfiguration3_2.streams.resize(config->num_streams);
     requestedConfiguration3_4.streams.resize(config->num_streams);
+    requestedConfiguration3_7.streams.resize(config->num_streams);
     for (size_t i = 0; i < config->num_streams; i++) {
         device::V3_2::Stream &dst3_2 = requestedConfiguration3_2.streams[i];
         device::V3_4::Stream &dst3_4 = requestedConfiguration3_4.streams[i];
+        device::V3_7::Stream &dst3_7 = requestedConfiguration3_7.streams[i];
         camera3::camera_stream_t *src = config->streams[i];
 
         Camera3Stream* cam3stream = Camera3Stream::cast(src);
@@ -3207,6 +3227,8 @@
         if (src->physical_camera_id != nullptr) {
             dst3_4.physicalCameraId = src->physical_camera_id;
         }
+        dst3_7.v3_4 = dst3_4;
+        dst3_7.groupId = cam3stream->getHalStreamGroupId();
 
         activeStreams.insert(streamId);
         // Create Buffer ID map if necessary
@@ -3227,6 +3249,10 @@
     requestedConfiguration3_4.sessionParams.setToExternal(
             reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(sessionParams)),
             get_camera_metadata_size(sessionParams));
+    requestedConfiguration3_7.operationMode = operationMode;
+    requestedConfiguration3_7.sessionParams.setToExternal(
+            reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(sessionParams)),
+            get_camera_metadata_size(sessionParams));
 
     // Invoke configureStreams
     device::V3_3::HalStreamConfiguration finalConfiguration;
@@ -3273,7 +3299,17 @@
             };
 
     // See which version of HAL we have
-    if (mHidlSession_3_6 != nullptr) {
+    if (mHidlSession_3_7 != nullptr) {
+        ALOGV("%s: v3.7 device found", __FUNCTION__);
+        requestedConfiguration3_7.streamConfigCounter = mNextStreamConfigCounter++;
+        requestedConfiguration3_7.multiResolutionInputImage = config->input_is_multi_resolution;
+        auto err = mHidlSession_3_7->configureStreams_3_7(
+                requestedConfiguration3_7, configStream36Cb);
+        res = postprocConfigStream36(err);
+        if (res != OK) {
+            return res;
+        }
+    } else if (mHidlSession_3_6 != nullptr) {
         ALOGV("%s: v3.6 device found", __FUNCTION__);
         device::V3_5::StreamConfiguration requestedConfiguration3_5;
         requestedConfiguration3_5.v3_4 = requestedConfiguration3_4;
@@ -3531,6 +3567,11 @@
     if (!valid()) return INVALID_OPERATION;
 
     sp<device::V3_4::ICameraDeviceSession> hidlSession_3_4;
+    sp<device::V3_7::ICameraDeviceSession> hidlSession_3_7;
+    auto castResult_3_7 = device::V3_7::ICameraDeviceSession::castFrom(mHidlSession);
+    if (castResult_3_7.isOk()) {
+        hidlSession_3_7 = castResult_3_7;
+    }
     auto castResult_3_4 = device::V3_4::ICameraDeviceSession::castFrom(mHidlSession);
     if (castResult_3_4.isOk()) {
         hidlSession_3_4 = castResult_3_4;
@@ -3538,8 +3579,11 @@
 
     hardware::hidl_vec<device::V3_2::CaptureRequest> captureRequests;
     hardware::hidl_vec<device::V3_4::CaptureRequest> captureRequests_3_4;
+    hardware::hidl_vec<device::V3_7::CaptureRequest> captureRequests_3_7;
     size_t batchSize = requests.size();
-    if (hidlSession_3_4 != nullptr) {
+    if (hidlSession_3_7 != nullptr) {
+        captureRequests_3_7.resize(batchSize);
+    } else if (hidlSession_3_4 != nullptr) {
         captureRequests_3_4.resize(batchSize);
     } else {
         captureRequests.resize(batchSize);
@@ -3549,7 +3593,10 @@
 
     status_t res = OK;
     for (size_t i = 0; i < batchSize; i++) {
-        if (hidlSession_3_4 != nullptr) {
+        if (hidlSession_3_7 != nullptr) {
+            res = wrapAsHidlRequest(requests[i], /*out*/&captureRequests_3_7[i].v3_4.v3_2,
+                    /*out*/&handlesCreated, /*out*/&inflightBuffers);
+        } else if (hidlSession_3_4 != nullptr) {
             res = wrapAsHidlRequest(requests[i], /*out*/&captureRequests_3_4[i].v3_2,
                     /*out*/&handlesCreated, /*out*/&inflightBuffers);
         } else {
@@ -3582,7 +3629,9 @@
     for (size_t i = 0; i < batchSize; i++) {
         camera_capture_request_t* request = requests[i];
         device::V3_2::CaptureRequest* captureRequest;
-        if (hidlSession_3_4 != nullptr) {
+        if (hidlSession_3_7 != nullptr) {
+            captureRequest = &captureRequests_3_7[i].v3_4.v3_2;
+        } else if (hidlSession_3_4 != nullptr) {
             captureRequest = &captureRequests_3_4[i].v3_2;
         } else {
             captureRequest = &captureRequests[i];
@@ -3609,33 +3658,42 @@
             captureRequest->fmqSettingsSize = 0u;
         }
 
-        if (hidlSession_3_4 != nullptr) {
-            captureRequests_3_4[i].physicalCameraSettings.resize(request->num_physcam_settings);
+        // hidl session 3.7 specific handling.
+        if (hidlSession_3_7 != nullptr) {
+            captureRequests_3_7[i].inputWidth = request->input_width;
+            captureRequests_3_7[i].inputHeight = request->input_height;
+        }
+
+        // hidl session 3.7 and 3.4 specific handling.
+        if (hidlSession_3_7 != nullptr || hidlSession_3_4 != nullptr) {
+            hardware::hidl_vec<device::V3_4::PhysicalCameraSetting>& physicalCameraSettings =
+                    (hidlSession_3_7 != nullptr) ?
+                    captureRequests_3_7[i].v3_4.physicalCameraSettings :
+                    captureRequests_3_4[i].physicalCameraSettings;
+            physicalCameraSettings.resize(request->num_physcam_settings);
             for (size_t j = 0; j < request->num_physcam_settings; j++) {
                 if (request->physcam_settings != nullptr) {
                     size_t settingsSize = get_camera_metadata_size(request->physcam_settings[j]);
                     if (mRequestMetadataQueue != nullptr && mRequestMetadataQueue->write(
                                 reinterpret_cast<const uint8_t*>(request->physcam_settings[j]),
                                 settingsSize)) {
-                        captureRequests_3_4[i].physicalCameraSettings[j].settings.resize(0);
-                        captureRequests_3_4[i].physicalCameraSettings[j].fmqSettingsSize =
-                            settingsSize;
+                        physicalCameraSettings[j].settings.resize(0);
+                        physicalCameraSettings[j].fmqSettingsSize = settingsSize;
                     } else {
                         if (mRequestMetadataQueue != nullptr) {
                             ALOGW("%s: couldn't utilize fmq, fallback to hwbinder", __FUNCTION__);
                         }
-                        captureRequests_3_4[i].physicalCameraSettings[j].settings.setToExternal(
+                        physicalCameraSettings[j].settings.setToExternal(
                                 reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(
                                         request->physcam_settings[j])),
                                 get_camera_metadata_size(request->physcam_settings[j]));
-                        captureRequests_3_4[i].physicalCameraSettings[j].fmqSettingsSize = 0u;
+                        physicalCameraSettings[j].fmqSettingsSize = 0u;
                     }
                 } else {
-                    captureRequests_3_4[i].physicalCameraSettings[j].fmqSettingsSize = 0u;
-                    captureRequests_3_4[i].physicalCameraSettings[j].settings.resize(0);
+                    physicalCameraSettings[j].fmqSettingsSize = 0u;
+                    physicalCameraSettings[j].settings.resize(0);
                 }
-                captureRequests_3_4[i].physicalCameraSettings[j].physicalCameraId =
-                    request->physcam_id[j];
+                physicalCameraSettings[j].physicalCameraId = request->physcam_id[j];
             }
         }
     }
@@ -3646,7 +3704,10 @@
                 status = s;
                 *numRequestProcessed = n;
         };
-    if (hidlSession_3_4 != nullptr) {
+    if (hidlSession_3_7 != nullptr) {
+        err = hidlSession_3_7->processCaptureRequest_3_7(captureRequests_3_7, cachesToRemove,
+                                                         resultCallback);
+    } else if (hidlSession_3_4 != nullptr) {
         err = hidlSession_3_4->processCaptureRequest_3_4(captureRequests_3_4, cachesToRemove,
                                                          resultCallback);
     } else {
@@ -4055,8 +4116,9 @@
             // Abort the input buffers for reprocess requests.
             if ((*it)->mInputStream != NULL) {
                 camera_stream_buffer_t inputBuffer;
+                camera3::Size inputBufferSize;
                 status_t res = (*it)->mInputStream->getInputBuffer(&inputBuffer,
-                        /*respectHalLimit*/ false);
+                        &inputBufferSize, /*respectHalLimit*/ false);
                 if (res != OK) {
                     ALOGW("%s: %d: couldn't get input buffer while clearing the request "
                             "list: %s (%d)", __FUNCTION__, __LINE__, strerror(-res), res);
@@ -4262,33 +4324,34 @@
 
 void Camera3Device::RequestThread::updateNextRequest(NextRequest& nextRequest) {
     // Update the latest request sent to HAL
-    if (nextRequest.halRequest.settings != NULL) { // Don't update if they were unchanged
+    camera_capture_request_t& halRequest = nextRequest.halRequest;
+    if (halRequest.settings != NULL) { // Don't update if they were unchanged
         Mutex::Autolock al(mLatestRequestMutex);
 
-        camera_metadata_t* cloned = clone_camera_metadata(nextRequest.halRequest.settings);
+        camera_metadata_t* cloned = clone_camera_metadata(halRequest.settings);
         mLatestRequest.acquire(cloned);
 
         mLatestPhysicalRequest.clear();
-        for (uint32_t i = 0; i < nextRequest.halRequest.num_physcam_settings; i++) {
-            cloned = clone_camera_metadata(nextRequest.halRequest.physcam_settings[i]);
-            mLatestPhysicalRequest.emplace(nextRequest.halRequest.physcam_id[i],
+        for (uint32_t i = 0; i < halRequest.num_physcam_settings; i++) {
+            cloned = clone_camera_metadata(halRequest.physcam_settings[i]);
+            mLatestPhysicalRequest.emplace(halRequest.physcam_id[i],
                     CameraMetadata(cloned));
         }
 
         sp<Camera3Device> parent = mParent.promote();
         if (parent != NULL) {
             parent->monitorMetadata(TagMonitor::REQUEST,
-                    nextRequest.halRequest.frame_number,
+                    halRequest.frame_number,
                     0, mLatestRequest, mLatestPhysicalRequest);
         }
     }
 
-    if (nextRequest.halRequest.settings != NULL) {
+    if (halRequest.settings != NULL) {
         nextRequest.captureRequest->mSettingsList.begin()->metadata.unlock(
-                nextRequest.halRequest.settings);
+                halRequest.settings);
     }
 
-    cleanupPhysicalSettings(nextRequest.captureRequest, &nextRequest.halRequest);
+    cleanupPhysicalSettings(nextRequest.captureRequest, &halRequest);
 }
 
 bool Camera3Device::RequestThread::updateSessionParameters(const CameraMetadata& settings) {
@@ -4651,6 +4714,9 @@
         // Fill in buffers
         if (captureRequest->mInputStream != NULL) {
             halRequest->input_buffer = &captureRequest->mInputBuffer;
+
+            halRequest->input_width = captureRequest->mInputBufferSize.width;
+            halRequest->input_height = captureRequest->mInputBufferSize.height;
             totalNumBuffers += 1;
         } else {
             halRequest->input_buffer = NULL;
@@ -4754,13 +4820,7 @@
             }
 
             String8 physicalCameraId = outputStream->getPhysicalCameraId();
-
             if (!physicalCameraId.isEmpty()) {
-                // Physical stream isn't supported for input request.
-                if (halRequest->input_buffer) {
-                    CLOGE("Physical stream is not supported for input request");
-                    return INVALID_OPERATION;
-                }
                 requestedPhysicalCameras.insert(physicalCameraId);
             }
             halRequest->num_output_buffers++;
@@ -5237,7 +5297,8 @@
         // Since RequestThread::clear() removes buffers from the input stream,
         // get the right buffer here before unlocking mRequestLock
         if (nextRequest->mInputStream != NULL) {
-            res = nextRequest->mInputStream->getInputBuffer(&nextRequest->mInputBuffer);
+            res = nextRequest->mInputStream->getInputBuffer(&nextRequest->mInputBuffer,
+                    &nextRequest->mInputBufferSize);
             if (res != OK) {
                 // Can't get input buffer from gralloc queue - this could be due to
                 // disconnected queue or other producer misbehavior, so not a fatal
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 09fa30a..018dbe5 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -35,6 +35,7 @@
 #include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
 #include <android/hardware/camera/device/3.5/ICameraDeviceSession.h>
 #include <android/hardware/camera/device/3.6/ICameraDeviceSession.h>
+#include <android/hardware/camera/device/3.7/ICameraDeviceSession.h>
 #include <android/hardware/camera/device/3.2/ICameraDeviceCallback.h>
 #include <android/hardware/camera/device/3.4/ICameraDeviceCallback.h>
 #include <android/hardware/camera/device/3.5/ICameraDeviceCallback.h>
@@ -52,6 +53,7 @@
 #include "device3/InFlightRequest.h"
 #include "device3/Camera3OutputInterface.h"
 #include "device3/Camera3OfflineSession.h"
+#include "device3/Camera3StreamInterface.h"
 #include "utils/TagMonitor.h"
 #include "utils/LatencyHistogram.h"
 #include <camera_metadata_hidden.h>
@@ -71,7 +73,6 @@
 
 class Camera3Stream;
 class Camera3ZslStream;
-class Camera3OutputStreamInterface;
 class Camera3StreamInterface;
 
 } // namespace camera3
@@ -133,17 +134,19 @@
             const String8& physicalCameraId,
             std::vector<int> *surfaceIds = nullptr,
             int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
-            bool isShared = false, uint64_t consumerUsage = 0) override;
+            bool isShared = false, bool isMultiResolution = false,
+            uint64_t consumerUsage = 0) override;
     status_t createStream(const std::vector<sp<Surface>>& consumers,
             bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
             android_dataspace dataSpace, camera_stream_rotation_t rotation, int *id,
             const String8& physicalCameraId,
             std::vector<int> *surfaceIds = nullptr,
             int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
-            bool isShared = false, uint64_t consumerUsage = 0) override;
+            bool isShared = false, bool isMultiResolution = false,
+            uint64_t consumerUsage = 0) override;
 
     status_t createInputStream(
-            uint32_t width, uint32_t height, int format,
+            uint32_t width, uint32_t height, int format, bool isMultiResolution,
             int *id) override;
 
     status_t getStreamInfo(int id, StreamInfo *streamInfo) override;
@@ -418,6 +421,8 @@
         sp<hardware::camera::device::V3_5::ICameraDeviceSession> mHidlSession_3_5;
         // Valid if ICameraDeviceSession is @3.6 or newer
         sp<hardware::camera::device::V3_6::ICameraDeviceSession> mHidlSession_3_6;
+        // Valid if ICameraDeviceSession is @3.7 or newer
+        sp<hardware::camera::device::V3_7::ICameraDeviceSession> mHidlSession_3_7;
 
         std::shared_ptr<RequestMetadataQueue> mRequestMetadataQueue;
 
@@ -490,6 +495,7 @@
 
     camera3::StreamSet         mOutputStreams;
     sp<camera3::Camera3Stream> mInputStream;
+    bool                       mIsInputStreamMultiResolution;
     SessionStatsBuilder        mSessionStatsBuilder;
 
     int                        mNextStreamId;
@@ -523,6 +529,7 @@
         PhysicalCameraSettingsList          mSettingsList;
         sp<camera3::Camera3Stream>          mInputStream;
         camera_stream_buffer_t              mInputBuffer;
+        camera3::Size                       mInputBufferSize;
         Vector<sp<camera3::Camera3OutputStreamInterface> >
                                             mOutputStreams;
         SurfaceMap                          mOutputSurfaces;
@@ -748,7 +755,7 @@
      * Helper function to get the largest Jpeg resolution (in area)
      * Return Size(0, 0) if static metatdata is invalid
      */
-    Size getMaxJpegResolution() const;
+    camera3::Size getMaxJpegResolution() const;
 
     /**
      * Helper function to get the offset between MONOTONIC and BOOTTIME
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index f6acda8..a837900 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -32,10 +32,10 @@
 Camera3IOStreamBase::Camera3IOStreamBase(int id, camera_stream_type_t type,
         uint32_t width, uint32_t height, size_t maxSize, int format,
         android_dataspace dataSpace, camera_stream_rotation_t rotation,
-        const String8& physicalCameraId, int setId) :
+        const String8& physicalCameraId, int setId, bool isMultiResolution) :
         Camera3Stream(id, type,
                 width, height, maxSize, format, dataSpace, rotation,
-                physicalCameraId, setId),
+                physicalCameraId, setId, isMultiResolution),
         mTotalBufferCount(0),
         mHandoutTotalBufferCount(0),
         mHandoutOutputBufferCount(0),
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
index 719fa14..2e744ee 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
@@ -36,7 +36,7 @@
             uint32_t width, uint32_t height, size_t maxSize, int format,
             android_dataspace dataSpace, camera_stream_rotation_t rotation,
             const String8& physicalCameraId,
-            int setId = CAMERA3_STREAM_SET_ID_INVALID);
+            int setId = CAMERA3_STREAM_SET_ID_INVALID, bool isMultiResolution = false);
 
   public:
 
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index ad70a3a..b00a963 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -46,10 +46,14 @@
 }
 
 status_t Camera3InputStream::getInputBufferLocked(
-        camera_stream_buffer *buffer) {
+        camera_stream_buffer *buffer, Size *size) {
     ATRACE_CALL();
     status_t res;
 
+    if (size == nullptr) {
+        ALOGE("%s: size must not be null", __FUNCTION__);
+        return BAD_VALUE;
+    }
     // FIXME: will not work in (re-)registration
     if (mState == STATE_IN_CONFIG || mState == STATE_IN_RECONFIG) {
         ALOGE("%s: Stream %d: Buffer registration for input streams"
@@ -77,10 +81,12 @@
         return res;
     }
 
+    size->width  = bufferItem.mGraphicBuffer->getWidth();
+    size->height = bufferItem.mGraphicBuffer->getHeight();
+
     anb = bufferItem.mGraphicBuffer->getNativeBuffer();
     assert(anb != NULL);
     fenceFd = bufferItem.mFence->dup();
-
     /**
      * FenceFD now owned by HAL except in case of error,
      * in which case we reassign it to acquire_fence
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.h b/services/camera/libcameraservice/device3/Camera3InputStream.h
index 03afa17..46221d1 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.h
@@ -70,7 +70,7 @@
      * Camera3Stream interface
      */
 
-    virtual status_t getInputBufferLocked(camera_stream_buffer *buffer);
+    virtual status_t getInputBufferLocked(camera_stream_buffer *buffer, Size *size);
     virtual status_t returnInputBufferLocked(
             const camera_stream_buffer &buffer);
     virtual status_t getInputBufferProducerLocked(
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index c835f51..3ec3b6b 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -44,10 +44,10 @@
         uint32_t width, uint32_t height, int format,
         android_dataspace dataSpace, camera_stream_rotation_t rotation,
         nsecs_t timestampOffset, const String8& physicalCameraId,
-        int setId) :
+        int setId, bool isMultiResolution) :
         Camera3IOStreamBase(id, CAMERA_STREAM_OUTPUT, width, height,
                             /*maxSize*/0, format, dataSpace, rotation,
-                            physicalCameraId, setId),
+                            physicalCameraId, setId, isMultiResolution),
         mConsumer(consumer),
         mTransform(0),
         mTraceFirstBuffer(true),
@@ -70,9 +70,11 @@
         sp<Surface> consumer,
         uint32_t width, uint32_t height, size_t maxSize, int format,
         android_dataspace dataSpace, camera_stream_rotation_t rotation,
-        nsecs_t timestampOffset, const String8& physicalCameraId, int setId) :
+        nsecs_t timestampOffset, const String8& physicalCameraId, int setId,
+        bool isMultiResolution) :
         Camera3IOStreamBase(id, CAMERA_STREAM_OUTPUT, width, height, maxSize,
-                            format, dataSpace, rotation, physicalCameraId, setId),
+                            format, dataSpace, rotation, physicalCameraId, setId,
+                            isMultiResolution),
         mConsumer(consumer),
         mTransform(0),
         mTraceFirstBuffer(true),
@@ -102,10 +104,10 @@
         uint32_t width, uint32_t height, int format,
         uint64_t consumerUsage, android_dataspace dataSpace,
         camera_stream_rotation_t rotation, nsecs_t timestampOffset,
-        const String8& physicalCameraId, int setId) :
+        const String8& physicalCameraId, int setId, bool isMultiResolution) :
         Camera3IOStreamBase(id, CAMERA_STREAM_OUTPUT, width, height,
                             /*maxSize*/0, format, dataSpace, rotation,
-                            physicalCameraId, setId),
+                            physicalCameraId, setId, isMultiResolution),
         mConsumer(nullptr),
         mTransform(0),
         mTraceFirstBuffer(true),
@@ -141,11 +143,11 @@
                                          camera_stream_rotation_t rotation,
                                          const String8& physicalCameraId,
                                          uint64_t consumerUsage, nsecs_t timestampOffset,
-                                         int setId) :
+                                         int setId, bool isMultiResolution) :
         Camera3IOStreamBase(id, type, width, height,
                             /*maxSize*/0,
                             format, dataSpace, rotation,
-                            physicalCameraId, setId),
+                            physicalCameraId, setId, isMultiResolution),
         mTransform(0),
         mTraceFirstBuffer(true),
         mUseMonoTimestamp(false),
@@ -570,10 +572,12 @@
             !(isConsumedByHWComposer() || isConsumedByHWTexture())) {
         uint64_t consumerUsage = 0;
         getEndpointUsage(&consumerUsage);
+        uint32_t width = (mMaxSize == 0) ? getWidth() : mMaxSize;
+        uint32_t height = (mMaxSize == 0) ? getHeight() : 1;
         StreamInfo streamInfo(
-                getId(), getStreamSetId(), getWidth(), getHeight(), getFormat(), getDataSpace(),
+                getId(), getStreamSetId(), width, height, getFormat(), getDataSpace(),
                 mUsage | consumerUsage, mTotalBufferCount,
-                /*isConfigured*/true);
+                /*isConfigured*/true, isMultiResolution());
         wp<Camera3OutputStream> weakThis(this);
         res = mBufferManager->registerStream(weakThis,
                 streamInfo);
@@ -604,7 +608,8 @@
 
     if (mUseBufferManager) {
         sp<GraphicBuffer> gb;
-        res = mBufferManager->getBufferForStream(getId(), getStreamSetId(), &gb, fenceFd);
+        res = mBufferManager->getBufferForStream(getId(), getStreamSetId(),
+                isMultiResolution(), &gb, fenceFd);
         if (res == OK) {
             // Attach this buffer to the bufferQueue: the buffer will be in dequeue state after a
             // successful return.
@@ -693,7 +698,8 @@
 
             sp<GraphicBuffer> gb;
             res = mBufferManager->getBufferForStream(
-                    getId(), getStreamSetId(), &gb, fenceFd, /*noFreeBuffer*/true);
+                    getId(), getStreamSetId(), isMultiResolution(),
+                    &gb, fenceFd, /*noFreeBuffer*/true);
 
             if (res == OK) {
                 // Attach this buffer to the bufferQueue: the buffer will be in dequeue state after
@@ -740,7 +746,8 @@
         onBuffersRemovedLocked(removedBuffers);
 
         if (notifyBufferManager && mUseBufferManager && removedBuffers.size() > 0) {
-            mBufferManager->onBuffersRemoved(getId(), getStreamSetId(), removedBuffers.size());
+            mBufferManager->onBuffersRemoved(getId(), getStreamSetId(), isMultiResolution(),
+                    removedBuffers.size());
         }
     }
 }
@@ -802,7 +809,7 @@
     // Since device is already idle, there is no getBuffer call to buffer manager, unregister the
     // stream at this point should be safe.
     if (mUseBufferManager) {
-        res = mBufferManager->unregisterStream(getId(), getStreamSetId());
+        res = mBufferManager->unregisterStream(getId(), getStreamSetId(), isMultiResolution());
         if (res != OK) {
             ALOGE("%s: Unable to unregister stream %d from buffer manager "
                     "(error %d %s)", __FUNCTION__, mId, res, strerror(-res));
@@ -914,7 +921,8 @@
     ALOGV("Stream %d: Buffer released", stream->getId());
     bool shouldFreeBuffer = false;
     status_t res = stream->mBufferManager->onBufferReleased(
-        stream->getId(), stream->getStreamSetId(), &shouldFreeBuffer);
+        stream->getId(), stream->getStreamSetId(), stream->isMultiResolution(),
+        &shouldFreeBuffer);
     if (res != OK) {
         ALOGE("%s: signaling buffer release to buffer manager failed: %s (%d).", __FUNCTION__,
                 strerror(-res), res);
@@ -927,7 +935,7 @@
         stream->detachBufferLocked(&buffer, /*fenceFd*/ nullptr);
         if (buffer.get() != nullptr) {
             stream->mBufferManager->notifyBufferRemoved(
-                    stream->getId(), stream->getStreamSetId());
+                    stream->getId(), stream->getStreamSetId(), stream->isMultiResolution());
         }
     }
 }
@@ -945,7 +953,7 @@
         stream->onBuffersRemovedLocked(buffers);
         if (stream->mUseBufferManager) {
             stream->mBufferManager->onBuffersRemoved(stream->getId(),
-                    stream->getStreamSetId(), buffers.size());
+                    stream->getStreamSetId(), stream->isMultiResolution(), buffers.size());
         }
         ALOGV("Stream %d: %zu Buffers discarded.", stream->getId(), buffers.size());
     }
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 366d22a..c82f2a6 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -48,6 +48,7 @@
     uint64_t combinedUsage;
     size_t totalBufferCount;
     bool isConfigured;
+    bool isMultiRes;
     explicit StreamInfo(int id = CAMERA3_STREAM_ID_INVALID,
             int setId = CAMERA3_STREAM_SET_ID_INVALID,
             uint32_t w = 0,
@@ -56,7 +57,8 @@
             android_dataspace ds = HAL_DATASPACE_UNKNOWN,
             uint64_t usage = 0,
             size_t bufferCount = 0,
-            bool configured = false) :
+            bool configured = false,
+            bool multiRes = false) :
                 streamId(id),
                 streamSetId(setId),
                 width(w),
@@ -65,7 +67,8 @@
                 dataSpace(ds),
                 combinedUsage(usage),
                 totalBufferCount(bufferCount),
-                isConfigured(configured){}
+                isConfigured(configured),
+                isMultiRes(multiRes) {}
 };
 
 /**
@@ -84,7 +87,7 @@
             uint32_t width, uint32_t height, int format,
             android_dataspace dataSpace, camera_stream_rotation_t rotation,
             nsecs_t timestampOffset, const String8& physicalCameraId,
-            int setId = CAMERA3_STREAM_SET_ID_INVALID);
+            int setId = CAMERA3_STREAM_SET_ID_INVALID, bool isMultiResolution = false);
 
     /**
      * Set up a stream for formats that have a variable buffer size for the same
@@ -96,7 +99,7 @@
             uint32_t width, uint32_t height, size_t maxSize, int format,
             android_dataspace dataSpace, camera_stream_rotation_t rotation,
             nsecs_t timestampOffset, const String8& physicalCameraId,
-            int setId = CAMERA3_STREAM_SET_ID_INVALID);
+            int setId = CAMERA3_STREAM_SET_ID_INVALID, bool isMultiResolution = false);
 
     /**
      * Set up a stream with deferred consumer for formats that have 2 dimensions, such as
@@ -107,7 +110,7 @@
             uint64_t consumerUsage, android_dataspace dataSpace,
             camera_stream_rotation_t rotation, nsecs_t timestampOffset,
             const String8& physicalCameraId,
-            int setId = CAMERA3_STREAM_SET_ID_INVALID);
+            int setId = CAMERA3_STREAM_SET_ID_INVALID, bool isMultiResolution = false);
 
     virtual ~Camera3OutputStream();
 
@@ -232,7 +235,7 @@
             android_dataspace dataSpace, camera_stream_rotation_t rotation,
             const String8& physicalCameraId,
             uint64_t consumerUsage = 0, nsecs_t timestampOffset = 0,
-            int setId = CAMERA3_STREAM_SET_ID_INVALID);
+            int setId = CAMERA3_STREAM_SET_ID_INVALID, bool isMultiResolution = false);
 
     /**
      * Note that we release the lock briefly in this function
diff --git a/services/camera/libcameraservice/device3/Camera3OutputUtils.h b/services/camera/libcameraservice/device3/Camera3OutputUtils.h
index 772fe6e..142889a 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputUtils.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputUtils.h
@@ -46,6 +46,7 @@
         uint32_t num_streams;
         camera_stream_t **streams;
         uint32_t operation_mode;
+        bool input_is_multi_resolution;
     } camera_stream_configuration_t;
 
     typedef struct camera_capture_request {
@@ -57,6 +58,8 @@
         uint32_t num_physcam_settings;
         const char **physcam_id;
         const camera_metadata_t **physcam_settings;
+        int32_t input_width;
+        int32_t input_height;
     } camera_capture_request_t;
 
     typedef struct camera_capture_result {
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 4cb954e..c6e7002 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -49,7 +49,7 @@
         camera_stream_type type,
         uint32_t width, uint32_t height, size_t maxSize, int format,
         android_dataspace dataSpace, camera_stream_rotation_t rotation,
-        const String8& physicalCameraId, int setId) :
+        const String8& physicalCameraId, int setId, bool isMultiResolution) :
     camera_stream(),
     mId(id),
     mSetId(setId),
@@ -73,7 +73,8 @@
     mDataSpaceOverridden(false),
     mOriginalDataSpace(dataSpace),
     mPhysicalCameraId(physicalCameraId),
-    mLastTimestamp(0) {
+    mLastTimestamp(0),
+    mIsMultiResolution(isMultiResolution) {
 
     camera_stream::stream_type = type;
     camera_stream::width = width;
@@ -99,6 +100,14 @@
     return mSetId;
 }
 
+int Camera3Stream::getHalStreamGroupId() const {
+    return mIsMultiResolution ? mSetId : -1;
+}
+
+bool Camera3Stream::isMultiResolution() const {
+    return mIsMultiResolution;
+}
+
 uint32_t Camera3Stream::getWidth() const {
     return camera_stream::width;
 }
@@ -743,11 +752,16 @@
     return res;
 }
 
-status_t Camera3Stream::getInputBuffer(camera_stream_buffer *buffer, bool respectHalLimit) {
+status_t Camera3Stream::getInputBuffer(camera_stream_buffer *buffer,
+        Size* size, bool respectHalLimit) {
     ATRACE_CALL();
     Mutex::Autolock l(mLock);
     status_t res = OK;
 
+    if (size == nullptr) {
+        ALOGE("%s: size must not be null", __FUNCTION__);
+        return BAD_VALUE;
+    }
     // This function should be only called when the stream is configured already.
     if (mState != STATE_CONFIGURED) {
         ALOGE("%s: Stream %d: Can't get input buffers if stream is not in CONFIGURED state %d",
@@ -769,7 +783,7 @@
         }
     }
 
-    res = getInputBufferLocked(buffer);
+    res = getInputBufferLocked(buffer, size);
     if (res == OK) {
         fireBufferListenersLocked(*buffer, /*acquired*/true, /*output*/false);
         if (buffer->buffer) {
@@ -918,7 +932,7 @@
     ALOGE("%s: This type of stream does not support output", __FUNCTION__);
     return INVALID_OPERATION;
 }
-status_t Camera3Stream::getInputBufferLocked(camera_stream_buffer *) {
+status_t Camera3Stream::getInputBufferLocked(camera_stream_buffer *, Size *) {
     ALOGE("%s: This type of stream does not support input", __FUNCTION__);
     return INVALID_OPERATION;
 }
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 55ed2f2..45d8478 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -147,6 +147,14 @@
      * Get the output stream set id.
      */
     int              getStreamSetId() const;
+    /**
+     * Is this stream part of a multi-resolution stream set
+     */
+    bool             isMultiResolution() const;
+    /**
+     * Get the HAL stream group id for a multi-resolution stream set
+     */
+    int              getHalStreamGroupId() const;
 
     /**
      * Get the stream's dimensions and format
@@ -356,10 +364,13 @@
      * For bidirectional streams, this method applies to the input-side
      * buffers.
      *
+     * This method also returns the size of the returned input buffer.
+     *
      * Normally this call will block until the handed out buffer count is less than the stream
      * max buffer count; if respectHalLimit is set to false, this is ignored.
      */
-    status_t         getInputBuffer(camera_stream_buffer *buffer, bool respectHalLimit = true);
+    status_t         getInputBuffer(camera_stream_buffer *buffer,
+                             Size* size, bool respectHalLimit = true);
 
     /**
      * Return a buffer to the stream after use by the HAL.
@@ -487,7 +498,7 @@
     Camera3Stream(int id, camera_stream_type type,
             uint32_t width, uint32_t height, size_t maxSize, int format,
             android_dataspace dataSpace, camera_stream_rotation_t rotation,
-            const String8& physicalCameraId, int setId);
+            const String8& physicalCameraId, int setId, bool isMultiResolution);
 
     wp<Camera3StreamBufferFreedListener> mBufferFreedListener;
 
@@ -509,7 +520,7 @@
 
     virtual status_t getBuffersLocked(std::vector<OutstandingBuffer>*);
 
-    virtual status_t getInputBufferLocked(camera_stream_buffer *buffer);
+    virtual status_t getInputBufferLocked(camera_stream_buffer *buffer, Size* size);
 
     virtual status_t returnInputBufferLocked(
             const camera_stream_buffer &buffer);
@@ -608,6 +619,7 @@
     String8 mPhysicalCameraId;
     nsecs_t mLastTimestamp;
 
+    bool mIsMultiResolution = false;
     bool mSupportOfflineProcessing = false;
 }; // class Camera3Stream
 
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index c558b07..a567cb4 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -72,6 +72,12 @@
     int release_fence;
 } camera_stream_buffer_t;
 
+struct Size {
+    uint32_t width;
+    uint32_t height;
+    explicit Size(uint32_t w = 0, uint32_t h = 0) : width(w), height(h){}
+};
+
 enum {
     /**
      * This stream set ID indicates that the set ID is invalid, and this stream doesn't intend to
@@ -352,7 +358,8 @@
      * Normally this call will block until the handed out buffer count is less than the stream
      * max buffer count; if respectHalLimit is set to false, this is ignored.
      */
-    virtual status_t getInputBuffer(camera_stream_buffer *buffer, bool respectHalLimit = true) = 0;
+    virtual status_t getInputBuffer(camera_stream_buffer *buffer,
+            Size *size, bool respectHalLimit = true) = 0;
 
     /**
      * Return a buffer to the stream after use by the HAL.
diff --git a/services/camera/libcameraservice/libcameraservice_fuzzer/Android.bp b/services/camera/libcameraservice/libcameraservice_fuzzer/Android.bp
index d06b2c5..c7d7c4b 100644
--- a/services/camera/libcameraservice/libcameraservice_fuzzer/Android.bp
+++ b/services/camera/libcameraservice/libcameraservice_fuzzer/Android.bp
@@ -50,12 +50,14 @@
         "android.hardware.camera.provider@2.4",
         "android.hardware.camera.provider@2.5",
         "android.hardware.camera.provider@2.6",
+        "android.hardware.camera.provider@2.7",
         "android.hardware.camera.device@1.0",
         "android.hardware.camera.device@3.2",
         "android.hardware.camera.device@3.3",
         "android.hardware.camera.device@3.4",
         "android.hardware.camera.device@3.5",
         "android.hardware.camera.device@3.6",
+        "android.hardware.camera.device@3.7",
     ],
     fuzz_config: {
         cc: [
diff --git a/services/camera/libcameraservice/tests/Android.mk b/services/camera/libcameraservice/tests/Android.mk
index b530342..0b5ad79 100644
--- a/services/camera/libcameraservice/tests/Android.mk
+++ b/services/camera/libcameraservice/tests/Android.mk
@@ -33,9 +33,11 @@
     android.hardware.camera.provider@2.4 \
     android.hardware.camera.provider@2.5 \
     android.hardware.camera.provider@2.6 \
+    android.hardware.camera.provider@2.7 \
     android.hardware.camera.device@1.0 \
     android.hardware.camera.device@3.2 \
     android.hardware.camera.device@3.4 \
+    android.hardware.camera.device@3.7 \
     android.hidl.token@1.0-utils
 
 LOCAL_STATIC_LIBRARIES := \
diff --git a/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp b/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
index c28f427..8f42a85 100644
--- a/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
+++ b/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
@@ -262,23 +262,24 @@
 
 void SessionConfigurationUtils::mapStreamInfo(const OutputStreamInfo &streamInfo,
             camera3::camera_stream_rotation_t rotation, String8 physicalId,
-            hardware::camera::device::V3_4::Stream *stream /*out*/) {
+            int32_t groupId, hardware::camera::device::V3_7::Stream *stream /*out*/) {
     if (stream == nullptr) {
         return;
     }
 
-    stream->v3_2.streamType = hardware::camera::device::V3_2::StreamType::OUTPUT;
-    stream->v3_2.width = streamInfo.width;
-    stream->v3_2.height = streamInfo.height;
-    stream->v3_2.format = Camera3Device::mapToPixelFormat(streamInfo.format);
+    stream->v3_4.v3_2.streamType = hardware::camera::device::V3_2::StreamType::OUTPUT;
+    stream->v3_4.v3_2.width = streamInfo.width;
+    stream->v3_4.v3_2.height = streamInfo.height;
+    stream->v3_4.v3_2.format = Camera3Device::mapToPixelFormat(streamInfo.format);
     auto u = streamInfo.consumerUsage;
     camera3::Camera3OutputStream::applyZSLUsageQuirk(streamInfo.format, &u);
-    stream->v3_2.usage = Camera3Device::mapToConsumerUsage(u);
-    stream->v3_2.dataSpace = Camera3Device::mapToHidlDataspace(streamInfo.dataSpace);
-    stream->v3_2.rotation = Camera3Device::mapToStreamRotation(rotation);
-    stream->v3_2.id = -1; // Invalid stream id
-    stream->physicalCameraId = std::string(physicalId.string());
-    stream->bufferSize = 0;
+    stream->v3_4.v3_2.usage = Camera3Device::mapToConsumerUsage(u);
+    stream->v3_4.v3_2.dataSpace = Camera3Device::mapToHidlDataspace(streamInfo.dataSpace);
+    stream->v3_4.v3_2.rotation = Camera3Device::mapToStreamRotation(rotation);
+    stream->v3_4.v3_2.id = -1; // Invalid stream id
+    stream->v3_4.physicalCameraId = std::string(physicalId.string());
+    stream->v3_4.bufferSize = 0;
+    stream->groupId = groupId;
 }
 
 binder::Status SessionConfigurationUtils::checkPhysicalCameraId(
@@ -358,7 +359,7 @@
         const SessionConfiguration& sessionConfiguration,
         const String8 &logicalCameraId, const CameraMetadata &deviceInfo,
         metadataGetter getMetadata, const std::vector<std::string> &physicalCameraIds,
-        hardware::camera::device::V3_4::StreamConfiguration &streamConfiguration, bool *earlyExit) {
+        hardware::camera::device::V3_7::StreamConfiguration &streamConfiguration, bool *earlyExit) {
 
     auto operatingMode = sessionConfiguration.getOperatingMode();
     binder::Status res = checkOperatingMode(operatingMode, deviceInfo, logicalCameraId);
@@ -393,14 +394,16 @@
     streamConfiguration.streams.resize(streamCount);
     size_t streamIdx = 0;
     if (isInputValid) {
-        streamConfiguration.streams[streamIdx++] = {{/*streamId*/0,
+        streamConfiguration.streams[streamIdx++] = {{{/*streamId*/0,
                 hardware::camera::device::V3_2::StreamType::INPUT,
                 static_cast<uint32_t> (sessionConfiguration.getInputWidth()),
                 static_cast<uint32_t> (sessionConfiguration.getInputHeight()),
                 Camera3Device::mapToPixelFormat(sessionConfiguration.getInputFormat()),
                 /*usage*/ 0, HAL_DATASPACE_UNKNOWN,
                 hardware::camera::device::V3_2::StreamRotation::ROTATION_0},
-                /*physicalId*/ nullptr, /*bufferSize*/0};
+                /*physicalId*/ nullptr, /*bufferSize*/0}, /*groupId*/-1};
+        streamConfiguration.multiResolutionInputImage =
+                sessionConfiguration.inputIsMultiResolution();
     }
 
     for (const auto &it : outputConfigs) {
@@ -410,6 +413,7 @@
         String8 physicalCameraId = String8(it.getPhysicalCameraId());
         size_t numBufferProducers = bufferProducers.size();
         bool isStreamInfoValid = false;
+        int32_t groupId = it.isMultiResolution() ? it.getSurfaceSetID() : -1;
         OutputStreamInfo streamInfo;
 
         res = checkSurfaceType(numBufferProducers, deferredConsumer, it.getSurfaceType());
@@ -432,7 +436,7 @@
             if (surfaceType == OutputConfiguration::SURFACE_TYPE_SURFACE_VIEW) {
                 streamInfo.consumerUsage |= GraphicBuffer::USAGE_HW_COMPOSER;
             }
-            mapStreamInfo(streamInfo, camera3::CAMERA_STREAM_ROTATION_0, physicalCameraId,
+            mapStreamInfo(streamInfo, camera3::CAMERA_STREAM_ROTATION_0, physicalCameraId, groupId,
                     &streamConfiguration.streams[streamIdx++]);
             isStreamInfoValid = true;
 
@@ -488,12 +492,13 @@
                     for (const auto& compositeStream : compositeStreams) {
                         mapStreamInfo(compositeStream,
                                 static_cast<camera_stream_rotation_t> (it.getRotation()),
-                                physicalCameraId, &streamConfiguration.streams[streamIdx++]);
+                                physicalCameraId, groupId,
+                                &streamConfiguration.streams[streamIdx++]);
                     }
                 } else {
                     mapStreamInfo(streamInfo,
                             static_cast<camera_stream_rotation_t> (it.getRotation()),
-                            physicalCameraId, &streamConfiguration.streams[streamIdx++]);
+                            physicalCameraId, groupId, &streamConfiguration.streams[streamIdx++]);
                 }
                 isStreamInfoValid = true;
             }
@@ -503,4 +508,27 @@
 
 }
 
+bool SessionConfigurationUtils::convertHALStreamCombinationFromV37ToV34(
+        hardware::camera::device::V3_4::StreamConfiguration &streamConfigV34,
+        const hardware::camera::device::V3_7::StreamConfiguration &streamConfigV37) {
+    if (streamConfigV37.multiResolutionInputImage) {
+        // ICameraDevice older than 3.7 doesn't support multi-resolution input image.
+        return false;
+    }
+
+    streamConfigV34.streams.resize(streamConfigV37.streams.size());
+    for (size_t i = 0; i < streamConfigV37.streams.size(); i++) {
+        if (streamConfigV37.streams[i].groupId != -1) {
+            // ICameraDevice older than 3.7 doesn't support multi-resolution output
+            // image
+            return false;
+        }
+        streamConfigV34.streams[i] = streamConfigV37.streams[i].v3_4;
+    }
+    streamConfigV34.operationMode = streamConfigV37.operationMode;
+    streamConfigV34.sessionParams = streamConfigV37.sessionParams;
+
+    return true;
+}
+
 }// namespace android
diff --git a/services/camera/libcameraservice/utils/SessionConfigurationUtils.h b/services/camera/libcameraservice/utils/SessionConfigurationUtils.h
index 6ac7ab4..36e1dd7 100644
--- a/services/camera/libcameraservice/utils/SessionConfigurationUtils.h
+++ b/services/camera/libcameraservice/utils/SessionConfigurationUtils.h
@@ -21,7 +21,7 @@
 #include <camera/camera2/OutputConfiguration.h>
 #include <camera/camera2/SessionConfiguration.h>
 #include <camera/camera2/SubmitInfo.h>
-#include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
+#include <android/hardware/camera/device/3.7/types.h>
 
 #include <device3/Camera3StreamInterface.h>
 
@@ -53,8 +53,8 @@
         const String8 &cameraId, const CameraMetadata &physicalCameraMetadata);
 
     static void mapStreamInfo(const camera3::OutputStreamInfo &streamInfo,
-            camera3::camera_stream_rotation_t rotation, String8 physicalId,
-            hardware::camera::device::V3_4::Stream *stream /*out*/);
+            camera3::camera_stream_rotation_t rotation, String8 physicalId, int32_t groupId,
+            hardware::camera::device::V3_7::Stream *stream /*out*/);
 
     // Check that the physicalCameraId passed in is spported by the camera
     // device.
@@ -76,9 +76,16 @@
     convertToHALStreamCombination(const SessionConfiguration& sessionConfiguration,
             const String8 &cameraId, const CameraMetadata &deviceInfo,
             metadataGetter getMetadata, const std::vector<std::string> &physicalCameraIds,
-            hardware::camera::device::V3_4::StreamConfiguration &streamConfiguration,
+            hardware::camera::device::V3_7::StreamConfiguration &streamConfiguration,
             bool *earlyExit);
 
+    // Utility function to convert a V3_7::StreamConfiguration to
+    // V3_4::StreamConfiguration. Return false if the original V3_7 configuration cannot
+    // be used by older version HAL.
+    static bool convertHALStreamCombinationFromV37ToV34(
+            hardware::camera::device::V3_4::StreamConfiguration &streamConfigV34,
+            const hardware::camera::device::V3_7::StreamConfiguration &streamConfigV37);
+
     static const int32_t MAX_SURFACES_PER_STREAM = 4;
 
     static const int32_t ROUNDING_WIDTH_CAP = 1920;
diff --git a/services/mediametrics/AudioAnalytics.cpp b/services/mediametrics/AudioAnalytics.cpp
index 9b7da0f..4ef87e4 100644
--- a/services/mediametrics/AudioAnalytics.cpp
+++ b/services/mediametrics/AudioAnalytics.cpp
@@ -138,24 +138,53 @@
     "connection_count",
 };
 
-// static constexpr const char * const AAudioStreamFields[] {
-//     "mediametrics_aaudiostream_reported",
-//     "caller_name",
-//     "path",
-//     "direction",
-//     "frames_per_burst",
-//     "buffer_size",
-//     "buffer_capacity",
-//     "channel_count",
-//     "total_frames_transferred",
-//     "perf_mode_requested",
-//     "perf_mode_actual",
-//     "sharing",
-//     "xrun_count",
-//     "device_type",
-//     "format_app",
-//     "format_device",
-// };
+static constexpr const char * const AAudioStreamFields[] {
+    "mediametrics_aaudiostream_reported",
+    "caller_name",
+    "path",
+    "direction",
+    "frames_per_burst",
+    "buffer_size",
+    "buffer_capacity",
+    "channel_count",
+    "total_frames_transferred",
+    "perf_mode_requested",
+    "perf_mode_actual",
+    "sharing",
+    "xrun_count",
+    "device_type",
+    "format_app",
+    "format_device",
+    "log_session_id",
+};
+
+/**
+ * printFields is a helper method that prints the fields and corresponding values
+ * in a human readable style.
+ */
+template <size_t N, typename ...Types>
+std::string printFields(const char * const (& fields)[N], Types ... args)
+{
+    std::stringstream ss;
+    ss << " { ";
+    stringutils::fieldPrint(ss, fields, args...);
+    ss << "}";
+    return ss.str();
+}
+
+/**
+ * sendToStatsd is a helper method that sends the arguments to statsd
+ */
+template <typename ...Types>
+int sendToStatsd(Types ... args)
+{
+    int result = 0;
+
+#ifdef STATSD_ENABLE
+    result = android::util::stats_write(args...);
+#endif
+    return result;
+}
 
 /**
  * sendToStatsd is a helper method that sends the arguments to statsd
@@ -387,7 +416,7 @@
             } else {
                 ss << "Statsd atoms:\n" << statsd;
             }
-            ll -= n;
+            ll -= (int32_t)n;
         }
     }
 
@@ -793,7 +822,7 @@
         mA2dpConnectionServiceNs = 0;
         ++mA2dpConnectionSuccesses;
 
-        const auto connectionTimeMs = float(timeDiffNs * 1e-6);
+        const auto connectionTimeMs = float((double)timeDiffNs * 1e-6);
 
         const auto outputDeviceBits = types::lookup<types::OUTPUT_DEVICE, long_enum_type_t>(
                 "AUDIO_DEVICE_OUT_BLUETOOTH_A2DP");
@@ -951,7 +980,7 @@
     mAudioAnalytics.mAnalyticsState->timeMachine().get(
             key, AMEDIAMETRICS_PROP_UNDERRUN, &xrunCount);
 
-    std::string deviceType;
+    std::string serializedDeviceTypes;
     // TODO: only routed device id is logged, but no device type
 
     int32_t formatApp = 0;
@@ -962,6 +991,9 @@
             key, AMEDIAMETRICS_PROP_ENCODING, &formatDeviceStr);
     const auto formatDevice = types::lookup<types::ENCODING, int32_t>(formatDeviceStr);
 
+    std::string logSessionId;
+    // TODO: log logSessionId
+
     LOG(LOG_LEVEL) << "key:" << key
             << " caller_name:" << callerName << "(" << callerNameStr << ")"
             << " path:" << path
@@ -975,33 +1007,59 @@
             << " perf_mode_actual:" << perfModeActual
             << " sharing:" << sharingMode << "(" << sharingModeStr << ")"
             << " xrun_count:" << xrunCount
-            << " device_type:" << deviceType
+            << " device_type:" << serializedDeviceTypes
             << " format_app:" << formatApp
-            << " format_device: " << formatDevice << "(" << formatDeviceStr << ")";
+            << " format_device: " << formatDevice << "(" << formatDeviceStr << ")"
+            << " log_session_id: " << logSessionId;
 
-    // TODO: send the metric to statsd when the proto is ready
-    // if (mAudioAnalytics.mDeliverStatistics) {
-    //     const auto [ result, str ] = sendToStatsd(AAudioStreamFields,
-    //             CONDITION(android::util::MEDIAMETRICS_AAUDIOSTREAM_REPORTED)
-    //             , callerName
-    //             , path
-    //             , direction
-    //             , framesPerBurst
-    //             , bufferSizeInFrames
-    //             , bufferCapacityInFrames
-    //             , channelCount
-    //             , totalFramesTransferred
-    //             , perfModeRequested
-    //             , perfModeActual
-    //             , sharingMode
-    //             , xrunCount
-    //             , deviceType.c_str()
-    //             , formatApp
-    //             , formatDevice
-    //             );
-    //     ALOGV("%s: statsd %s", __func__, str.c_str());
-    //     mAudioAnalytics.mStatsdLog.log("%s", str.c_str());
-    // }
+    if (mAudioAnalytics.mDeliverStatistics) {
+        android::util::BytesField bf_serialized(
+            serializedDeviceTypes.c_str(), serializedDeviceTypes.size());
+        const auto result = sendToStatsd(
+                CONDITION(android::util::MEDIAMETRICS_AAUDIOSTREAM_REPORTED)
+                , callerName
+                , path
+                , direction
+                , framesPerBurst
+                , bufferSizeInFrames
+                , bufferCapacityInFrames
+                , channelCount
+                , totalFramesTransferred
+                , perfModeRequested
+                , perfModeActual
+                , sharingMode
+                , xrunCount
+                , bf_serialized
+                , formatApp
+                , formatDevice
+                , logSessionId.c_str()
+                );
+        std::stringstream ss;
+        ss << "result:" << result;
+        const auto fieldsStr = printFields(AAudioStreamFields,
+                CONDITION(android::util::MEDIAMETRICS_AAUDIOSTREAM_REPORTED)
+                , callerName
+                , path
+                , direction
+                , framesPerBurst
+                , bufferSizeInFrames
+                , bufferCapacityInFrames
+                , channelCount
+                , totalFramesTransferred
+                , perfModeRequested
+                , perfModeActual
+                , sharingMode
+                , xrunCount
+                , serializedDeviceTypes.c_str()
+                , formatApp
+                , formatDevice
+                , logSessionId.c_str()
+                );
+        ss << " " << fieldsStr;
+        std::string str = ss.str();
+        ALOGV("%s: statsd %s", __func__, str.c_str());
+        mAudioAnalytics.mStatsdLog.log("%s", str.c_str());
+    }
 }
 
 } // namespace android::mediametrics
diff --git a/services/mediametrics/AudioPowerUsage.cpp b/services/mediametrics/AudioPowerUsage.cpp
index 34be0b9..e584f12 100644
--- a/services/mediametrics/AudioPowerUsage.cpp
+++ b/services/mediametrics/AudioPowerUsage.cpp
@@ -174,8 +174,8 @@
         if (item_device == device && item_type == type) {
             int64_t final_duration_ns = item_duration_ns + duration_ns;
             double final_volume = (device & INPUT_DEVICE_BIT) ? 1.0:
-                            ((item_volume * item_duration_ns +
-                            average_vol * duration_ns) / final_duration_ns);
+                            ((item_volume * (double)item_duration_ns +
+                            average_vol * (double)duration_ns) / (double)final_duration_ns);
 
             item->setInt64(AUDIO_POWER_USAGE_PROP_DURATION_NS, final_duration_ns);
             item->setDouble(AUDIO_POWER_USAGE_PROP_VOLUME, final_volume);
@@ -289,7 +289,7 @@
         const int64_t durationNs = endCallNs - mDeviceTimeNs;
         if (durationNs > 0) {
             mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
-                    mVoiceVolume * double(endCallNs - mVolumeTimeNs)) / durationNs;
+                    mVoiceVolume * double(endCallNs - mVolumeTimeNs)) / (double)durationNs;
             saveAsItems_l(mPrimaryDevice, durationNs, VOICE_CALL_TYPE, mDeviceVolume);
         }
     } else if (mode == "AUDIO_MODE_IN_CALL") { // entering call mode
@@ -317,7 +317,7 @@
         const int64_t durationNs = timeNs - mDeviceTimeNs;
         if (durationNs > 0) {
             mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
-                    mVoiceVolume * double(timeNs - mVolumeTimeNs)) / durationNs;
+                    mVoiceVolume * double(timeNs - mVolumeTimeNs)) / (double)durationNs;
             mVolumeTimeNs = timeNs;
         }
     }
@@ -348,7 +348,7 @@
         const int64_t durationNs = endDeviceNs - mDeviceTimeNs;
         if (durationNs > 0) {
             mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
-                    mVoiceVolume * double(endDeviceNs - mVolumeTimeNs)) / durationNs;
+                    mVoiceVolume * double(endDeviceNs - mVolumeTimeNs)) / (double)durationNs;
             saveAsItems_l(mPrimaryDevice, durationNs, VOICE_CALL_TYPE, mDeviceVolume);
         }
         // reset statistics
diff --git a/services/mediametrics/MediaMetricsService.cpp b/services/mediametrics/MediaMetricsService.cpp
index 9d380ec..1a0f6a4 100644
--- a/services/mediametrics/MediaMetricsService.cpp
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -108,7 +108,7 @@
 {
     ALOGD("%s", __func__);
     // the class destructor clears anyhow, but we enforce clearing items first.
-    mItemsDiscarded += mItems.size();
+    mItemsDiscarded += (int64_t)mItems.size();
     mItems.clear();
 }
 
@@ -288,7 +288,7 @@
         std::lock_guard _l(mLock);
 
         if (clear) {
-            mItemsDiscarded += mItems.size();
+            mItemsDiscarded += (int64_t)mItems.size();
             mItems.clear();
             mAudioAnalytics.clear();
         } else {
@@ -417,10 +417,10 @@
 
     if (const size_t toErase = overlimit + expired;
             toErase > 0) {
-        mItemsDiscardedCount += overlimit;
-        mItemsDiscardedExpire += expired;
-        mItemsDiscarded += toErase;
-        mItems.erase(mItems.begin(), mItems.begin() + toErase); // erase from front
+        mItemsDiscardedCount += (int64_t)overlimit;
+        mItemsDiscardedExpire += (int64_t)expired;
+        mItemsDiscarded += (int64_t)toErase;
+        mItems.erase(mItems.begin(), mItems.begin() + (ptrdiff_t)toErase); // erase from front
     }
     return more;
 }
diff --git a/services/mediametrics/statsd_codec.cpp b/services/mediametrics/statsd_codec.cpp
index d502b30..1c5ab77 100644
--- a/services/mediametrics/statsd_codec.cpp
+++ b/services/mediametrics/statsd_codec.cpp
@@ -186,6 +186,16 @@
         metrics_proto.set_lifetime_millis(lifetimeMs);
     }
 
+    // new for S; need to plumb through to westworld
+    // android.media.mediacodec.channelCount int32
+    // android.media.mediacodec.sampleRate int32
+
+    // new for S; need to plumb through to westworld
+    // TODO PWG may want these fuzzed up a bit to obscure some precision
+    // android.media.mediacodec.vencode.bytes int64
+    // android.media.mediacodec.vencode.frames int64
+    // android.media.mediacodec.vencode.durationUs int64
+
     std::string serialized;
     if (!metrics_proto.SerializeToString(&serialized)) {
         ALOGE("Failed to serialize codec metrics");
diff --git a/services/mediametrics/tests/Android.bp b/services/mediametrics/tests/Android.bp
index 71193a2..3baf739 100644
--- a/services/mediametrics/tests/Android.bp
+++ b/services/mediametrics/tests/Android.bp
@@ -11,6 +11,10 @@
     name: "mediametrics_tests",
     test_suites: ["device-tests"],
 
+    // not all shared libraries are populated in the 2nd architecture in
+    // particular, libmediametricsservice we use to have a tame copy of the service
+    compile_multilib: "first",
+
     cflags: [
         "-Wall",
         "-Werror",
diff --git a/services/mediatranscoding/MediaTranscodingService.cpp b/services/mediatranscoding/MediaTranscodingService.cpp
index cca36fb..8b64134 100644
--- a/services/mediatranscoding/MediaTranscodingService.cpp
+++ b/services/mediatranscoding/MediaTranscodingService.cpp
@@ -24,6 +24,7 @@
 #include <cutils/properties.h>
 #include <media/TranscoderWrapper.h>
 #include <media/TranscodingClientManager.h>
+#include <media/TranscodingLogger.h>
 #include <media/TranscodingResourcePolicy.h>
 #include <media/TranscodingSessionController.h>
 #include <media/TranscodingThermalPolicy.h>
@@ -41,20 +42,49 @@
             errorCode,                                \
             String8::format("%s:%d: " errorString, __FUNCTION__, __LINE__, ##__VA_ARGS__))
 
-MediaTranscodingService::MediaTranscodingService(bool simulated)
+static constexpr int64_t kTranscoderHeartBeatIntervalUs = 1000000LL;
+
+MediaTranscodingService::MediaTranscodingService()
       : mUidPolicy(new TranscodingUidPolicy()),
         mResourcePolicy(new TranscodingResourcePolicy()),
-        mThermalPolicy(new TranscodingThermalPolicy()) {
+        mThermalPolicy(new TranscodingThermalPolicy()),
+        mLogger(new TranscodingLogger()) {
     ALOGV("MediaTranscodingService is created");
-    mSessionController.reset(new TranscodingSessionController(
-            [simulated](const std::shared_ptr<TranscoderCallbackInterface>& cb,
-                        int64_t heartBeatUs) -> std::shared_ptr<TranscoderInterface> {
-                if (simulated) {
-                    return std::make_shared<SimulatedTranscoder>(cb, heartBeatUs);
-                }
-                return std::make_shared<TranscoderWrapper>(cb, heartBeatUs);
-            },
-            mUidPolicy, mResourcePolicy, mThermalPolicy));
+    bool simulated = property_get_bool("debug.transcoding.simulated_transcoder", false);
+    if (simulated) {
+        // Overrid default config params with shorter values for testing.
+        TranscodingSessionController::ControllerConfig config = {
+                .pacerBurstThresholdMs = 500,
+                .pacerBurstCountQuota = 10,
+                .pacerBurstTimeQuotaSeconds = 3,
+        };
+        mSessionController.reset(new TranscodingSessionController(
+                [](const std::shared_ptr<TranscoderCallbackInterface>& cb)
+                        -> std::shared_ptr<TranscoderInterface> {
+                    return std::make_shared<SimulatedTranscoder>(cb);
+                },
+                mUidPolicy, mResourcePolicy, mThermalPolicy, &config));
+    } else {
+        int32_t overrideBurstCountQuota =
+                property_get_int32("persist.transcoding.burst_count_quota", -1);
+        int32_t pacerBurstTimeQuotaSeconds =
+                property_get_int32("persist.transcoding.burst_time_quota_seconds", -1);
+        // Override default config params with properties if present.
+        TranscodingSessionController::ControllerConfig config;
+        if (overrideBurstCountQuota > 0) {
+            config.pacerBurstCountQuota = overrideBurstCountQuota;
+        }
+        if (pacerBurstTimeQuotaSeconds > 0) {
+            config.pacerBurstTimeQuotaSeconds = pacerBurstTimeQuotaSeconds;
+        }
+        mSessionController.reset(new TranscodingSessionController(
+                [logger = mLogger](const std::shared_ptr<TranscoderCallbackInterface>& cb)
+                        -> std::shared_ptr<TranscoderInterface> {
+                    return std::make_shared<TranscoderWrapper>(cb, logger,
+                                                               kTranscoderHeartBeatIntervalUs);
+                },
+                mUidPolicy, mResourcePolicy, mThermalPolicy, &config));
+    }
     mClientManager.reset(new TranscodingClientManager(mSessionController));
     mUidPolicy->setCallback(mSessionController);
     mResourcePolicy->setCallback(mSessionController);
@@ -100,8 +130,7 @@
 //static
 void MediaTranscodingService::instantiate() {
     std::shared_ptr<MediaTranscodingService> service =
-            ::ndk::SharedRefBase::make<MediaTranscodingService>(
-                    property_get_bool("debug.transcoding.simulated_transcoder", false));
+            ::ndk::SharedRefBase::make<MediaTranscodingService>();
     binder_status_t status =
             AServiceManager_addService(service->asBinder().get(), getServiceName());
     if (status != STATUS_OK) {
diff --git a/services/mediatranscoding/MediaTranscodingService.h b/services/mediatranscoding/MediaTranscodingService.h
index d024c54..12be131 100644
--- a/services/mediatranscoding/MediaTranscodingService.h
+++ b/services/mediatranscoding/MediaTranscodingService.h
@@ -29,6 +29,7 @@
 using ::aidl::android::media::TranscodingRequestParcel;
 using ::aidl::android::media::TranscodingSessionParcel;
 class TranscodingClientManager;
+class TranscodingLogger;
 class TranscodingSessionController;
 class UidPolicyInterface;
 class ResourcePolicyInterface;
@@ -39,7 +40,7 @@
     static constexpr int32_t kInvalidSessionId = -1;
     static constexpr int32_t kInvalidClientId = -1;
 
-    MediaTranscodingService(bool simulated);
+    MediaTranscodingService();
     virtual ~MediaTranscodingService();
 
     static void instantiate();
@@ -62,6 +63,7 @@
     std::shared_ptr<UidPolicyInterface> mUidPolicy;
     std::shared_ptr<ResourcePolicyInterface> mResourcePolicy;
     std::shared_ptr<ThermalPolicyInterface> mThermalPolicy;
+    std::shared_ptr<TranscodingLogger> mLogger;
     std::shared_ptr<TranscodingSessionController> mSessionController;
     std::shared_ptr<TranscodingClientManager> mClientManager;
 };
diff --git a/services/mediatranscoding/SimulatedTranscoder.cpp b/services/mediatranscoding/SimulatedTranscoder.cpp
index db83ccb..e80dbc5 100644
--- a/services/mediatranscoding/SimulatedTranscoder.cpp
+++ b/services/mediatranscoding/SimulatedTranscoder.cpp
@@ -47,8 +47,7 @@
     return "(unknown)";
 }
 
-SimulatedTranscoder::SimulatedTranscoder(const std::shared_ptr<TranscoderCallbackInterface>& cb,
-                                         int64_t heartBeatUs __unused)
+SimulatedTranscoder::SimulatedTranscoder(const std::shared_ptr<TranscoderCallbackInterface>& cb)
       : mCallback(cb), mLooperReady(false) {
     ALOGV("SimulatedTranscoder CTOR: %p", this);
 }
@@ -59,6 +58,7 @@
 
 void SimulatedTranscoder::start(
         ClientIdType clientId, SessionIdType sessionId, const TranscodingRequestParcel& request,
+        uid_t /*callingUid*/,
         const std::shared_ptr<ITranscodingClientCallback>& /*clientCallback*/) {
     {
         auto lock = std::scoped_lock(mLock);
@@ -91,6 +91,7 @@
 
 void SimulatedTranscoder::resume(
         ClientIdType clientId, SessionIdType sessionId, const TranscodingRequestParcel& /*request*/,
+        uid_t /*callingUid*/,
         const std::shared_ptr<ITranscodingClientCallback>& /*clientCallback*/) {
     queueEvent(Event::Resume, clientId, sessionId, [=] {
         auto callback = mCallback.lock();
@@ -130,7 +131,7 @@
 
 void SimulatedTranscoder::threadLoop() {
     bool running = false;
-    std::chrono::system_clock::time_point lastRunningTime;
+    std::chrono::steady_clock::time_point lastRunningTime;
     Event lastRunningEvent;
 
     std::unique_lock<std::mutex> lock(mLock);
@@ -162,8 +163,9 @@
                 // Advance last running time and remaining time. This is needed to guard
                 // against bad events (which will be ignored) or spurious wakeups, in that
                 // case we don't want to wait for the same time again.
-                auto now = std::chrono::system_clock::now();
-                mRemainingTimeMap[key] -= (now - lastRunningTime);
+                auto now = std::chrono::steady_clock::now();
+                mRemainingTimeMap[key] -= std::chrono::duration_cast<std::chrono::microseconds>(
+                        now - lastRunningTime);
                 lastRunningTime = now;
             }
         }
@@ -182,7 +184,7 @@
         SessionKeyType key = std::make_pair(event.clientId, event.sessionId);
         if (!running && (event.type == Event::Start || event.type == Event::Resume)) {
             running = true;
-            lastRunningTime = std::chrono::system_clock::now();
+            lastRunningTime = std::chrono::steady_clock::now();
             lastRunningEvent = event;
             ALOGV("%s: session {%lld, %d}: remaining time: %lld", __FUNCTION__,
                   (long long)event.clientId, event.sessionId,
@@ -193,7 +195,8 @@
             if (event.type == Event::Stop) {
                 mRemainingTimeMap.erase(key);
             } else {
-                mRemainingTimeMap[key] -= (std::chrono::system_clock::now() - lastRunningTime);
+                mRemainingTimeMap[key] -= std::chrono::duration_cast<std::chrono::microseconds>(
+                        std::chrono::steady_clock::now() - lastRunningTime);
             }
         } else {
             ALOGW("%s: discarding bad event: session {%lld, %d}: %s", __FUNCTION__,
diff --git a/services/mediatranscoding/SimulatedTranscoder.h b/services/mediatranscoding/SimulatedTranscoder.h
index 010f0f0..58e2e30 100644
--- a/services/mediatranscoding/SimulatedTranscoder.h
+++ b/services/mediatranscoding/SimulatedTranscoder.h
@@ -49,17 +49,16 @@
 
     static constexpr int64_t kSessionDurationUs = 1000000;
 
-    SimulatedTranscoder(const std::shared_ptr<TranscoderCallbackInterface>& cb,
-                        int64_t heartBeatUs);
+    SimulatedTranscoder(const std::shared_ptr<TranscoderCallbackInterface>& cb);
     ~SimulatedTranscoder();
 
     // TranscoderInterface
     void start(ClientIdType clientId, SessionIdType sessionId,
-               const TranscodingRequestParcel& request,
+               const TranscodingRequestParcel& request, uid_t callingUid,
                const std::shared_ptr<ITranscodingClientCallback>& clientCallback) override;
     void pause(ClientIdType clientId, SessionIdType sessionId) override;
     void resume(ClientIdType clientId, SessionIdType sessionId,
-                const TranscodingRequestParcel& request,
+                const TranscodingRequestParcel& request, uid_t callingUid,
                 const std::shared_ptr<ITranscodingClientCallback>& clientCallback) override;
     void stop(ClientIdType clientId, SessionIdType sessionId, bool abandon = false) override;
     // ~TranscoderInterface
diff --git a/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h b/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
index 5256a3f..3f7d8d6 100644
--- a/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
+++ b/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
@@ -208,7 +208,9 @@
         std::unique_lock lock(mLock);
 
         mEventQueue.push_back(event);
-        mLastErr = err;
+        if (err != TranscodingErrorCode::kNoError) {
+            mLastErrQueue.push_back(err);
+        }
         mCondition.notify_one();
     }
 
@@ -226,7 +228,12 @@
 
     TranscodingErrorCode getLastError() {
         std::unique_lock lock(mLock);
-        return mLastErr;
+        if (mLastErrQueue.empty()) {
+            return TranscodingErrorCode::kNoError;
+        }
+        TranscodingErrorCode err = mLastErrQueue.front();
+        mLastErrQueue.pop_front();
+        return err;
     }
 
 private:
@@ -234,7 +241,7 @@
     std::condition_variable mCondition;
     Event mPoppedEvent;
     std::list<Event> mEventQueue;
-    TranscodingErrorCode mLastErr;
+    std::list<TranscodingErrorCode> mLastErrQueue;
     int mUpdateCount = 0;
     int mLastProgress = -1;
 };
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
index b8a6f76..c8994ac 100644
--- a/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
+++ b/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
@@ -54,6 +54,10 @@
 constexpr int64_t kPaddingUs = 1000000;
 constexpr int64_t kSessionWithPaddingUs = SimulatedTranscoder::kSessionDurationUs + kPaddingUs;
 constexpr int64_t kWatchdogTimeoutUs = 3000000;
+// Pacer settings used for simulated tests. Listed here for reference.
+constexpr int32_t kSimulatedPacerBurstThresholdMs = 500;
+//constexpr int32_t kSimulatedPacerBurstCountQuota = 10;
+//constexpr int32_t kSimulatedPacerBurstTimeQuotaSec = 3;
 
 constexpr const char* kClientOpPackageName = "TestClientPackage";
 
@@ -64,6 +68,25 @@
     virtual ~MediaTranscodingServiceSimulatedTest() {
         ALOGI("MediaTranscodingServiceResourceTest destroyed");
     }
+
+    void testPacerHelper(int numSubmits, int sessionDurationMs, int expectedSuccess) {
+        // Idle to clear out burst history.
+        usleep(kSimulatedPacerBurstThresholdMs * 2 * 1000);
+        for (int i = 0; i < numSubmits; i++) {
+            EXPECT_TRUE(mClient3->submit(i, "test_source_file_0", "test_destination_file_0",
+                                         TranscodingSessionPriority::kNormal, -1 /*bitrateBps*/,
+                                         -1 /*overridePid*/, -1 /*overrideUid*/,
+                                         sessionDurationMs));
+        }
+        for (int i = 0; i < expectedSuccess; i++) {
+            EXPECT_EQ(mClient3->pop(kPaddingUs), EventTracker::Start(CLIENT(3), i));
+            EXPECT_EQ(mClient3->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(3), i));
+        }
+        for (int i = expectedSuccess; i < numSubmits; i++) {
+            EXPECT_EQ(mClient3->pop(kPaddingUs), EventTracker::Failed(CLIENT(3), i));
+            EXPECT_EQ(mClient3->getLastError(), TranscodingErrorCode::kDroppedByService);
+        }
+    }
 };
 
 TEST_F(MediaTranscodingServiceSimulatedTest, TestRegisterNullClient) {
@@ -414,5 +437,36 @@
     ALOGD("TestTranscodingWatchdog finished.");
 }
 
+TEST_F(MediaTranscodingServiceSimulatedTest, TestTranscodingPacerOverCountQuotaOnly) {
+    ALOGD("TestTranscodingPacerOverCountQuotaOnly starting...");
+
+    registerMultipleClients();
+    testPacerHelper(12 /*numSubmits*/, 100 /*sessionDurationMs*/, 12 /*expectedSuccess*/);
+    unregisterMultipleClients();
+
+    ALOGD("TestTranscodingPacerOverCountQuotaOnly finished.");
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestTranscodingPacerOverTimeQuotaOnly) {
+    ALOGD("TestTranscodingPacerOverTimeQuotaOnly starting...");
+
+    registerMultipleClients();
+    testPacerHelper(5 /*numSubmits*/, 1000 /*sessionDurationMs*/, 5 /*expectedSuccess*/);
+    unregisterMultipleClients();
+
+    ALOGD("TestTranscodingPacerOverTimeQuotaOnly finished.");
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestTranscodingPacerOverQuota) {
+    ALOGD("TestTranscodingPacerOverQuota starting...");
+
+    registerMultipleClients();
+    testPacerHelper(12 /*numSubmits*/, 400 /*sessionDurationMs*/, 10 /*expectedSuccess*/);
+    unregisterMultipleClients();
+
+    // Idle to clear out burst history. Since we expect it to actually fail, wait for cooldown.
+    ALOGD("TestTranscodingPacerOverQuota finished.");
+}
+
 }  // namespace media
 }  // namespace android
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index 407f6d5..3224cfc 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -24,6 +24,7 @@
 #include <mutex>
 #include <sstream>
 #include <utility/AAudioUtilities.h>
+#include <media/AidlConversion.h>
 
 #include "AAudioClientTracker.h"
 #include "AAudioEndpointManager.h"
@@ -182,7 +183,9 @@
             // and START calls. This will help preserve app compatibility.
             // An app can avoid having this happen by closing their streams when
             // the app is paused.
-            AAudioClientTracker::getInstance().setExclusiveEnabled(request.getProcessId(), false);
+            pid_t pid = VALUE_OR_FATAL(
+                aidl2legacy_int32_t_pid_t(request.getIdentity().pid));
+            AAudioClientTracker::getInstance().setExclusiveEnabled(pid, false);
             endpointToSteal = endpoint; // return it to caller
         }
         return nullptr;
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index 69e58f6..0b69bf6 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -22,7 +22,9 @@
 #include <iostream>
 #include <sstream>
 
+#include <android/media/permission/Identity.h>
 #include <aaudio/AAudio.h>
+#include <media/AidlConversion.h>
 #include <mediautils/ServiceUtilities.h>
 #include <utils/String16.h>
 
@@ -37,18 +39,26 @@
 using namespace aaudio;
 
 #define MAX_STREAMS_PER_PROCESS   8
-#define AIDL_RETURN(x) *_aidl_return = (x); return Status::ok();
+#define AIDL_RETURN(x) { *_aidl_return = (x); return Status::ok(); }
 
+#define VALUE_OR_RETURN_ILLEGAL_ARG_STATUS(x) \
+    ({ auto _tmp = (x); \
+       if (!_tmp.ok()) AIDL_RETURN(AAUDIO_ERROR_ILLEGAL_ARGUMENT); \
+       std::move(_tmp.value()); })
 
 using android::AAudioService;
+using android::media::permission::Identity;
 using binder::Status;
 
 android::AAudioService::AAudioService()
     : BnAAudioService(),
       mAdapter(this) {
-    mAudioClient.clientUid = getuid();   // TODO consider using geteuid()
-    mAudioClient.clientPid = getpid();
-    mAudioClient.packageName = String16("");
+    // TODO consider using geteuid()
+    // TODO b/182392769: use identity util
+    mAudioClient.identity.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(getuid()));
+    mAudioClient.identity.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(getpid()));
+    mAudioClient.identity.packageName = std::nullopt;
+    mAudioClient.identity.attributionTag = std::nullopt;
     AAudioClientTracker::getInstance().setAAudioService(this);
 }
 
@@ -105,8 +115,13 @@
     aaudio_sharing_mode_t sharingMode = configurationInput.getSharingMode();
 
     // Enforce limit on client processes.
-    pid_t pid = request.getProcessId();
-    if (pid != mAudioClient.clientPid) {
+    Identity callingIdentity = request.getIdentity();
+    pid_t pid = IPCThreadState::self()->getCallingPid();
+    callingIdentity.pid = VALUE_OR_RETURN_ILLEGAL_ARG_STATUS(
+        legacy2aidl_pid_t_int32_t(pid));
+    callingIdentity.uid = VALUE_OR_RETURN_ILLEGAL_ARG_STATUS(
+        legacy2aidl_uid_t_int32_t(IPCThreadState::self()->getCallingUid()));
+    if (callingIdentity.pid != mAudioClient.identity.pid) {
         int32_t count = AAudioClientTracker::getInstance().getStreamCount(pid);
         if (count >= MAX_STREAMS_PER_PROCESS) {
             ALOGE("openStream(): exceeded max streams per process %d >= %d",
@@ -121,7 +136,7 @@
     }
 
     if (sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE
-        && AAudioClientTracker::getInstance().isExclusiveEnabled(request.getProcessId())) {
+        && AAudioClientTracker::getInstance().isExclusiveEnabled(pid)) {
         // only trust audioserver for in service indication
         bool inService = false;
         if (isCallerInService()) {
@@ -154,7 +169,6 @@
     } else {
         aaudio_handle_t handle = mStreamTracker.addStreamForHandle(serviceStream.get());
         serviceStream->setHandle(handle);
-        pid_t pid = request.getProcessId();
         AAudioClientTracker::getInstance().registerClientStream(pid, serviceStream);
         paramsOut.copyFrom(*serviceStream);
         *_paramsOut = std::move(paramsOut).parcelable();
@@ -266,8 +280,10 @@
 }
 
 bool AAudioService::isCallerInService() {
-    return mAudioClient.clientPid == IPCThreadState::self()->getCallingPid() &&
-        mAudioClient.clientUid == IPCThreadState::self()->getCallingUid();
+    pid_t clientPid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mAudioClient.identity.pid));
+    uid_t clientUid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(mAudioClient.identity.uid));
+    return clientPid == IPCThreadState::self()->getCallingPid() &&
+        clientUid == IPCThreadState::self()->getCallingUid();
 }
 
 aaudio_result_t AAudioService::closeStream(sp<AAudioServiceStreamBase> serviceStream) {
@@ -290,9 +306,11 @@
         // Only allow owner or the aaudio service to access the stream.
         const uid_t callingUserId = IPCThreadState::self()->getCallingUid();
         const uid_t ownerUserId = serviceStream->getOwnerUserId();
+        const uid_t clientUid = VALUE_OR_FATAL(
+            aidl2legacy_int32_t_uid_t(mAudioClient.identity.uid));
         bool callerOwnsIt = callingUserId == ownerUserId;
-        bool serverCalling = callingUserId == mAudioClient.clientUid;
-        bool serverOwnsIt = ownerUserId == mAudioClient.clientUid;
+        bool serverCalling = callingUserId == clientUid;
+        bool serverOwnsIt = ownerUserId == clientUid;
         bool allowed = callerOwnsIt || serverCalling || serverOwnsIt;
         if (!allowed) {
             ALOGE("AAudioService: calling uid %d cannot access stream 0x%08X owned by %d",
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index 85b2057..556710d 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -73,9 +73,12 @@
 aaudio_result_t AAudioServiceEndpointMMAP::open(const aaudio::AAudioStreamRequest &request) {
     aaudio_result_t result = AAUDIO_OK;
     copyFrom(request.getConstantConfiguration());
-    mMmapClient.clientUid = request.getUserId();
-    mMmapClient.clientPid = request.getProcessId();
-    mMmapClient.packageName.setTo(String16(""));
+    mMmapClient.identity = request.getIdentity();
+    // TODO b/182392769: use identity util
+    mMmapClient.identity.uid = VALUE_OR_FATAL(
+        legacy2aidl_uid_t_int32_t(IPCThreadState::self()->getCallingUid()));
+    mMmapClient.identity.pid = VALUE_OR_FATAL(
+        legacy2aidl_pid_t_int32_t(IPCThreadState::self()->getCallingPid()));
 
     audio_format_t audioFormat = getFormat();
 
@@ -159,8 +162,8 @@
                                                           this, // callback
                                                           mMmapStream,
                                                           &mPortHandle);
-    ALOGD("%s() mMapClient.uid = %d, pid = %d => portHandle = %d\n",
-          __func__, mMmapClient.clientUid,  mMmapClient.clientPid, mPortHandle);
+    ALOGD("%s() mMapClient.identity = %s => portHandle = %d\n",
+          __func__, mMmapClient.identity.toString().c_str(), mPortHandle);
     if (status != OK) {
         // This can happen if the resource is busy or the config does
         // not match the hardware.
@@ -208,8 +211,9 @@
     setBufferCapacity(mMmapBufferinfo.buffer_size_frames);
     if (!isBufferShareable) {
         // Exclusive mode can only be used by the service because the FD cannot be shared.
-        uid_t audioServiceUid = getuid();
-        if ((mMmapClient.clientUid != audioServiceUid) &&
+        int32_t audioServiceUid =
+            VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(getuid()));
+        if ((mMmapClient.identity.uid != audioServiceUid) &&
             getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
             ALOGW("%s() - exclusive FD cannot be used by client", __func__);
             result = AAUDIO_ERROR_UNAVAILABLE;
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index 7edc25c..694094c 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -39,6 +39,8 @@
 using namespace android;  // TODO just import names needed
 using namespace aaudio;   // TODO just import names needed
 
+using media::permission::Identity;
+
 /**
  * Base class for streams in the service.
  * @return
@@ -48,9 +50,7 @@
         : mTimestampThread("AATime")
         , mAtomicStreamTimestamp()
         , mAudioService(audioService) {
-    mMmapClient.clientUid = -1;
-    mMmapClient.clientPid = -1;
-    mMmapClient.packageName = String16("");
+    mMmapClient.identity = Identity();
 }
 
 AAudioServiceStreamBase::~AAudioServiceStreamBase() {
@@ -82,7 +82,7 @@
 
     result << "    0x" << std::setfill('0') << std::setw(8) << std::hex << mHandle
            << std::dec << std::setfill(' ') ;
-    result << std::setw(6) << mMmapClient.clientUid;
+    result << std::setw(6) << mMmapClient.identity.uid;
     result << std::setw(7) << mClientHandle;
     result << std::setw(4) << (isRunning() ? "yes" : " no");
     result << std::setw(6) << getState();
@@ -128,9 +128,12 @@
     AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
     aaudio_result_t result = AAUDIO_OK;
 
-    mMmapClient.clientUid = request.getUserId();
-    mMmapClient.clientPid = request.getProcessId();
-    mMmapClient.packageName.setTo(String16("")); // TODO What should we do here?
+    mMmapClient.identity = request.getIdentity();
+    // TODO b/182392769: use identity util
+    mMmapClient.identity.uid = VALUE_OR_FATAL(
+        legacy2aidl_uid_t_int32_t(IPCThreadState::self()->getCallingUid()));
+    mMmapClient.identity.pid = VALUE_OR_FATAL(
+        legacy2aidl_pid_t_int32_t(IPCThreadState::self()->getCallingPid()));
 
     // Limit scope of lock to avoid recursive lock in close().
     {
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index 0f752b7..06c9f21 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -21,6 +21,7 @@
 #include <mutex>
 
 #include <android-base/thread_annotations.h>
+#include <media/AidlConversion.h>
 #include <media/AudioClient.h>
 #include <utils/RefBase.h>
 
@@ -159,11 +160,11 @@
     }
 
     uid_t getOwnerUserId() const {
-        return mMmapClient.clientUid;
+        return VALUE_OR_FATAL(android::aidl2legacy_int32_t_uid_t(mMmapClient.identity.uid));
     }
 
     pid_t getOwnerProcessId() const {
-        return mMmapClient.clientPid;
+        return VALUE_OR_FATAL(android::aidl2legacy_int32_t_pid_t(mMmapClient.identity.pid));
     }
 
     aaudio_handle_t getHandle() const {
diff --git a/services/oboeservice/Android.bp b/services/oboeservice/Android.bp
index 21f3247..a419dd5 100644
--- a/services/oboeservice/Android.bp
+++ b/services/oboeservice/Android.bp
@@ -66,10 +66,13 @@
         "liblog",
         "libutils",
         "aaudio-aidl-cpp",
+        "media_permission-aidl-cpp",
+        "libaudioclient_aidl_conversion",
     ],
 
     export_shared_lib_headers: [
         "libaaudio_internal",
+        "media_permission-aidl-cpp",
     ],
 
     header_libs: [
diff --git a/services/oboeservice/fuzzer/Android.bp b/services/oboeservice/fuzzer/Android.bp
new file mode 100644
index 0000000..f4e8a81
--- /dev/null
+++ b/services/oboeservice/fuzzer/Android.bp
@@ -0,0 +1,72 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2020 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.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+
+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_fuzz {
+    name: "oboeservice_fuzzer",
+    srcs: [
+        "oboeservice_fuzzer.cpp",
+    ],
+    shared_libs: [
+        "libaaudio_internal",
+        "libaudioclient",
+        "libaudioflinger",
+        "libaudioutils",
+        "libmedia_helper",
+        "libmediametrics",
+        "libmediautils",
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "liblog",
+        "libutils",
+        "aaudio-aidl-cpp",
+        "media_permission-aidl-cpp",
+        "libaudioclient_aidl_conversion",
+    ],
+    static_libs: [
+        "libaaudioservice",
+    ],
+    include_dirs: [
+        "frameworks/av/services/oboeservice",
+    ],
+    header_libs: [
+        "libaudiohal_headers",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+    ],
+    fuzz_config: {
+        cc: [
+            "android-media-fuzzing-reports@google.com",
+        ],
+        componentid: 155276,
+    },
+}
diff --git a/services/oboeservice/fuzzer/README.md b/services/oboeservice/fuzzer/README.md
new file mode 100644
index 0000000..00b85df
--- /dev/null
+++ b/services/oboeservice/fuzzer/README.md
@@ -0,0 +1,65 @@
+# Fuzzer for libaaudioservice
+
+## Plugin Design Considerations
+The fuzzer plugin for libaaudioservice is designed based on the
+understanding of the service and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+AAudio Service request contains the following parameters:
+1. AAudioFormat
+2. UserId
+3. ProcessId
+4. InService
+5. DeviceId
+6. SampleRate
+7. SamplesPerFrame
+8. Direction
+9. SharingMode
+10. Usage
+11. ContentType
+12. InputPreset
+13. BufferCapacity
+
+| Parameter| Valid Input Values| Configured Value|
+|------------- |-------------| ----- |
+| `AAudioFormat` | `AAUDIO_FORMAT_UNSPECIFIED`, `AAUDIO_FORMAT_PCM_I16`, `AAUDIO_FORMAT_PCM_FLOAT` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `UserId`   | `INT32_MIN` to `INT32_MAX` | Value obtained from getuid() |
+| `ProcessId`   | `INT32_MIN` to `INT32_MAX` | Value obtained from getpid() |
+| `InService`   | `bool` | Value obtained from FuzzedDataProvider |
+| `DeviceId`   | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider |
+| `SampleRate`   | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider |
+| `SamplesPerFrame` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider |
+| `Direction` | `AAUDIO_DIRECTION_OUTPUT`, `AAUDIO_DIRECTION_INPUT` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `SharingMode` | `AAUDIO_SHARING_MODE_EXCLUSIVE`, `AAUDIO_SHARING_MODE_SHARED` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `Usage` | `AAUDIO_USAGE_MEDIA`, `AAUDIO_USAGE_VOICE_COMMUNICATION`, `AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING`, `AAUDIO_USAGE_ALARM`, `AAUDIO_USAGE_NOTIFICATION`, `AAUDIO_USAGE_NOTIFICATION_RINGTONE`, `AAUDIO_USAGE_NOTIFICATION_EVENT`, `AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY`, `AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE`, `AAUDIO_USAGE_ASSISTANCE_SONIFICATION`, `AAUDIO_USAGE_GAME`, `AAUDIO_USAGE_ASSISTANT`, `AAUDIO_SYSTEM_USAGE_EMERGENCY`, `AAUDIO_SYSTEM_USAGE_SAFETY`, `AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS`, `AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `ContentType` | `AAUDIO_CONTENT_TYPE_SPEECH`, `AAUDIO_CONTENT_TYPE_MUSIC`, `AAUDIO_CONTENT_TYPE_MOVIE`, `AAUDIO_CONTENT_TYPE_SONIFICATION` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `InputPreset` | `AAUDIO_INPUT_PRESET_GENERIC`, `AAUDIO_INPUT_PRESET_CAMCORDER`, `AAUDIO_INPUT_PRESET_VOICE_RECOGNITION`, `AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION`, `AAUDIO_INPUT_PRESET_UNPROCESSED`, `AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `BufferCapacity` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider |
+
+This also ensures that the plugin is always deterministic for any given input.
+
+## Build
+
+This describes steps to build oboeservice_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+  $ mm -j$(nproc) oboeservice_fuzzer
+```
+
+#### Steps to run
+To run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/arm64/oboeservice_fuzzer/oboeservice_fuzzer
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/services/oboeservice/fuzzer/oboeservice_fuzzer.cpp b/services/oboeservice/fuzzer/oboeservice_fuzzer.cpp
new file mode 100644
index 0000000..8e508d3
--- /dev/null
+++ b/services/oboeservice/fuzzer/oboeservice_fuzzer.cpp
@@ -0,0 +1,369 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2020 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.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+#include <fuzzer/FuzzedDataProvider.h>
+#include <stdio.h>
+
+#include <AAudioService.h>
+#include <aaudio/AAudio.h>
+#include "aaudio/BnAAudioClient.h"
+#include <android/media/permission/Identity.h>
+
+#define UNUSED_PARAM __attribute__((unused))
+
+using namespace android;
+using namespace aaudio;
+
+aaudio_format_t kAAudioFormats[] = {
+    AAUDIO_FORMAT_UNSPECIFIED,
+    AAUDIO_FORMAT_PCM_I16,
+    AAUDIO_FORMAT_PCM_FLOAT,
+};
+
+aaudio_usage_t kAAudioUsages[] = {
+    AAUDIO_USAGE_MEDIA,
+    AAUDIO_USAGE_VOICE_COMMUNICATION,
+    AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING,
+    AAUDIO_USAGE_ALARM,
+    AAUDIO_USAGE_NOTIFICATION,
+    AAUDIO_USAGE_NOTIFICATION_RINGTONE,
+    AAUDIO_USAGE_NOTIFICATION_EVENT,
+    AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY,
+    AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
+    AAUDIO_USAGE_ASSISTANCE_SONIFICATION,
+    AAUDIO_USAGE_GAME,
+    AAUDIO_USAGE_ASSISTANT,
+    AAUDIO_SYSTEM_USAGE_EMERGENCY,
+    AAUDIO_SYSTEM_USAGE_SAFETY,
+    AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS,
+    AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT,
+};
+
+aaudio_content_type_t kAAudioContentTypes[] = {
+    AAUDIO_CONTENT_TYPE_SPEECH,
+    AAUDIO_CONTENT_TYPE_MUSIC,
+    AAUDIO_CONTENT_TYPE_MOVIE,
+    AAUDIO_CONTENT_TYPE_SONIFICATION,
+};
+
+aaudio_input_preset_t kAAudioInputPresets[] = {
+    AAUDIO_INPUT_PRESET_GENERIC,           AAUDIO_INPUT_PRESET_CAMCORDER,
+    AAUDIO_INPUT_PRESET_VOICE_RECOGNITION, AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION,
+    AAUDIO_INPUT_PRESET_UNPROCESSED,       AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE,
+};
+
+const size_t kNumAAudioFormats = std::size(kAAudioFormats);
+const size_t kNumAAudioUsages = std::size(kAAudioUsages);
+const size_t kNumAAudioContentTypes = std::size(kAAudioContentTypes);
+const size_t kNumAAudioInputPresets = std::size(kAAudioInputPresets);
+
+class FuzzAAudioClient : public virtual RefBase, public AAudioServiceInterface {
+   public:
+    FuzzAAudioClient(sp<AAudioService> service);
+
+    virtual ~FuzzAAudioClient();
+
+    AAudioServiceInterface *getAAudioService();
+
+    void dropAAudioService();
+
+    void registerClient(const sp<IAAudioClient> &client UNUSED_PARAM) override {}
+
+    aaudio_handle_t openStream(const AAudioStreamRequest &request,
+                               AAudioStreamConfiguration &configurationOutput) override;
+
+    aaudio_result_t closeStream(aaudio_handle_t streamHandle) override;
+
+    aaudio_result_t getStreamDescription(aaudio_handle_t streamHandle,
+                                         AudioEndpointParcelable &parcelable) override;
+
+    aaudio_result_t startStream(aaudio_handle_t streamHandle) override;
+
+    aaudio_result_t pauseStream(aaudio_handle_t streamHandle) override;
+
+    aaudio_result_t stopStream(aaudio_handle_t streamHandle) override;
+
+    aaudio_result_t flushStream(aaudio_handle_t streamHandle) override;
+
+    aaudio_result_t registerAudioThread(aaudio_handle_t streamHandle, pid_t clientThreadId,
+                                        int64_t periodNanoseconds) override;
+
+    aaudio_result_t unregisterAudioThread(aaudio_handle_t streamHandle,
+                                          pid_t clientThreadId) override;
+
+    aaudio_result_t startClient(aaudio_handle_t streamHandle UNUSED_PARAM,
+                                const AudioClient &client UNUSED_PARAM,
+                                const audio_attributes_t *attr UNUSED_PARAM,
+                                audio_port_handle_t *clientHandle UNUSED_PARAM) override {
+        return AAUDIO_ERROR_UNAVAILABLE;
+    }
+
+    aaudio_result_t stopClient(aaudio_handle_t streamHandle UNUSED_PARAM,
+                               audio_port_handle_t clientHandle UNUSED_PARAM) override {
+        return AAUDIO_ERROR_UNAVAILABLE;
+    }
+
+    void onStreamChange(aaudio_handle_t handle, int32_t opcode, int32_t value) {}
+
+    int getDeathCount() { return mDeathCount; }
+
+    void incDeathCount() { ++mDeathCount; }
+
+    class AAudioClient : public IBinder::DeathRecipient, public BnAAudioClient {
+       public:
+        AAudioClient(wp<FuzzAAudioClient> fuzzAAudioClient) : mBinderClient(fuzzAAudioClient) {}
+
+        virtual void binderDied(const wp<IBinder> &who UNUSED_PARAM) {
+            sp<FuzzAAudioClient> client = mBinderClient.promote();
+            if (client.get()) {
+                client->dropAAudioService();
+                client->incDeathCount();
+            }
+        }
+
+        android::binder::Status onStreamChange(int32_t handle, int32_t opcode, int32_t value) {
+            static_assert(std::is_same_v<aaudio_handle_t, int32_t>);
+            android::sp<FuzzAAudioClient> client = mBinderClient.promote();
+            if (client.get() != nullptr) {
+                client->onStreamChange(handle, opcode, value);
+            }
+            return android::binder::Status::ok();
+        }
+
+       private:
+        wp<FuzzAAudioClient> mBinderClient;
+    };
+
+   private:
+    sp<AAudioService> mAAudioService;
+    sp<AAudioClient> mAAudioClient;
+    AAudioServiceInterface *mAAudioServiceInterface;
+    int mDeathCount;
+};
+
+FuzzAAudioClient::FuzzAAudioClient(sp<AAudioService> service) : AAudioServiceInterface() {
+    mAAudioService = service;
+    mAAudioServiceInterface = &service->asAAudioServiceInterface();
+    mAAudioClient = new AAudioClient(this);
+    mDeathCount = 0;
+    if (mAAudioClient.get() && mAAudioService.get()) {
+        mAAudioService->linkToDeath(mAAudioClient);
+        mAAudioService->registerClient(mAAudioClient);
+    }
+}
+
+FuzzAAudioClient::~FuzzAAudioClient() { dropAAudioService(); }
+
+AAudioServiceInterface *FuzzAAudioClient::getAAudioService() {
+    if (!mAAudioServiceInterface && mAAudioService.get()) {
+        mAAudioServiceInterface = &mAAudioService->asAAudioServiceInterface();
+    }
+    return mAAudioServiceInterface;
+}
+
+void FuzzAAudioClient::dropAAudioService() {
+    mAAudioService.clear();
+}
+
+aaudio_handle_t FuzzAAudioClient::openStream(const AAudioStreamRequest &request,
+                                             AAudioStreamConfiguration &configurationOutput) {
+    aaudio_handle_t stream;
+    for (int i = 0; i < 2; ++i) {
+        AAudioServiceInterface *service = getAAudioService();
+        if (!service) {
+            return AAUDIO_ERROR_NO_SERVICE;
+        }
+
+        stream = service->openStream(request, configurationOutput);
+
+        if (stream == AAUDIO_ERROR_NO_SERVICE) {
+            dropAAudioService();
+        } else {
+            break;
+        }
+    }
+    return stream;
+}
+
+aaudio_result_t FuzzAAudioClient::closeStream(aaudio_handle_t streamHandle) {
+    AAudioServiceInterface *service = getAAudioService();
+    if (!service) {
+        return AAUDIO_ERROR_NO_SERVICE;
+    }
+    return service->closeStream(streamHandle);
+}
+
+aaudio_result_t FuzzAAudioClient::getStreamDescription(aaudio_handle_t streamHandle,
+                                                       AudioEndpointParcelable &parcelable) {
+    AAudioServiceInterface *service = getAAudioService();
+    if (!service) {
+        return AAUDIO_ERROR_NO_SERVICE;
+    }
+    return service->getStreamDescription(streamHandle, parcelable);
+}
+
+aaudio_result_t FuzzAAudioClient::startStream(aaudio_handle_t streamHandle) {
+    AAudioServiceInterface *service = getAAudioService();
+    if (!service) {
+        return AAUDIO_ERROR_NO_SERVICE;
+    }
+    return service->startStream(streamHandle);
+}
+
+aaudio_result_t FuzzAAudioClient::pauseStream(aaudio_handle_t streamHandle) {
+    AAudioServiceInterface *service = getAAudioService();
+    if (!service) {
+        return AAUDIO_ERROR_NO_SERVICE;
+    }
+    return service->pauseStream(streamHandle);
+}
+
+aaudio_result_t FuzzAAudioClient::stopStream(aaudio_handle_t streamHandle) {
+    AAudioServiceInterface *service = getAAudioService();
+    if (!service) {
+        return AAUDIO_ERROR_NO_SERVICE;
+    }
+    return service->stopStream(streamHandle);
+}
+
+aaudio_result_t FuzzAAudioClient::flushStream(aaudio_handle_t streamHandle) {
+    AAudioServiceInterface *service = getAAudioService();
+    if (!service) {
+        return AAUDIO_ERROR_NO_SERVICE;
+    }
+    return service->flushStream(streamHandle);
+}
+
+aaudio_result_t FuzzAAudioClient::registerAudioThread(aaudio_handle_t streamHandle,
+                                                      pid_t clientThreadId,
+                                                      int64_t periodNanoseconds) {
+    AAudioServiceInterface *service = getAAudioService();
+    if (!service) {
+        return AAUDIO_ERROR_NO_SERVICE;
+    }
+    return service->registerAudioThread(streamHandle, clientThreadId, periodNanoseconds);
+}
+
+aaudio_result_t FuzzAAudioClient::unregisterAudioThread(aaudio_handle_t streamHandle,
+                                                        pid_t clientThreadId) {
+    AAudioServiceInterface *service = getAAudioService();
+    if (!service) {
+        return AAUDIO_ERROR_NO_SERVICE;
+    }
+    return service->unregisterAudioThread(streamHandle, clientThreadId);
+}
+
+class OboeserviceFuzzer {
+   public:
+    OboeserviceFuzzer();
+    ~OboeserviceFuzzer() = default;
+    void process(const uint8_t *data, size_t size);
+
+   private:
+    sp<FuzzAAudioClient> mClient;
+};
+
+OboeserviceFuzzer::OboeserviceFuzzer() {
+    sp<AAudioService> service = new AAudioService();
+    mClient = new FuzzAAudioClient(service);
+}
+
+void OboeserviceFuzzer::process(const uint8_t *data, size_t size) {
+    FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+    AAudioStreamRequest request;
+    AAudioStreamConfiguration configurationOutput;
+
+    // Initialize stream request
+    request.getConfiguration().setFormat((audio_format_t)(
+        fdp.ConsumeBool()
+            ? fdp.ConsumeIntegral<int32_t>()
+            : kAAudioFormats[fdp.ConsumeIntegralInRange<int32_t>(0, kNumAAudioFormats - 1)]));
+
+    // TODO b/182392769: use identity util
+    media::permission::Identity identity;
+    identity.uid = getuid();
+    identity.pid = getpid();
+    request.setIdentity(identity);
+    request.setInService(fdp.ConsumeBool());
+
+    request.getConfiguration().setDeviceId(fdp.ConsumeIntegral<int32_t>());
+    request.getConfiguration().setSampleRate(fdp.ConsumeIntegral<int32_t>());
+    request.getConfiguration().setSamplesPerFrame(fdp.ConsumeIntegral<int32_t>());
+    request.getConfiguration().setDirection(
+        fdp.ConsumeBool() ? fdp.ConsumeIntegral<int32_t>()
+                          : (fdp.ConsumeBool() ? AAUDIO_DIRECTION_OUTPUT : AAUDIO_DIRECTION_INPUT));
+    request.getConfiguration().setSharingMode(
+        fdp.ConsumeBool()
+            ? fdp.ConsumeIntegral<int32_t>()
+            : (fdp.ConsumeBool() ? AAUDIO_SHARING_MODE_EXCLUSIVE : AAUDIO_SHARING_MODE_SHARED));
+
+    request.getConfiguration().setUsage(
+        fdp.ConsumeBool()
+            ? fdp.ConsumeIntegral<int32_t>()
+            : kAAudioUsages[fdp.ConsumeIntegralInRange<int32_t>(0, kNumAAudioUsages - 1)]);
+    request.getConfiguration().setContentType(
+        fdp.ConsumeBool() ? fdp.ConsumeIntegral<int32_t>()
+                          : kAAudioContentTypes[fdp.ConsumeIntegralInRange<int32_t>(
+                                0, kNumAAudioContentTypes - 1)]);
+    request.getConfiguration().setInputPreset(
+        fdp.ConsumeBool() ? fdp.ConsumeIntegral<int32_t>()
+                          : kAAudioInputPresets[fdp.ConsumeIntegralInRange<int32_t>(
+                                0, kNumAAudioInputPresets - 1)]);
+    request.getConfiguration().setPrivacySensitive(fdp.ConsumeBool());
+
+    request.getConfiguration().setBufferCapacity(fdp.ConsumeIntegral<int32_t>());
+
+    aaudio_handle_t stream = mClient->openStream(request, configurationOutput);
+    if (stream < 0) {
+        // invalid request, stream not opened.
+        return;
+    }
+    while (fdp.remaining_bytes()) {
+        AudioEndpointParcelable audioEndpointParcelable;
+        int action = fdp.ConsumeIntegralInRange<int32_t>(0, 4);
+        switch (action) {
+            case 0:
+                mClient->getStreamDescription(stream, audioEndpointParcelable);
+                break;
+            case 1:
+                mClient->startStream(stream);
+                break;
+            case 2:
+                mClient->pauseStream(stream);
+                break;
+            case 3:
+                mClient->stopStream(stream);
+                break;
+            case 4:
+                mClient->flushStream(stream);
+                break;
+        }
+    }
+    mClient->closeStream(stream);
+    assert(mClient->getDeathCount() == 0);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+    if (size < 1) {
+        return 0;
+    }
+    OboeserviceFuzzer oboeserviceFuzzer;
+    oboeserviceFuzzer.process(data, size);
+    return 0;
+}