Merge "Camera: Allow out of order ZSL shutter vs regular shutter" into qt-dev
diff --git a/apex/Android.bp b/apex/Android.bp
index 2cc6fcb..42a620b 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -33,6 +33,9 @@
             ],
         },
     },
+    prebuilts: [
+        "mediaextractor.policy",
+    ],
     key: "com.android.media.key",
     certificate: ":com.android.media.certificate",
 
@@ -65,6 +68,7 @@
         "com.android.media.swcodec-mediaswcodec.rc",
         "com.android.media.swcodec-ld.config.txt",
         "mediaswcodec.policy",
+        "mediaswcodec.xml",
     ],
     use_vendor: true,
     key: "com.android.media.swcodec.key",
diff --git a/apex/AndroidManifest-media.xml b/apex/AndroidManifest-media.xml
index 17d3f3a..78ed0ed 100644
--- a/apex/AndroidManifest-media.xml
+++ b/apex/AndroidManifest-media.xml
@@ -18,8 +18,10 @@
   package="com.android.media">
   <!-- APEX does not have classes.dex -->
   <application android:hasCode="false" />
+  <!-- Setting maxSdk to lock the module to Q. minSdk is 28 for now to cover Q beta devices. -->
   <uses-sdk
       android:minSdkVersion="28"
+      android:maxSdkVersion="29"
       android:targetSdkVersion="28"
   />
 </manifest>
diff --git a/apex/AndroidManifest-swcodec.xml b/apex/AndroidManifest-swcodec.xml
index bd20dc0..9558644 100644
--- a/apex/AndroidManifest-swcodec.xml
+++ b/apex/AndroidManifest-swcodec.xml
@@ -18,8 +18,10 @@
   package="com.android.media.swcodec">
   <!-- APEX does not have classes.dex -->
   <application android:hasCode="false" />
+  <!-- Setting maxSdk to lock the module to Q. minSdk is 28 for now to cover Q beta devices. -->
   <uses-sdk
       android:minSdkVersion="28"
+      android:maxSdkVersion="29"
       android:targetSdkVersion="28"
   />
 </manifest>
diff --git a/camera/ndk/Android.bp b/camera/ndk/Android.bp
index 7786856..a2ee65d 100644
--- a/camera/ndk/Android.bp
+++ b/camera/ndk/Android.bp
@@ -77,7 +77,7 @@
 
 cc_library_shared {
     name: "libcamera2ndk_vendor",
-    vendor_available: true,
+    vendor: true,
     srcs: [
         "ndk_vendor/impl/ACameraDevice.cpp",
         "ndk_vendor/impl/ACameraManager.cpp",
diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
index 1fdff40..529c167 100644
--- a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
+++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
@@ -259,15 +259,27 @@
     }
 }
 
+static void addMetadataToPhysicalCameraSettings(const CameraMetadata *metadata,
+        const std::string &cameraId, PhysicalCameraSettings *physicalCameraSettings) {
+    CameraMetadata metadataCopy = *metadata;
+    camera_metadata_t *camera_metadata = metadataCopy.release();
+    HCameraMetadata hCameraMetadata;
+    utils::convertToHidl(camera_metadata, &hCameraMetadata, /*shouldOwn*/ true);
+    physicalCameraSettings->settings.metadata(std::move(hCameraMetadata));
+    physicalCameraSettings->id = cameraId;
+}
+
 void CameraDevice::addRequestSettingsMetadata(ACaptureRequest *aCaptureRequest,
         sp<CaptureRequest> &req) {
-    CameraMetadata metadataCopy = aCaptureRequest->settings->getInternalData();
-    const camera_metadata_t *camera_metadata = metadataCopy.getAndLock();
-    HCameraMetadata hCameraMetadata;
-    utils::convertToHidl(camera_metadata, &hCameraMetadata);
-    metadataCopy.unlock(camera_metadata);
-    req->mPhysicalCameraSettings.resize(1);
-    req->mPhysicalCameraSettings[0].settings.metadata(std::move(hCameraMetadata));
+    req->mPhysicalCameraSettings.resize(1 + aCaptureRequest->physicalSettings.size());
+    addMetadataToPhysicalCameraSettings(&(aCaptureRequest->settings->getInternalData()), getId(),
+                    &(req->mPhysicalCameraSettings[0]));
+    size_t i = 1;
+    for (auto &physicalSetting : aCaptureRequest->physicalSettings) {
+        addMetadataToPhysicalCameraSettings(&(physicalSetting.second->getInternalData()),
+                physicalSetting.first, &(req->mPhysicalCameraSettings[i]));
+        i++;
+    }
 }
 
 camera_status_t CameraDevice::updateOutputConfigurationLocked(ACaptureSessionOutput *output) {
@@ -398,10 +410,9 @@
     cameraSettings.id = id;
     // TODO: Do we really need to copy the metadata here ?
     CameraMetadata metadataCopy = metadata->getInternalData();
-    const camera_metadata_t *cameraMetadata = metadataCopy.getAndLock();
+    camera_metadata_t *cameraMetadata = metadataCopy.release();
     HCameraMetadata hCameraMetadata;
-    utils::convertToHidl(cameraMetadata, &hCameraMetadata);
-    metadataCopy.unlock(cameraMetadata);
+    utils::convertToHidl(cameraMetadata, &hCameraMetadata, true);
     if (metadata != nullptr) {
         if (hCameraMetadata.data() != nullptr &&
             mCaptureRequestMetadataQueue != nullptr &&
@@ -426,11 +437,12 @@
         const std::string& id = req->mPhysicalCameraSettings[i].id;
         CameraMetadata clone;
         utils::convertFromHidlCloned(req->mPhysicalCameraSettings[i].settings.metadata(), &clone);
+        camera_metadata_t *clonep = clone.release();
         if (id == deviceId) {
-            pRequest->settings = new ACameraMetadata(clone.release(), ACameraMetadata::ACM_REQUEST);
+            pRequest->settings = new ACameraMetadata(clonep, ACameraMetadata::ACM_REQUEST);
         } else {
             pRequest->physicalSettings[req->mPhysicalCameraSettings[i].id] =
-                    new ACameraMetadata(clone.release(), ACameraMetadata::ACM_REQUEST);
+                    new ACameraMetadata(clonep, ACameraMetadata::ACM_REQUEST);
         }
     }
     pRequest->targets = new ACameraOutputTargets();
diff --git a/camera/ndk/ndk_vendor/impl/utils.cpp b/camera/ndk/ndk_vendor/impl/utils.cpp
index 5d2d47c..e4fb204 100644
--- a/camera/ndk/ndk_vendor/impl/utils.cpp
+++ b/camera/ndk/ndk_vendor/impl/utils.cpp
@@ -64,13 +64,14 @@
     return true;
 }
 
-// Note: existing data in dst will be gone. Caller still owns the memory of src
-void convertToHidl(const camera_metadata_t *src, HCameraMetadata* dst) {
+// Note: existing data in dst will be gone. dst owns memory if shouldOwn is set
+//       to true.
+void convertToHidl(const camera_metadata_t *src, HCameraMetadata* dst, bool shouldOwn) {
     if (src == nullptr) {
         return;
     }
     size_t size = get_camera_metadata_size(src);
-    dst->setToExternal((uint8_t *) src, size);
+    dst->setToExternal((uint8_t *) src, size, shouldOwn);
     return;
 }
 
diff --git a/camera/ndk/ndk_vendor/impl/utils.h b/camera/ndk/ndk_vendor/impl/utils.h
index a03c7bc..f389f03 100644
--- a/camera/ndk/ndk_vendor/impl/utils.h
+++ b/camera/ndk/ndk_vendor/impl/utils.h
@@ -168,8 +168,8 @@
 
 bool convertFromHidlCloned(const HCameraMetadata &metadata, CameraMetadata *rawMetadata);
 
-// Note: existing data in dst will be gone. Caller still owns the memory of src
-void convertToHidl(const camera_metadata_t *src, HCameraMetadata* dst);
+// Note: existing data in dst will be gone.
+void convertToHidl(const camera_metadata_t *src, HCameraMetadata* dst, bool shouldOwn = false);
 
 TemplateId convertToHidl(ACameraDevice_request_template templateId);
 
diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp
index 01efb22..d6db1d4 100644
--- a/drm/libmediadrm/Android.bp
+++ b/drm/libmediadrm/Android.bp
@@ -59,6 +59,7 @@
     shared_libs: [
         "android.hardware.drm@1.0",
         "android.hardware.drm@1.1",
+        "android.hardware.drm@1.2",
         "libbinder",
         "libhidlbase",
         "liblog",
@@ -89,6 +90,7 @@
     shared_libs: [
         "android.hardware.drm@1.0",
         "android.hardware.drm@1.1",
+        "android.hardware.drm@1.2",
         "libbase",
         "libbinder",
         "libhidlbase",
diff --git a/drm/libmediadrm/CryptoHal.cpp b/drm/libmediadrm/CryptoHal.cpp
index 4dda5d7..d62ccd6 100644
--- a/drm/libmediadrm/CryptoHal.cpp
+++ b/drm/libmediadrm/CryptoHal.cpp
@@ -301,7 +301,7 @@
     ssize_t offset;
     size_t size;
 
-    if (memory == NULL && buffer == NULL) {
+    if (memory == NULL || buffer == NULL) {
         return UNEXPECTED_NULL;
     }
 
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index 5888af0..919f4ee 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -40,7 +40,6 @@
 #include <mediadrm/DrmSessionManager.h>
 
 using drm::V1_0::KeyedVector;
-using drm::V1_0::KeyStatusType;
 using drm::V1_0::KeyRequestType;
 using drm::V1_0::KeyType;
 using drm::V1_0::KeyValue;
@@ -51,6 +50,7 @@
 using drm::V1_1::SecureStopRelease;
 using drm::V1_1::SecurityLevel;
 using drm::V1_2::KeySetId;
+using drm::V1_2::KeyStatusType;
 using ::android::hardware::drm::V1_1::DrmMetricGroup;
 using ::android::hardware::hidl_array;
 using ::android::hardware::hidl_string;
@@ -517,6 +517,17 @@
 }
 
 Return<void> DrmHal::sendKeysChange(const hidl_vec<uint8_t>& sessionId,
+        const hidl_vec<KeyStatus_V1_0>& keyStatusList_V1_0, bool hasNewUsableKey) {
+    std::vector<KeyStatus> keyStatusVec;
+    for (const auto &keyStatus_V1_0 : keyStatusList_V1_0) {
+        keyStatusVec.push_back({keyStatus_V1_0.keyId,
+                static_cast<KeyStatusType>(keyStatus_V1_0.type)});
+    }
+    hidl_vec<KeyStatus> keyStatusList_V1_2(keyStatusVec);
+    return sendKeysChange_1_2(sessionId, keyStatusList_V1_2, hasNewUsableKey);
+}
+
+Return<void> DrmHal::sendKeysChange_1_2(const hidl_vec<uint8_t>& sessionId,
         const hidl_vec<KeyStatus>& keyStatusList, bool hasNewUsableKey) {
 
     mEventLock.lock();
@@ -546,6 +557,9 @@
             case KeyStatusType::STATUSPENDING:
                 type = DrmPlugin::kKeyStatusType_StatusPending;
                 break;
+            case KeyStatusType::USABLEINFUTURE:
+                type = DrmPlugin::kKeyStatusType_UsableInFuture;
+                break;
             case KeyStatusType::INTERNALERROR:
             default:
                 type = DrmPlugin::kKeyStatusType_InternalError;
diff --git a/drm/libmediadrm/DrmMetrics.cpp b/drm/libmediadrm/DrmMetrics.cpp
index 4fed707..3080802 100644
--- a/drm/libmediadrm/DrmMetrics.cpp
+++ b/drm/libmediadrm/DrmMetrics.cpp
@@ -32,7 +32,7 @@
 using ::android::hardware::hidl_string;
 using ::android::hardware::hidl_vec;
 using ::android::hardware::drm::V1_0::EventType;
-using ::android::hardware::drm::V1_0::KeyStatusType;
+using ::android::hardware::drm::V1_2::KeyStatusType;
 using ::android::hardware::drm::V1_1::DrmMetricGroup;
 using ::android::os::PersistableBundle;
 
diff --git a/drm/libmediadrm/tests/DrmMetrics_test.cpp b/drm/libmediadrm/tests/DrmMetrics_test.cpp
index 64aa9d0..5c8a1b0 100644
--- a/drm/libmediadrm/tests/DrmMetrics_test.cpp
+++ b/drm/libmediadrm/tests/DrmMetrics_test.cpp
@@ -30,7 +30,7 @@
 using ::android::drm_metrics::DrmFrameworkMetrics;
 using ::android::hardware::hidl_vec;
 using ::android::hardware::drm::V1_0::EventType;
-using ::android::hardware::drm::V1_0::KeyStatusType;
+using ::android::hardware::drm::V1_2::KeyStatusType;
 using ::android::hardware::drm::V1_0::Status;
 using ::android::hardware::drm::V1_1::DrmMetricGroup;
 using ::android::os::PersistableBundle;
diff --git a/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp b/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp
index 8ebb42b..b988ce0 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp
@@ -51,16 +51,16 @@
     // Build a list of the key IDs
     std::vector<const uint8_t*> keyIds;
 
-    if (mimeType == kIsoBmffVideoMimeType ||
-        mimeType == kIsoBmffAudioMimeType ||
-        mimeType == kCencInitDataFormat) {
+    if (mimeType == kIsoBmffVideoMimeType.c_str() ||
+        mimeType == kIsoBmffAudioMimeType.c_str() ||
+        mimeType == kCencInitDataFormat.c_str()) {
         Status res = parsePssh(initData, &keyIds);
         if (res != Status::OK) {
             return res;
         }
-    } else if (mimeType == kWebmVideoMimeType ||
-        mimeType == kWebmAudioMimeType ||
-        mimeType == kWebmInitDataFormat) {
+    } else if (mimeType == kWebmVideoMimeType.c_str() ||
+        mimeType == kWebmAudioMimeType.c_str() ||
+        mimeType == kWebmInitDataFormat.c_str()) {
         // WebM "init data" is just a single key ID
         if (initData.size() != kKeyIdSize) {
             return Status::ERROR_DRM_CANNOT_HANDLE;
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
index ba5fa65..f294d4d 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
@@ -62,6 +62,7 @@
 
 typedef drm::V1_1::KeyRequestType KeyRequestType_V1_1;
 typedef drm::V1_2::IDrmPluginListener IDrmPluginListener_V1_2;
+typedef drm::V1_2::KeyStatus KeyStatus_V1_2;
 typedef drm::V1_2::Status Status_V1_2;
 typedef drm::V1_2::HdcpLevel HdcpLevel_V1_2;
 
@@ -335,6 +336,15 @@
         return Void();
     }
 
+    Return<void> sendKeysChange_1_2(
+            const hidl_vec<uint8_t>& sessionId,
+            const hidl_vec<KeyStatus_V1_2>& keyStatusList, bool hasNewUsableKey) {
+        if (mListenerV1_2 != NULL) {
+            mListenerV1_2->sendKeysChange_1_2(sessionId, keyStatusList, hasNewUsableKey);
+        }
+        return Void();
+    }
+
     Return<void> sendSessionLostState(
             const hidl_vec<uint8_t>& sessionId) {
         if (mListenerV1_2 != NULL) {
diff --git a/media/bufferpool/1.0/Connection.cpp b/media/bufferpool/1.0/Connection.cpp
index e58f595..be89701 100644
--- a/media/bufferpool/1.0/Connection.cpp
+++ b/media/bufferpool/1.0/Connection.cpp
@@ -32,14 +32,22 @@
             status = mAccessor->fetch(
                     mConnectionId, transactionId, bufferId, &handle);
             if (status == ResultStatus::OK) {
-                _hidl_cb(status, Buffer{bufferId, handle});
+                Buffer buffer = {};
+                buffer.id = bufferId;
+                buffer.buffer = handle;
+                _hidl_cb(status, buffer);
                 return Void();
             }
         } else {
             mAccessor->cleanUp(false);
         }
     }
-    _hidl_cb(status, Buffer{0, nullptr});
+
+    Buffer buffer = {};
+    buffer.id = 0;
+    buffer.buffer = nullptr;
+
+    _hidl_cb(status, buffer);
     return Void();
 }
 
diff --git a/media/bufferpool/2.0/Connection.cpp b/media/bufferpool/2.0/Connection.cpp
index 6bd0e79..57d0c7e 100644
--- a/media/bufferpool/2.0/Connection.cpp
+++ b/media/bufferpool/2.0/Connection.cpp
@@ -32,14 +32,22 @@
             status = mAccessor->fetch(
                     mConnectionId, transactionId, bufferId, &handle);
             if (status == ResultStatus::OK) {
-                _hidl_cb(status, Buffer{bufferId, handle});
+                Buffer buffer = {};
+                buffer.id = bufferId;
+                buffer.buffer = handle;
+                _hidl_cb(status, buffer);
                 return Void();
             }
         } else {
             mAccessor->cleanUp(false);
         }
     }
-    _hidl_cb(status, Buffer{0, nullptr});
+
+    Buffer buffer = {};
+    buffer.id = 0;
+    buffer.buffer = nullptr;
+
+    _hidl_cb(status, buffer);
     return Void();
 }
 
diff --git a/media/codec2/components/aac/C2SoftAacDec.cpp b/media/codec2/components/aac/C2SoftAacDec.cpp
index 4d00d35..2d4e126 100644
--- a/media/codec2/components/aac/C2SoftAacDec.cpp
+++ b/media/codec2/components/aac/C2SoftAacDec.cpp
@@ -205,10 +205,6 @@
     int32_t getDrcEffectType() const { return mDrcEffectType->value; }
 
 private:
-    std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
-    std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat;
-    std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
-    std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
     std::shared_ptr<C2StreamSampleRateInfo::output> mSampleRate;
     std::shared_ptr<C2StreamChannelCountInfo::output> mChannelCount;
     std::shared_ptr<C2StreamBitrateInfo::input> mBitrate;
diff --git a/media/codec2/components/aac/C2SoftAacEnc.cpp b/media/codec2/components/aac/C2SoftAacEnc.cpp
index 71eb1ac..7efc7f1 100644
--- a/media/codec2/components/aac/C2SoftAacEnc.cpp
+++ b/media/codec2/components/aac/C2SoftAacEnc.cpp
@@ -29,33 +29,32 @@
 
 namespace android {
 
-class C2SoftAacEnc::IntfImpl : public C2InterfaceHelper {
+namespace {
+
+constexpr char COMPONENT_NAME[] = "c2.android.aac.encoder";
+
+}  // namespace
+
+class C2SoftAacEnc::IntfImpl : public SimpleInterface<void>::BaseParams {
 public:
     explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
-        : C2InterfaceHelper(helper) {
-
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_ENCODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_AAC) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
         setDerivedInstance(this);
 
         addParameter(
-                DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-                DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-                DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::input>(
-                        MEDIA_MIMETYPE_AUDIO_RAW))
-                .build());
-
-        addParameter(
-                DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::output>(
-                        MEDIA_MIMETYPE_AUDIO_AAC))
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
                 .build());
 
         addParameter(
@@ -125,10 +124,6 @@
     }
 
 private:
-    std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
-    std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat;
-    std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
-    std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
     std::shared_ptr<C2StreamSampleRateInfo::input> mSampleRate;
     std::shared_ptr<C2StreamChannelCountInfo::input> mChannelCount;
     std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
@@ -136,8 +131,6 @@
     std::shared_ptr<C2StreamProfileLevelInfo::output> mProfileLevel;
 };
 
-constexpr char COMPONENT_NAME[] = "c2.android.aac.encoder";
-
 C2SoftAacEnc::C2SoftAacEnc(
         const char *name,
         c2_node_id_t id,
diff --git a/media/codec2/components/amr_nb_wb/C2SoftAmrDec.cpp b/media/codec2/components/amr_nb_wb/C2SoftAmrDec.cpp
index edad75a..6578ad2 100644
--- a/media/codec2/components/amr_nb_wb/C2SoftAmrDec.cpp
+++ b/media/codec2/components/amr_nb_wb/C2SoftAmrDec.cpp
@@ -33,43 +33,41 @@
 
 namespace android {
 
+namespace {
+
 #ifdef AMRNB
   constexpr char COMPONENT_NAME[] = "c2.android.amrnb.decoder";
 #else
   constexpr char COMPONENT_NAME[] = "c2.android.amrwb.decoder";
 #endif
 
-class C2SoftAmrDec::IntfImpl : public C2InterfaceHelper {
+}  // namespace
+
+class C2SoftAmrDec::IntfImpl : public SimpleInterface<void>::BaseParams {
 public:
     explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
-        : C2InterfaceHelper(helper) {
-
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_DECODER,
+                C2Component::DOMAIN_AUDIO,
+#ifdef AMRNB
+                MEDIA_MIMETYPE_AUDIO_AMR_NB
+#else
+                MEDIA_MIMETYPE_AUDIO_AMR_WB
+#endif
+                ) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
         setDerivedInstance(this);
 
         addParameter(
-                DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-                DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-                DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::input>(
-#ifdef AMRNB
-                        MEDIA_MIMETYPE_AUDIO_AMR_NB
-#else
-                        MEDIA_MIMETYPE_AUDIO_AMR_WB
-#endif
-                )).build());
-
-        addParameter(
-                DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::output>(
-                        MEDIA_MIMETYPE_AUDIO_RAW))
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
                 .build());
 
         addParameter(
@@ -110,10 +108,6 @@
     }
 
 private:
-    std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
-    std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat;
-    std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
-    std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
     std::shared_ptr<C2StreamSampleRateInfo::output> mSampleRate;
     std::shared_ptr<C2StreamChannelCountInfo::output> mChannelCount;
     std::shared_ptr<C2StreamBitrateInfo::input> mBitrate;
diff --git a/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.cpp b/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.cpp
index 3d3aa7d..e2d8cb6 100644
--- a/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.cpp
+++ b/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.cpp
@@ -27,36 +27,33 @@
 
 namespace android {
 
+namespace {
+
 constexpr char COMPONENT_NAME[] = "c2.android.amrnb.encoder";
 
-class C2SoftAmrNbEnc::IntfImpl : public C2InterfaceHelper {
-   public:
-    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper>& helper)
-        : C2InterfaceHelper(helper) {
+}  // namespace
+
+
+class C2SoftAmrNbEnc::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_ENCODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_AMR_NB) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
         setDerivedInstance(this);
 
         addParameter(
-            DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(
-                    new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-            DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(
-                    new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-            DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::input>(
-                    MEDIA_MIMETYPE_AUDIO_RAW))
-                .build());
-
-        addParameter(
-            DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::output>(
-                    MEDIA_MIMETYPE_AUDIO_AMR_NB))
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
                 .build());
 
         addParameter(
@@ -91,11 +88,7 @@
     uint32_t getChannelCount() const { return mChannelCount->value; }
     uint32_t getBitrate() const { return mBitrate->value; }
 
-   private:
-    std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
-    std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat;
-    std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
-    std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
+private:
     std::shared_ptr<C2StreamSampleRateInfo::input> mSampleRate;
     std::shared_ptr<C2StreamChannelCountInfo::input> mChannelCount;
     std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
diff --git a/media/codec2/components/amr_nb_wb/C2SoftAmrWbEnc.cpp b/media/codec2/components/amr_nb_wb/C2SoftAmrWbEnc.cpp
index 379cb32..84ae4b7 100644
--- a/media/codec2/components/amr_nb_wb/C2SoftAmrWbEnc.cpp
+++ b/media/codec2/components/amr_nb_wb/C2SoftAmrWbEnc.cpp
@@ -29,36 +29,32 @@
 
 namespace android {
 
+namespace {
+
 constexpr char COMPONENT_NAME[] = "c2.android.amrwb.encoder";
 
-class C2SoftAmrWbEnc::IntfImpl : public C2InterfaceHelper {
-   public:
-    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper>& helper)
-        : C2InterfaceHelper(helper) {
+}  // namespace
+
+class C2SoftAmrWbEnc::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_ENCODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_AMR_WB) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
         setDerivedInstance(this);
 
         addParameter(
-            DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(
-                    new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-            DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(
-                    new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-            DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::input>(
-                    MEDIA_MIMETYPE_AUDIO_RAW))
-                .build());
-
-        addParameter(
-            DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::output>(
-                    MEDIA_MIMETYPE_AUDIO_AMR_WB))
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
                 .build());
 
         addParameter(
@@ -93,11 +89,7 @@
     uint32_t getChannelCount() const { return mChannelCount->value; }
     uint32_t getBitrate() const { return mBitrate->value; }
 
-   private:
-    std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
-    std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat;
-    std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
-    std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
+private:
     std::shared_ptr<C2StreamSampleRateInfo::input> mSampleRate;
     std::shared_ptr<C2StreamChannelCountInfo::input> mChannelCount;
     std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
diff --git a/media/codec2/components/aom/C2SoftAomDec.cpp b/media/codec2/components/aom/C2SoftAomDec.cpp
index df4dadb..769895c 100644
--- a/media/codec2/components/aom/C2SoftAomDec.cpp
+++ b/media/codec2/components/aom/C2SoftAomDec.cpp
@@ -501,15 +501,13 @@
     }
 }
 
-static void copyOutputBufferToYV12Frame(uint8_t *dst,
-        const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV,
+static void copyOutputBufferToYuvPlanarFrame(
+        uint8_t *dst, const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV,
         size_t srcYStride, size_t srcUStride, size_t srcVStride,
+        size_t dstYStride, size_t dstUVStride,
         uint32_t width, uint32_t height) {
-    size_t dstYStride = align(width, 16);
-    size_t dstUVStride = align(dstYStride / 2, 16);
     uint8_t* dstStart = dst;
 
-
     for (size_t i = 0; i < height; ++i) {
         memcpy(dst, srcY, width);
         srcY += srcYStride;
@@ -597,11 +595,10 @@
 static void convertYUV420Planar16ToYUV420Planar(uint8_t *dst,
         const uint16_t *srcY, const uint16_t *srcU, const uint16_t *srcV,
         size_t srcYStride, size_t srcUStride, size_t srcVStride,
-        size_t dstStride, size_t width, size_t height) {
+        size_t dstYStride, size_t dstUVStride, size_t width, size_t height) {
 
     uint8_t *dstY = (uint8_t *)dst;
-    size_t dstYSize = dstStride * height;
-    size_t dstUVStride = align(dstStride / 2, 16);
+    size_t dstYSize = dstYStride * height;
     size_t dstUVSize = dstUVStride * height / 2;
     uint8_t *dstV = dstY + dstYSize;
     uint8_t *dstU = dstV + dstUVSize;
@@ -612,7 +609,7 @@
         }
 
         srcY += srcYStride;
-        dstY += dstStride;
+        dstY += dstYStride;
     }
 
     for (size_t y = 0; y < (height + 1) / 2; ++y) {
@@ -699,6 +696,9 @@
     size_t srcYStride = img->stride[AOM_PLANE_Y];
     size_t srcUStride = img->stride[AOM_PLANE_U];
     size_t srcVStride = img->stride[AOM_PLANE_V];
+    C2PlanarLayout layout = wView.layout();
+    size_t dstYStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
+    size_t dstUVStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc;
 
     if (img->fmt == AOM_IMG_FMT_I42016) {
         const uint16_t *srcY = (const uint16_t *)img->planes[AOM_PLANE_Y];
@@ -708,20 +708,23 @@
         if (format == HAL_PIXEL_FORMAT_RGBA_1010102) {
             convertYUV420Planar16ToY410((uint32_t *)dst, srcY, srcU, srcV, srcYStride / 2,
                                     srcUStride / 2, srcVStride / 2,
-                                    align(mWidth, 16),
+                                    dstYStride / sizeof(uint32_t),
                                     mWidth, mHeight);
         } else {
             convertYUV420Planar16ToYUV420Planar(dst, srcY, srcU, srcV, srcYStride / 2,
                                     srcUStride / 2, srcVStride / 2,
-                                    align(mWidth, 16),
+                                    dstYStride, dstUVStride,
                                     mWidth, mHeight);
         }
     } else {
         const uint8_t *srcY = (const uint8_t *)img->planes[AOM_PLANE_Y];
         const uint8_t *srcU = (const uint8_t *)img->planes[AOM_PLANE_U];
         const uint8_t *srcV = (const uint8_t *)img->planes[AOM_PLANE_V];
-        copyOutputBufferToYV12Frame(dst, srcY, srcU, srcV,
-                                srcYStride, srcUStride, srcVStride, mWidth, mHeight);
+        copyOutputBufferToYuvPlanarFrame(
+                dst, srcY, srcU, srcV,
+                srcYStride, srcUStride, srcVStride,
+                dstYStride, dstUVStride,
+                mWidth, mHeight);
     }
     finishWork(*(int64_t*)img->user_priv, work, std::move(block));
     block = nullptr;
diff --git a/media/codec2/components/avc/C2SoftAvcDec.cpp b/media/codec2/components/avc/C2SoftAvcDec.cpp
index 063c537..3f015b4 100644
--- a/media/codec2/components/avc/C2SoftAvcDec.cpp
+++ b/media/codec2/components/avc/C2SoftAvcDec.cpp
@@ -335,7 +335,8 @@
       mIvColorFormat(IV_YUV_420P),
       mWidth(320),
       mHeight(240),
-      mHeaderDecoded(false) {
+      mHeaderDecoded(false),
+      mOutIndex(0u) {
     GENERATE_FILE_NAMES();
     CREATE_DUMP_FILE(mInFile);
 }
@@ -692,6 +693,33 @@
         buffer->setInfo(mIntf->getColorAspects_l());
     }
 
+    class FillWork {
+       public:
+        FillWork(uint32_t flags, C2WorkOrdinalStruct ordinal,
+                 const std::shared_ptr<C2Buffer>& buffer)
+            : mFlags(flags), mOrdinal(ordinal), mBuffer(buffer) {}
+        ~FillWork() = default;
+
+        void operator()(const std::unique_ptr<C2Work>& work) {
+            work->worklets.front()->output.flags = (C2FrameData::flags_t)mFlags;
+            work->worklets.front()->output.buffers.clear();
+            work->worklets.front()->output.ordinal = mOrdinal;
+            work->workletsProcessed = 1u;
+            work->result = C2_OK;
+            if (mBuffer) {
+                work->worklets.front()->output.buffers.push_back(mBuffer);
+            }
+            ALOGV("timestamp = %lld, index = %lld, w/%s buffer",
+                  mOrdinal.timestamp.peekll(), mOrdinal.frameIndex.peekll(),
+                  mBuffer ? "" : "o");
+        }
+
+       private:
+        const uint32_t mFlags;
+        const C2WorkOrdinalStruct mOrdinal;
+        const std::shared_ptr<C2Buffer> mBuffer;
+    };
+
     auto fillWork = [buffer](const std::unique_ptr<C2Work> &work) {
         work->worklets.front()->output.flags = (C2FrameData::flags_t)0;
         work->worklets.front()->output.buffers.clear();
@@ -700,7 +728,20 @@
         work->workletsProcessed = 1u;
     };
     if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
-        fillWork(work);
+        bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+        // TODO: Check if cloneAndSend can be avoided by tracking number of frames remaining
+        if (eos) {
+            if (buffer) {
+                mOutIndex = index;
+                C2WorkOrdinalStruct outOrdinal = work->input.ordinal;
+                cloneAndSend(
+                    mOutIndex, work,
+                    FillWork(C2FrameData::FLAG_INCOMPLETE, outOrdinal, buffer));
+                buffer.reset();
+            }
+        } else {
+            fillWork(work);
+        }
     } else {
         finish(index, fillWork);
     }
diff --git a/media/codec2/components/avc/C2SoftAvcDec.h b/media/codec2/components/avc/C2SoftAvcDec.h
index 2127a93..72ee583 100644
--- a/media/codec2/components/avc/C2SoftAvcDec.h
+++ b/media/codec2/components/avc/C2SoftAvcDec.h
@@ -21,6 +21,7 @@
 
 #include <media/stagefright/foundation/ColorUtils.h>
 
+#include <atomic>
 #include <SimpleC2Component.h>
 
 #include "ih264_typedefs.h"
@@ -163,6 +164,7 @@
     bool mSignalledOutputEos;
     bool mSignalledError;
     bool mHeaderDecoded;
+    std::atomic_uint64_t mOutIndex;
     // Color aspects. These are ISO values and are meant to detect changes in aspects to avoid
     // converting them to C2 values for each frame
     struct VuiColorAspects {
diff --git a/media/codec2/components/avc/C2SoftAvcEnc.cpp b/media/codec2/components/avc/C2SoftAvcEnc.cpp
index b851908..8d9f21a 100644
--- a/media/codec2/components/avc/C2SoftAvcEnc.cpp
+++ b/media/codec2/components/avc/C2SoftAvcEnc.cpp
@@ -37,42 +37,40 @@
 
 namespace android {
 
-class C2SoftAvcEnc::IntfImpl : public C2InterfaceHelper {
+namespace {
+
+constexpr char COMPONENT_NAME[] = "c2.android.avc.encoder";
+
+}  // namespace
+
+class C2SoftAvcEnc::IntfImpl : public SimpleInterface<void>::BaseParams {
 public:
     explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
-        : C2InterfaceHelper(helper) {
-
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_ENCODER,
+                C2Component::DOMAIN_VIDEO,
+                MEDIA_MIMETYPE_VIDEO_AVC) {
+        noPrivateBuffers(); // TODO: account for our buffers here
+        noInputReferences();
+        noOutputReferences();
+        noTimeStretch();
         setDerivedInstance(this);
 
         addParameter(
-                DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::GRAPHIC))
-                .build());
-
-        addParameter(
-                DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-                DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::input>(
-                        MEDIA_MIMETYPE_VIDEO_RAW))
-                .build());
-
-        addParameter(
-                DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::output>(
-                        MEDIA_MIMETYPE_VIDEO_AVC))
-                .build());
-
-        addParameter(
                 DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE)
                 .withConstValue(new C2StreamUsageTuning::input(
                         0u, (uint64_t)C2MemoryUsage::CPU_READ))
                 .build());
 
         addParameter(
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
+                .build());
+
+        addParameter(
                 DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
                 .withDefault(new C2StreamPictureSizeInfo::input(0u, 320, 240))
                 .withFields({
@@ -83,6 +81,13 @@
                 .build());
 
         addParameter(
+                DefineParam(mActualInputDelay, C2_PARAMKEY_INPUT_DELAY)
+                .withDefault(new C2PortActualDelayTuning::input(DEFAULT_B_FRAMES))
+                .withFields({C2F(mActualInputDelay, value).inRange(0, MAX_B_FRAMES)})
+                .withSetter(Setter<decltype(*mActualInputDelay)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
                 DefineParam(mFrameRate, C2_PARAMKEY_FRAME_RATE)
                 .withDefault(new C2StreamFrameRateInfo::output(0u, 30.))
                 // TODO: More restriction?
@@ -325,10 +330,6 @@
     std::shared_ptr<C2StreamRequestSyncFrameTuning::output> getRequestSync_l() const { return mRequestSync; }
 
 private:
-    std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
-    std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat;
-    std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
-    std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
     std::shared_ptr<C2StreamUsageTuning::input> mUsage;
     std::shared_ptr<C2StreamPictureSizeInfo::input> mSize;
     std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
@@ -341,8 +342,6 @@
 
 #define ive_api_function  ih264e_api_function
 
-constexpr char COMPONENT_NAME[] = "c2.android.avc.encoder";
-
 namespace {
 
 // From external/libavc/encoder/ih264e_bitstream.h
@@ -372,9 +371,9 @@
       mAVCEncLevel(41),
       mStarted(false),
       mSawInputEOS(false),
-      mSawOutputEOS(false),
       mSignalledError(false),
       mCodecCtx(nullptr),
+      mOutBlock(nullptr),
       // TODO: output buffer size
       mOutBufferSize(524288) {
 
@@ -387,7 +386,7 @@
 }
 
 C2SoftAvcEnc::~C2SoftAvcEnc() {
-    releaseEncoder();
+    onRelease();
 }
 
 c2_status_t C2SoftAvcEnc::onInit() {
@@ -401,11 +400,17 @@
 void C2SoftAvcEnc::onReset() {
     // TODO: use IVE_CMD_CTL_RESET?
     releaseEncoder();
+    if (mOutBlock) {
+        mOutBlock.reset();
+    }
     initEncParams();
 }
 
 void C2SoftAvcEnc::onRelease() {
     releaseEncoder();
+    if (mOutBlock) {
+        mOutBlock.reset();
+    }
 }
 
 c2_status_t C2SoftAvcEnc::onFlush_sm() {
@@ -1113,8 +1118,10 @@
         const C2GraphicView *const input,
         uint8_t *base,
         uint32_t capacity,
-        uint64_t timestamp) {
+        uint64_t workIndex) {
     iv_raw_buf_t *ps_inp_raw_buf;
+    memset(ps_encode_ip, 0, sizeof(*ps_encode_ip));
+    memset(ps_encode_op, 0, sizeof(*ps_encode_op));
 
     ps_inp_raw_buf = &ps_encode_ip->s_inp_buf;
     ps_encode_ip->s_out_buf.pv_buf = base;
@@ -1130,8 +1137,8 @@
     ps_encode_ip->u4_mb_info_type = 0;
     ps_encode_ip->u4_pic_info_type = 0;
     ps_encode_ip->u4_is_last = 0;
-    ps_encode_ip->u4_timestamp_high = timestamp >> 32;
-    ps_encode_ip->u4_timestamp_low = timestamp & 0xFFFFFFFF;
+    ps_encode_ip->u4_timestamp_high = workIndex >> 32;
+    ps_encode_ip->u4_timestamp_low = workIndex & 0xFFFFFFFF;
     ps_encode_op->s_out_buf.pv_buf = nullptr;
 
     /* Initialize color formats */
@@ -1139,7 +1146,7 @@
     ps_inp_raw_buf->u4_size = sizeof(iv_raw_buf_t);
     ps_inp_raw_buf->e_color_fmt = mIvVideoColorFormat;
     if (input == nullptr) {
-        if (mSawInputEOS){
+        if (mSawInputEOS) {
             ps_encode_ip->u4_is_last = 1;
         }
         return C2_OK;
@@ -1278,17 +1285,46 @@
     return C2_OK;
 }
 
+void C2SoftAvcEnc::finishWork(uint64_t workIndex, const std::unique_ptr<C2Work> &work,
+                              ive_video_encode_op_t *ps_encode_op) {
+    std::shared_ptr<C2Buffer> buffer =
+            createLinearBuffer(mOutBlock, 0, ps_encode_op->s_out_buf.u4_bytes);
+    if (IV_IDR_FRAME == ps_encode_op->u4_encoded_frame_type) {
+        ALOGV("IDR frame produced");
+        buffer->setInfo(std::make_shared<C2StreamPictureTypeMaskInfo::output>(
+                0u /* stream id */, C2Config::SYNC_FRAME));
+    }
+    mOutBlock = nullptr;
+
+    auto fillWork = [buffer](const std::unique_ptr<C2Work> &work) {
+        work->worklets.front()->output.flags = (C2FrameData::flags_t)0;
+        work->worklets.front()->output.buffers.clear();
+        work->worklets.front()->output.buffers.push_back(buffer);
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        work->workletsProcessed = 1u;
+    };
+    if (work && c2_cntr64_t(workIndex) == work->input.ordinal.frameIndex) {
+        fillWork(work);
+        if (mSawInputEOS) {
+            work->worklets.front()->output.flags = C2FrameData::FLAG_END_OF_STREAM;
+        }
+    } else {
+        finish(workIndex, fillWork);
+    }
+}
+
 void C2SoftAvcEnc::process(
         const std::unique_ptr<C2Work> &work,
         const std::shared_ptr<C2BlockPool> &pool) {
     // Initialize output work
     work->result = C2_OK;
-    work->workletsProcessed = 1u;
+    work->workletsProcessed = 0u;
     work->worklets.front()->output.flags = work->input.flags;
 
     IV_STATUS_T status;
-    WORD32 timeDelay, timeTaken;
-    uint64_t timestamp = work->input.ordinal.timestamp.peekull();
+    WORD32 timeDelay = 0;
+    WORD32 timeTaken = 0;
+    uint64_t workIndex = work->input.ordinal.frameIndex.peekull();
 
     // Initialize encoder if not already initialized
     if (mCodecCtx == nullptr) {
@@ -1296,27 +1332,29 @@
             ALOGE("Failed to initialize encoder");
             mSignalledError = true;
             work->result = C2_CORRUPTED;
+            work->workletsProcessed = 1u;
             return;
         }
     }
     if (mSignalledError) {
         return;
     }
-
     // while (!mSawOutputEOS && !outQueue.empty()) {
     c2_status_t error;
     ive_video_encode_ip_t s_encode_ip;
     ive_video_encode_op_t s_encode_op;
+    memset(&s_encode_op, 0, sizeof(s_encode_op));
 
     if (!mSpsPpsHeaderReceived) {
         constexpr uint32_t kHeaderLength = MIN_STREAM_SIZE;
         uint8_t header[kHeaderLength];
         error = setEncodeArgs(
-                &s_encode_ip, &s_encode_op, nullptr, header, kHeaderLength, timestamp);
+                &s_encode_ip, &s_encode_op, nullptr, header, kHeaderLength, workIndex);
         if (error != C2_OK) {
             ALOGE("setEncodeArgs failed: %d", error);
             mSignalledError = true;
             work->result = C2_CORRUPTED;
+            work->workletsProcessed = 1u;
             return;
         }
         status = ive_api_function(mCodecCtx, &s_encode_ip, &s_encode_op);
@@ -1324,6 +1362,7 @@
         if (IV_SUCCESS != status) {
             ALOGE("Encode header failed = 0x%x\n",
                     s_encode_op.u4_error_code);
+            work->workletsProcessed = 1u;
             return;
         } else {
             ALOGV("Bytes Generated in header %d\n",
@@ -1338,6 +1377,7 @@
             ALOGE("CSD allocation failed");
             mSignalledError = true;
             work->result = C2_NO_MEMORY;
+            work->workletsProcessed = 1u;
             return;
         }
         memcpy(csd->m.value, header, s_encode_op.s_out_buf.u4_bytes);
@@ -1345,6 +1385,10 @@
 
         DUMP_TO_FILE(
                 mOutFile, csd->m.value, csd->flexCount());
+        if (work->input.buffers.empty()) {
+            work->workletsProcessed = 1u;
+            return;
+        }
     }
 
     // handle dynamic config parameters
@@ -1401,34 +1445,41 @@
                 inputBuffer->data().graphicBlocks().front().map().get());
         if (view->error() != C2_OK) {
             ALOGE("graphic view map err = %d", view->error());
+            work->workletsProcessed = 1u;
             return;
         }
     }
 
-    std::shared_ptr<C2LinearBlock> block;
-
     do {
-        C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
-        // TODO: error handling, proper usage, etc.
-        c2_status_t err = pool->fetchLinearBlock(mOutBufferSize, usage, &block);
-        if (err != C2_OK) {
-            ALOGE("fetch linear block err = %d", err);
-            work->result = err;
-            return;
+        if (mSawInputEOS && work->input.buffers.empty()) break;
+        if (!mOutBlock) {
+            C2MemoryUsage usage = {C2MemoryUsage::CPU_READ,
+                                   C2MemoryUsage::CPU_WRITE};
+            // TODO: error handling, proper usage, etc.
+            c2_status_t err =
+                pool->fetchLinearBlock(mOutBufferSize, usage, &mOutBlock);
+            if (err != C2_OK) {
+                ALOGE("fetch linear block err = %d", err);
+                work->result = err;
+                work->workletsProcessed = 1u;
+                return;
+            }
         }
-        C2WriteView wView = block->map().get();
+        C2WriteView wView = mOutBlock->map().get();
         if (wView.error() != C2_OK) {
             ALOGE("write view map err = %d", wView.error());
             work->result = wView.error();
+            work->workletsProcessed = 1u;
             return;
         }
 
         error = setEncodeArgs(
-                &s_encode_ip, &s_encode_op, view.get(), wView.base(), wView.capacity(), timestamp);
+                &s_encode_ip, &s_encode_op, view.get(), wView.base(), wView.capacity(), workIndex);
         if (error != C2_OK) {
             ALOGE("setEncodeArgs failed : %d", error);
             mSignalledError = true;
             work->result = error;
+            work->workletsProcessed = 1u;
             return;
         }
 
@@ -1446,12 +1497,14 @@
             if ((s_encode_op.u4_error_code & 0xFF) == IH264E_BITSTREAM_BUFFER_OVERFLOW) {
                 // TODO: use IVE_CMD_CTL_GETBUFINFO for proper max input size?
                 mOutBufferSize *= 2;
+                mOutBlock.reset();
                 continue;
             }
             ALOGE("Encode Frame failed = 0x%x\n",
                     s_encode_op.u4_error_code);
             mSignalledError = true;
             work->result = C2_CORRUPTED;
+            work->workletsProcessed = 1u;
             return;
         }
     } while (IV_SUCCESS != status);
@@ -1480,41 +1533,104 @@
         }
     }
 
-    work->worklets.front()->output.flags = work->input.flags;
-    work->worklets.front()->output.ordinal = work->input.ordinal;
-    work->worklets.front()->output.ordinal.timestamp =
-        ((uint64_t)s_encode_op.u4_timestamp_high << 32) | s_encode_op.u4_timestamp_low;
-    work->worklets.front()->output.buffers.clear();
-
-    if (s_encode_op.s_out_buf.u4_bytes) {
-        std::shared_ptr<C2Buffer> buffer =
-            createLinearBuffer(block, 0, s_encode_op.s_out_buf.u4_bytes);
-        if (IV_IDR_FRAME == s_encode_op.u4_encoded_frame_type) {
-            ALOGV("IDR frame produced");
-            buffer->setInfo(std::make_shared<C2StreamPictureTypeMaskInfo::output>(
-                    0u /* stream id */, C2Config::SYNC_FRAME));
+    if (s_encode_op.output_present) {
+        if (!s_encode_op.s_out_buf.u4_bytes) {
+            ALOGE("Error: Output present but bytes generated is zero");
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            work->workletsProcessed = 1u;
+            return;
         }
-        work->worklets.front()->output.buffers.push_back(buffer);
+        uint64_t workId = ((uint64_t)s_encode_op.u4_timestamp_high << 32) |
+                      s_encode_op.u4_timestamp_low;
+        finishWork(workId, work, &s_encode_op);
+    }
+    if (mSawInputEOS) {
+        drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
+    }
+}
+
+c2_status_t C2SoftAvcEnc::drainInternal(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool,
+        const std::unique_ptr<C2Work> &work) {
+
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
     }
 
-    if (s_encode_op.u4_is_last) {
-        // outputBufferHeader->nFlags |= OMX_BUFFERFLAG_EOS;
-        mSawOutputEOS = true;
-    } else {
-        // outputBufferHeader->nFlags &= ~OMX_BUFFERFLAG_EOS;
+    while (true) {
+        if (!mOutBlock) {
+            C2MemoryUsage usage = {C2MemoryUsage::CPU_READ,
+                                   C2MemoryUsage::CPU_WRITE};
+            // TODO: error handling, proper usage, etc.
+            c2_status_t err =
+                pool->fetchLinearBlock(mOutBufferSize, usage, &mOutBlock);
+            if (err != C2_OK) {
+                ALOGE("fetch linear block err = %d", err);
+                work->result = err;
+                work->workletsProcessed = 1u;
+                return err;
+            }
+        }
+        C2WriteView wView = mOutBlock->map().get();
+        if (wView.error()) {
+            ALOGE("graphic view map failed %d", wView.error());
+            return C2_CORRUPTED;
+        }
+        ive_video_encode_ip_t s_encode_ip;
+        ive_video_encode_op_t s_encode_op;
+        if (C2_OK != setEncodeArgs(&s_encode_ip, &s_encode_op, nullptr,
+                                   wView.base(), wView.capacity(), 0)) {
+            ALOGE("setEncodeArgs failed for drainInternal");
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            work->workletsProcessed = 1u;
+            return C2_CORRUPTED;
+        }
+        (void)ive_api_function(mCodecCtx, &s_encode_ip, &s_encode_op);
+
+        void *freed = s_encode_op.s_inp_buf.apv_bufs[0];
+        /* If encoder frees up an input buffer, mark it as free */
+        if (freed != nullptr) {
+            if (mBuffers.count(freed) == 0u) {
+                ALOGD("buffer not tracked");
+            } else {
+                // Release input buffer reference
+                mBuffers.erase(freed);
+                mConversionBuffersInUse.erase(freed);
+            }
+        }
+
+        if (s_encode_op.output_present) {
+            uint64_t workId = ((uint64_t)s_encode_op.u4_timestamp_high << 32) |
+                          s_encode_op.u4_timestamp_low;
+            finishWork(workId, work, &s_encode_op);
+        } else {
+            if (work->workletsProcessed != 1u) {
+                work->worklets.front()->output.flags = work->input.flags;
+                work->worklets.front()->output.ordinal = work->input.ordinal;
+                work->worklets.front()->output.buffers.clear();
+                work->workletsProcessed = 1u;
+            }
+            break;
+        }
     }
+
+    return C2_OK;
 }
 
 c2_status_t C2SoftAvcEnc::drain(
         uint32_t drainMode,
         const std::shared_ptr<C2BlockPool> &pool) {
-    // TODO: use IVE_CMD_CTL_FLUSH?
-    (void)drainMode;
-    (void)pool;
-    return C2_OK;
+    return drainInternal(drainMode, pool, nullptr);
 }
 
-
 class C2SoftAvcEncFactory : public C2ComponentFactory {
 public:
     C2SoftAvcEncFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
diff --git a/media/codec2/components/avc/C2SoftAvcEnc.h b/media/codec2/components/avc/C2SoftAvcEnc.h
index 58a86d8..555055b 100644
--- a/media/codec2/components/avc/C2SoftAvcEnc.h
+++ b/media/codec2/components/avc/C2SoftAvcEnc.h
@@ -33,6 +33,7 @@
 #define LEN_STATUS_BUFFER        (10  * 1024)
 #define MAX_VBV_BUFF_SIZE        (120 * 16384)
 #define MAX_NUM_IO_BUFS           3
+#define MAX_B_FRAMES              1
 
 #define DEFAULT_MAX_REF_FRM         2
 #define DEFAULT_MAX_REORDER_FRM     0
@@ -167,7 +168,6 @@
     bool     mSpsPpsHeaderReceived;
 
     bool     mSawInputEOS;
-    bool     mSawOutputEOS;
     bool     mSignalledError;
     bool     mIntra4x4;
     bool     mEnableFastSad;
@@ -183,6 +183,8 @@
     size_t mNumMemRecords;       // Number of memory records requested by codec
     size_t mNumCores;            // Number of cores used by the codec
 
+    std::shared_ptr<C2LinearBlock> mOutBlock;
+
     // configurations used by component in process
     // (TODO: keep this in intf but make them internal only)
     std::shared_ptr<C2StreamPictureSizeInfo::input> mSize;
@@ -230,7 +232,13 @@
             const C2GraphicView *const input,
             uint8_t *base,
             uint32_t capacity,
-            uint64_t timestamp);
+            uint64_t workIndex);
+    void finishWork(uint64_t workIndex,
+            const std::unique_ptr<C2Work> &work,
+            ive_video_encode_op_t *ps_encode_op);
+    c2_status_t drainInternal(uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool,
+            const std::unique_ptr<C2Work> &work);
 
     C2_DO_NOT_COPY(C2SoftAvcEnc);
 };
diff --git a/media/codec2/components/flac/C2SoftFlacDec.cpp b/media/codec2/components/flac/C2SoftFlacDec.cpp
index 10b14ce..4039b9b 100644
--- a/media/codec2/components/flac/C2SoftFlacDec.cpp
+++ b/media/codec2/components/flac/C2SoftFlacDec.cpp
@@ -27,35 +27,32 @@
 
 namespace android {
 
+namespace {
+
 constexpr char COMPONENT_NAME[] = "c2.android.flac.decoder";
 
-class C2SoftFlacDec::IntfImpl : public C2InterfaceHelper {
+}  // namespace
+
+class C2SoftFlacDec::IntfImpl : public SimpleInterface<void>::BaseParams {
 public:
     explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
-        : C2InterfaceHelper(helper) {
-
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_DECODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_FLAC) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
         setDerivedInstance(this);
 
         addParameter(
-                DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-                DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-                DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::input>(
-                        MEDIA_MIMETYPE_AUDIO_FLAC))
-                .build());
-
-        addParameter(
-                DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::output>(
-                        MEDIA_MIMETYPE_AUDIO_RAW))
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
                 .build());
 
         addParameter(
@@ -99,10 +96,6 @@
     int32_t getPcmEncodingInfo() const { return mPcmEncodingInfo->value; }
 
 private:
-    std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
-    std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat;
-    std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
-    std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
     std::shared_ptr<C2StreamSampleRateInfo::output> mSampleRate;
     std::shared_ptr<C2StreamChannelCountInfo::output> mChannelCount;
     std::shared_ptr<C2StreamBitrateInfo::input> mBitrate;
diff --git a/media/codec2/components/flac/C2SoftFlacEnc.cpp b/media/codec2/components/flac/C2SoftFlacEnc.cpp
index 0ce2543..cf34dff 100644
--- a/media/codec2/components/flac/C2SoftFlacEnc.cpp
+++ b/media/codec2/components/flac/C2SoftFlacEnc.cpp
@@ -28,28 +28,32 @@
 
 namespace android {
 
-class C2SoftFlacEnc::IntfImpl : public C2InterfaceHelper {
+namespace {
+
+constexpr char COMPONENT_NAME[] = "c2.android.flac.encoder";
+
+}  // namespace
+
+class C2SoftFlacEnc::IntfImpl : public SimpleInterface<void>::BaseParams {
 public:
     explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
-        : C2InterfaceHelper(helper) {
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_ENCODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_FLAC) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
         setDerivedInstance(this);
+
         addParameter(
-                DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR))
-                .build());
-        addParameter(
-                DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR))
-                .build());
-        addParameter(
-                DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::input>(
-                        MEDIA_MIMETYPE_AUDIO_RAW))
-                .build());
-        addParameter(
-                DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::output>(
-                        MEDIA_MIMETYPE_AUDIO_FLAC))
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
                 .build());
         addParameter(
                 DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE)
@@ -92,17 +96,12 @@
     int32_t getPcmEncodingInfo() const { return mPcmEncodingInfo->value; }
 
 private:
-    std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
-    std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat;
-    std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
-    std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
     std::shared_ptr<C2StreamSampleRateInfo::input> mSampleRate;
     std::shared_ptr<C2StreamChannelCountInfo::input> mChannelCount;
     std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
     std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mInputMaxBufSize;
     std::shared_ptr<C2StreamPcmEncodingInfo::input> mPcmEncodingInfo;
 };
-constexpr char COMPONENT_NAME[] = "c2.android.flac.encoder";
 
 C2SoftFlacEnc::C2SoftFlacEnc(
         const char *name,
diff --git a/media/codec2/components/g711/C2SoftG711Dec.cpp b/media/codec2/components/g711/C2SoftG711Dec.cpp
index 504ca78..43b843a 100644
--- a/media/codec2/components/g711/C2SoftG711Dec.cpp
+++ b/media/codec2/components/g711/C2SoftG711Dec.cpp
@@ -27,43 +27,41 @@
 
 namespace android {
 
+namespace {
+
 #ifdef ALAW
 constexpr char COMPONENT_NAME[] = "c2.android.g711.alaw.decoder";
 #else
 constexpr char COMPONENT_NAME[] = "c2.android.g711.mlaw.decoder";
 #endif
 
-class C2SoftG711Dec::IntfImpl : public C2InterfaceHelper {
+}  // namespace
+
+class C2SoftG711Dec::IntfImpl : public SimpleInterface<void>::BaseParams {
 public:
     explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
-        : C2InterfaceHelper(helper) {
-
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_DECODER,
+                C2Component::DOMAIN_AUDIO,
+#ifdef ALAW
+                MEDIA_MIMETYPE_AUDIO_G711_ALAW
+#else
+                MEDIA_MIMETYPE_AUDIO_G711_MLAW
+#endif
+                ) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
         setDerivedInstance(this);
 
         addParameter(
-                DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-                DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-                DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::input>(
-#ifdef ALAW
-                        MEDIA_MIMETYPE_AUDIO_G711_ALAW
-#else
-                        MEDIA_MIMETYPE_AUDIO_G711_MLAW
-#endif
-                )).build());
-
-        addParameter(
-                DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::output>(
-                        MEDIA_MIMETYPE_AUDIO_RAW))
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
                 .build());
 
         addParameter(
@@ -94,10 +92,6 @@
     }
 
 private:
-    std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
-    std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat;
-    std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
-    std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
     std::shared_ptr<C2StreamSampleRateInfo::output> mSampleRate;
     std::shared_ptr<C2StreamChannelCountInfo::output> mChannelCount;
     std::shared_ptr<C2StreamBitrateInfo::input> mBitrate;
diff --git a/media/codec2/components/gsm/C2SoftGsmDec.cpp b/media/codec2/components/gsm/C2SoftGsmDec.cpp
index 69d4885..287cfc6 100644
--- a/media/codec2/components/gsm/C2SoftGsmDec.cpp
+++ b/media/codec2/components/gsm/C2SoftGsmDec.cpp
@@ -27,34 +27,32 @@
 
 namespace android {
 
+namespace {
+
 constexpr char COMPONENT_NAME[] = "c2.android.gsm.decoder";
 
-class C2SoftGsmDec::IntfImpl : public C2InterfaceHelper {
-   public:
-    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper>& helper)
-        : C2InterfaceHelper(helper) {
+}  // namespace
+
+class C2SoftGsmDec::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_DECODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_MSGSM) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
         setDerivedInstance(this);
 
         addParameter(
-                DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-                DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-                DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::input>(
-                        MEDIA_MIMETYPE_AUDIO_MSGSM))
-                .build());
-
-        addParameter(
-                DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::output>(
-                        MEDIA_MIMETYPE_AUDIO_RAW))
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
                 .build());
 
         addParameter(
@@ -85,10 +83,6 @@
     }
 
    private:
-    std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
-    std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat;
-    std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
-    std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
     std::shared_ptr<C2StreamSampleRateInfo::output> mSampleRate;
     std::shared_ptr<C2StreamChannelCountInfo::output> mChannelCount;
     std::shared_ptr<C2StreamBitrateInfo::input> mBitrate;
diff --git a/media/codec2/components/hevc/C2SoftHevcDec.cpp b/media/codec2/components/hevc/C2SoftHevcDec.cpp
index 5da59bd..7232572 100644
--- a/media/codec2/components/hevc/C2SoftHevcDec.cpp
+++ b/media/codec2/components/hevc/C2SoftHevcDec.cpp
@@ -329,7 +329,8 @@
         mIvColorformat(IV_YUV_420P),
         mWidth(320),
         mHeight(240),
-        mHeaderDecoded(false) {
+        mHeaderDecoded(false),
+        mOutIndex(0u) {
 }
 
 C2SoftHevcDec::~C2SoftHevcDec() {
@@ -688,6 +689,33 @@
         buffer->setInfo(mIntf->getColorAspects_l());
     }
 
+    class FillWork {
+       public:
+        FillWork(uint32_t flags, C2WorkOrdinalStruct ordinal,
+                 const std::shared_ptr<C2Buffer>& buffer)
+            : mFlags(flags), mOrdinal(ordinal), mBuffer(buffer) {}
+        ~FillWork() = default;
+
+        void operator()(const std::unique_ptr<C2Work>& work) {
+            work->worklets.front()->output.flags = (C2FrameData::flags_t)mFlags;
+            work->worklets.front()->output.buffers.clear();
+            work->worklets.front()->output.ordinal = mOrdinal;
+            work->workletsProcessed = 1u;
+            work->result = C2_OK;
+            if (mBuffer) {
+                work->worklets.front()->output.buffers.push_back(mBuffer);
+            }
+            ALOGV("timestamp = %lld, index = %lld, w/%s buffer",
+                  mOrdinal.timestamp.peekll(), mOrdinal.frameIndex.peekll(),
+                  mBuffer ? "" : "o");
+        }
+
+       private:
+        const uint32_t mFlags;
+        const C2WorkOrdinalStruct mOrdinal;
+        const std::shared_ptr<C2Buffer> mBuffer;
+    };
+
     auto fillWork = [buffer](const std::unique_ptr<C2Work> &work) {
         work->worklets.front()->output.flags = (C2FrameData::flags_t)0;
         work->worklets.front()->output.buffers.clear();
@@ -696,7 +724,20 @@
         work->workletsProcessed = 1u;
     };
     if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
-        fillWork(work);
+        bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+        // TODO: Check if cloneAndSend can be avoided by tracking number of frames remaining
+        if (eos) {
+            if (buffer) {
+                mOutIndex = index;
+                C2WorkOrdinalStruct outOrdinal = work->input.ordinal;
+                cloneAndSend(
+                    mOutIndex, work,
+                    FillWork(C2FrameData::FLAG_INCOMPLETE, outOrdinal, buffer));
+                buffer.reset();
+            }
+        } else {
+            fillWork(work);
+        }
     } else {
         finish(index, fillWork);
     }
diff --git a/media/codec2/components/hevc/C2SoftHevcDec.h b/media/codec2/components/hevc/C2SoftHevcDec.h
index 75111fc..b7664e6 100644
--- a/media/codec2/components/hevc/C2SoftHevcDec.h
+++ b/media/codec2/components/hevc/C2SoftHevcDec.h
@@ -19,6 +19,7 @@
 
 #include <media/stagefright/foundation/ColorUtils.h>
 
+#include <atomic>
 #include <SimpleC2Component.h>
 
 #include "ihevc_typedefs.h"
@@ -121,6 +122,7 @@
     bool mSignalledOutputEos;
     bool mSignalledError;
     bool mHeaderDecoded;
+    std::atomic_uint64_t mOutIndex;
 
     // Color aspects. These are ISO values and are meant to detect changes in aspects to avoid
     // converting them to C2 values for each frame
diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.cpp b/media/codec2/components/hevc/C2SoftHevcEnc.cpp
index 402d9aa..efeab6c 100644
--- a/media/codec2/components/hevc/C2SoftHevcEnc.cpp
+++ b/media/codec2/components/hevc/C2SoftHevcEnc.cpp
@@ -38,41 +38,49 @@
 
 namespace android {
 
-class C2SoftHevcEnc::IntfImpl : public C2InterfaceHelper {
+namespace {
+
+constexpr char COMPONENT_NAME[] = "c2.android.hevc.encoder";
+
+} // namepsace
+
+class C2SoftHevcEnc::IntfImpl : public SimpleInterface<void>::BaseParams {
   public:
-    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper>& helper)
-        : C2InterfaceHelper(helper) {
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_ENCODER,
+                C2Component::DOMAIN_VIDEO,
+                MEDIA_MIMETYPE_VIDEO_HEVC) {
+        noPrivateBuffers(); // TODO: account for our buffers here
+        noInputReferences();
+        noOutputReferences();
+        noTimeStretch();
         setDerivedInstance(this);
 
         addParameter(
-            DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(
-                    new C2StreamBufferTypeSetting::input(0u, C2BufferData::GRAPHIC))
+                DefineParam(mActualInputDelay, C2_PARAMKEY_INPUT_DELAY)
+                .withDefault(new C2PortActualDelayTuning::input(
+                    DEFAULT_B_FRAMES + DEFAULT_RC_LOOKAHEAD))
+                .withFields({C2F(mActualInputDelay, value).inRange(
+                    0, MAX_B_FRAMES + MAX_RC_LOOKAHEAD)})
+                .withSetter(
+                    Setter<decltype(*mActualInputDelay)>::StrictValueWithNoDeps)
                 .build());
 
         addParameter(
-            DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(
-                    new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR))
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
                 .build());
 
         addParameter(
-            DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::input>(
-                    MEDIA_MIMETYPE_VIDEO_RAW))
+                DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE)
+                .withConstValue(new C2StreamUsageTuning::input(
+                        0u, (uint64_t)C2MemoryUsage::CPU_READ))
                 .build());
 
-        addParameter(
-            DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::output>(
-                    MEDIA_MIMETYPE_VIDEO_HEVC))
-                .build());
-
-        addParameter(DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE)
-                         .withConstValue(new C2StreamUsageTuning::input(
-                             0u, (uint64_t)C2MemoryUsage::CPU_READ))
-                         .build());
-
         // matches size limits in codec library
         addParameter(
             DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
@@ -332,10 +340,6 @@
     }
 
    private:
-    std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
-    std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat;
-    std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
-    std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
     std::shared_ptr<C2StreamUsageTuning::input> mUsage;
     std::shared_ptr<C2StreamPictureSizeInfo::input> mSize;
     std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
@@ -348,8 +352,6 @@
     std::shared_ptr<C2StreamSyncFrameIntervalTuning::output> mSyncFramePeriod;
 };
 
-constexpr char COMPONENT_NAME[] = "c2.android.hevc.encoder";
-
 static size_t GetCPUCoreCount() {
     long cpuCoreCount = 0;
 
@@ -469,7 +471,8 @@
     mIvVideoColorFormat = IV_YUV_420P;
     mEncParams.s_multi_thrd_prms.i4_max_num_cores = mNumCores;
     mEncParams.s_out_strm_prms.i4_codec_profile = mHevcEncProfile;
-    mEncParams.s_lap_prms.i4_rc_look_ahead_pics = 0;
+    mEncParams.s_lap_prms.i4_rc_look_ahead_pics = DEFAULT_RC_LOOKAHEAD;
+    mEncParams.s_coding_tools_prms.i4_max_temporal_layers = DEFAULT_B_FRAMES;
 
     switch (mBitrateMode->value) {
         case C2Config::BITRATE_IGNORE:
@@ -519,10 +522,9 @@
 
 c2_status_t C2SoftHevcEnc::drain(uint32_t drainMode,
                                  const std::shared_ptr<C2BlockPool>& pool) {
-    (void)drainMode;
-    (void)pool;
-    return C2_OK;
+    return drainInternal(drainMode, pool, nullptr);
 }
+
 c2_status_t C2SoftHevcEnc::initEncoder() {
     CHECK(!mCodecCtx);
     {
@@ -559,7 +561,7 @@
 
 c2_status_t C2SoftHevcEnc::setEncodeArgs(ihevce_inp_buf_t* ps_encode_ip,
                                          const C2GraphicView* const input,
-                                         uint64_t timestamp) {
+                                         uint64_t workIndex) {
     ihevce_static_cfg_params_t* params = &mEncParams;
     memset(ps_encode_ip, 0, sizeof(*ps_encode_ip));
 
@@ -703,7 +705,92 @@
     ps_encode_ip->i4_curr_peak_bitrate =
         params->s_tgt_lyr_prms.as_tgt_params[0].ai4_peak_bitrate[0];
     ps_encode_ip->i4_curr_rate_factor = params->s_config_prms.i4_rate_factor;
-    ps_encode_ip->u8_pts = timestamp;
+    ps_encode_ip->u8_pts = workIndex;
+    return C2_OK;
+}
+
+void C2SoftHevcEnc::finishWork(uint64_t index,
+                               const std::unique_ptr<C2Work>& work,
+                               const std::shared_ptr<C2BlockPool>& pool,
+                               ihevce_out_buf_t* ps_encode_op) {
+    std::shared_ptr<C2LinearBlock> block;
+    C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
+    c2_status_t status =
+        pool->fetchLinearBlock(ps_encode_op->i4_bytes_generated, usage, &block);
+    if (C2_OK != status) {
+        ALOGE("fetchLinearBlock for Output failed with status 0x%x", status);
+        mSignalledError = true;
+        work->result = status;
+        work->workletsProcessed = 1u;
+        return;
+    }
+    C2WriteView wView = block->map().get();
+    if (C2_OK != wView.error()) {
+        ALOGE("write view map failed with status 0x%x", wView.error());
+        mSignalledError = true;
+        work->result = wView.error();
+        work->workletsProcessed = 1u;
+        return;
+    }
+    memcpy(wView.data(), ps_encode_op->pu1_output_buf,
+           ps_encode_op->i4_bytes_generated);
+
+    std::shared_ptr<C2Buffer> buffer =
+        createLinearBuffer(block, 0, ps_encode_op->i4_bytes_generated);
+
+    DUMP_TO_FILE(mOutFile, ps_encode_op->pu1_output_buf,
+                 ps_encode_op->i4_bytes_generated);
+
+    if (ps_encode_op->i4_is_key_frame) {
+        ALOGV("IDR frame produced");
+        buffer->setInfo(std::make_shared<C2StreamPictureTypeMaskInfo::output>(
+            0u /* stream id */, C2Config::SYNC_FRAME));
+    }
+
+    auto fillWork = [buffer](const std::unique_ptr<C2Work>& work) {
+        work->worklets.front()->output.flags = (C2FrameData::flags_t)0;
+        work->worklets.front()->output.buffers.clear();
+        work->worklets.front()->output.buffers.push_back(buffer);
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        work->workletsProcessed = 1u;
+    };
+    if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
+        fillWork(work);
+        if (mSignalledEos) {
+            work->worklets.front()->output.flags =
+                C2FrameData::FLAG_END_OF_STREAM;
+        }
+    } else {
+        finish(index, fillWork);
+    }
+}
+
+c2_status_t C2SoftHevcEnc::drainInternal(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool,
+        const std::unique_ptr<C2Work> &work) {
+
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    while (true) {
+        ihevce_out_buf_t s_encode_op{};
+        memset(&s_encode_op, 0, sizeof(s_encode_op));
+
+        ihevce_encode(mCodecCtx, nullptr, &s_encode_op);
+        if (s_encode_op.i4_bytes_generated) {
+            finishWork(s_encode_op.u8_pts, work, pool, &s_encode_op);
+        } else {
+            if (work->workletsProcessed != 1u) fillEmptyWork(work);
+            break;
+        }
+    }
     return C2_OK;
 }
 
@@ -711,7 +798,7 @@
                             const std::shared_ptr<C2BlockPool>& pool) {
     // Initialize output work
     work->result = C2_OK;
-    work->workletsProcessed = 1u;
+    work->workletsProcessed = 0u;
     work->worklets.front()->output.flags = work->input.flags;
 
     if (mSignalledError || mSignalledEos) {
@@ -728,6 +815,7 @@
             ALOGE("Failed to initialize encoder : 0x%x", status);
             mSignalledError = true;
             work->result = status;
+            work->workletsProcessed = 1u;
             return;
         }
     }
@@ -735,6 +823,8 @@
     std::shared_ptr<const C2GraphicView> view;
     std::shared_ptr<C2Buffer> inputBuffer = nullptr;
     bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+    if (eos) mSignalledEos = true;
+
     if (!work->input.buffers.empty()) {
         inputBuffer = work->input.buffers[0];
         view = std::make_shared<const C2GraphicView>(
@@ -743,13 +833,12 @@
             ALOGE("graphic view map err = %d", view->error());
             mSignalledError = true;
             work->result = C2_CORRUPTED;
+            work->workletsProcessed = 1u;
             return;
         }
     }
-
     IHEVCE_PLUGIN_STATUS_T err = IHEVCE_EOK;
 
-    fillEmptyWork(work);
     if (!mSpsPpsHeaderReceived) {
         ihevce_out_buf_t s_header_op{};
         err = ihevce_encode_header(mCodecCtx, &s_header_op);
@@ -761,6 +850,7 @@
                 ALOGE("CSD allocation failed");
                 mSignalledError = true;
                 work->result = C2_NO_MEMORY;
+                work->workletsProcessed = 1u;
                 return;
             }
             memcpy(csd->m.value, s_header_op.pu1_output_buf,
@@ -771,34 +861,40 @@
             mSpsPpsHeaderReceived = true;
         }
         if (!inputBuffer) {
+            work->workletsProcessed = 1u;
             return;
         }
     }
     ihevce_inp_buf_t s_encode_ip{};
     ihevce_out_buf_t s_encode_op{};
-    uint64_t timestamp = work->input.ordinal.timestamp.peekull();
+    uint64_t workIndex = work->input.ordinal.frameIndex.peekull();
 
-    status = setEncodeArgs(&s_encode_ip, view.get(), timestamp);
+    status = setEncodeArgs(&s_encode_ip, view.get(), workIndex);
     if (C2_OK != status) {
         ALOGE("setEncodeArgs failed : 0x%x", status);
         mSignalledError = true;
         work->result = status;
+        work->workletsProcessed = 1u;
         return;
     }
 
     uint64_t timeDelay = 0;
     uint64_t timeTaken = 0;
+    memset(&s_encode_op, 0, sizeof(s_encode_op));
     GETTIME(&mTimeStart, nullptr);
     TIME_DIFF(mTimeEnd, mTimeStart, timeDelay);
 
-    ihevce_inp_buf_t* ps_encode_ip = (inputBuffer) ? &s_encode_ip : nullptr;
-
-    err = ihevce_encode(mCodecCtx, ps_encode_ip, &s_encode_op);
-    if (IHEVCE_EOK != err) {
-        ALOGE("Encode Frame failed : 0x%x", err);
-        mSignalledError = true;
-        work->result = C2_CORRUPTED;
-        return;
+    if (inputBuffer) {
+        err = ihevce_encode(mCodecCtx, &s_encode_ip, &s_encode_op);
+        if (IHEVCE_EOK != err) {
+            ALOGE("Encode Frame failed : 0x%x", err);
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            work->workletsProcessed = 1u;
+            return;
+        }
+    } else if (!eos) {
+        fillEmptyWork(work);
     }
 
     GETTIME(&mTimeEnd, nullptr);
@@ -809,42 +905,11 @@
           (int)timeDelay, s_encode_op.i4_bytes_generated);
 
     if (s_encode_op.i4_bytes_generated) {
-        std::shared_ptr<C2LinearBlock> block;
-        C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
-        status = pool->fetchLinearBlock(s_encode_op.i4_bytes_generated, usage, &block);
-        if (C2_OK != status) {
-            ALOGE("fetchLinearBlock for Output failed with status 0x%x", status);
-            work->result = C2_NO_MEMORY;
-            mSignalledError = true;
-            return;
-        }
-        C2WriteView wView = block->map().get();
-        if (C2_OK != wView.error()) {
-            ALOGE("write view map failed with status 0x%x", wView.error());
-            work->result = wView.error();
-            mSignalledError = true;
-            return;
-        }
-        memcpy(wView.data(), s_encode_op.pu1_output_buf,
-               s_encode_op.i4_bytes_generated);
-
-        std::shared_ptr<C2Buffer> buffer =
-            createLinearBuffer(block, 0, s_encode_op.i4_bytes_generated);
-
-        DUMP_TO_FILE(mOutFile, s_encode_op.pu1_output_buf,
-                     s_encode_op.i4_bytes_generated);
-
-        work->worklets.front()->output.ordinal.timestamp = s_encode_op.u8_pts;
-        if (s_encode_op.i4_is_key_frame) {
-            ALOGV("IDR frame produced");
-            buffer->setInfo(
-                std::make_shared<C2StreamPictureTypeMaskInfo::output>(
-                    0u /* stream id */, C2Config::SYNC_FRAME));
-        }
-        work->worklets.front()->output.buffers.push_back(buffer);
+        finishWork(s_encode_op.u8_pts, work, pool, &s_encode_op);
     }
+
     if (eos) {
-        mSignalledEos = true;
+        drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
     }
 }
 
diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.h b/media/codec2/components/hevc/C2SoftHevcEnc.h
index 8569a3e..f2c7642 100644
--- a/media/codec2/components/hevc/C2SoftHevcEnc.h
+++ b/media/codec2/components/hevc/C2SoftHevcEnc.h
@@ -35,7 +35,12 @@
     diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \
            ((end).tv_usec - (start).tv_usec);
 
-#define CODEC_MAX_CORES 4
+#define CODEC_MAX_CORES  4
+#define MAX_B_FRAMES     1
+#define MAX_RC_LOOKAHEAD 1
+
+#define DEFAULT_B_FRAMES     0
+#define DEFAULT_RC_LOOKAHEAD 0
 
 struct C2SoftHevcEnc : public SimpleC2Component {
     class IntfImpl;
@@ -95,10 +100,15 @@
     c2_status_t releaseEncoder();
     c2_status_t setEncodeArgs(ihevce_inp_buf_t* ps_encode_ip,
                               const C2GraphicView* const input,
-                              uint64_t timestamp);
+                              uint64_t workIndex);
+    void finishWork(uint64_t index, const std::unique_ptr<C2Work>& work,
+                    const std::shared_ptr<C2BlockPool>& pool,
+                    ihevce_out_buf_t* ps_encode_op);
+    c2_status_t drainInternal(uint32_t drainMode,
+                              const std::shared_ptr<C2BlockPool>& pool,
+                              const std::unique_ptr<C2Work>& work);
     C2_DO_NOT_COPY(C2SoftHevcEnc);
 };
-
 #ifdef FILE_DUMP_ENABLE
 
 #define INPUT_DUMP_PATH "/data/local/tmp/hevc"
diff --git a/media/codec2/components/mp3/C2SoftMp3Dec.cpp b/media/codec2/components/mp3/C2SoftMp3Dec.cpp
index 9db6d8f..5ba7e3d 100644
--- a/media/codec2/components/mp3/C2SoftMp3Dec.cpp
+++ b/media/codec2/components/mp3/C2SoftMp3Dec.cpp
@@ -30,35 +30,32 @@
 
 namespace android {
 
+namespace {
+
 constexpr char COMPONENT_NAME[] = "c2.android.mp3.decoder";
 
-class C2SoftMP3::IntfImpl : public C2InterfaceHelper {
+}  // namespace
+
+class C2SoftMP3::IntfImpl : public SimpleInterface<void>::BaseParams {
 public:
     explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
-        : C2InterfaceHelper(helper) {
-
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_DECODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_MPEG) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
         setDerivedInstance(this);
 
         addParameter(
-                DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-                DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-                DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::input>(
-                        MEDIA_MIMETYPE_AUDIO_MPEG))
-                .build());
-
-        addParameter(
-                DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::output>(
-                        MEDIA_MIMETYPE_AUDIO_RAW))
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
                 .build());
 
         addParameter(
@@ -90,10 +87,6 @@
     }
 
 private:
-    std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
-    std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat;
-    std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
-    std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
     std::shared_ptr<C2StreamSampleRateInfo::output> mSampleRate;
     std::shared_ptr<C2StreamChannelCountInfo::output> mChannelCount;
     std::shared_ptr<C2StreamBitrateInfo::input> mBitrate;
diff --git a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
index 290677e..df7b403 100644
--- a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
+++ b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
@@ -315,7 +315,8 @@
         mOutBufferDrain(nullptr),
         mIvColorformat(IV_YUV_420P),
         mWidth(320),
-        mHeight(240) {
+        mHeight(240),
+        mOutIndex(0u) {
     // If input dump is enabled, then open create an empty file
     GENERATE_FILE_NAMES();
     CREATE_DUMP_FILE(mInFile);
@@ -766,6 +767,33 @@
         buffer->setInfo(mIntf->getColorAspects_l());
     }
 
+    class FillWork {
+       public:
+        FillWork(uint32_t flags, C2WorkOrdinalStruct ordinal,
+                 const std::shared_ptr<C2Buffer>& buffer)
+            : mFlags(flags), mOrdinal(ordinal), mBuffer(buffer) {}
+        ~FillWork() = default;
+
+        void operator()(const std::unique_ptr<C2Work>& work) {
+            work->worklets.front()->output.flags = (C2FrameData::flags_t)mFlags;
+            work->worklets.front()->output.buffers.clear();
+            work->worklets.front()->output.ordinal = mOrdinal;
+            work->workletsProcessed = 1u;
+            work->result = C2_OK;
+            if (mBuffer) {
+                work->worklets.front()->output.buffers.push_back(mBuffer);
+            }
+            ALOGV("timestamp = %lld, index = %lld, w/%s buffer",
+                  mOrdinal.timestamp.peekll(), mOrdinal.frameIndex.peekll(),
+                  mBuffer ? "" : "o");
+        }
+
+       private:
+        const uint32_t mFlags;
+        const C2WorkOrdinalStruct mOrdinal;
+        const std::shared_ptr<C2Buffer> mBuffer;
+    };
+
     auto fillWork = [buffer](const std::unique_ptr<C2Work> &work) {
         work->worklets.front()->output.flags = (C2FrameData::flags_t)0;
         work->worklets.front()->output.buffers.clear();
@@ -774,7 +802,20 @@
         work->workletsProcessed = 1u;
     };
     if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
-        fillWork(work);
+        bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+        // TODO: Check if cloneAndSend can be avoided by tracking number of frames remaining
+        if (eos) {
+            if (buffer) {
+                mOutIndex = index;
+                C2WorkOrdinalStruct outOrdinal = work->input.ordinal;
+                cloneAndSend(
+                    mOutIndex, work,
+                    FillWork(C2FrameData::FLAG_INCOMPLETE, outOrdinal, buffer));
+                buffer.reset();
+            }
+        } else {
+            fillWork(work);
+        }
     } else {
         finish(index, fillWork);
     }
diff --git a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h
index 9999872..65d3b87 100644
--- a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h
+++ b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_C2_SOFT_MPEG2_DEC_H_
 #define ANDROID_C2_SOFT_MPEG2_DEC_H_
 
+#include <atomic>
 #include <SimpleC2Component.h>
 
 #include <media/stagefright/foundation/ColorUtils.h>
@@ -161,6 +162,7 @@
     uint32_t mStride;
     bool mSignalledOutputEos;
     bool mSignalledError;
+    std::atomic_uint64_t mOutIndex;
 
     // Color aspects. These are ISO values and are meant to detect changes in aspects to avoid
     // converting them to C2 values for each frame
diff --git a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp
index 3d4a733..7e6685e 100644
--- a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp
+++ b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp
@@ -466,9 +466,11 @@
 
 /* TODO: can remove temporary copy after library supports writing to display
  * buffer Y, U and V plane pointers using stride info. */
-static void copyOutputBufferToYV12Frame(uint8_t *dst, uint8_t *src, size_t dstYStride,
-                                        size_t srcYStride, uint32_t width, uint32_t height) {
-    size_t dstUVStride = align(dstYStride / 2, 16);
+static void copyOutputBufferToYuvPlanarFrame(
+        uint8_t *dst, uint8_t *src,
+        size_t dstYStride, size_t dstUVStride,
+        size_t srcYStride, uint32_t width,
+        uint32_t height) {
     size_t srcUVStride = srcYStride / 2;
     uint8_t *srcStart = src;
     uint8_t *dstStart = dst;
@@ -673,8 +675,14 @@
         }
 
         uint8_t *outputBufferY = wView.data()[C2PlanarLayout::PLANE_Y];
-        (void)copyOutputBufferToYV12Frame(outputBufferY, mOutputBuffer[mNumSamplesOutput & 1],
-                                          wView.width(), align(mWidth, 16), mWidth, mHeight);
+        C2PlanarLayout layout = wView.layout();
+        size_t dstYStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
+        size_t dstUVStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc;
+        (void)copyOutputBufferToYuvPlanarFrame(
+                outputBufferY,
+                mOutputBuffer[mNumSamplesOutput & 1],
+                dstYStride, dstUVStride,
+                align(mWidth, 16), mWidth, mHeight);
 
         inPos += inSize - (size_t)tmpInSize;
         finishWork(workIndex, work);
diff --git a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Enc.cpp b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Enc.cpp
index 89fa59d..36053f6 100644
--- a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Enc.cpp
+++ b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Enc.cpp
@@ -39,53 +39,47 @@
 
 namespace android {
 
+namespace {
+
 #ifdef MPEG4
 constexpr char COMPONENT_NAME[] = "c2.android.mpeg4.encoder";
+const char *MEDIA_MIMETYPE_VIDEO = MEDIA_MIMETYPE_VIDEO_MPEG4;
 #else
 constexpr char COMPONENT_NAME[] = "c2.android.h263.encoder";
+const char *MEDIA_MIMETYPE_VIDEO = MEDIA_MIMETYPE_VIDEO_H263;
 #endif
 
-class C2SoftMpeg4Enc::IntfImpl : public C2InterfaceHelper {
+} // namepsace
+
+class C2SoftMpeg4Enc::IntfImpl : public SimpleInterface<void>::BaseParams {
    public:
-    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper>& helper)
-        : C2InterfaceHelper(helper) {
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_ENCODER,
+                C2Component::DOMAIN_VIDEO,
+                MEDIA_MIMETYPE_VIDEO) {
+        noPrivateBuffers(); // TODO: account for our buffers here
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
         setDerivedInstance(this);
 
         addParameter(
-            DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(
-                    new C2StreamBufferTypeSetting::input(0u, C2BufferData::GRAPHIC))
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
                 .build());
 
         addParameter(
-            DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(
-                    new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR))
+                DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE)
+                .withConstValue(new C2StreamUsageTuning::input(
+                        0u, (uint64_t)C2MemoryUsage::CPU_READ))
                 .build());
 
         addParameter(
-            DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::input>(
-                    MEDIA_MIMETYPE_VIDEO_RAW))
-                .build());
-
-        addParameter(
-            DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::output>(
-#ifdef MPEG4
-                    MEDIA_MIMETYPE_VIDEO_MPEG4
-#else
-                    MEDIA_MIMETYPE_VIDEO_H263
-#endif
-                    ))
-                .build());
-
-        addParameter(DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE)
-                         .withConstValue(new C2StreamUsageTuning::input(
-                             0u, (uint64_t)C2MemoryUsage::CPU_READ))
-                         .build());
-
-        addParameter(
             DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
                 .withDefault(new C2StreamPictureSizeInfo::input(0u, 176, 144))
                 .withFields({
@@ -217,10 +211,6 @@
     }
 
    private:
-    std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
-    std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat;
-    std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
-    std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
     std::shared_ptr<C2StreamUsageTuning::input> mUsage;
     std::shared_ptr<C2StreamPictureSizeInfo::input> mSize;
     std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
diff --git a/media/codec2/components/opus/C2SoftOpusDec.cpp b/media/codec2/components/opus/C2SoftOpusDec.cpp
index 7dcd53d..6b6974f 100644
--- a/media/codec2/components/opus/C2SoftOpusDec.cpp
+++ b/media/codec2/components/opus/C2SoftOpusDec.cpp
@@ -31,34 +31,32 @@
 
 namespace android {
 
+namespace {
+
 constexpr char COMPONENT_NAME[] = "c2.android.opus.decoder";
 
-class C2SoftOpusDec::IntfImpl : public C2InterfaceHelper {
-   public:
-    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper>& helper)
-        : C2InterfaceHelper(helper) {
+}  // namespace
+
+class C2SoftOpusDec::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_DECODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_OPUS) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
         setDerivedInstance(this);
 
         addParameter(
-                DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-                DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-                DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::input>(
-                        MEDIA_MIMETYPE_AUDIO_OPUS))
-                .build());
-
-        addParameter(
-                DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::output>(
-                        MEDIA_MIMETYPE_AUDIO_RAW))
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
                 .build());
 
         addParameter(
@@ -88,11 +86,7 @@
                 .build());
     }
 
-   private:
-    std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
-    std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat;
-    std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
-    std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
+private:
     std::shared_ptr<C2StreamSampleRateInfo::output> mSampleRate;
     std::shared_ptr<C2StreamChannelCountInfo::output> mChannelCount;
     std::shared_ptr<C2StreamBitrateInfo::input> mBitrate;
diff --git a/media/codec2/components/opus/C2SoftOpusEnc.cpp b/media/codec2/components/opus/C2SoftOpusEnc.cpp
index f1020c3..7b58c9b 100644
--- a/media/codec2/components/opus/C2SoftOpusEnc.cpp
+++ b/media/codec2/components/opus/C2SoftOpusEnc.cpp
@@ -32,35 +32,32 @@
 #define DEFAULT_FRAME_DURATION_MS 20
 namespace android {
 
+namespace {
+
 constexpr char COMPONENT_NAME[] = "c2.android.opus.encoder";
 
-class C2SoftOpusEnc::IntfImpl : public C2InterfaceHelper {
+}  // namespace
+
+class C2SoftOpusEnc::IntfImpl : public SimpleInterface<void>::BaseParams {
 public:
     explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
-        : C2InterfaceHelper(helper) {
-
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_ENCODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_OPUS) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
         setDerivedInstance(this);
 
         addParameter(
-                DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-                DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-                DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::input>(
-                        MEDIA_MIMETYPE_AUDIO_RAW))
-                .build());
-
-        addParameter(
-                DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::output>(
-                        MEDIA_MIMETYPE_AUDIO_OPUS))
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
                 .build());
 
         addParameter(
@@ -104,10 +101,6 @@
     uint32_t getComplexity() const { return mComplexity->value; }
 
 private:
-    std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
-    std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat;
-    std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
-    std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
     std::shared_ptr<C2StreamSampleRateInfo::input> mSampleRate;
     std::shared_ptr<C2StreamChannelCountInfo::input> mChannelCount;
     std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
diff --git a/media/codec2/components/raw/C2SoftRawDec.cpp b/media/codec2/components/raw/C2SoftRawDec.cpp
index 802caa4..8e94a18 100644
--- a/media/codec2/components/raw/C2SoftRawDec.cpp
+++ b/media/codec2/components/raw/C2SoftRawDec.cpp
@@ -27,35 +27,32 @@
 
 namespace android {
 
+namespace {
+
 constexpr char COMPONENT_NAME[] = "c2.android.raw.decoder";
 
-class C2SoftRawDec::IntfImpl : public C2InterfaceHelper {
+}  // namespace
+
+class C2SoftRawDec::IntfImpl : public SimpleInterface<void>::BaseParams {
 public:
     explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
-        : C2InterfaceHelper(helper) {
-
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_DECODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_RAW) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
         setDerivedInstance(this);
 
         addParameter(
-                DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-                DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-                DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::input>(
-                        MEDIA_MIMETYPE_AUDIO_RAW))
-                .build());
-
-        addParameter(
-                DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::output>(
-                        MEDIA_MIMETYPE_AUDIO_RAW))
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
                 .build());
 
         addParameter(
@@ -98,10 +95,6 @@
     }
 
 private:
-    std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
-    std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat;
-    std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
-    std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
     std::shared_ptr<C2StreamSampleRateInfo::output> mSampleRate;
     std::shared_ptr<C2StreamChannelCountInfo::output> mChannelCount;
     std::shared_ptr<C2StreamBitrateInfo::input> mBitrate;
diff --git a/media/codec2/components/vorbis/C2SoftVorbisDec.cpp b/media/codec2/components/vorbis/C2SoftVorbisDec.cpp
index e3bafd3..18e6db2 100644
--- a/media/codec2/components/vorbis/C2SoftVorbisDec.cpp
+++ b/media/codec2/components/vorbis/C2SoftVorbisDec.cpp
@@ -35,35 +35,32 @@
 
 namespace android {
 
+namespace {
+
 constexpr char COMPONENT_NAME[] = "c2.android.vorbis.decoder";
 
-class C2SoftVorbisDec::IntfImpl : public C2InterfaceHelper {
+}  // namespace
+
+class C2SoftVorbisDec::IntfImpl : public SimpleInterface<void>::BaseParams {
 public:
     explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
-        : C2InterfaceHelper(helper) {
-
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_DECODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_VORBIS) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
         setDerivedInstance(this);
 
         addParameter(
-                DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-                DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-                DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::input>(
-                        MEDIA_MIMETYPE_AUDIO_VORBIS))
-                .build());
-
-        addParameter(
-                DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::output>(
-                        MEDIA_MIMETYPE_AUDIO_RAW))
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
                 .build());
 
         addParameter(
@@ -94,10 +91,6 @@
     }
 
 private:
-    std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
-    std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat;
-    std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
-    std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
     std::shared_ptr<C2StreamSampleRateInfo::output> mSampleRate;
     std::shared_ptr<C2StreamChannelCountInfo::output> mChannelCount;
     std::shared_ptr<C2StreamBitrateInfo::input> mBitrate;
diff --git a/media/codec2/components/vpx/Android.bp b/media/codec2/components/vpx/Android.bp
index abfd379..34f5753 100644
--- a/media/codec2/components/vpx/Android.bp
+++ b/media/codec2/components/vpx/Android.bp
@@ -7,7 +7,7 @@
 
     srcs: ["C2SoftVpxDec.cpp"],
 
-    static_libs: ["libvpx"],
+    shared_libs: ["libvpx"],
 
     cflags: [
         "-DVP9",
@@ -23,7 +23,7 @@
 
     srcs: ["C2SoftVpxDec.cpp"],
 
-    static_libs: ["libvpx"],
+    shared_libs: ["libvpx"],
 }
 
 cc_library_shared {
@@ -38,7 +38,7 @@
         "C2SoftVpxEnc.cpp",
     ],
 
-    static_libs: ["libvpx"],
+    shared_libs: ["libvpx"],
 
     cflags: ["-DVP9"],
 }
@@ -55,6 +55,6 @@
         "C2SoftVpxEnc.cpp",
     ],
 
-    static_libs: ["libvpx"],
+    shared_libs: ["libvpx"],
 }
 
diff --git a/media/codec2/components/vpx/C2SoftVp8Enc.cpp b/media/codec2/components/vpx/C2SoftVp8Enc.cpp
index 0ae717a..f18f5d0 100644
--- a/media/codec2/components/vpx/C2SoftVp8Enc.cpp
+++ b/media/codec2/components/vpx/C2SoftVp8Enc.cpp
@@ -23,8 +23,6 @@
 
 namespace android {
 
-constexpr char COMPONENT_NAME[] = "c2.android.vp8.encoder";
-
 C2SoftVp8Enc::C2SoftVp8Enc(const char* name, c2_node_id_t id,
                            const std::shared_ptr<IntfImpl>& intfImpl)
     : C2SoftVpxEnc(name, id, intfImpl), mDCTPartitions(0), mProfile(1) {}
diff --git a/media/codec2/components/vpx/C2SoftVp9Enc.cpp b/media/codec2/components/vpx/C2SoftVp9Enc.cpp
index b26170f..740dbda 100644
--- a/media/codec2/components/vpx/C2SoftVp9Enc.cpp
+++ b/media/codec2/components/vpx/C2SoftVp9Enc.cpp
@@ -23,8 +23,6 @@
 
 namespace android {
 
-constexpr char COMPONENT_NAME[] = "c2.android.vp9.encoder";
-
 C2SoftVp9Enc::C2SoftVp9Enc(const char* name, c2_node_id_t id,
                            const std::shared_ptr<IntfImpl>& intfImpl)
     : C2SoftVpxEnc(name, id, intfImpl),
diff --git a/media/codec2/components/vpx/C2SoftVpxDec.cpp b/media/codec2/components/vpx/C2SoftVpxDec.cpp
index 3120f7a..42f507f 100644
--- a/media/codec2/components/vpx/C2SoftVpxDec.cpp
+++ b/media/codec2/components/vpx/C2SoftVpxDec.cpp
@@ -559,12 +559,11 @@
     }
 }
 
-static void copyOutputBufferToYV12Frame(uint8_t *dst,
-        const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV,
+static void copyOutputBufferToYuvPlanarFrame(
+        uint8_t *dst, const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV,
         size_t srcYStride, size_t srcUStride, size_t srcVStride,
+        size_t dstYStride, size_t dstUVStride,
         uint32_t width, uint32_t height) {
-    size_t dstYStride = align(width, 16);
-    size_t dstUVStride = align(dstYStride / 2, 16);
     uint8_t *dstStart = dst;
 
     for (size_t i = 0; i < height; ++i) {
@@ -654,11 +653,10 @@
 static void convertYUV420Planar16ToYUV420Planar(uint8_t *dst,
         const uint16_t *srcY, const uint16_t *srcU, const uint16_t *srcV,
         size_t srcYStride, size_t srcUStride, size_t srcVStride,
-        size_t dstStride, size_t width, size_t height) {
+        size_t dstYStride, size_t dstUVStride, size_t width, size_t height) {
 
     uint8_t *dstY = (uint8_t *)dst;
-    size_t dstYSize = dstStride * height;
-    size_t dstUVStride = align(dstStride / 2, 16);
+    size_t dstYSize = dstYStride * height;
     size_t dstUVSize = dstUVStride * height / 2;
     uint8_t *dstV = dstY + dstYSize;
     uint8_t *dstU = dstV + dstUVSize;
@@ -669,7 +667,7 @@
         }
 
         srcY += srcYStride;
-        dstY += dstStride;
+        dstY += dstYStride;
     }
 
     for (size_t y = 0; y < (height + 1) / 2; ++y) {
@@ -751,6 +749,9 @@
     size_t srcYStride = img->stride[VPX_PLANE_Y];
     size_t srcUStride = img->stride[VPX_PLANE_U];
     size_t srcVStride = img->stride[VPX_PLANE_V];
+    C2PlanarLayout layout = wView.layout();
+    size_t dstYStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
+    size_t dstUVStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc;
 
     if (img->fmt == VPX_IMG_FMT_I42016) {
         const uint16_t *srcY = (const uint16_t *)img->planes[VPX_PLANE_Y];
@@ -760,20 +761,23 @@
         if (format == HAL_PIXEL_FORMAT_RGBA_1010102) {
             convertYUV420Planar16ToY410((uint32_t *)dst, srcY, srcU, srcV, srcYStride / 2,
                                     srcUStride / 2, srcVStride / 2,
-                                    align(mWidth, 16),
+                                    dstYStride / sizeof(uint32_t),
                                     mWidth, mHeight);
         } else {
             convertYUV420Planar16ToYUV420Planar(dst, srcY, srcU, srcV, srcYStride / 2,
                                     srcUStride / 2, srcVStride / 2,
-                                    align(mWidth, 16),
+                                    dstYStride, dstUVStride,
                                     mWidth, mHeight);
         }
     } else {
         const uint8_t *srcY = (const uint8_t *)img->planes[VPX_PLANE_Y];
         const uint8_t *srcU = (const uint8_t *)img->planes[VPX_PLANE_U];
         const uint8_t *srcV = (const uint8_t *)img->planes[VPX_PLANE_V];
-        copyOutputBufferToYV12Frame(dst, srcY, srcU, srcV,
-                                srcYStride, srcUStride, srcVStride, mWidth, mHeight);
+        copyOutputBufferToYuvPlanarFrame(
+                dst, srcY, srcU, srcV,
+                srcYStride, srcUStride, srcVStride,
+                dstYStride, dstUVStride,
+                mWidth, mHeight);
     }
     finishWork(*(int64_t *)img->user_priv, work, std::move(block));
     return true;
diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.h b/media/codec2/components/vpx/C2SoftVpxEnc.h
index 5591a49..90758f9 100644
--- a/media/codec2/components/vpx/C2SoftVpxEnc.h
+++ b/media/codec2/components/vpx/C2SoftVpxEnc.h
@@ -222,47 +222,47 @@
      C2_DO_NOT_COPY(C2SoftVpxEnc);
 };
 
-class C2SoftVpxEnc::IntfImpl : public C2InterfaceHelper {
+namespace {
+
+#ifdef VP9
+constexpr char COMPONENT_NAME[] = "c2.android.vp9.encoder";
+const char *MEDIA_MIMETYPE_VIDEO = MEDIA_MIMETYPE_VIDEO_VP9;
+#else
+constexpr char COMPONENT_NAME[] = "c2.android.vp8.encoder";
+const char *MEDIA_MIMETYPE_VIDEO = MEDIA_MIMETYPE_VIDEO_VP8;
+#endif
+
+} // namepsace
+
+class C2SoftVpxEnc::IntfImpl : public SimpleInterface<void>::BaseParams {
    public:
-    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper>& helper)
-        : C2InterfaceHelper(helper) {
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_ENCODER,
+                C2Component::DOMAIN_VIDEO,
+                MEDIA_MIMETYPE_VIDEO) {
+        noPrivateBuffers(); // TODO: account for our buffers here
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
         setDerivedInstance(this);
 
         addParameter(
-            DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(
-                    new C2StreamBufferTypeSetting::input(0u, C2BufferData::GRAPHIC))
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
                 .build());
 
         addParameter(
-            DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(
-                    new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR))
+                DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE)
+                .withConstValue(new C2StreamUsageTuning::input(
+                        0u, (uint64_t)C2MemoryUsage::CPU_READ))
                 .build());
 
         addParameter(
-            DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::input>(
-                    MEDIA_MIMETYPE_VIDEO_RAW))
-                .build());
-
-        addParameter(
-            DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::output>(
-#ifdef VP9
-                    MEDIA_MIMETYPE_VIDEO_VP9
-#else
-                    MEDIA_MIMETYPE_VIDEO_VP8
-#endif
-                    ))
-                .build());
-
-        addParameter(DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE)
-                         .withConstValue(new C2StreamUsageTuning::input(
-                             0u, (uint64_t)C2MemoryUsage::CPU_READ))
-                         .build());
-
-        addParameter(
             DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
                 .withDefault(new C2StreamPictureSizeInfo::input(0u, 320, 240))
                 .withFields({
@@ -416,10 +416,6 @@
     }
 
    private:
-    std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
-    std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat;
-    std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
-    std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
     std::shared_ptr<C2StreamUsageTuning::input> mUsage;
     std::shared_ptr<C2StreamPictureSizeInfo::input> mSize;
     std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
diff --git a/media/codec2/components/xaac/C2SoftXaacDec.cpp b/media/codec2/components/xaac/C2SoftXaacDec.cpp
index ed730c3..a3ebadb 100644
--- a/media/codec2/components/xaac/C2SoftXaacDec.cpp
+++ b/media/codec2/components/xaac/C2SoftXaacDec.cpp
@@ -56,35 +56,31 @@
 
 namespace android {
 
+namespace {
+
 constexpr char COMPONENT_NAME[] = "c2.android.xaac.decoder";
 
-class C2SoftXaacDec::IntfImpl : public C2InterfaceHelper {
+}  // namespace
+
+class C2SoftXaacDec::IntfImpl : public SimpleInterface<void>::BaseParams {
 public:
     explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
-        : C2InterfaceHelper(helper) {
-
-        setDerivedInstance(this);
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_DECODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_AAC) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
 
         addParameter(
-                DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-                DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE)
-                .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR))
-                .build());
-
-        addParameter(
-                DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::input>(
-                        MEDIA_MIMETYPE_AUDIO_AAC))
-                .build());
-
-        addParameter(
-                DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
-                .withConstValue(AllocSharedString<C2PortMediaTypeSetting::output>(
-                        MEDIA_MIMETYPE_AUDIO_RAW))
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
                 .build());
 
         addParameter(
@@ -218,10 +214,6 @@
     int32_t getDrcEffectType() const { return mDrcEffectType->value; }
 
 private:
-    std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
-    std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat;
-    std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
-    std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
     std::shared_ptr<C2StreamSampleRateInfo::output> mSampleRate;
     std::shared_ptr<C2StreamChannelCountInfo::output> mChannelCount;
     std::shared_ptr<C2StreamBitrateInfo::input> mBitrate;
diff --git a/media/codec2/hidl/1.0/utils/Android.bp b/media/codec2/hidl/1.0/utils/Android.bp
index b73f0c8..63fe36b 100644
--- a/media/codec2/hidl/1.0/utils/Android.bp
+++ b/media/codec2/hidl/1.0/utils/Android.bp
@@ -48,9 +48,6 @@
 cc_library {
     name: "libcodec2_hidl@1.0",
     vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
 
     defaults: ["hidl_defaults"],
 
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp
index 89947d4..6469735 100644
--- a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp
@@ -117,17 +117,10 @@
         const size_t kNumStringToName =
             sizeof(kStringToName) / sizeof(kStringToName[0]);
 
-        std::string substring;
-        std::string comp;
-        substring = std::string(gEnv->getComponent());
-        /* TODO: better approach to find the component */
-        /* "c2.android." => 11th position */
-        size_t pos = 11;
-        size_t len = substring.find(".decoder", pos);
-        comp = substring.substr(pos, len - pos);
-
+        // Find the component type
+        std::string comp = std::string(gEnv->getComponent());
         for (size_t i = 0; i < kNumStringToName; ++i) {
-            if (!strcasecmp(comp.c_str(), kStringToName[i].Name)) {
+            if (strcasestr(comp.c_str(), kStringToName[i].Name)) {
                 mCompName = kStringToName[i].CompName;
                 break;
             }
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp
index 0946fa6..01baf7e 100644
--- a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp
@@ -103,17 +103,10 @@
         const size_t kNumStringToName =
             sizeof(kStringToName) / sizeof(kStringToName[0]);
 
-        std::string substring;
-        std::string comp;
-        substring = std::string(gEnv->getComponent());
-        /* TODO: better approach to find the component */
-        /* "c2.android." => 11th position */
-        size_t pos = 11;
-        size_t len = substring.find(".encoder", pos);
-        comp = substring.substr(pos, len - pos);
-
+        // Find the component type
+        std::string comp = std::string(gEnv->getComponent());
         for (size_t i = 0; i < kNumStringToName; ++i) {
-            if (!strcasecmp(comp.c_str(), kStringToName[i].Name)) {
+            if (strcasestr(comp.c_str(), kStringToName[i].Name)) {
                 mCompName = kStringToName[i].CompName;
                 break;
             }
diff --git a/media/codec2/hidl/1.0/vts/functional/common/README.md b/media/codec2/hidl/1.0/vts/functional/common/README.md
index da569a8..3deab10 100644
--- a/media/codec2/hidl/1.0/vts/functional/common/README.md
+++ b/media/codec2/hidl/1.0/vts/functional/common/README.md
@@ -1,22 +1,31 @@
-## Codec2 Hal @ 1.0 tests ##
+## Codec2 VTS Hal @ 1.0 tests ##
 ---
 #### master :
 Functionality of master is to enumerate all the Codec2 components available in C2 media service.
 
-usage: MtsHidlC2V1\_0TargetMasterTest -I software
+usage: VtsHidlC2V1\_0TargetMasterTest -I default
 
 #### component :
-Functionality of component is to test common functionality across all the Codec2 components available in C2 media service. For a standard C2 component, these tests are expected to pass.
+Functionality of component test is to validate common functionality across all the Codec2 components available in C2 media service. For a standard C2 component, these tests are expected to pass.
 
-usage: MtsHidlC2V1\_0TargetComponentTest -I software -C <comp name>
+usage: VtsHidlC2V1\_0TargetComponentTest -I software -C <comp name>
+example: VtsHidlC2V1\_0TargetComponentTest -I software -C c2.android.vorbis.decoder
 
 #### audio :
-Functionality of audio test is to validate audio specific functionality Codec2 components. The resource files for this test are taken from hardware/interfaces/media/res. The path to these files on the device is required to be given for bitstream tests.
+Functionality of audio test is to validate audio specific functionality Codec2 components. The resource files for this test are taken from media/codec2/hidl/1.0/vts/functional/res. The path to these files on the device is required to be given for bitstream tests.
 
-usage: MtsHidlC2V1\_0TargetAudioDecTest -I software -C <comp name> -P /sdcard/media
+usage: VtsHidlC2V1\_0TargetAudioDecTest -I default -C <comp name> -P /sdcard/res/
+usage: VtsHidlC2V1\_0TargetAudioEncTest -I software -C <comp name> -P /sdcard/res/
+
+example: VtsHidlC2V1\_0TargetAudioDecTest -I software -C c2.android.flac.decoder -P /sdcard/res/
+example: VtsHidlC2V1\_0TargetAudioEncTest -I software -C c2.android.opus.encoder -P /sdcard/res/
 
 #### video :
-Functionality of video test is to validate video specific functionality Codec2 components. The resource files for this test are taken from hardware/interfaces/media/res. The path to these files on the device is required to be given for bitstream tests.
+Functionality of video test is to validate video specific functionality Codec2 components. The resource files for this test are taken from media/codec2/hidl/1.0/vts/functional/res. The path to these files on the device is required to be given for bitstream tests.
 
-usage: MtsHidlC2V1\_0TargetVideoDecTest -I software -C <comp name> -P /sdcard/media
+usage: VtsHidlC2V1\_0TargetVideoDecTest -I default -C <comp name> -P /sdcard/res/
+usage: VtsHidlC2V1\_0TargetVideoEncTest -I software -C <comp name> -P /sdcard/res/
+
+example: VtsHidlC2V1\_0TargetVideoDecTest -I software -C c2.android.avc.decoder -P /sdcard/res/
+example: VtsHidlC2V1\_0TargetVideoEncTest -I software -C c2.android.vp9.encoder -P /sdcard/res/
 
diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp
index dffcb6e..33fa848 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp
@@ -107,17 +107,10 @@
         const size_t kNumStringToName =
             sizeof(kStringToName) / sizeof(kStringToName[0]);
 
-        std::string substring;
-        std::string comp;
-        substring = std::string(gEnv->getComponent());
-        /* TODO: better approach to find the component */
-        /* "c2.android." => 11th position */
-        size_t pos = 11;
-        size_t len = substring.find(".decoder", pos);
-        comp = substring.substr(pos, len - pos);
-
+        // Find the component type
+        std::string comp = std::string(gEnv->getComponent());
         for (size_t i = 0; i < kNumStringToName; ++i) {
-            if (!strcasecmp(comp.c_str(), kStringToName[i].Name)) {
+            if (strcasestr(comp.c_str(), kStringToName[i].Name)) {
                 mCompName = kStringToName[i].CompName;
                 break;
             }
@@ -445,6 +438,9 @@
     int bytesCount = 0;
     uint32_t flags = 0;
     uint32_t timestamp = 0;
+    mTimestampDevTest = true;
+    mFlushedIndices.clear();
+    mTimestampUslist.clear();
     while (1) {
         if (!(eleInfo >> bytesCount)) break;
         eleInfo >> flags;
diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp
index 673dc85..6bcf840 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp
@@ -101,17 +101,10 @@
         const size_t kNumStringToName =
             sizeof(kStringToName) / sizeof(kStringToName[0]);
 
-        std::string substring;
-        std::string comp;
-        substring = std::string(gEnv->getComponent());
-        /* TODO: better approach to find the component */
-        /* "c2.android." => 11th position */
-        size_t pos = 11;
-        size_t len = substring.find(".encoder", pos);
-        comp = substring.substr(pos, len - pos);
-
+        // Find the component type
+        std::string comp = std::string(gEnv->getComponent());
         for (size_t i = 0; i < kNumStringToName; ++i) {
-            if (!strcasecmp(comp.c_str(), kStringToName[i].Name)) {
+            if (strcasestr(comp.c_str(), kStringToName[i].Name)) {
                 mCompName = kStringToName[i].CompName;
                 break;
             }
@@ -120,6 +113,8 @@
         mCsd = false;
         mFramesReceived = 0;
         mFailedWorkReceived = 0;
+        mTimestampUs = 0u;
+        mTimestampDevTest = false;
         if (mCompName == unknown_comp) mDisableTest = true;
         if (mDisableTest) std::cout << "[   WARN   ] Test Disabled \n";
     }
@@ -139,6 +134,45 @@
     void handleWorkDone(std::list<std::unique_ptr<C2Work>>& workItems) {
         for (std::unique_ptr<C2Work>& work : workItems) {
             if (!work->worklets.empty()) {
+                // For encoder components current timestamp always exceeds
+                // previous timestamp
+                typedef std::unique_lock<std::mutex> ULock;
+                if (!mTimestampUslist.empty()) {
+                    EXPECT_GE((work->worklets.front()
+                                   ->output.ordinal.timestamp.peeku()),
+                              mTimestampUs);
+                    mTimestampUs = work->worklets.front()
+                                       ->output.ordinal.timestamp.peeku();
+                    // Currently this lock is redundant as no mTimestampUslist is only initialized
+                    // before queuing any work to component. Once AdaptiveTest is added similar to
+                    // the one in video decoders, this is needed.
+                    ULock l(mQueueLock);
+
+                    if (mTimestampDevTest) {
+                        bool tsHit = false;
+                        std::list<uint64_t>::iterator it =
+                            mTimestampUslist.begin();
+                        while (it != mTimestampUslist.end()) {
+                            if (*it == mTimestampUs) {
+                                mTimestampUslist.erase(it);
+                                tsHit = true;
+                                break;
+                            }
+                            it++;
+                        }
+                        if (tsHit == false) {
+                            if (mTimestampUslist.empty() == false) {
+                                EXPECT_EQ(tsHit, true)
+                                    << "TimeStamp not recognized";
+                            } else {
+                                std::cout
+                                    << "[   INFO   ] Received non-zero "
+                                       "output / TimeStamp not recognized \n";
+                            }
+                        }
+                    }
+                }
+
                 if (work->result != C2_OK) mFailedWorkReceived++;
                 workDone(mComponent, work, mFlushedIndices, mQueueLock,
                          mQueueCondition, mWorkQueue, mEos, mCsd,
@@ -161,10 +195,13 @@
     bool mCsd;
     bool mDisableTest;
     bool mConfig;
+    bool mTimestampDevTest;
     standardComp mCompName;
     uint32_t mFramesReceived;
     uint32_t mFailedWorkReceived;
+    uint64_t mTimestampUs;
 
+    std::list<uint64_t> mTimestampUslist;
     std::list<uint64_t> mFlushedIndices;
 
     C2BlockPool::local_id_t mBlockPoolId;
@@ -364,6 +401,18 @@
     ASSERT_EQ(eleStream.is_open(), true) << mURL << " file not found";
     ALOGV("mURL : %s", mURL);
 
+    mTimestampUs = 0;
+    mTimestampDevTest = true;
+    mFlushedIndices.clear();
+    mTimestampUslist.clear();
+    uint32_t inputFrames = ENC_NUM_FRAMES;
+    uint32_t timestamp = 0;
+    // Add input timestamp to timestampUslist
+    while (inputFrames) {
+        if (mTimestampDevTest) mTimestampUslist.push_back(timestamp);
+        timestamp += ENCODER_TIMESTAMP_INCREMENT;
+        inputFrames--;
+    }
     if (!setupConfigParam(nWidth, nHeight)) {
         std::cout << "[   WARN   ] Test Skipped \n";
         return;
@@ -375,7 +424,7 @@
                       0, ENC_NUM_FRAMES, nWidth, nHeight, false, signalEOS));
 
     // If EOS is not sent, sending empty input with EOS flag
-    uint32_t inputFrames = ENC_NUM_FRAMES;
+    inputFrames = ENC_NUM_FRAMES;
     if (!signalEOS) {
         ASSERT_NO_FATAL_FAILURE(
             waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue, 1));
@@ -407,6 +456,7 @@
         ASSERT_TRUE(false) << "CSD Buffer not expected";
     }
 
+    if (mTimestampDevTest) EXPECT_EQ(mTimestampUslist.empty(), true);
     ASSERT_EQ(mComponent->stop(), C2_OK);
 }
 
diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
index ead0a9b..1919597 100644
--- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp
+++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
@@ -212,6 +212,76 @@
     }
 }
 
+class Switch {
+    enum Flags : uint8_t {
+        // flags
+        IS_ENABLED = (1 << 0),
+        BY_DEFAULT = (1 << 1),
+    };
+
+    constexpr Switch(uint8_t flags) : mFlags(flags) {}
+
+    uint8_t mFlags;
+
+public:
+    // have to create class due to this bool conversion operator...
+    constexpr operator bool() const {
+        return mFlags & IS_ENABLED;
+    }
+
+    constexpr Switch operator!() const {
+        return Switch(mFlags ^ IS_ENABLED);
+    }
+
+    static constexpr Switch DISABLED() { return 0; };
+    static constexpr Switch ENABLED() { return IS_ENABLED; };
+    static constexpr Switch DISABLED_BY_DEFAULT() { return BY_DEFAULT; };
+    static constexpr Switch ENABLED_BY_DEFAULT() { return IS_ENABLED | BY_DEFAULT; };
+
+    const char *toString(const char *def = "??") const {
+        switch (mFlags) {
+        case 0:                         return "0";
+        case IS_ENABLED:                return "1";
+        case BY_DEFAULT:                return "(0)";
+        case IS_ENABLED | BY_DEFAULT:   return "(1)";
+        default: return def;
+        }
+    }
+
+};
+
+const char *asString(const Switch &s, const char *def = "??") {
+    return s.toString(def);
+}
+
+Switch isSettingEnabled(
+        std::string setting, const MediaCodecsXmlParser::AttributeMap &settings,
+        Switch def = Switch::DISABLED_BY_DEFAULT()) {
+    const auto enablement = settings.find(setting);
+    if (enablement == settings.end()) {
+        return def;
+    }
+    return enablement->second == "1" ? Switch::ENABLED() : Switch::DISABLED();
+}
+
+Switch isVariantEnabled(
+        std::string variant, const MediaCodecsXmlParser::AttributeMap &settings) {
+    return isSettingEnabled("variant-" + variant, settings);
+}
+
+Switch isVariantExpressionEnabled(
+        std::string exp, const MediaCodecsXmlParser::AttributeMap &settings) {
+    if (!exp.empty() && exp.at(0) == '!') {
+        return !isVariantEnabled(exp.substr(1, exp.size() - 1), settings);
+    }
+    return isVariantEnabled(exp, settings);
+}
+
+Switch isDomainEnabled(
+        std::string domain, const MediaCodecsXmlParser::AttributeMap &settings) {
+    return isSettingEnabled("domain-" + domain, settings);
+}
+
 } // unnamed namespace
 
 status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) {
@@ -248,15 +318,33 @@
     // Obtain Codec2Client
     std::vector<Traits> traits = Codec2Client::ListComponents();
 
-    MediaCodecsXmlParser parser(
-            MediaCodecsXmlParser::defaultSearchDirs,
-            "media_codecs_c2.xml",
-            "media_codecs_performance_c2.xml");
+    // parse APEX XML first, followed by vendor XML
+    MediaCodecsXmlParser parser;
+    parser.parseXmlFilesInSearchDirs(
+            parser.getDefaultXmlNames(),
+            { "/apex/com.android.media.swcodec/etc" });
+
+    // TODO: remove these c2-specific files once product moved to default file names
+    parser.parseXmlFilesInSearchDirs(
+            { "media_codecs_c2.xml", "media_codecs_performance_c2.xml" });
+
+    // parse default XML files
+    parser.parseXmlFilesInSearchDirs();
+
     if (parser.getParsingStatus() != OK) {
         ALOGD("XML parser no good");
         return OK;
     }
 
+    MediaCodecsXmlParser::AttributeMap settings = parser.getServiceAttributeMap();
+    for (const auto &v : settings) {
+        if (!hasPrefix(v.first, "media-type-")
+                && !hasPrefix(v.first, "domain-")
+                && !hasPrefix(v.first, "variant-")) {
+            writer->addGlobalSetting(v.first.c_str(), v.second.c_str());
+        }
+    }
+
     for (const Traits& trait : traits) {
         C2Component::rank_t rank = trait.rank;
 
@@ -341,12 +429,42 @@
                 break;
             }
 
+            const MediaCodecsXmlParser::CodecProperties &codec =
+                parser.getCodecMap().at(nameOrAlias);
+
+            // verify that either the codec is explicitly enabled, or one of its domains is
+            bool codecEnabled = codec.quirkSet.find("attribute::disabled") == codec.quirkSet.end();
+            if (!codecEnabled) {
+                for (const std::string &domain : codec.domainSet) {
+                    const Switch enabled = isDomainEnabled(domain, settings);
+                    ALOGV("codec entry '%s' is in domain '%s' that is '%s'",
+                            nameOrAlias.c_str(), domain.c_str(), asString(enabled));
+                    if (enabled) {
+                        codecEnabled = true;
+                        break;
+                    }
+                }
+            }
+            // if codec has variants, also check that at least one of them is enabled
+            bool variantEnabled = codec.variantSet.empty();
+            for (const std::string &variant : codec.variantSet) {
+                const Switch enabled = isVariantExpressionEnabled(variant, settings);
+                ALOGV("codec entry '%s' has a variant '%s' that is '%s'",
+                        nameOrAlias.c_str(), variant.c_str(), asString(enabled));
+                if (enabled) {
+                    variantEnabled = true;
+                    break;
+                }
+            }
+            if (!codecEnabled || !variantEnabled) {
+                ALOGD("codec entry for '%s' is disabled", nameOrAlias.c_str());
+                continue;
+            }
+
             ALOGV("adding codec entry for '%s'", nameOrAlias.c_str());
             std::unique_ptr<MediaCodecInfoWriter> codecInfo = writer->addMediaCodecInfo();
             codecInfo->setName(nameOrAlias.c_str());
             codecInfo->setOwner(("codec2::" + trait.owner).c_str());
-            const MediaCodecsXmlParser::CodecProperties &codec =
-                parser.getCodecMap().at(nameOrAlias);
 
             bool encoder = trait.kind == C2Component::KIND_ENCODER;
             typename std::underlying_type<MediaCodecInfo::Attributes>::type attrs = 0;
@@ -373,6 +491,7 @@
                     rank = xmlRank;
                 }
             }
+            ALOGV("rank: %u", (unsigned)rank);
             codecInfo->setRank(rank);
 
             for (const std::string &alias : codec.aliases) {
@@ -382,12 +501,39 @@
 
             for (auto typeIt = codec.typeMap.begin(); typeIt != codec.typeMap.end(); ++typeIt) {
                 const std::string &mediaType = typeIt->first;
+                const Switch typeEnabled = isSettingEnabled(
+                        "media-type-" + mediaType, settings, Switch::ENABLED_BY_DEFAULT());
+                const Switch domainTypeEnabled = isSettingEnabled(
+                        "media-type-" + mediaType + (encoder ? "-encoder" : "-decoder"),
+                        settings, Switch::ENABLED_BY_DEFAULT());
+                ALOGV("type '%s-%s' is '%s/%s'",
+                        mediaType.c_str(), (encoder ? "encoder" : "decoder"),
+                        asString(typeEnabled), asString(domainTypeEnabled));
+                if (!typeEnabled || !domainTypeEnabled) {
+                    ALOGD("media type '%s' for codec entry '%s' is disabled", mediaType.c_str(),
+                            nameOrAlias.c_str());
+                    continue;
+                }
+
+                ALOGI("adding type '%s'", typeIt->first.c_str());
                 const MediaCodecsXmlParser::AttributeMap &attrMap = typeIt->second;
                 std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
                     codecInfo->addMediaType(mediaType.c_str());
-                for (auto attrIt = attrMap.begin(); attrIt != attrMap.end(); ++attrIt) {
-                    std::string key, value;
-                    std::tie(key, value) = *attrIt;
+                for (const auto &v : attrMap) {
+                    std::string key = v.first;
+                    std::string value = v.second;
+
+                    size_t variantSep = key.find(":::");
+                    if (variantSep != std::string::npos) {
+                        std::string variant = key.substr(0, variantSep);
+                        const Switch enabled = isVariantExpressionEnabled(variant, settings);
+                        ALOGV("variant '%s' is '%s'", variant.c_str(), asString(enabled));
+                        if (!enabled) {
+                            continue;
+                        }
+                        key = key.substr(variantSep + 3);
+                    }
+
                     if (key.find("feature-") == 0 && key.find("feature-bitrate-modes") != 0) {
                         int32_t intValue = 0;
                         // Ignore trailing bad characters and default to 0.
diff --git a/media/codec2/sfplugin/PipelineWatcher.cpp b/media/codec2/sfplugin/PipelineWatcher.cpp
index df81d49..74d14e8 100644
--- a/media/codec2/sfplugin/PipelineWatcher.cpp
+++ b/media/codec2/sfplugin/PipelineWatcher.cpp
@@ -121,6 +121,13 @@
               sizeWithInputReleased);
         return true;
     }
+
+    size_t sizeWithInputsPending = mFramesInPipeline.size() - sizeWithInputReleased;
+    if (sizeWithInputsPending > mPipelineDelay + mInputDelay + mSmoothnessFactor) {
+        ALOGV("pipelineFull: too many inputs pending (%zu) in pipeline, with inputs released (%zu)",
+              sizeWithInputsPending, sizeWithInputReleased);
+        return true;
+    }
     ALOGV("pipeline has room (total: %zu, input released: %zu)",
           mFramesInPipeline.size(), sizeWithInputReleased);
     return false;
diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp
index 198bd72..ca69810 100644
--- a/media/codec2/vndk/Android.bp
+++ b/media/codec2/vndk/Android.bp
@@ -14,9 +14,6 @@
 cc_library_shared {
     name: "libcodec2_vndk",
     vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
 
     srcs: [
         "C2AllocatorIon.cpp",
diff --git a/media/codec2/vndk/C2Store.cpp b/media/codec2/vndk/C2Store.cpp
index f5dc838..10c4dcc 100644
--- a/media/codec2/vndk/C2Store.cpp
+++ b/media/codec2/vndk/C2Store.cpp
@@ -26,7 +26,6 @@
 #include <C2Config.h>
 #include <C2PlatformStorePluginLoader.h>
 #include <C2PlatformSupport.h>
-#include <media/stagefright/foundation/ADebug.h>
 #include <util/C2InterfaceHelper.h>
 
 #include <dlfcn.h>
@@ -662,36 +661,30 @@
     ALOGV("in %s", __func__);
     ALOGV("loading dll");
     mLibHandle = dlopen(libPath.c_str(), RTLD_NOW|RTLD_NODELETE);
-    if (mLibHandle == nullptr) {
-        LOG_ALWAYS_FATAL_IN_CHILD_PROC("could not dlopen %s: %s", libPath.c_str(), dlerror());
-        mInit = C2_CORRUPTED;
-        return mInit;
-    }
+    LOG_ALWAYS_FATAL_IF(mLibHandle == nullptr,
+            "could not dlopen %s: %s", libPath.c_str(), dlerror());
 
     createFactory =
         (C2ComponentFactory::CreateCodec2FactoryFunc)dlsym(mLibHandle, "CreateCodec2Factory");
-    if (createFactory == nullptr) {
-        LOG_ALWAYS_FATAL_IN_CHILD_PROC("createFactory is null in %s", libPath.c_str());
-        mInit = C2_CORRUPTED;
-        return mInit;
-    }
+    LOG_ALWAYS_FATAL_IF(createFactory == nullptr,
+            "createFactory is null in %s", libPath.c_str());
 
     destroyFactory =
         (C2ComponentFactory::DestroyCodec2FactoryFunc)dlsym(mLibHandle, "DestroyCodec2Factory");
-    if (destroyFactory == nullptr) {
-        LOG_ALWAYS_FATAL_IN_CHILD_PROC("destroyFactory is null in %s", libPath.c_str());
-        mInit = C2_CORRUPTED;
-        return mInit;
-    }
+    LOG_ALWAYS_FATAL_IF(destroyFactory == nullptr,
+            "destroyFactory is null in %s", libPath.c_str());
 
     mComponentFactory = createFactory();
     if (mComponentFactory == nullptr) {
         ALOGD("could not create factory in %s", libPath.c_str());
         mInit = C2_NO_MEMORY;
-        return mInit;
+    } else {
+        mInit = C2_OK;
     }
 
-    mInit = C2_OK;
+    if (mInit != C2_OK) {
+        return mInit;
+    }
 
     std::shared_ptr<C2ComponentInterface> intf;
     c2_status_t res = createInterface(0, &intf);
diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp
index a1c81f3..ab76edc 100644
--- a/media/extractors/mkv/MatroskaExtractor.cpp
+++ b/media/extractors/mkv/MatroskaExtractor.cpp
@@ -151,6 +151,7 @@
         HEVC,
         MP3,
         PCM,
+        VORBIS,
         OTHER
     };
 
@@ -273,6 +274,8 @@
         mType = MP3;
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) {
         mType = PCM;
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
+        mType = VORBIS;
     }
 }
 
@@ -802,6 +805,26 @@
         AMediaFormat_setInt64(meta, AMEDIAFORMAT_KEY_TIME_US, timeUs);
         AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, block->IsKey());
 
+        if (mType == VORBIS) {
+            int32_t sampleRate;
+            if (!AMediaFormat_getInt32(trackInfo->mMeta, AMEDIAFORMAT_KEY_SAMPLE_RATE,
+                                       &sampleRate)) {
+                return AMEDIA_ERROR_MALFORMED;
+            }
+            int64_t durationUs;
+            if (!AMediaFormat_getInt64(trackInfo->mMeta, AMEDIAFORMAT_KEY_DURATION,
+                                       &durationUs)) {
+                return AMEDIA_ERROR_MALFORMED;
+            }
+            // TODO: Explore if this can be handled similar to MPEG4 extractor where padding is
+            // signalled instead of VALID_SAMPLES
+            // Remaining valid samples in Vorbis track
+            if (durationUs > timeUs) {
+                int32_t validSamples = ((durationUs - timeUs) * sampleRate) / 1000000ll;
+                AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_VALID_SAMPLES, validSamples);
+            }
+        }
+
         status_t err = frame.Read(mExtractor->mReader, data + trackInfo->mHeaderLen);
         if (err == OK
                 && mExtractor->mIsWebm
diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp
index d56abaa..8c8e6d1 100644
--- a/media/extractors/mp4/ItemTable.cpp
+++ b/media/extractors/mp4/ItemTable.cpp
@@ -426,10 +426,8 @@
         }
         ALOGV("extent_count %d", extent_count);
 
-        if (extent_count > 1 && (offset_size == 0 || length_size == 0)) {
-            // if the item is dividec into more than one extents, offset and
-            // length must be present.
-            return ERROR_MALFORMED;
+        if (extent_count > 1) {
+            return ERROR_UNSUPPORTED;
         }
         offset += 2;
 
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index e1bfb62..e01fc90 100755
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -45,6 +45,7 @@
 #include <media/stagefright/foundation/ColorUtils.h>
 #include <media/stagefright/foundation/avc_utils.h>
 #include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/foundation/OpusHeader.h>
 #include <media/stagefright/MediaBufferGroup.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MetaDataBase.h>
@@ -1735,15 +1736,21 @@
             AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, sample_rate);
 
             if (chunk_type == FOURCC("Opus")) {
-                uint8_t opusInfo[19];
+                uint8_t opusInfo[AOPUS_OPUSHEAD_MAXSIZE];
                 data_offset += sizeof(buffer);
+                size_t opusInfoSize = chunk_data_size - sizeof(buffer);
+
+                if (opusInfoSize < AOPUS_OPUSHEAD_MINSIZE ||
+                    opusInfoSize > AOPUS_OPUSHEAD_MAXSIZE) {
+                    return ERROR_MALFORMED;
+                }
                 // Read Opus Header
                 if (mDataSource->readAt(
-                        data_offset, opusInfo, sizeof(opusInfo)) < (ssize_t)sizeof(opusInfo)) {
+                        data_offset, opusInfo, opusInfoSize) < opusInfoSize) {
                     return ERROR_IO;
                 }
 
-                // OpusHeader must start with this magic sequence
+                // OpusHeader must start with this magic sequence, overwrite first 8 bytes
                 // http://wiki.xiph.org/OggOpus#ID_Header
                 strncpy((char *)opusInfo, "OpusHead", 8);
 
@@ -1760,17 +1767,18 @@
                 memcpy(&opusInfo[opusOffset + 2], &sample_rate, sizeof(sample_rate));
                 memcpy(&opusInfo[opusOffset + 6], &out_gain, sizeof(out_gain));
 
-                int64_t codecDelay = 6500000;
-                int64_t seekPreRollNs = 80000000;  // Fixed 80 msec
+                static const int64_t kSeekPreRollNs = 80000000;  // Fixed 80 msec
+                static const int32_t kOpusSampleRate = 48000;
+                int64_t codecDelay = pre_skip * 1000000000ll / kOpusSampleRate;
 
                 AMediaFormat_setBuffer(mLastTrack->meta,
                             AMEDIAFORMAT_KEY_CSD_0, opusInfo, sizeof(opusInfo));
                 AMediaFormat_setBuffer(mLastTrack->meta,
                         AMEDIAFORMAT_KEY_CSD_1, &codecDelay, sizeof(codecDelay));
                 AMediaFormat_setBuffer(mLastTrack->meta,
-                        AMEDIAFORMAT_KEY_CSD_2, &seekPreRollNs, sizeof(seekPreRollNs));
+                        AMEDIAFORMAT_KEY_CSD_2, &kSeekPreRollNs, sizeof(kSeekPreRollNs));
 
-                data_offset += sizeof(opusInfo);
+                data_offset += opusInfoSize;
                 *offset = data_offset;
                 CHECK_EQ(*offset, stop_offset);
             }
@@ -1964,6 +1972,8 @@
                 return err;
             }
 
+            adjustRawDefaultFrameSize();
+
             size_t max_size;
             err = mLastTrack->sampleTable->getMaxSampleSize(&max_size);
 
@@ -4297,6 +4307,77 @@
         return ERROR_MALFORMED;
     }
 
+    if (objectTypeIndication == 0xdd) {
+        // vorbis audio
+        if (csd[0] != 0x02) {
+            return ERROR_MALFORMED;
+        }
+
+        // codecInfo starts with two lengths, len1 and len2, that are
+        // "Xiph-style-lacing encoded"..
+
+        size_t offset = 1;
+        size_t len1 = 0;
+        while (offset < csd_size && csd[offset] == 0xff) {
+            if (__builtin_add_overflow(len1, 0xff, &len1)) {
+                return ERROR_MALFORMED;
+            }
+            ++offset;
+        }
+        if (offset >= csd_size) {
+            return ERROR_MALFORMED;
+        }
+        if (__builtin_add_overflow(len1, csd[offset], &len1)) {
+            return ERROR_MALFORMED;
+        }
+        ++offset;
+        if (len1 == 0) {
+            return ERROR_MALFORMED;
+        }
+
+        size_t len2 = 0;
+        while (offset < csd_size && csd[offset] == 0xff) {
+            if (__builtin_add_overflow(len2, 0xff, &len2)) {
+                return ERROR_MALFORMED;
+            }
+            ++offset;
+        }
+        if (offset >= csd_size) {
+            return ERROR_MALFORMED;
+        }
+        if (__builtin_add_overflow(len2, csd[offset], &len2)) {
+            return ERROR_MALFORMED;
+        }
+        ++offset;
+        if (len2 == 0) {
+            return ERROR_MALFORMED;
+        }
+        if (offset >= csd_size || csd[offset] != 0x01) {
+            return ERROR_MALFORMED;
+        }
+        // formerly kKeyVorbisInfo
+        AMediaFormat_setBuffer(mLastTrack->meta,
+                AMEDIAFORMAT_KEY_CSD_0, &csd[offset], len1);
+
+        if (__builtin_add_overflow(offset, len1, &offset) ||
+                offset >= csd_size || csd[offset] != 0x03) {
+            return ERROR_MALFORMED;
+        }
+
+        if (__builtin_add_overflow(offset, len2, &offset) ||
+                offset >= csd_size || csd[offset] != 0x05) {
+            return ERROR_MALFORMED;
+        }
+
+        // formerly kKeyVorbisBooks
+        AMediaFormat_setBuffer(mLastTrack->meta,
+                AMEDIAFORMAT_KEY_CSD_1, &csd[offset], csd_size - offset);
+        AMediaFormat_setString(mLastTrack->meta,
+                AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_VORBIS);
+
+        return OK;
+    }
+
     static uint32_t kSamplingRate[] = {
         96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
         16000, 12000, 11025, 8000, 7350
@@ -4527,6 +4608,20 @@
     return OK;
 }
 
+void MPEG4Extractor::adjustRawDefaultFrameSize() {
+    int32_t chanCount = 0;
+    int32_t bitWidth = 0;
+    const char *mimeStr = NULL;
+
+    if(AMediaFormat_getString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, &mimeStr) &&
+        !strcasecmp(mimeStr, MEDIA_MIMETYPE_AUDIO_RAW) &&
+        AMediaFormat_getInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &chanCount) &&
+        AMediaFormat_getInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, &bitWidth)) {
+        // samplesize in stsz may not right , so updade default samplesize
+        mLastTrack->sampleTable->setPredictSampleSize(chanCount * bitWidth / 8);
+    }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 MPEG4Source::MPEG4Source(
diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h
index 031e793..e10bf8a 100644
--- a/media/extractors/mp4/MPEG4Extractor.h
+++ b/media/extractors/mp4/MPEG4Extractor.h
@@ -179,6 +179,7 @@
     status_t parseAC3SpecificBox(off64_t offset);
     status_t parseEAC3SpecificBox(off64_t offset);
     status_t parseAC4SpecificBox(off64_t offset);
+    void adjustRawDefaultFrameSize();
 
     MPEG4Extractor(const MPEG4Extractor &);
     MPEG4Extractor &operator=(const MPEG4Extractor &);
diff --git a/media/extractors/mp4/SampleTable.h b/media/extractors/mp4/SampleTable.h
index 57f6e62..076f4c3 100644
--- a/media/extractors/mp4/SampleTable.h
+++ b/media/extractors/mp4/SampleTable.h
@@ -89,6 +89,10 @@
 
     status_t findThumbnailSample(uint32_t *sample_index);
 
+    void setPredictSampleSize(uint32_t sampleSize) {
+        mDefaultSampleSize = sampleSize;
+    }
+
 protected:
     ~SampleTable();
 
diff --git a/media/libaudioclient/AudioPolicy.cpp b/media/libaudioclient/AudioPolicy.cpp
index 65e797f..3cdf095 100644
--- a/media/libaudioclient/AudioPolicy.cpp
+++ b/media/libaudioclient/AudioPolicy.cpp
@@ -159,4 +159,29 @@
     mCriteria.add(crit);
 }
 
+bool AudioMix::hasUidRule(bool match, uid_t uid) const {
+    const uint32_t rule = match ? RULE_MATCH_UID : RULE_EXCLUDE_UID;
+    for (size_t i = 0; i < mCriteria.size(); i++) {
+        if (mCriteria[i].mRule == rule
+                && mCriteria[i].mValue.mUid == uid) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool AudioMix::hasMatchUidRule() const {
+    for (size_t i = 0; i < mCriteria.size(); i++) {
+        if (mCriteria[i].mRule == RULE_MATCH_UID) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool AudioMix::isDeviceAffinityCompatible() const {
+    return ((mMixType == MIX_TYPE_PLAYERS)
+            && (mRouteFlags == MIX_ROUTE_FLAG_RENDER));
+}
+
 } // namespace android
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index d359f52..1e7f9fa 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -864,7 +864,7 @@
     return aps->getOutput(stream);
 }
 
-status_t AudioSystem::getOutputForAttr(const audio_attributes_t *attr,
+status_t AudioSystem::getOutputForAttr(audio_attributes_t *attr,
                                         audio_io_handle_t *output,
                                         audio_session_t session,
                                         audio_stream_type_t *stream,
diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp
index 9b4221c..c548457 100644
--- a/media/libaudioclient/IAudioPolicyService.cpp
+++ b/media/libaudioclient/IAudioPolicyService.cpp
@@ -198,7 +198,7 @@
         return static_cast <audio_io_handle_t> (reply.readInt32());
     }
 
-    status_t getOutputForAttr(const audio_attributes_t *attr,
+    status_t getOutputForAttr(audio_attributes_t *attr,
                               audio_io_handle_t *output,
                               audio_session_t session,
                               audio_stream_type_t *stream,
@@ -212,38 +212,27 @@
         {
             Parcel data, reply;
             data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
-            if (attr == NULL) {
-                if (stream == NULL) {
-                    ALOGE("getOutputForAttr(): NULL audio attributes and stream type");
-                    return BAD_VALUE;
-                }
-                if (*stream == AUDIO_STREAM_DEFAULT) {
-                    ALOGE("getOutputForAttr unspecified stream type");
-                    return BAD_VALUE;
-                }
-            }
-            if (output == NULL) {
-                ALOGE("getOutputForAttr NULL output - shouldn't happen");
+            if (attr == nullptr) {
+                ALOGE("%s NULL audio attributes", __func__);
                 return BAD_VALUE;
             }
-            if (selectedDeviceId == NULL) {
-                ALOGE("getOutputForAttr NULL selectedDeviceId - shouldn't happen");
+            if (output == nullptr) {
+                ALOGE("%s NULL output - shouldn't happen", __func__);
                 return BAD_VALUE;
             }
-            if (portId == NULL) {
-                ALOGE("getOutputForAttr NULL portId - shouldn't happen");
+            if (selectedDeviceId == nullptr) {
+                ALOGE("%s NULL selectedDeviceId - shouldn't happen", __func__);
                 return BAD_VALUE;
             }
-            if (secondaryOutputs == NULL) {
-                ALOGE("getOutputForAttr NULL secondaryOutputs - shouldn't happen");
+            if (portId == nullptr) {
+                ALOGE("%s NULL portId - shouldn't happen", __func__);
                 return BAD_VALUE;
             }
-            if (attr == NULL) {
-                data.writeInt32(0);
-            } else {
-                data.writeInt32(1);
-                data.write(attr, sizeof(audio_attributes_t));
+            if (secondaryOutputs == nullptr) {
+                ALOGE("%s NULL secondaryOutputs - shouldn't happen", __func__);
+                return BAD_VALUE;
             }
+            data.write(attr, sizeof(audio_attributes_t));
             data.writeInt32(session);
             if (stream == NULL) {
                 data.writeInt32(0);
@@ -265,6 +254,10 @@
             if (status != NO_ERROR) {
                 return status;
             }
+            status = (status_t)reply.read(&attr, sizeof(audio_attributes_t));
+            if (status != NO_ERROR) {
+                return status;
+            }
             *output = (audio_io_handle_t)reply.readInt32();
             audio_stream_type_t lStream = (audio_stream_type_t)reply.readInt32();
             if (stream != NULL) {
@@ -1449,12 +1442,12 @@
 
         case GET_OUTPUT_FOR_ATTR: {
             CHECK_INTERFACE(IAudioPolicyService, data, reply);
-            audio_attributes_t attr = {};
-            bool hasAttributes = data.readInt32() != 0;
-            if (hasAttributes) {
-                data.read(&attr, sizeof(audio_attributes_t));
-                sanetizeAudioAttributes(&attr);
+            audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
+            status_t status = data.read(&attr, sizeof(audio_attributes_t));
+            if (status != NO_ERROR) {
+                return status;
             }
+            sanetizeAudioAttributes(&attr);
             audio_session_t session = (audio_session_t)data.readInt32();
             audio_stream_type_t stream = AUDIO_STREAM_DEFAULT;
             bool hasStream = data.readInt32() != 0;
@@ -1472,11 +1465,15 @@
             audio_port_handle_t portId = (audio_port_handle_t)data.readInt32();
             audio_io_handle_t output = 0;
             std::vector<audio_io_handle_t> secondaryOutputs;
-            status_t status = getOutputForAttr(hasAttributes ? &attr : NULL,
+            status = getOutputForAttr(&attr,
                     &output, session, &stream, pid, uid,
                     &config,
                     flags, &selectedDeviceId, &portId, &secondaryOutputs);
             reply->writeInt32(status);
+            status = reply->write(&attr, sizeof(audio_attributes_t));
+            if (status != NO_ERROR) {
+                return status;
+            }
             reply->writeInt32(output);
             reply->writeInt32(stream);
             reply->writeInt32(selectedDeviceId);
diff --git a/media/libaudioclient/RecordingActivityTracker.cpp b/media/libaudioclient/RecordingActivityTracker.cpp
index bd10895..42d4361 100644
--- a/media/libaudioclient/RecordingActivityTracker.cpp
+++ b/media/libaudioclient/RecordingActivityTracker.cpp
@@ -36,6 +36,9 @@
 
 RecordingActivityTracker::~RecordingActivityTracker()
 {
+    if (mRIId != RECORD_RIID_INVALID && mAudioManager) {
+        mAudioManager->releaseRecorder(mRIId);
+    }
 }
 
 audio_unique_id_t RecordingActivityTracker::getRiid()
diff --git a/media/libaudioclient/include/media/AudioPolicy.h b/media/libaudioclient/include/media/AudioPolicy.h
index a40e019..ef39fd1 100644
--- a/media/libaudioclient/include/media/AudioPolicy.h
+++ b/media/libaudioclient/include/media/AudioPolicy.h
@@ -106,6 +106,12 @@
 
     void setExcludeUid(uid_t uid) const;
     void setMatchUid(uid_t uid) const;
+    /** returns true if this mix has a rule to match or exclude the given uid */
+    bool hasUidRule(bool match, uid_t uid) const;
+    /** returns true if this mix has a rule for uid match (any uid) */
+    bool hasMatchUidRule() const;
+    /** returns true if this mix can be used for uid-device affinity routing */
+    bool isDeviceAffinityCompatible() const;
 
     mutable Vector<AudioMixMatchCriterion> mCriteria;
     uint32_t        mMixType;
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index f79ec21..56c69f6 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -223,7 +223,7 @@
     static status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config);
     static audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage);
 
-    static status_t getOutputForAttr(const audio_attributes_t *attr,
+    static status_t getOutputForAttr(audio_attributes_t *attr,
                                      audio_io_handle_t *output,
                                      audio_session_t session,
                                      audio_stream_type_t *stream,
diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h
index b639044..33ab1f9 100644
--- a/media/libaudioclient/include/media/IAudioPolicyService.h
+++ b/media/libaudioclient/include/media/IAudioPolicyService.h
@@ -57,7 +57,7 @@
                                     audio_policy_forced_cfg_t config) = 0;
     virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage) = 0;
     virtual audio_io_handle_t getOutput(audio_stream_type_t stream) = 0;
-    virtual status_t getOutputForAttr(const audio_attributes_t *attr,
+    virtual status_t getOutputForAttr(audio_attributes_t *attr,
                                       audio_io_handle_t *output,
                                       audio_session_t session,
                                       audio_stream_type_t *stream,
diff --git a/media/libmedia/include/media/DrmHal.h b/media/libmedia/include/media/DrmHal.h
index a630bfd..bdf1b30 100644
--- a/media/libmedia/include/media/DrmHal.h
+++ b/media/libmedia/include/media/DrmHal.h
@@ -37,14 +37,15 @@
 using drm::V1_0::IDrmFactory;
 using drm::V1_0::IDrmPlugin;
 using drm::V1_0::IDrmPluginListener;
-using drm::V1_0::KeyStatus;
 using drm::V1_1::SecurityLevel;
+using drm::V1_2::KeyStatus;
 using drm::V1_2::OfflineLicenseState;
 using ::android::hardware::hidl_vec;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
 
 typedef drm::V1_2::IDrmPluginListener IDrmPluginListener_V1_2;
+typedef drm::V1_0::KeyStatus KeyStatus_V1_0;
 
 namespace android {
 
@@ -56,7 +57,7 @@
 }
 
 struct DrmHal : public BnDrm,
-             public IBinder::DeathRecipient,
+                public IBinder::DeathRecipient,
                 public IDrmPluginListener_V1_2 {
     DrmHal();
     virtual ~DrmHal();
@@ -180,6 +181,9 @@
             int64_t expiryTimeInMS);
 
     Return<void> sendKeysChange(const hidl_vec<uint8_t>& sessionId,
+            const hidl_vec<KeyStatus_V1_0>& keyStatusList, bool hasNewUsableKey);
+
+    Return<void> sendKeysChange_1_2(const hidl_vec<uint8_t>& sessionId,
             const hidl_vec<KeyStatus>& keyStatusList, bool hasNewUsableKey);
 
     Return<void> sendSessionLostState(const hidl_vec<uint8_t>& sessionId);
diff --git a/media/libmedia/include/media/DrmMetrics.h b/media/libmedia/include/media/DrmMetrics.h
index 261998f..6f132bf 100644
--- a/media/libmedia/include/media/DrmMetrics.h
+++ b/media/libmedia/include/media/DrmMetrics.h
@@ -21,6 +21,7 @@
 
 #include <android/hardware/drm/1.0/types.h>
 #include <android/hardware/drm/1.1/types.h>
+#include <android/hardware/drm/1.2/types.h>
 #include <binder/PersistableBundle.h>
 #include <media/CounterMetric.h>
 #include <media/EventMetric.h>
@@ -50,7 +51,7 @@
   CounterMetric<status_t> mProvideProvisionResponseCounter;
 
   // Count of key status events broken out by status type.
-  CounterMetric<::android::hardware::drm::V1_0::KeyStatusType>
+  CounterMetric<::android::hardware::drm::V1_2::KeyStatusType>
       mKeyStatusChangeCounter;
   // Count of events broken out by event type
   CounterMetric<::android::hardware::drm::V1_0::EventType> mEventCounter;
diff --git a/media/libmedia/xsd/api/current.txt b/media/libmedia/xsd/api/current.txt
index 05e8a49..73b5f8d 100644
--- a/media/libmedia/xsd/api/current.txt
+++ b/media/libmedia/xsd/api/current.txt
@@ -44,20 +44,20 @@
   public class CamcorderProfiles {
     ctor public CamcorderProfiles();
     method public int getCameraId();
-    method public java.util.List<media.profiles.EncoderProfile> getEncoderProfile();
-    method public java.util.List<media.profiles.CamcorderProfiles.ImageDecoding> getImageDecoding();
-    method public java.util.List<media.profiles.CamcorderProfiles.ImageEncoding> getImageEncoding();
+    method public java.util.List<media.profiles.EncoderProfile> getEncoderProfile_optional();
+    method public java.util.List<media.profiles.CamcorderProfiles.ImageDecodingOptional> getImageDecoding_optional();
+    method public java.util.List<media.profiles.CamcorderProfiles.ImageEncodingOptional> getImageEncoding_optional();
     method public void setCameraId(int);
   }
 
-  public static class CamcorderProfiles.ImageDecoding {
-    ctor public CamcorderProfiles.ImageDecoding();
+  public static class CamcorderProfiles.ImageDecodingOptional {
+    ctor public CamcorderProfiles.ImageDecodingOptional();
     method public int getMemCap();
     method public void setMemCap(int);
   }
 
-  public static class CamcorderProfiles.ImageEncoding {
-    ctor public CamcorderProfiles.ImageEncoding();
+  public static class CamcorderProfiles.ImageEncodingOptional {
+    ctor public CamcorderProfiles.ImageEncodingOptional();
     method public int getQuality();
     method public void setQuality(int);
   }
diff --git a/media/libmedia/xsd/media_profiles.xsd b/media/libmedia/xsd/media_profiles.xsd
index a02252a..9664456 100644
--- a/media/libmedia/xsd/media_profiles.xsd
+++ b/media/libmedia/xsd/media_profiles.xsd
@@ -35,19 +35,19 @@
         </xs:complexType>
     </xs:element>
     <xs:complexType name="CamcorderProfiles">
-        <xs:sequence>
-            <xs:element name="EncoderProfile" type="EncoderProfile" minOccurs="0" maxOccurs="unbounded"/>
-            <xs:element name="ImageEncoding" minOccurs="0" maxOccurs="unbounded">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+            <xs:element name="EncoderProfile" type="EncoderProfile"/>
+            <xs:element name="ImageEncoding">
                 <xs:complexType>
                     <xs:attribute name="quality" type="xs:int"/>
                 </xs:complexType>
             </xs:element>
-            <xs:element name="ImageDecoding" minOccurs="0" maxOccurs="unbounded">
+            <xs:element name="ImageDecoding">
                 <xs:complexType>
                     <xs:attribute name="memCap" type="xs:int"/>
                 </xs:complexType>
             </xs:element>
-        </xs:sequence>
+        </xs:choice>
         <xs:attribute name="cameraId" type="xs:int"/>
     </xs:complexType>
     <xs:complexType name="EncoderProfile">
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 2b813e7..865cb2a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -635,7 +635,7 @@
 
         // re-init in case we prepare() and start() again.
         delete mAnalyticsItem ;
-        mAnalyticsItem = MediaAnalyticsItem::create("nuplayer");
+        mAnalyticsItem = MediaAnalyticsItem::create(kKeyPlayer);
         if (mAnalyticsItem) {
             mAnalyticsItem->setUid(mClientUid);
         }
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 317b5ec..3d67c91 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -1138,6 +1138,8 @@
         return err;
     }
 
+    static_cast<Surface *>(mNativeWindow.get())->setDequeueTimeout(-1);
+
     // Exits here for tunneled video playback codecs -- i.e. skips native window
     // buffer allocation step as this is managed by the tunneled OMX omponent
     // itself and explicitly sets def.nBufferCountActual to 0.
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 5932518..9170805 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -212,7 +212,6 @@
         "libstagefright_mediafilter",
         "libstagefright_webm",
         "libstagefright_timedtext",
-        "libvpx",
         "libogg",
         "libwebm",
         "libstagefright_esds",
diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp
index 81d2abb..4c8be1f 100644
--- a/media/libstagefright/MediaExtractorFactory.cpp
+++ b/media/libstagefright/MediaExtractorFactory.cpp
@@ -19,11 +19,11 @@
 #include <utils/Log.h>
 
 #include <android/dlext.h>
+#include <android-base/logging.h>
 #include <binder/IPCThreadState.h>
 #include <binder/PermissionCache.h>
 #include <binder/IServiceManager.h>
 #include <media/DataSource.h>
-#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/InterfaceUtils.h>
 #include <media/stagefright/MediaExtractor.h>
 #include <media/stagefright/MediaExtractorFactory.h>
@@ -71,8 +71,6 @@
 
     ALOGV("MediaExtractorFactory::CreateFromService %s", mime);
 
-    UpdateExtractors();
-
     // initialize source decryption if needed
     source->DrmInitialization(nullptr /* mime */);
 
@@ -245,21 +243,17 @@
             void *libHandle = android_dlopen_ext(
                     libPath.string(),
                     RTLD_NOW | RTLD_LOCAL, dlextinfo);
-            if (libHandle) {
-                GetExtractorDef getDef =
-                    (GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF");
-                if (getDef) {
-                    ALOGV("registering sniffer for %s", libPath.string());
-                    RegisterExtractor(
-                            new ExtractorPlugin(getDef(), libHandle, libPath), pluginList);
-                } else {
-                    LOG_ALWAYS_FATAL_IN_CHILD_PROC("%s does not contain sniffer", libPath.string());
-                    dlclose(libHandle);
-                }
-            } else {
-                LOG_ALWAYS_FATAL_IN_CHILD_PROC(
-                        "couldn't dlopen(%s) %s", libPath.string(), strerror(errno));
-            }
+            CHECK(libHandle != nullptr)
+                    << "couldn't dlopen(" << libPath.string() << ") " << strerror(errno);
+
+            GetExtractorDef getDef =
+                (GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF");
+            CHECK(getDef != nullptr)
+                    << libPath.string() << " does not contain sniffer";
+
+            ALOGV("registering sniffer for %s", libPath.string());
+            RegisterExtractor(
+                    new ExtractorPlugin(getDef(), libHandle, libPath), pluginList);
         }
         closedir(libDir);
     } else {
@@ -274,7 +268,7 @@
 static std::unordered_set<std::string> gSupportedExtensions;
 
 // static
-void MediaExtractorFactory::UpdateExtractors() {
+void MediaExtractorFactory::LoadExtractors() {
     Mutex::Autolock autoLock(gPluginMutex);
 
     if (gPluginsRegistered) {
diff --git a/media/libstagefright/codecs/amrnb/common/Android.bp b/media/libstagefright/codecs/amrnb/common/Android.bp
index 5177593..772ebf9 100644
--- a/media/libstagefright/codecs/amrnb/common/Android.bp
+++ b/media/libstagefright/codecs/amrnb/common/Android.bp
@@ -1,9 +1,6 @@
 cc_library_shared {
     name: "libstagefright_amrnb_common",
     vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
 
     srcs: [
         "src/add.cpp",
diff --git a/media/libstagefright/codecs/common/Android.bp b/media/libstagefright/codecs/common/Android.bp
index 3726922..c5a076a 100644
--- a/media/libstagefright/codecs/common/Android.bp
+++ b/media/libstagefright/codecs/common/Android.bp
@@ -1,9 +1,6 @@
 cc_library {
     name: "libstagefright_enc_common",
     vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
 
     srcs: ["cmnMemory.c"],
 
diff --git a/media/libstagefright/codecs/on2/dec/Android.bp b/media/libstagefright/codecs/on2/dec/Android.bp
index 577231c..82bb8d1 100644
--- a/media/libstagefright/codecs/on2/dec/Android.bp
+++ b/media/libstagefright/codecs/on2/dec/Android.bp
@@ -4,7 +4,7 @@
 
     srcs: ["SoftVPX.cpp"],
 
-    static_libs: ["libvpx"],
+    shared_libs: ["libvpx"],
 
     version_script: "exports.lds",
 
diff --git a/media/libstagefright/codecs/on2/enc/Android.bp b/media/libstagefright/codecs/on2/enc/Android.bp
index 82c215e..cd69e0d 100644
--- a/media/libstagefright/codecs/on2/enc/Android.bp
+++ b/media/libstagefright/codecs/on2/enc/Android.bp
@@ -20,5 +20,5 @@
         cfi: true,
     },
 
-    static_libs: ["libvpx"],
+    shared_libs: ["libvpx"],
 }
diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp
index d136d9e..d685321 100644
--- a/media/libstagefright/colorconversion/ColorConverter.cpp
+++ b/media/libstagefright/colorconversion/ColorConverter.cpp
@@ -395,11 +395,16 @@
                 dst.mStride, src.cropWidth(), src.cropHeight());
         break;
 
-    case OMX_COLOR_Format32BitRGBA8888:
+    case OMX_COLOR_Format32bitBGRA8888:
         libyuv::NV12ToARGB(src_y, src.mStride, src_u, src.mStride, (uint8 *)dst_ptr,
                 dst.mStride, src.cropWidth(), src.cropHeight());
         break;
 
+    case OMX_COLOR_Format32BitRGBA8888:
+        libyuv::NV12ToABGR(src_y, src.mStride, src_u, src.mStride, (uint8 *)dst_ptr,
+                dst.mStride, src.cropWidth(), src.cropHeight());
+        break;
+
     default:
         return ERROR_UNSUPPORTED;
    }
diff --git a/media/libstagefright/data/Android.bp b/media/libstagefright/data/Android.bp
new file mode 100644
index 0000000..616b4b3
--- /dev/null
+++ b/media/libstagefright/data/Android.bp
@@ -0,0 +1,6 @@
+prebuilt_etc {
+    name: "mediaswcodec.xml",
+    src: "media_codecs_sw.xml",
+    filename: "media_codecs.xml",
+    installable: false,
+}
diff --git a/media/libstagefright/data/media_codecs_sw.xml b/media/libstagefright/data/media_codecs_sw.xml
new file mode 100644
index 0000000..7ee1f4d
--- /dev/null
+++ b/media/libstagefright/data/media_codecs_sw.xml
@@ -0,0 +1,321 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<MediaCodecs>
+    <Settings>
+        <!-- disable TV and telephony domains by default. These must be opted in by OEMs -->
+        <Domain name="telephony" enabled="false" />
+        <Domain name="tv" enabled="false" />
+        <Variant name="slow-cpu" enabled="false" />
+    </Settings>
+    <Decoders>
+        <MediaCodec name="c2.android.mp3.decoder" type="audio/mpeg">
+            <Alias name="OMX.google.mp3.decoder" />
+            <Limit name="channel-count" max="2" />
+            <Limit name="sample-rate" ranges="8000,11025,12000,16000,22050,24000,32000,44100,48000" />
+            <Limit name="bitrate" range="8000-320000" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.amrnb.decoder" type="audio/3gpp">
+            <Alias name="OMX.google.amrnb.decoder" />
+            <Limit name="channel-count" max="1" />
+            <Limit name="sample-rate" ranges="8000" />
+            <Limit name="bitrate" range="4750-12200" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.amrwb.decoder" type="audio/amr-wb">
+            <Alias name="OMX.google.amrwb.decoder" />
+            <Limit name="channel-count" max="1" />
+            <Limit name="sample-rate" ranges="16000" />
+            <Limit name="bitrate" range="6600-23850" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.aac.decoder" type="audio/mp4a-latm">
+            <Alias name="OMX.google.aac.decoder" />
+            <Limit name="channel-count" max="8" />
+            <Limit name="sample-rate" ranges="7350,8000,11025,12000,16000,22050,24000,32000,44100,48000" />
+            <Limit name="bitrate" range="8000-960000" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.g711.alaw.decoder" type="audio/g711-alaw">
+            <Alias name="OMX.google.g711.alaw.decoder" />
+            <Limit name="channel-count" max="1" />
+            <Limit name="sample-rate" ranges="8000-48000" />
+            <Limit name="bitrate" range="64000" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.g711.mlaw.decoder" type="audio/g711-mlaw">
+            <Alias name="OMX.google.g711.mlaw.decoder" />
+            <Limit name="channel-count" max="1" />
+            <Limit name="sample-rate" ranges="8000-48000" />
+            <Limit name="bitrate" range="64000" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.vorbis.decoder" type="audio/vorbis">
+            <Alias name="OMX.google.vorbis.decoder" />
+            <Limit name="channel-count" max="8" />
+            <Limit name="sample-rate" ranges="8000-96000" />
+            <Limit name="bitrate" range="32000-500000" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.opus.decoder" type="audio/opus">
+            <Alias name="OMX.google.opus.decoder" />
+            <Limit name="channel-count" max="8" />
+            <Limit name="sample-rate" ranges="48000" />
+            <Limit name="bitrate" range="6000-510000" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.raw.decoder" type="audio/raw">
+            <Alias name="OMX.google.raw.decoder" />
+            <Limit name="channel-count" max="8" />
+            <Limit name="sample-rate" ranges="8000-96000" />
+            <Limit name="bitrate" range="1-10000000" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.flac.decoder" type="audio/flac">
+            <Alias name="OMX.google.flac.decoder" />
+            <Limit name="channel-count" max="8" />
+            <Limit name="sample-rate" ranges="1-655350" />
+            <Limit name="bitrate" range="1-21000000" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.gsm.decoder" type="audio/gsm" domain="telephony">
+            <Alias name="OMX.google.gsm.decoder" />
+            <Limit name="channel-count" max="1" />
+            <Limit name="sample-rate" ranges="8000" />
+            <Limit name="bitrate" range="13000" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.mpeg4.decoder" type="video/mp4v-es">
+            <Alias name="OMX.google.mpeg4.decoder" />
+            <!-- profiles and levels:  ProfileSimple : Level3 -->
+            <Limit name="size" min="2x2" max="352x288" />
+            <Limit name="alignment" value="2x2" />
+            <Limit name="block-size" value="16x16" />
+            <Limit name="blocks-per-second" range="12-11880" />
+            <Limit name="bitrate" range="1-384000" />
+            <Feature name="adaptive-playback" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.h263.decoder" type="video/3gpp">
+            <Alias name="OMX.google.h263.decoder" />
+            <!-- profiles and levels:  ProfileBaseline : Level30, ProfileBaseline : Level45
+                    ProfileISWV2 : Level30, ProfileISWV2 : Level45 -->
+            <Limit name="size" min="2x2" max="352x288" />
+            <Limit name="alignment" value="2x2" />
+            <Limit name="bitrate" range="1-384000" />
+            <Feature name="adaptive-playback" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.avc.decoder" type="video/avc" variant="slow-cpu,!slow-cpu">
+            <Alias name="OMX.google.h264.decoder" />
+            <Limit name="alignment" value="2x2" />
+            <Limit name="block-size" value="16x16" />
+            <Variant name="!slow-cpu">
+                <Limit name="size" min="2x2" max="4080x4080" />
+                <!-- profiles and levels:  ProfileHigh : Level52 -->
+                <Limit name="block-count" range="1-32768" /> <!-- max 4096x2048 equivalent -->
+                <Limit name="blocks-per-second" range="1-1966080" />
+                <Limit name="bitrate" range="1-48000000" />
+            </Variant>
+            <Variant name="slow-cpu">
+                <Limit name="size" min="2x2" max="2048x2048" />
+                <!-- profiles and levels:  ProfileHigh : Level51 -->
+                <Limit name="block-count" range="1-16384" />
+                <Limit name="blocks-per-second" range="1-491520" />
+                <Limit name="bitrate" range="1-40000000" />
+            </Variant>
+            <Feature name="adaptive-playback" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.hevc.decoder" type="video/hevc" variant="slow-cpu,!slow-cpu">
+            <Alias name="OMX.google.hevc.decoder" />
+            <Limit name="alignment" value="2x2" />
+            <Limit name="block-size" value="8x8" />
+            <Variant name="!slow-cpu">
+                <Limit name="size" min="2x2" max="4096x4096" />
+                <!-- profiles and levels:  ProfileMain : MainTierLevel51 -->
+                <Limit name="block-count" range="1-196608" /> <!-- max 4096x3072 -->
+                <Limit name="blocks-per-second" range="1-2000000" />
+                <Limit name="bitrate" range="1-10000000" />
+            </Variant>
+            <Variant name="slow-cpu">
+                <Limit name="size" min="2x2" max="2048x2048" />
+                <!-- profiles and levels:  ProfileMain : MainTierLevel51 -->
+                <Limit name="block-count" range="1-65536" />
+                <Limit name="blocks-per-second" range="1-491520" />
+                <Limit name="bitrate" range="1-5000000" />
+            </Variant>
+            <Feature name="adaptive-playback" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.vp8.decoder" type="video/x-vnd.on2.vp8" variant="slow-cpu,!slow-cpu">
+            <Alias name="OMX.google.vp8.decoder" />
+            <Limit name="size" min="2x2" max="2048x2048" />
+            <Limit name="alignment" value="2x2" />
+            <Limit name="block-size" value="16x16" />
+            <Variant name="!slow-cpu">
+                <Limit name="block-count" range="1-16384" />
+                <Limit name="blocks-per-second" range="1-1000000" />
+                <Limit name="bitrate" range="1-40000000" />
+            </Variant>
+            <Variant name="slow-cpu">
+                <Limit name="block-count" range="1-8192" /> <!-- max 2048x1024 -->
+                <Limit name="blocks-per-second" range="1-1000000" />
+                <Limit name="bitrate" range="1-40000000" />
+            </Variant>
+            <Feature name="adaptive-playback" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.vp9.decoder" type="video/x-vnd.on2.vp9" variant="slow-cpu,!slow-cpu">
+            <Alias name="OMX.google.vp9.decoder" />
+            <Limit name="alignment" value="2x2" />
+            <Limit name="block-size" value="16x16" />
+            <Variant name="!slow-cpu">
+                <Limit name="size" min="2x2" max="2048x2048" />
+                <Limit name="block-count" range="1-16384" />
+                <Limit name="blocks-per-second" range="1-500000" />
+                <Limit name="bitrate" range="1-40000000" />
+            </Variant>
+            <Variant name="slow-cpu">
+                <Limit name="size" min="2x2" max="1280x1280" />
+                <Limit name="block-count" range="1-3600" /> <!-- max 1280x720 -->
+                <Limit name="blocks-per-second" range="1-108000" />
+                <Limit name="bitrate" range="1-5000000" />
+            </Variant>
+            <Feature name="adaptive-playback" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.av1.decoder" type="video/av01" variant="!slow-cpu">
+            <Limit name="size" min="2x2" max="1920x1080" />
+            <Limit name="alignment" value="2x2" />
+            <Limit name="block-size" value="16x16" />
+            <Limit name="block-count" range="1-16384" />
+            <Limit name="blocks-per-second" range="1-2073600" />
+            <Limit name="bitrate" range="1-120000000" />
+            <Feature name="adaptive-playback" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.mpeg2.decoder" type="video/mpeg2" domain="tv">
+            <Alias name="OMX.google.mpeg2.decoder" />
+            <!-- profiles and levels:  ProfileMain : LevelHL -->
+            <Limit name="size" min="16x16" max="1920x1088" />
+            <Limit name="alignment" value="2x2" />
+            <Limit name="block-size" value="16x16" />
+            <Limit name="blocks-per-second" range="1-244800" />
+            <Limit name="bitrate" range="1-20000000" />
+            <Feature name="adaptive-playback" />
+        </MediaCodec>
+    </Decoders>
+    <Encoders>
+        <MediaCodec name="c2.android.aac.encoder" type="audio/mp4a-latm">
+            <Alias name="OMX.google.aac.encoder" />
+            <Limit name="channel-count" max="6" />
+            <Limit name="sample-rate" ranges="8000,11025,12000,16000,22050,24000,32000,44100,48000" />
+            <!-- also may support 64000, 88200  and 96000 Hz -->
+            <Limit name="bitrate" range="8000-960000" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.amrnb.encoder" type="audio/3gpp">
+            <Alias name="OMX.google.amrnb.encoder" />
+            <Limit name="channel-count" max="1" />
+            <Limit name="sample-rate" ranges="8000" />
+            <Limit name="bitrate" range="4750-12200" />
+            <Feature name="bitrate-modes" value="CBR" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.amrwb.encoder" type="audio/amr-wb">
+            <Alias name="OMX.google.amrwb.encoder" />
+            <Limit name="channel-count" max="1" />
+            <Limit name="sample-rate" ranges="16000" />
+            <Limit name="bitrate" range="6600-23850" />
+            <Feature name="bitrate-modes" value="CBR" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.flac.encoder" type="audio/flac">
+            <Alias name="OMX.google.flac.encoder" />
+            <Limit name="channel-count" max="2" />
+            <Limit name="sample-rate" ranges="1-655350" />
+            <Limit name="bitrate" range="1-21000000" />
+            <Limit name="complexity" range="0-8"  default="5" />
+            <Feature name="bitrate-modes" value="CQ" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.opus.encoder" type="audio/opus">
+            <Limit name="channel-count" max="2" />
+            <Limit name="sample-rate" ranges="8000,12000,16000,24000,48000" />
+            <Limit name="bitrate" range="500-512000" />
+            <Limit name="complexity" range="0-10"  default="5" />
+            <Feature name="bitrate-modes" value="CQ" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.h263.encoder" type="video/3gpp">
+            <Alias name="OMX.google.h263.encoder" />
+            <!-- profiles and levels:  ProfileBaseline : Level45 -->
+            <Limit name="size" min="176x144" max="176x144" />
+            <Limit name="alignment" value="16x16" />
+            <Limit name="bitrate" range="1-128000" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.mpeg4.encoder" type="video/mp4v-es">
+            <Alias name="OMX.google.mpeg4.encoder" />
+            <!-- profiles and levels:  ProfileCore : Level2 -->
+            <Limit name="size" min="16x16" max="176x144" />
+            <Limit name="alignment" value="16x16" />
+            <Limit name="block-size" value="16x16" />
+            <Limit name="blocks-per-second" range="12-1485" />
+            <Limit name="bitrate" range="1-64000" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.avc.encoder" type="video/avc" variant="slow-cpu,!slow-cpu">
+            <Alias name="OMX.google.h264.encoder" />
+            <Limit name="alignment" value="2x2" />
+            <Limit name="block-size" value="16x16" />
+            <Variant name="!slow-cpu">
+                <Limit name="size" min="16x16" max="2048x2048" />
+                <!-- profiles and levels:  ProfileBaseline : Level41 -->
+                <Limit name="block-count" range="1-8192" /> <!-- max 2048x1024 -->
+                <Limit name="blocks-per-second" range="1-245760" />
+                <Limit name="bitrate" range="1-12000000" />
+            </Variant>
+            <Variant name="slow-cpu">
+                <Limit name="size" min="16x16" max="1808x1808" />
+                <!-- profiles and levels:  ProfileBaseline : Level3 -->
+                <Limit name="block-count" range="1-1620" />
+                <Limit name="blocks-per-second" range="1-40500" />
+                <Limit name="bitrate" range="1-2000000" />
+            </Variant>
+            <Feature name="intra-refresh" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.vp8.encoder" type="video/x-vnd.on2.vp8" variant="slow-cpu,!slow-cpu">
+            <Alias name="OMX.google.vp8.encoder" />
+            <Limit name="alignment" value="2x2" />
+            <Limit name="block-size" value="16x16" />
+            <Variant name="!slow-cpu">
+                <Limit name="size" min="2x2" max="2048x2048" />
+                <!-- profiles and levels:  ProfileMain : Level_Version0-3 -->
+                <!-- 2016 devices can encode at about 10fps at this block count -->
+                <Limit name="block-count" range="1-16384" />
+                <Limit name="bitrate" range="1-40000000" />
+            </Variant>
+            <Variant name="slow-cpu">
+                <Limit name="size" min="2x2" max="1280x1280" />
+                <!-- profiles and levels:  ProfileMain : Level_Version0-3 -->
+                <Limit name="block-count" range="1-3600" /> <!-- max 1280x720 -->
+                <Limit name="bitrate" range="1-20000000" />
+            </Variant>
+            <Feature name="bitrate-modes" value="VBR,CBR" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.hevc.encoder" type="video/hevc" variant="!slow-cpu">
+            <!-- profiles and levels:  ProfileMain : MainTierLevel51 -->
+            <Limit name="size" min="320x128" max="512x512" />
+            <Limit name="alignment" value="2x2" />
+            <Limit name="block-size" value="8x8" />
+            <Limit name="block-count" range="1-4096" /> <!-- max 512x512 -->
+            <Limit name="blocks-per-second" range="1-122880" />
+            <Limit name="bitrate" range="1-10000000" />
+            <Limit name="complexity" range="0-10"  default="0" />
+            <Limit name="quality" range="0-100"  default="80" />
+            <Feature name="bitrate-modes" value="VBR,CBR,CQ" />
+        </MediaCodec>
+        <MediaCodec name="c2.android.vp9.encoder" type="video/x-vnd.on2.vp9" variant="!slow-cpu">
+            <Alias name="OMX.google.vp9.encoder" />
+            <!-- profiles and levels:  ProfileMain : Level_Version0-3 -->
+            <Limit name="size" min="2x2" max="2048x2048" />
+            <Limit name="alignment" value="2x2" />
+            <Limit name="block-size" value="16x16" />
+            <!-- 2016 devices can encode at about 8fps at this block count -->
+            <Limit name="block-count" range="1-3600" /> <!-- max 1280x720 -->
+            <Limit name="bitrate" range="1-40000000" />
+            <Feature name="bitrate-modes" value="VBR,CBR" />
+        </MediaCodec>
+    </Encoders>
+</MediaCodecs>
diff --git a/media/libstagefright/flac/dec/Android.bp b/media/libstagefright/flac/dec/Android.bp
index b270808..b494e16 100644
--- a/media/libstagefright/flac/dec/Android.bp
+++ b/media/libstagefright/flac/dec/Android.bp
@@ -1,9 +1,6 @@
 cc_library {
     name: "libstagefright_flacdec",
     vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
 
     srcs: [
         "FLACDecoder.cpp",
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/ADebug.h b/media/libstagefright/foundation/include/media/stagefright/foundation/ADebug.h
index 180694b..ab17a02 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/ADebug.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/ADebug.h
@@ -53,9 +53,11 @@
 #define LITERAL_TO_STRING_INTERNAL(x)    #x
 #define LITERAL_TO_STRING(x) LITERAL_TO_STRING_INTERNAL(x)
 
-#ifdef CHECK
-#undef CHECK
-#endif
+// allow to use CHECK_OP from android-base/logging.h
+// TODO: longterm replace this with android-base/logging.h, but there are some nuances, e.g.
+// android-base CHECK_OP requires a copy constructor, whereas we don't.
+#ifndef CHECK_OP
+
 #define CHECK(condition)                                \
     LOG_ALWAYS_FATAL_IF(                                \
             !(condition),                               \
@@ -63,10 +65,6 @@
             __FILE__ ":" LITERAL_TO_STRING(__LINE__)    \
             " CHECK(" #condition ") failed.")
 
-#ifdef CHECK_OP
-#undef CHECK_OP
-#endif
-
 #define CHECK_OP(x,y,suffix,op)                                         \
     do {                                                                \
         const auto &a = x;                                              \
@@ -82,15 +80,6 @@
         }                                                               \
     } while (false)
 
-#ifdef CHECK_EQ
-#undef CHECK_EQ
-#undef CHECK_NE
-#undef CHECK_LE
-#undef CHECK_LT
-#undef CHECK_GE
-#undef CHECK_GT
-#endif
-
 #define CHECK_EQ(x,y)   CHECK_OP(x,y,EQ,==)
 #define CHECK_NE(x,y)   CHECK_OP(x,y,NE,!=)
 #define CHECK_LE(x,y)   CHECK_OP(x,y,LE,<=)
@@ -98,6 +87,8 @@
 #define CHECK_GE(x,y)   CHECK_OP(x,y,GE,>=)
 #define CHECK_GT(x,y)   CHECK_OP(x,y,GT,>)
 
+#endif
+
 #define TRESPASS(...) \
         LOG_ALWAYS_FATAL(                                       \
             __FILE__ ":" LITERAL_TO_STRING(__LINE__)            \
@@ -123,15 +114,6 @@
 #define TRESPASS_DBG(...)
 #endif
 
-#ifndef LOG_ALWAYS_FATAL_IN_CHILD_PROC
-#define LOG_ALWAYS_FATAL_IN_CHILD_PROC(...)   \
-    do {                                      \
-        if (fork() == 0) {                    \
-            LOG_ALWAYS_FATAL(__VA_ARGS__);    \
-        }                                     \
-    } while (false)
-#endif
-
 struct ADebug {
     enum Level {
         kDebugNone,             // no debug
diff --git a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h
index ea87948..2ab98e1 100644
--- a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h
+++ b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h
@@ -37,6 +37,7 @@
             const sp<DataSource> &source, const char *mime = NULL);
     static status_t dump(int fd, const Vector<String16>& args);
     static std::unordered_set<std::string> getSupportedTypes();
+    static void LoadExtractors();
 
 private:
     static Mutex gPluginMutex;
@@ -53,8 +54,6 @@
     static void *sniff(const sp<DataSource> &source,
             float *confidence, void **meta, FreeMetaFunc *freeMeta,
             sp<ExtractorPlugin> &plugin, uint32_t *creatorVersion);
-
-    static void UpdateExtractors();
 };
 
 }  // namespace android
diff --git a/media/libstagefright/omx/1.0/Omx.cpp b/media/libstagefright/omx/1.0/Omx.cpp
index bb79f54..eef9ce3 100644
--- a/media/libstagefright/omx/1.0/Omx.cpp
+++ b/media/libstagefright/omx/1.0/Omx.cpp
@@ -43,6 +43,8 @@
 Omx::Omx() :
     mMaster(new OMXMaster()),
     mParser() {
+    (void)mParser.parseXmlFilesInSearchDirs();
+    (void)mParser.parseXmlPath(mParser.defaultProfilingResultsXmlPath);
 }
 
 Omx::~Omx() {
diff --git a/media/libstagefright/omx/1.0/OmxStore.cpp b/media/libstagefright/omx/1.0/OmxStore.cpp
index 2e041e3..67f478e 100644
--- a/media/libstagefright/omx/1.0/OmxStore.cpp
+++ b/media/libstagefright/omx/1.0/OmxStore.cpp
@@ -17,6 +17,8 @@
 #include <ios>
 #include <list>
 
+#define LOG_TAG "OmxStore"
+
 #include <android-base/logging.h>
 
 #include <media/stagefright/omx/1.0/Conversion.h>
@@ -30,16 +32,33 @@
 namespace V1_0 {
 namespace implementation {
 
+using ::android::hardware::media::omx::V1_0::Status;
+using ::android::hardware::media::omx::V1_0::IOmx;
+
 OmxStore::OmxStore(
+        const sp<IOmx> &omx,
         const char* owner,
-        const char* const* searchDirs,
-        const char* mainXmlName,
-        const char* performanceXmlName,
+        const std::vector<std::string> &searchDirs,
+        const std::vector<std::string> &xmlNames,
         const char* profilingResultsXmlPath) {
-    MediaCodecsXmlParser parser(searchDirs,
-            mainXmlName,
-            performanceXmlName,
-            profilingResultsXmlPath);
+    // retrieve list of omx nodes
+    std::set<std::string> nodes;
+    if (omx != nullptr) {
+        omx->listNodes([&nodes](const Status &status,
+                                const hidl_vec<IOmx::ComponentInfo> &nodeList) {
+            if (status == Status::OK) {
+                for (const IOmx::ComponentInfo& info : nodeList) {
+                    nodes.emplace(info.mName.c_str());
+                }
+            }
+        });
+    }
+
+    MediaCodecsXmlParser parser;
+    parser.parseXmlFilesInSearchDirs(xmlNames, searchDirs);
+    if (profilingResultsXmlPath != nullptr) {
+        parser.parseXmlPath(profilingResultsXmlPath);
+    }
     mParsingStatus = toStatus(parser.getParsingStatus());
 
     const auto& serviceAttributeMap = parser.getServiceAttributeMap();
@@ -66,6 +85,13 @@
         nodeList.resize(rolePair.second.nodeList.size());
         size_t j = 0;
         for (const auto& nodePair : rolePair.second.nodeList) {
+            if (!nodes.count(nodePair.second.name)) {
+                // not supported by this OMX instance
+                if (!strncasecmp(nodePair.second.name.c_str(), "omx.", 4)) {
+                    LOG(INFO) << "node [" << nodePair.second.name.c_str() << "] not found in IOmx";
+                }
+                continue;
+            }
             NodeInfo node;
             node.name = nodePair.second.name;
             node.owner = owner;
@@ -82,6 +108,7 @@
             nodeList[j] = std::move(node);
             ++j;
         }
+        nodeList.resize(j);
         mRoleList[i] = std::move(role);
         ++i;
     }
diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp
index 0c50752..e260cae 100644
--- a/media/libstagefright/omx/Android.bp
+++ b/media/libstagefright/omx/Android.bp
@@ -41,7 +41,6 @@
         "libcutils",
         "libstagefright_foundation",
         "libstagefright_bufferqueue_helper",
-        "libstagefright_softomx",
         "libstagefright_xmlparser",
         "libdl",
         "libhidlbase",
@@ -79,14 +78,10 @@
 cc_library_shared {
     name: "libstagefright_softomx",
     vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
 
     srcs: [
         "SimpleSoftOMXComponent.cpp",
         "SoftOMXComponent.cpp",
-        "SoftOMXPlugin.cpp",
         "SoftVideoDecoderOMXComponent.cpp",
         "SoftVideoEncoderOMXComponent.cpp",
     ],
@@ -126,6 +121,49 @@
     },
 }
 
+cc_library_shared {
+    name: "libstagefright_softomx_plugin",
+    vendor_available: true,
+
+    srcs: [
+        "SoftOMXPlugin.cpp",
+    ],
+
+    export_include_dirs: [
+        "include",
+    ],
+
+    header_libs: [
+        "media_plugin_headers",
+    ],
+
+    export_header_lib_headers: [
+        "media_plugin_headers",
+    ],
+
+    shared_libs: [
+        "libstagefright_softomx",
+        "libstagefright_foundation",
+        "liblog",
+        "libutils",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-Wno-unused-parameter",
+        "-Wno-documentation",
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+            "unsigned-integer-overflow",
+        ],
+        cfi: true,
+    },
+}
+
 cc_defaults {
     name: "libstagefright_softomx-defaults",
     vendor_available: true,
diff --git a/media/libstagefright/omx/OMXMaster.cpp b/media/libstagefright/omx/OMXMaster.cpp
index 0967b5f..8c5ca6e 100644
--- a/media/libstagefright/omx/OMXMaster.cpp
+++ b/media/libstagefright/omx/OMXMaster.cpp
@@ -29,8 +29,7 @@
 
 namespace android {
 
-OMXMaster::OMXMaster()
-    : mVendorLibHandle(NULL) {
+OMXMaster::OMXMaster() {
 
     pid_t pid = getpid();
     char filename[20];
@@ -52,47 +51,52 @@
     }
 
     addVendorPlugin();
-    addPlugin(new SoftOMXPlugin);
+    addPlatformPlugin();
 }
 
 OMXMaster::~OMXMaster() {
     clearPlugins();
-
-    if (mVendorLibHandle != NULL) {
-        dlclose(mVendorLibHandle);
-        mVendorLibHandle = NULL;
-    }
 }
 
 void OMXMaster::addVendorPlugin() {
     addPlugin("libstagefrighthw.so");
 }
 
-void OMXMaster::addPlugin(const char *libname) {
-    mVendorLibHandle = android_load_sphal_library(libname, RTLD_NOW);
+void OMXMaster::addPlatformPlugin() {
+    addPlugin("libstagefright_softomx_plugin.so");
+}
 
-    if (mVendorLibHandle == NULL) {
+void OMXMaster::addPlugin(const char *libname) {
+    void *libHandle = android_load_sphal_library(libname, RTLD_NOW);
+
+    if (libHandle == NULL) {
         return;
     }
 
     typedef OMXPluginBase *(*CreateOMXPluginFunc)();
     CreateOMXPluginFunc createOMXPlugin =
         (CreateOMXPluginFunc)dlsym(
-                mVendorLibHandle, "createOMXPlugin");
+                libHandle, "createOMXPlugin");
     if (!createOMXPlugin)
         createOMXPlugin = (CreateOMXPluginFunc)dlsym(
-                mVendorLibHandle, "_ZN7android15createOMXPluginEv");
+                libHandle, "_ZN7android15createOMXPluginEv");
 
+    OMXPluginBase *plugin = nullptr;
     if (createOMXPlugin) {
-        addPlugin((*createOMXPlugin)());
+        plugin = (*createOMXPlugin)();
+    }
+
+    if (plugin) {
+        mPlugins.push_back({ plugin, libHandle });
+        addPlugin(plugin);
+    } else {
+        android_unload_sphal_library(libHandle);
     }
 }
 
 void OMXMaster::addPlugin(OMXPluginBase *plugin) {
     Mutex::Autolock autoLock(mLock);
 
-    mPlugins.push_back(plugin);
-
     OMX_U32 index = 0;
 
     char name[128];
@@ -120,20 +124,20 @@
 void OMXMaster::clearPlugins() {
     Mutex::Autolock autoLock(mLock);
 
-    typedef void (*DestroyOMXPluginFunc)(OMXPluginBase*);
-    DestroyOMXPluginFunc destroyOMXPlugin =
-        (DestroyOMXPluginFunc)dlsym(
-                mVendorLibHandle, "destroyOMXPlugin");
-
     mPluginByComponentName.clear();
+    mPluginByInstance.clear();
 
-    for (List<OMXPluginBase *>::iterator it = mPlugins.begin();
-            it != mPlugins.end(); ++it) {
+    typedef void (*DestroyOMXPluginFunc)(OMXPluginBase*);
+    for (const Plugin &plugin : mPlugins) {
+        DestroyOMXPluginFunc destroyOMXPlugin =
+            (DestroyOMXPluginFunc)dlsym(
+                    plugin.mLibHandle, "destroyOMXPlugin");
         if (destroyOMXPlugin)
-            destroyOMXPlugin(*it);
+            destroyOMXPlugin(plugin.mOmx);
         else
-            delete *it;
-        *it = NULL;
+            delete plugin.mOmx;
+
+        android_unload_sphal_library(plugin.mLibHandle);
     }
 
     mPlugins.clear();
diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp
index 1f3e8c1..a720bc9 100644
--- a/media/libstagefright/omx/SoftOMXPlugin.cpp
+++ b/media/libstagefright/omx/SoftOMXPlugin.cpp
@@ -71,6 +71,16 @@
 static const size_t kNumComponents =
     sizeof(kComponents) / sizeof(kComponents[0]);
 
+extern "C" OMXPluginBase* createOMXPlugin() {
+    ALOGI("createOMXPlugin");
+    return new SoftOMXPlugin();
+}
+
+extern "C" void destroyOMXPlugin(OMXPluginBase* plugin) {
+    ALOGI("destroyOMXPlugin");
+    delete plugin;
+}
+
 SoftOMXPlugin::SoftOMXPlugin() {
 }
 
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/OmxStore.h b/media/libstagefright/omx/include/media/stagefright/omx/1.0/OmxStore.h
index 006d2d9..15d46991 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/OmxStore.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/1.0/OmxStore.h
@@ -20,6 +20,7 @@
 #include <hidl/MQDescriptor.h>
 #include <hidl/Status.h>
 
+#include <android/hardware/media/omx/1.0/IOmx.h>
 #include <android/hardware/media/omx/1.0/IOmxStore.h>
 #include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
 
@@ -43,15 +44,14 @@
 
 struct OmxStore : public IOmxStore {
     OmxStore(
+            const sp<IOmx> &omx = nullptr,
             const char* owner = "default",
-            const char* const* searchDirs
-                = MediaCodecsXmlParser::defaultSearchDirs,
-            const char* mainXmlName
-                = MediaCodecsXmlParser::defaultMainXmlName,
-            const char* performanceXmlName
-                = MediaCodecsXmlParser::defaultPerformanceXmlName,
-            const char* profilingResultsXmlPath
-                = MediaCodecsXmlParser::defaultProfilingResultsXmlPath);
+            const std::vector<std::string> &searchDirs =
+                MediaCodecsXmlParser::getDefaultSearchDirs(),
+            const std::vector<std::string> &xmlFiles =
+                MediaCodecsXmlParser::getDefaultXmlNames(),
+            const char *xmlProfilingResultsPath =
+                MediaCodecsXmlParser::defaultProfilingResultsXmlPath);
 
     virtual ~OmxStore();
 
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h b/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h
index 897f287..93eaef1 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h
@@ -52,13 +52,16 @@
 private:
     char mProcessName[16];
     Mutex mLock;
-    List<OMXPluginBase *> mPlugins;
+    struct Plugin {
+        OMXPluginBase *mOmx;
+        void *mLibHandle;
+    };
+    List<Plugin> mPlugins;
     KeyedVector<String8, OMXPluginBase *> mPluginByComponentName;
     KeyedVector<OMX_COMPONENTTYPE *, OMXPluginBase *> mPluginByInstance;
 
-    void *mVendorLibHandle;
-
     void addVendorPlugin();
+    void addPlatformPlugin();
     void addPlugin(const char *libname);
     void addPlugin(OMXPluginBase *plugin);
     void clearPlugins();
diff --git a/media/libstagefright/webm/WebmFrame.cpp b/media/libstagefright/webm/WebmFrame.cpp
index 4b0d47c..52c30ec 100644
--- a/media/libstagefright/webm/WebmFrame.cpp
+++ b/media/libstagefright/webm/WebmFrame.cpp
@@ -62,6 +62,14 @@
             mData);
 }
 
+uint64_t WebmFrame::getAbsTimecode() {
+    return mAbsTimecode;
+}
+
+void WebmFrame::updateAbsTimecode(uint64_t newAbsTimecode) {
+    mAbsTimecode = newAbsTimecode;
+}
+
 bool WebmFrame::operator<(const WebmFrame &other) const {
     if (this->mEos) {
         return false;
diff --git a/media/libstagefright/webm/WebmFrame.h b/media/libstagefright/webm/WebmFrame.h
index a410a87..47f2523 100644
--- a/media/libstagefright/webm/WebmFrame.h
+++ b/media/libstagefright/webm/WebmFrame.h
@@ -25,7 +25,7 @@
 public:
     const int mType;
     const bool mKey;
-    const uint64_t mAbsTimecode;
+    uint64_t mAbsTimecode;
     const sp<ABuffer> mData;
     const bool mEos;
 
@@ -33,6 +33,8 @@
     WebmFrame(int type, bool key, uint64_t absTimecode, MediaBufferBase *buf);
     ~WebmFrame() {}
 
+    uint64_t getAbsTimecode();
+    void updateAbsTimecode(uint64_t newAbsTimecode);
     sp<WebmElement> SimpleBlock(uint64_t baseTimecode) const;
 
     bool operator<(const WebmFrame &other) const;
diff --git a/media/libstagefright/webm/WebmFrameThread.cpp b/media/libstagefright/webm/WebmFrameThread.cpp
index 4b6f928..631a2ab 100644
--- a/media/libstagefright/webm/WebmFrameThread.cpp
+++ b/media/libstagefright/webm/WebmFrameThread.cpp
@@ -78,6 +78,7 @@
       mVideoFrames(videoThread->mSink),
       mAudioFrames(audioThread->mSink),
       mCues(cues),
+      mStartOffsetTimecode(UINT64_MAX),
       mDone(true) {
 }
 
@@ -92,6 +93,7 @@
       mVideoFrames(videoSource),
       mAudioFrames(audioSource),
       mCues(cues),
+      mStartOffsetTimecode(UINT64_MAX),
       mDone(true) {
 }
 
@@ -213,6 +215,11 @@
         const sp<WebmFrame> audioFrame = mAudioFrames.peek();
         ALOGV("a frame: %p", audioFrame.get());
 
+        if (mStartOffsetTimecode == UINT64_MAX) {
+            mStartOffsetTimecode =
+                    std::min(audioFrame->getAbsTimecode(), videoFrame->getAbsTimecode());
+        }
+
         if (videoFrame->mEos && audioFrame->mEos) {
             break;
         }
@@ -220,10 +227,12 @@
         if (*audioFrame < *videoFrame) {
             ALOGV("take a frame");
             mAudioFrames.take();
+            audioFrame->updateAbsTimecode(audioFrame->getAbsTimecode() - mStartOffsetTimecode);
             outstandingFrames.push_back(audioFrame);
         } else {
             ALOGV("take v frame");
             mVideoFrames.take();
+            videoFrame->updateAbsTimecode(videoFrame->getAbsTimecode() - mStartOffsetTimecode);
             outstandingFrames.push_back(videoFrame);
             if (videoFrame->mKey)
                 numVideoKeyFrames++;
@@ -350,7 +359,6 @@
         if (mStartTimeUs == kUninitialized) {
             mStartTimeUs = timestampUs;
         }
-        timestampUs -= mStartTimeUs;
 
         if (mPaused && !mResumed) {
             lastDurationUs = timestampUs - lastTimestampUs;
diff --git a/media/libstagefright/webm/WebmFrameThread.h b/media/libstagefright/webm/WebmFrameThread.h
index 76c91f1..1ddaf9a 100644
--- a/media/libstagefright/webm/WebmFrameThread.h
+++ b/media/libstagefright/webm/WebmFrameThread.h
@@ -83,6 +83,7 @@
     LinkedBlockingQueue<const sp<WebmFrame> >& mVideoFrames;
     LinkedBlockingQueue<const sp<WebmFrame> >& mAudioFrames;
     List<sp<WebmElement> >& mCues;
+    uint64_t mStartOffsetTimecode;
 
     volatile bool mDone;
 
diff --git a/media/libstagefright/webm/WebmWriter.cpp b/media/libstagefright/webm/WebmWriter.cpp
index 26e0884..5eaadbd 100644
--- a/media/libstagefright/webm/WebmWriter.cpp
+++ b/media/libstagefright/webm/WebmWriter.cpp
@@ -124,6 +124,11 @@
         return NULL;
     }
 
+    int32_t bitsPerSample = 0;
+    if (!md->findInt32(kKeyBitsPerSample, &bitsPerSample)) {
+        ALOGV("kKeyBitsPerSample not available");
+    }
+
     if (!strncasecmp(mimeType, MEDIA_MIMETYPE_AUDIO_OPUS, strlen(MEDIA_MIMETYPE_AUDIO_OPUS))) {
         // Opus in WebM is a well-known, yet under-documented, format. The codec private data
         // of the track is an Opus Ogg header (https://tools.ietf.org/html/rfc7845#section-5.1)
@@ -164,8 +169,8 @@
         uint8_t* codecPrivateData = codecPrivateBuf->data();
 
         memcpy(codecPrivateData + off, (uint8_t*)header_data, headerSize);
-        sp<WebmElement> entry =
-                WebmElement::AudioTrackEntry("A_OPUS", nChannels, samplerate, codecPrivateBuf);
+        sp<WebmElement> entry = WebmElement::AudioTrackEntry("A_OPUS", nChannels, samplerate,
+                                                             codecPrivateBuf, bitsPerSample);
         return entry;
     } else if (!strncasecmp(mimeType,
                             MEDIA_MIMETYPE_AUDIO_VORBIS,
@@ -203,8 +208,8 @@
         off += headerSize2;
         memcpy(codecPrivateData + off, headerData3, headerSize3);
 
-        sp<WebmElement> entry =
-                WebmElement::AudioTrackEntry("A_VORBIS", nChannels, samplerate, codecPrivateBuf);
+        sp<WebmElement> entry = WebmElement::AudioTrackEntry("A_VORBIS", nChannels, samplerate,
+                                                             codecPrivateBuf, bitsPerSample);
         return entry;
     } else {
         ALOGE("Track (%s) is not a supported audio format", mimeType);
diff --git a/media/libstagefright/xmlparser/Android.bp b/media/libstagefright/xmlparser/Android.bp
index 202e964..38de831 100644
--- a/media/libstagefright/xmlparser/Android.bp
+++ b/media/libstagefright/xmlparser/Android.bp
@@ -20,15 +20,12 @@
     ],
 
     shared_libs: [
+        "libbase",
         "libexpat",
         "liblog",
         "libstagefright_omx_utils",
     ],
 
-    header_libs: [
-        "libbase_headers",
-    ],
-
     cflags: [
         "-Werror",
         "-Wall",
diff --git a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
index 7046f61..9783e9b 100644
--- a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
+++ b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
@@ -19,12 +19,18 @@
 
 #include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
 
+#include <android-base/logging.h>
 #include <android-base/macros.h>
 #include <utils/Log.h>
+
 #include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/omx/OMXUtils.h>
-#include <sys/stat.h>
+
 #include <expat.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
 
 #include <algorithm>
 #include <cctype>
@@ -32,8 +38,15 @@
 
 namespace android {
 
+using MCXP = MediaCodecsXmlParser;
+
 namespace {
 
+bool fileExists(const std::string &path) {
+    struct stat fileStat;
+    return stat(path.c_str(), &fileStat) == 0 && S_ISREG(fileStat.st_mode);
+}
+
 /**
  * Search for a file in a list of search directories.
  *
@@ -44,7 +57,7 @@
  * search continues until the `nullptr` element in `searchDirs` is reached, at
  * which point the function returns `false`.
  *
- * \param[in] searchDirs Null-terminated array of search paths.
+ * \param[in] searchDirs array of search paths.
  * \param[in] fileName Name of the file to search.
  * \param[out] outPath Full path of the file. `outPath` will hold a valid file
  * name if the return value of this function is `true`.
@@ -52,14 +65,13 @@
  * valid file name; `false` otherwise.
  */
 bool findFileInDirs(
-        const char* const* searchDirs,
-        const char *fileName,
+        const std::vector<std::string> &searchDirs,
+        const std::string &fileName,
         std::string *outPath) {
-    for (; *searchDirs != nullptr; ++searchDirs) {
-        *outPath = std::string(*searchDirs) + "/" + fileName;
-        struct stat fileStat;
-        if (stat(outPath->c_str(), &fileStat) == 0 &&
-                S_ISREG(fileStat.st_mode)) {
+    for (const std::string &searchDir : searchDirs) {
+        std::string path = searchDir + "/" + fileName;
+        if (fileExists(path)) {
+            *outPath = path;
             return true;
         }
     }
@@ -85,123 +97,435 @@
 bool parseBoolean(const char* s) {
     return striEq(s, "y") ||
             striEq(s, "yes") ||
+            striEq(s, "enabled") ||
             striEq(s, "t") ||
             striEq(s, "true") ||
             striEq(s, "1");
 }
 
-status_t limitFoundMissingAttr(const char* name, const char *attr, bool found = true) {
-    ALOGE("limit '%s' with %s'%s' attribute", name,
-            (found ? "" : "no "), attr);
-    return BAD_VALUE;
+
+status_t combineStatus(status_t a, status_t b) {
+    if (a == NO_INIT) {
+        return b;
+    } else if ((a == OK && (b == NAME_NOT_FOUND || b == ALREADY_EXISTS || b == NO_INIT))
+            || (b == OK && (a == NAME_NOT_FOUND || a == ALREADY_EXISTS))) {
+        // ignore NAME_NOT_FOUND and ALREADY_EXIST errors as long as the other error is OK
+        // also handle OK + NO_INIT here
+        return OK;
+    } else {
+        // prefer the first error result
+        return a ? : b;
+    }
 }
 
-status_t limitError(const char* name, const char *msg) {
-    ALOGE("limit '%s' %s", name, msg);
-    return BAD_VALUE;
+MCXP::StringSet parseCommaSeparatedStringSet(const char *s) {
+    MCXP::StringSet result;
+    for (const char *ptr = s ? : ""; *ptr; ) {
+        const char *end = strchrnul(ptr, ',');
+        if (ptr != end) { // skip empty values
+            result.emplace(ptr, end - ptr);
+        }
+        ptr = end + ('\0' != *end);
+    }
+    return result;
 }
 
-status_t limitInvalidAttr(const char* name, const char* attr, const char* value) {
-    ALOGE("limit '%s' with invalid '%s' attribute (%s)", name,
-            attr, value);
-    return BAD_VALUE;
-}
+#define PLOGD(msg, ...) \
+        ALOGD(msg " at line %zu of %s", ##__VA_ARGS__, \
+                (size_t)::XML_GetCurrentLineNumber(mParser.get()), mPath.c_str());
 
-}; // unnamed namespace
+}  // unnamed namespace
 
-constexpr char const* MediaCodecsXmlParser::defaultSearchDirs[];
-constexpr char const* MediaCodecsXmlParser::defaultMainXmlName;
-constexpr char const* MediaCodecsXmlParser::defaultPerformanceXmlName;
+struct MediaCodecsXmlParser::Impl {
+    // status + error message
+    struct Result {
+    private:
+        status_t mStatus;
+        std::string mError;
+
+    public:
+        Result(status_t s, std::string error = "")
+            : mStatus(s),
+              mError(error) {
+            if (error.empty() && s) {
+                mError = "Failed (" + std::string(asString(s)) + ")";
+            }
+        }
+        operator status_t() const { return mStatus; }
+        std::string error() const { return mError; }
+    };
+
+
+    // Parsed data
+    struct Data {
+        // Service attributes
+        AttributeMap mServiceAttributeMap;
+        CodecMap mCodecMap;
+        Result addGlobal(std::string key, std::string value, bool updating);
+    };
+
+    enum Section {
+        SECTION_TOPLEVEL,
+        SECTION_SETTINGS,
+        SECTION_DECODERS,
+        SECTION_DECODER,
+        SECTION_DECODER_TYPE,
+        SECTION_ENCODERS,
+        SECTION_ENCODER,
+        SECTION_ENCODER_TYPE,
+        SECTION_INCLUDE,
+        SECTION_VARIANT,
+        SECTION_UNKNOWN,
+    };
+
+    // XML parsing state
+    struct State {
+    private:
+        Data *mData;
+
+        // current codec and/or type, plus whether we are updating
+        struct CodecAndType {
+            std::string mName;
+            CodecMap::iterator mCodec;
+            TypeMap::iterator mType;
+            bool mUpdating;
+        };
+
+        // using vectors as we need to reset their sizes
+        std::vector<std::string> mIncludeStack;
+        std::vector<Section> mSectionStack;
+        std::vector<StringSet> mVariantsStack;
+        std::vector<CodecAndType> mCurrent;
+
+    public:
+        State(Data *data);
+
+        Data &data() { return *mData; }
+
+        // used to restore parsing state at XML include boundaries, in case parsing the included
+        // file fails.
+        struct RestorePoint {
+            size_t numIncludes;
+            size_t numSections;
+            size_t numVariantSets;
+            size_t numCodecAndTypes;
+        };
+
+        // method manipulating restore points (all state stacks)
+        RestorePoint createRestorePoint() const {
+            return {
+                mIncludeStack.size(), mSectionStack.size(), mVariantsStack.size(), mCurrent.size()
+            };
+        }
+
+        void restore(RestorePoint rp) {
+            CHECK_GE(mIncludeStack.size(), rp.numIncludes);
+            CHECK_GE(mSectionStack.size(), rp.numSections);
+            CHECK_GE(mVariantsStack.size(), rp.numVariantSets);
+            CHECK_GE(mCurrent.size(), rp.numCodecAndTypes);
+
+            mIncludeStack.resize(rp.numIncludes);
+            mSectionStack.resize(rp.numSections);
+            mVariantsStack.resize(rp.numVariantSets);
+            mCurrent.resize(rp.numCodecAndTypes);
+        }
+
+        // methods manipulating the include stack
+        Result enterInclude(const std::string &path);
+        void exitInclude() {
+            mIncludeStack.pop_back();
+        }
+
+        // methods manipulating the codec/type stack/state
+        bool inCodec() const {
+            return !mCurrent.empty() && mCurrent.back().mCodec != mData->mCodecMap.end();
+        }
+
+        bool inType() const {
+            return inCodec()
+                    && mCurrent.back().mType != mCurrent.back().mCodec->second.typeMap.end();
+        }
+
+        Result enterMediaCodec(bool encoder, const char *name, const char *type, bool update);
+        Result enterType(const char *name, bool update);
+        void exitCodecOrType() {
+            mCurrent.pop_back();
+        }
+
+        // can only be called when inCodec()
+        MediaCodecsXmlParser::CodecProperties &codec() {
+            return mCurrent.back().mCodec->second;
+        }
+        // can only be called when inCodec()
+        std::string codecName() const {
+            return mCurrent.back().mName;
+        }
+        // can only be called when inCodec()
+        bool updating() const {
+            return mCurrent.back().mUpdating;
+        }
+        // can only be called when inType()
+        MediaCodecsXmlParser::AttributeMap &type() {
+            return mCurrent.back().mType->second;
+        }
+
+        // methods manipulating the section stack
+        Section section() const {
+            return mSectionStack.back();
+        }
+        Section lastNonIncludeSection() const;
+        void enterSection(Section s) {
+            mSectionStack.push_back(s);
+        }
+        void exitSection() {
+            mSectionStack.pop_back();
+            CHECK(!mSectionStack.empty());
+        }
+
+        // methods manipulating the variants stack
+        StringSet variants() const {
+            return mVariantsStack.back();
+        }
+        void enterVariants(StringSet variants) {
+            mVariantsStack.push_back(variants);
+        }
+        void exitVariants() {
+            mVariantsStack.pop_back();
+        }
+
+        // utility methods
+
+        // updates rank, domains, variants and enabledness on the current codec/type
+        Result updateCodec(
+                const char *rank, StringSet domains, StringSet variants, const char *enabled);
+        // adds a key-value attribute detail to the current type of the current codec
+        void addDetail(const std::string &key, const std::string &value);
+    };
+
+    /** XML Parser (state) */
+    struct Parser {
+        State *mState;
+
+        Parser(State *state, std::string path);
+
+        // keep track of the parser state
+        std::shared_ptr<XML_ParserStruct> mParser;
+        std::string mPath;
+        std::string mHrefBase;
+        status_t mStatus;
+
+        void parseXmlFile();
+
+        // XML parser callbacks
+        static void StartElementHandlerWrapper(void *me, const char *name, const char **attrs);
+        static void EndElementHandlerWrapper(void *me, const char *name);
+
+        void startElementHandler(const char *name, const char **attrs);
+        void endElementHandler(const char *name);
+
+        void updateStatus(status_t status);
+        void logAnyErrors(const Result &status) const;
+        status_t getStatus() const { return mStatus; }
+
+        status_t addAlias(const char **attrs);
+        status_t addFeature(const char **attrs);
+        status_t addLimit(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);
+        status_t enterType(const char **attrs);
+        status_t includeXmlFile(const char **attrs);
+        status_t limitVariants(const char **attrs);
+
+        status_t updateMediaCodec(
+                const char *rank, const StringSet &domain, const StringSet &variants,
+                const char *enabled);
+    };
+
+    status_t parseXmlFilesInSearchDirs(
+        const std::vector<std::string> &fileNames,
+        const std::vector<std::string> &searchDirs);
+
+    status_t parseXmlPath(const std::string &path);
+
+    // Computed longest common prefix
+    Data mData;
+    State mState;
+
+    // Role map
+    mutable std::string mCommonPrefix;
+    mutable RoleMap mRoleMap;
+    mutable std::mutex mLock;
+
+    status_t mParsingStatus;
+
+    Impl()
+        : mState(&mData),
+          mParsingStatus(NO_INIT) {
+    }
+
+    void generateRoleMap() const;
+    void generateCommonPrefix() const;
+
+    const AttributeMap& getServiceAttributeMap() const {
+        std::lock_guard<std::mutex> guard(mLock);
+        return mData.mServiceAttributeMap;
+    }
+
+    const CodecMap& getCodecMap() const {
+        std::lock_guard<std::mutex> guard(mLock);
+        return mData.mCodecMap;
+    }
+
+    const RoleMap& getRoleMap() const;
+    const char* getCommonPrefix() const;
+
+    status_t getParsingStatus() const {
+        std::lock_guard<std::mutex> guard(mLock);
+        return mParsingStatus;
+    }
+};
+
 constexpr char const* MediaCodecsXmlParser::defaultProfilingResultsXmlPath;
 
-MediaCodecsXmlParser::MediaCodecsXmlParser(
-        const char* const* searchDirs,
-        const char* mainXmlName,
-        const char* performanceXmlName,
-        const char* profilingResultsXmlPath) :
-    mParsingStatus(NO_INIT),
-    mUpdate(false),
-    mCodecCounter(0) {
-    std::string path;
-    if (findFileInDirs(searchDirs, mainXmlName, &path)) {
-        parseTopLevelXMLFile(path.c_str(), false);
-    } else {
-        ALOGE("Cannot find %s", mainXmlName);
-        mParsingStatus = NAME_NOT_FOUND;
-    }
-    if (findFileInDirs(searchDirs, performanceXmlName, &path)) {
-        parseTopLevelXMLFile(path.c_str(), true);
-    }
-    if (profilingResultsXmlPath != nullptr) {
-        parseTopLevelXMLFile(profilingResultsXmlPath, true);
-    }
+MediaCodecsXmlParser::MediaCodecsXmlParser()
+    : mImpl(new Impl()) {
 }
 
-bool MediaCodecsXmlParser::parseTopLevelXMLFile(
-        const char *codecs_xml,
-        bool ignore_errors) {
-    // get href_base
-    const char *href_base_end = strrchr(codecs_xml, '/');
-    if (href_base_end != nullptr) {
-        mHrefBase = std::string(codecs_xml, href_base_end - codecs_xml + 1);
-    }
+status_t MediaCodecsXmlParser::parseXmlFilesInSearchDirs(
+        const std::vector<std::string> &fileNames,
+        const std::vector<std::string> &searchDirs) {
+    return mImpl->parseXmlFilesInSearchDirs(fileNames, searchDirs);
+}
 
-    mParsingStatus = OK; // keeping this here for safety
-    mCurrentSection = SECTION_TOPLEVEL;
+status_t MediaCodecsXmlParser::parseXmlPath(const std::string &path) {
+    return mImpl->parseXmlPath(path);
+}
 
-    parseXMLFile(codecs_xml);
-
-    if (mParsingStatus != OK) {
-        ALOGW("parseTopLevelXMLFile(%s) failed", codecs_xml);
-        if (ignore_errors) {
-            mParsingStatus = OK;
-            return false;
+status_t MediaCodecsXmlParser::Impl::parseXmlFilesInSearchDirs(
+        const std::vector<std::string> &fileNames,
+        const std::vector<std::string> &searchDirs) {
+    status_t res = NO_INIT;
+    for (const std::string fileName : fileNames) {
+        status_t err = NO_INIT;
+        std::string path;
+        if (findFileInDirs(searchDirs, fileName, &path)) {
+            err = parseXmlPath(path);
+        } else {
+            ALOGD("Cannot find %s", path.c_str());
         }
-        mCodecMap.clear();
-        return false;
+        res = combineStatus(res, err);
     }
-    return true;
+    return res;
+}
+
+status_t MediaCodecsXmlParser::Impl::parseXmlPath(const std::string &path) {
+    std::lock_guard<std::mutex> guard(mLock);
+    if (!fileExists(path)) {
+        ALOGD("Cannot find %s", path.c_str());
+        mParsingStatus = combineStatus(mParsingStatus, NAME_NOT_FOUND);
+        return NAME_NOT_FOUND;
+    }
+
+    // save state (even though we should always be at toplevel here)
+    State::RestorePoint rp = mState.createRestorePoint();
+    Parser parser(&mState, path);
+    parser.parseXmlFile();
+    mState.restore(rp);
+
+    if (parser.getStatus() != OK) {
+        ALOGD("parseXmlPath(%s) failed with %s", path.c_str(), asString(parser.getStatus()));
+    }
+    mParsingStatus = combineStatus(mParsingStatus, parser.getStatus());
+    return parser.getStatus();
 }
 
 MediaCodecsXmlParser::~MediaCodecsXmlParser() {
 }
 
-void MediaCodecsXmlParser::parseXMLFile(const char *path) {
+MediaCodecsXmlParser::Impl::State::State(MediaCodecsXmlParser::Impl::Data *data)
+    : mData(data) {
+    mSectionStack.emplace_back(SECTION_TOPLEVEL);
+}
+
+MediaCodecsXmlParser::Impl::Section
+MediaCodecsXmlParser::Impl::State::lastNonIncludeSection() const {
+    for (auto it = mSectionStack.end(); it != mSectionStack.begin(); --it) {
+        if (it[-1] != SECTION_INCLUDE) {
+            return it[-1];
+        }
+    }
+    TRESPASS("must have non-include section");
+}
+
+void MediaCodecsXmlParser::Impl::Parser::updateStatus(status_t status) {
+    mStatus = combineStatus(mStatus, status);
+}
+
+void MediaCodecsXmlParser::Impl::Parser::logAnyErrors(const Result &status) const {
+    if (status) {
+        if (status.error().empty()) {
+            PLOGD("error %s", asString((status_t)status));
+        } else {
+            PLOGD("%s", status.error().c_str());
+        }
+    }
+}
+
+MediaCodecsXmlParser::Impl::Parser::Parser(State *state, std::string path)
+    : mState(state),
+      mPath(path),
+      mStatus(NO_INIT) {
+    // determine href_base
+    std::string::size_type end = path.rfind("/");
+    if (end != std::string::npos) {
+        mHrefBase = path.substr(0, end + 1);
+    }
+}
+
+void MediaCodecsXmlParser::Impl::Parser::parseXmlFile() {
+    const char *path = mPath.c_str();
+    ALOGD("parsing %s...", path);
     FILE *file = fopen(path, "r");
 
     if (file == nullptr) {
-        ALOGW("unable to open media codecs configuration xml file: %s", path);
-        mParsingStatus = NAME_NOT_FOUND;
+        ALOGD("unable to open media codecs configuration xml file: %s", path);
+        mStatus = NAME_NOT_FOUND;
         return;
     }
 
-    XML_Parser parser = ::XML_ParserCreate(nullptr);
-    LOG_FATAL_IF(parser == nullptr, "XML_MediaCodecsXmlParserCreate() failed.");
+    mParser = std::shared_ptr<XML_ParserStruct>(
+        ::XML_ParserCreate(nullptr),
+        [](XML_ParserStruct *parser) { ::XML_ParserFree(parser); });
+    LOG_FATAL_IF(!mParser, "XML_MediaCodecsXmlParserCreate() failed.");
 
-    ::XML_SetUserData(parser, this);
-    ::XML_SetElementHandler(
-            parser, StartElementHandlerWrapper, EndElementHandlerWrapper);
+    ::XML_SetUserData(mParser.get(), this);
+    ::XML_SetElementHandler(mParser.get(), StartElementHandlerWrapper, EndElementHandlerWrapper);
 
     static constexpr int BUFF_SIZE = 512;
-    while (mParsingStatus == OK) {
-        void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);
+    // updateStatus(OK);
+    if (mStatus == NO_INIT) {
+        mStatus = OK;
+    }
+    while (mStatus == OK) {
+        void *buff = ::XML_GetBuffer(mParser.get(), BUFF_SIZE);
         if (buff == nullptr) {
-            ALOGE("failed in call to XML_GetBuffer()");
-            mParsingStatus = UNKNOWN_ERROR;
+            ALOGD("failed in call to XML_GetBuffer()");
+            mStatus = UNKNOWN_ERROR;
             break;
         }
 
         int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
         if (bytes_read < 0) {
-            ALOGE("failed in call to read");
-            mParsingStatus = ERROR_IO;
+            ALOGD("failed in call to read");
+            mStatus = ERROR_IO;
             break;
         }
 
-        XML_Status status = ::XML_ParseBuffer(parser, bytes_read, bytes_read == 0);
+        XML_Status status = ::XML_ParseBuffer(mParser.get(), bytes_read, bytes_read == 0);
         if (status != XML_STATUS_OK) {
-            ALOGE("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(parser)));
-            mParsingStatus = ERROR_MALFORMED;
+            PLOGD("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(mParser.get())));
+            mStatus = ERROR_MALFORMED;
             break;
         }
 
@@ -210,39 +534,47 @@
         }
     }
 
-    ::XML_ParserFree(parser);
+    mParser.reset();
 
     fclose(file);
     file = nullptr;
 }
 
 // static
-void MediaCodecsXmlParser::StartElementHandlerWrapper(
+void MediaCodecsXmlParser::Impl::Parser::StartElementHandlerWrapper(
         void *me, const char *name, const char **attrs) {
-    static_cast<MediaCodecsXmlParser*>(me)->startElementHandler(name, attrs);
+    static_cast<MediaCodecsXmlParser::Impl::Parser*>(me)->startElementHandler(name, attrs);
 }
 
 // static
-void MediaCodecsXmlParser::EndElementHandlerWrapper(void *me, const char *name) {
-    static_cast<MediaCodecsXmlParser*>(me)->endElementHandler(name);
+void MediaCodecsXmlParser::Impl::Parser::EndElementHandlerWrapper(void *me, const char *name) {
+    static_cast<MediaCodecsXmlParser::Impl::Parser*>(me)->endElementHandler(name);
 }
 
-status_t MediaCodecsXmlParser::includeXMLFile(const char **attrs) {
+status_t MediaCodecsXmlParser::Impl::Parser::includeXmlFile(const char **attrs) {
     const char *href = nullptr;
     size_t i = 0;
     while (attrs[i] != nullptr) {
-        if (strEq(attrs[i], "href")) {
-            if (attrs[++i] == nullptr) {
-                return BAD_VALUE;
-            }
-            href = attrs[i];
-        } else {
-            ALOGE("includeXMLFile: unrecognized attribute: %s", attrs[i]);
+        CHECK((i & 1) == 0);
+        if (attrs[i + 1] == nullptr) {
+            PLOGD("Include: attribute '%s' is null", attrs[i]);
             return BAD_VALUE;
         }
+
+        if (strEq(attrs[i], "href")) {
+            href = attrs[++i];
+        } else {
+            PLOGD("Include: ignoring unrecognized attribute '%s'", attrs[i]);
+            ++i;
+        }
         ++i;
     }
 
+    if (href == nullptr) {
+        PLOGD("Include with no 'href' attribute");
+        return BAD_VALUE;
+    }
+
     // For security reasons and for simplicity, file names can only contain
     // [a-zA-Z0-9_.] and must start with  media_codecs_ and end with .xml
     for (i = 0; href[i] != '\0'; i++) {
@@ -252,74 +584,114 @@
                 (href[i] >= 'a' && href[i] <= 'z')) {
             continue;
         }
-        ALOGE("invalid include file name: %s", href);
+        PLOGD("invalid include file name: %s", href);
         return BAD_VALUE;
     }
 
     std::string filename = href;
     if (filename.compare(0, 13, "media_codecs_") != 0 ||
             filename.compare(filename.size() - 4, 4, ".xml") != 0) {
-        ALOGE("invalid include file name: %s", href);
+        PLOGD("invalid include file name: %s", href);
         return BAD_VALUE;
     }
     filename.insert(0, mHrefBase);
 
-    status_t oldParsingStatus = mParsingStatus;
+    Result res = mState->enterInclude(filename);
+    if (res) {
+        logAnyErrors(res);
+        return res;
+    }
 
-    parseXMLFile(filename.c_str());
-
-    status_t newParsingStatus = mParsingStatus;
-    mParsingStatus = oldParsingStatus;
-    return newParsingStatus;
+    // save state so that we can resume even if XML parsing of the included file failed midway
+    State::RestorePoint rp = mState->createRestorePoint();
+    Parser parser(mState, filename);
+    parser.parseXmlFile();
+    mState->restore(rp);
+    mState->exitInclude();
+    return parser.getStatus();
 }
 
-void MediaCodecsXmlParser::startElementHandler(
+MediaCodecsXmlParser::Impl::Result
+MediaCodecsXmlParser::Impl::State::enterInclude(const std::string &fileName) {
+    if (std::find(mIncludeStack.begin(), mIncludeStack.end(), fileName)
+            != mIncludeStack.end()) {
+        return { BAD_VALUE, "recursive include chain" };
+    }
+    mIncludeStack.emplace_back(fileName);
+    return OK;
+}
+
+void MediaCodecsXmlParser::Impl::Parser::startElementHandler(
         const char *name, const char **attrs) {
     bool inType = true;
+    Result err = NO_INIT;
 
+    Section section = mState->section();
+
+    // handle include at any level
     if (strEq(name, "Include")) {
-        if (includeXMLFile(attrs) == OK) {
-            mSectionStack.push_back(mCurrentSection);
-            mCurrentSection = SECTION_INCLUDE;
-        }
+        mState->enterSection(SECTION_INCLUDE);
+        updateStatus(includeXmlFile(attrs));
         return;
     }
 
-    switch (mCurrentSection) {
+    // handle include section (top level)
+    if (section == SECTION_INCLUDE) {
+        if (strEq(name, "Included")) {
+            return;
+        }
+        // imitate prior level
+        section = mState->lastNonIncludeSection();
+    }
+
+    switch (section) {
         case SECTION_TOPLEVEL:
         {
+            Section nextSection;
             if (strEq(name, "Decoders")) {
-                mCurrentSection = SECTION_DECODERS;
+                nextSection = SECTION_DECODERS;
             } else if (strEq(name, "Encoders")) {
-                mCurrentSection = SECTION_ENCODERS;
+                nextSection = SECTION_ENCODERS;
             } else if (strEq(name, "Settings")) {
-                mCurrentSection = SECTION_SETTINGS;
+                nextSection = SECTION_SETTINGS;
+            } else if (strEq(name, "MediaCodecs") || strEq(name, "Included")) {
+                return;
+            } else {
+                break;
             }
-            break;
+            mState->enterSection(nextSection);
+            return;
         }
 
         case SECTION_SETTINGS:
         {
             if (strEq(name, "Setting")) {
-                (void)addSettingFromAttributes(attrs);
+                err = addSetting(attrs);
+            } else if (strEq(name, "Variant")) {
+                err = addSetting(attrs, "variant-");
+            } else if (strEq(name, "Domain")) {
+                err = addSetting(attrs, "domain-");
+            } else {
+                break;
             }
-            break;
+            updateStatus(err);
+            return;
         }
 
         case SECTION_DECODERS:
-        {
-            if (strEq(name, "MediaCodec")) {
-                (void)addMediaCodecFromAttributes(false /* encoder */, attrs);
-                mCurrentSection = SECTION_DECODER;
-            }
-            break;
-        }
-
         case SECTION_ENCODERS:
         {
             if (strEq(name, "MediaCodec")) {
-                (void)addMediaCodecFromAttributes(true /* encoder */, attrs);
-                mCurrentSection = SECTION_ENCODER;
+                err = enterMediaCodec(attrs, section == SECTION_ENCODERS);
+                updateStatus(err);
+                if (err != OK) { // skip this element on error
+                    mState->enterSection(SECTION_UNKNOWN);
+                } else {
+                    mState->enterVariants(mState->codec().variantSet);
+                    mState->enterSection(
+                            section == SECTION_DECODERS ? SECTION_DECODER : SECTION_ENCODER);
+                }
+                return;
             }
             break;
         }
@@ -327,14 +699,21 @@
         case SECTION_DECODER:
         case SECTION_ENCODER:
         {
-            if (strEq(name, "Quirk") || strEq(name, "Attribute")) {
-                (void)addQuirk(attrs, name);
+            if (strEq(name, "Quirk")) {
+                err = addQuirk(attrs, "quirk::");
+            } else if (strEq(name, "Attribute")) {
+                err = addQuirk(attrs, "attribute::");
+            } else if (strEq(name, "Alias")) {
+                err = addAlias(attrs);
             } else if (strEq(name, "Type")) {
-                (void)addTypeFromAttributes(attrs,
-                        (mCurrentSection == SECTION_ENCODER));
-                mCurrentSection =
-                        (mCurrentSection == SECTION_DECODER ?
-                        SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
+                err = enterType(attrs);
+                if (err != OK) { // skip this element on error
+                    mState->enterSection(SECTION_UNKNOWN);
+                } else {
+                    mState->enterSection(
+                            section == SECTION_DECODER
+                                    ? SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
+                }
             }
         }
         inType = false;
@@ -342,91 +721,102 @@
 
         case SECTION_DECODER_TYPE:
         case SECTION_ENCODER_TYPE:
+        case SECTION_VARIANT:
         {
             // ignore limits and features specified outside of type
-            bool outside = !inType &&
-                    mCurrentType == mCurrentCodec->second.typeMap.end();
-            if (outside &&
-                    (strEq(name, "Limit") || strEq(name, "Feature"))) {
-                ALOGW("ignoring %s specified outside of a Type", name);
-            } else if (strEq(name, "Alias")) {
-                (void)addAlias(attrs);
+            if (!mState->inType()
+                    && (strEq(name, "Limit") || strEq(name, "Feature") || strEq(name, "Variant"))) {
+                PLOGD("ignoring %s specified outside of a Type", name);
+                return;
             } else if (strEq(name, "Limit")) {
-                (void)addLimit(attrs);
+                err = addLimit(attrs);
             } else if (strEq(name, "Feature")) {
-                (void)addFeature(attrs);
+                err = addFeature(attrs);
+            } else if (strEq(name, "Variant") && section != SECTION_VARIANT) {
+                err = limitVariants(attrs);
+                mState->enterSection(err == OK ? SECTION_VARIANT : SECTION_UNKNOWN);
+            } else if (inType
+                    && (strEq(name, "Alias") || strEq(name, "Attribute") || strEq(name, "Quirk"))) {
+                PLOGD("ignoring %s specified not directly in a MediaCodec", name);
+                return;
+            } else if (err == NO_INIT) {
+                break;
             }
-            break;
+            updateStatus(err);
+            return;
         }
 
         default:
             break;
     }
 
+    if (section != SECTION_UNKNOWN) {
+        PLOGD("Ignoring unrecognized tag <%s>", name);
+    }
+    mState->enterSection(SECTION_UNKNOWN);
 }
 
-void MediaCodecsXmlParser::endElementHandler(const char *name) {
-    switch (mCurrentSection) {
+void MediaCodecsXmlParser::Impl::Parser::endElementHandler(const char *name) {
+    // XMLParser handles tag matching, so we really just need to handle the section state here
+    Section section = mState->section();
+    switch (section) {
+        case SECTION_INCLUDE:
+        {
+            // this could also be any of: Included, MediaCodecs
+            if (strEq(name, "Include")) {
+                mState->exitSection();
+                return;
+            }
+            break;
+        }
+
         case SECTION_SETTINGS:
         {
+            // this could also be any of: Domain, Variant, Setting
             if (strEq(name, "Settings")) {
-                mCurrentSection = SECTION_TOPLEVEL;
+                mState->exitSection();
             }
             break;
         }
 
         case SECTION_DECODERS:
-        {
-            if (strEq(name, "Decoders")) {
-                mCurrentSection = SECTION_TOPLEVEL;
-            }
-            break;
-        }
-
         case SECTION_ENCODERS:
+        case SECTION_UNKNOWN:
         {
-            if (strEq(name, "Encoders")) {
-                mCurrentSection = SECTION_TOPLEVEL;
-            }
+            mState->exitSection();
             break;
         }
 
         case SECTION_DECODER_TYPE:
         case SECTION_ENCODER_TYPE:
         {
+            // this could also be any of: Alias, Limit, Feature
             if (strEq(name, "Type")) {
-                mCurrentSection =
-                        (mCurrentSection == SECTION_DECODER_TYPE ?
-                        SECTION_DECODER : SECTION_ENCODER);
-
-                mCurrentType = mCurrentCodec->second.typeMap.end();
+                mState->exitSection();
+                mState->exitCodecOrType();
             }
             break;
         }
 
         case SECTION_DECODER:
-        {
-            if (strEq(name, "MediaCodec")) {
-                mCurrentSection = SECTION_DECODERS;
-                mCurrentName.clear();
-            }
-            break;
-        }
-
         case SECTION_ENCODER:
         {
+            // this could also be any of: Alias, Limit, Quirk, Variant
             if (strEq(name, "MediaCodec")) {
-                mCurrentSection = SECTION_ENCODERS;
-                mCurrentName.clear();
+                mState->exitSection();
+                mState->exitCodecOrType();
+                mState->exitVariants();
             }
             break;
         }
 
-        case SECTION_INCLUDE:
+        case SECTION_VARIANT:
         {
-            if (strEq(name, "Include") && (mSectionStack.size() > 0)) {
-                mCurrentSection = mSectionStack.back();
-                mSectionStack.pop_back();
+            // this could also be any of: Alias, Limit, Quirk
+            if (strEq(name, "Variant")) {
+                mState->exitSection();
+                mState->exitVariants();
+                return;
             }
             break;
         }
@@ -434,264 +824,302 @@
         default:
             break;
     }
-
 }
 
-status_t MediaCodecsXmlParser::addSettingFromAttributes(const char **attrs) {
-    const char *name = nullptr;
-    const char *value = nullptr;
-    const char *update = nullptr;
+status_t MediaCodecsXmlParser::Impl::Parser::addSetting(const char **attrs, const char *prefix) {
+    const char *a_name = nullptr;
+    const char *a_value = nullptr;
+    const char *a_update = nullptr;
+    bool isBoolean = false;
 
     size_t i = 0;
     while (attrs[i] != nullptr) {
-        if (strEq(attrs[i], "name")) {
-            if (attrs[++i] == nullptr) {
-                ALOGE("addSettingFromAttributes: name is null");
-                return BAD_VALUE;
-            }
-            name = attrs[i];
-        } else if (strEq(attrs[i], "value")) {
-            if (attrs[++i] == nullptr) {
-                ALOGE("addSettingFromAttributes: value is null");
-                return BAD_VALUE;
-            }
-            value = attrs[i];
-        } else if (strEq(attrs[i], "update")) {
-            if (attrs[++i] == nullptr) {
-                ALOGE("addSettingFromAttributes: update is null");
-                return BAD_VALUE;
-            }
-            update = attrs[i];
-        } else {
-            ALOGE("addSettingFromAttributes: unrecognized attribute: %s", attrs[i]);
+        CHECK((i & 1) == 0);
+        if (attrs[i + 1] == nullptr) {
+            PLOGD("Setting: attribute '%s' is null", attrs[i]);
             return BAD_VALUE;
         }
+
+        if (strEq(attrs[i], "name")) {
+            a_name = attrs[++i];
+        } else if (strEq(attrs[i], "value") || strEq(attrs[i], "enabled")) {
+            if (a_value) {
+                PLOGD("Setting: redundant attribute '%s'", attrs[i]);
+                return BAD_VALUE;
+            }
+            isBoolean = strEq(attrs[i], "enabled");
+            a_value = attrs[++i];
+        } else if (strEq(attrs[i], "update")) {
+            a_update = attrs[++i];
+        } else {
+            PLOGD("Setting: ignoring unrecognized attribute '%s'", attrs[i]);
+            ++i;
+        }
         ++i;
     }
 
-    if (name == nullptr || value == nullptr) {
-        ALOGE("addSettingFromAttributes: name or value unspecified");
+    if (a_name == nullptr || a_value == nullptr) {
+        PLOGD("Setting with no 'name' or 'value' attribute");
         return BAD_VALUE;
     }
 
     // Boolean values are converted to "0" or "1".
-    if (strHasPrefix(name, "supports-")) {
-        value = parseBoolean(value) ? "1" : "0";
+    if (strHasPrefix(a_name, "supports-") || isBoolean) {
+        a_value = parseBoolean(a_value) ? "1" : "0";
     }
 
-    mUpdate = (update != nullptr) && parseBoolean(update);
-    auto attribute = mServiceAttributeMap.find(name);
+    bool update = (a_update != nullptr) && parseBoolean(a_update);
+    Result res = mState->data().addGlobal(std::string(prefix ? : "") + a_name, a_value, update);
+    if (res != OK) {
+        PLOGD("Setting: %s", res.error().c_str());
+    }
+    return res;
+}
+
+MediaCodecsXmlParser::Impl::Result MediaCodecsXmlParser::Impl::Data::addGlobal(
+        std::string key, std::string value, bool updating) {
+    auto attribute = mServiceAttributeMap.find(key);
     if (attribute == mServiceAttributeMap.end()) { // New attribute name
-        if (mUpdate) {
-            ALOGE("addSettingFromAttributes: updating non-existing setting");
-            return BAD_VALUE;
+        if (updating) {
+            return { NAME_NOT_FOUND, "cannot update non-existing setting" };
         }
-        mServiceAttributeMap.insert(Attribute(name, value));
+        mServiceAttributeMap.insert(Attribute(key, value));
     } else { // Existing attribute name
-        if (!mUpdate) {
-            ALOGE("addSettingFromAttributes: adding existing setting");
-        }
         attribute->second = value;
+        if (!updating) {
+            return { ALREADY_EXISTS, "updating existing setting" };
+        }
     }
 
     return OK;
 }
 
-status_t MediaCodecsXmlParser::addMediaCodecFromAttributes(
-        bool encoder, const char **attrs) {
-    const char *name = nullptr;
-    const char *type = nullptr;
-    const char *update = nullptr;
-    const char *rank = nullptr;
+status_t MediaCodecsXmlParser::Impl::Parser::enterMediaCodec(
+        const char **attrs, bool encoder) {
+    const char *a_name = nullptr;
+    const char *a_type = nullptr;
+    const char *a_update = nullptr;
+    const char *a_rank = nullptr;
+    const char *a_domain = nullptr;
+    const char *a_variant = nullptr;
+    const char *a_enabled = nullptr;
 
     size_t i = 0;
     while (attrs[i] != nullptr) {
-        if (strEq(attrs[i], "name")) {
-            if (attrs[++i] == nullptr) {
-                ALOGE("addMediaCodecFromAttributes: name is null");
-                return BAD_VALUE;
-            }
-            name = attrs[i];
-        } else if (strEq(attrs[i], "type")) {
-            if (attrs[++i] == nullptr) {
-                ALOGE("addMediaCodecFromAttributes: type is null");
-                return BAD_VALUE;
-            }
-            type = attrs[i];
-        } else if (strEq(attrs[i], "update")) {
-            if (attrs[++i] == nullptr) {
-                ALOGE("addMediaCodecFromAttributes: update is null");
-                return BAD_VALUE;
-            }
-            update = attrs[i];
-        } else if (strEq(attrs[i], "rank")) {
-            if (attrs[++i] == nullptr) {
-                ALOGE("addMediaCodecFromAttributes: rank is null");
-                return BAD_VALUE;
-            }
-            rank = attrs[i];
-        } else {
-            ALOGE("addMediaCodecFromAttributes: unrecognized attribute: %s", attrs[i]);
+        CHECK((i & 1) == 0);
+        if (attrs[i + 1] == nullptr) {
+            PLOGD("MediaCodec: attribute '%s' is null", attrs[i]);
             return BAD_VALUE;
         }
+
+        if (strEq(attrs[i], "name")) {
+            a_name = attrs[++i];
+        } else if (strEq(attrs[i], "type")) {
+            a_type = attrs[++i];
+        } else if (strEq(attrs[i], "update")) {
+            a_update = attrs[++i];
+        } else if (strEq(attrs[i], "rank")) {
+            a_rank = attrs[++i];
+        } else if (strEq(attrs[i], "domain")) {
+            a_domain = attrs[++i];
+        } else if (strEq(attrs[i], "variant")) {
+            a_variant = attrs[++i];
+        } else if (strEq(attrs[i], "enabled")) {
+            a_enabled = attrs[++i];
+        } else {
+            PLOGD("MediaCodec: ignoring unrecognized attribute '%s'", attrs[i]);
+            ++i;
+        }
         ++i;
     }
 
-    if (name == nullptr) {
-        ALOGE("addMediaCodecFromAttributes: name not found");
+    if (a_name == nullptr) {
+        PLOGD("MediaCodec with no 'name' attribute");
         return BAD_VALUE;
     }
 
-    mUpdate = (update != nullptr) && parseBoolean(update);
-    mCurrentCodec = mCodecMap.find(name);
-    if (mCurrentCodec == mCodecMap.end()) { // New codec name
-        if (mUpdate) {
-            ALOGW("addMediaCodecFromAttributes: cannot update "
-                  "non-existing codec \"%s\".", name);
-            return BAD_VALUE;
+    bool update = (a_update != nullptr) && parseBoolean(a_update);
+    if (a_domain != nullptr) {
+        // disable codecs with domain by default (unless updating)
+        if (!a_enabled && !update) {
+            a_enabled = "false";
+        }
+    }
+
+    Result res = mState->enterMediaCodec(encoder, a_name, a_type, update);
+    if (res != OK) {
+        logAnyErrors(res);
+        return res;
+    }
+
+    return updateMediaCodec(
+            a_rank, parseCommaSeparatedStringSet(a_domain),
+            parseCommaSeparatedStringSet(a_variant), a_enabled);
+}
+
+MediaCodecsXmlParser::Impl::Result
+MediaCodecsXmlParser::Impl::State::enterMediaCodec(
+        bool encoder, const char *name, const char *type, bool updating) {
+    // store name even in case of an error
+    CodecMap::iterator codecIt = mData->mCodecMap.find(name);
+    TypeMap::iterator typeIt;
+    if (codecIt == mData->mCodecMap.end()) { // New codec name
+        if (updating) {
+            return { NAME_NOT_FOUND, "MediaCodec: cannot update non-existing codec" };
         }
         // Create a new codec in mCodecMap
-        mCurrentCodec = mCodecMap.insert(
-                Codec(name, CodecProperties())).first;
+        codecIt = mData->mCodecMap.insert(Codec(name, CodecProperties())).first;
         if (type != nullptr) {
-            mCurrentType = mCurrentCodec->second.typeMap.insert(
-                    Type(type, AttributeMap())).first;
+            typeIt = codecIt->second.typeMap.insert(Type(type, AttributeMap())).first;
         } else {
-            mCurrentType = mCurrentCodec->second.typeMap.end();
+            typeIt = codecIt->second.typeMap.end();
         }
-        mCurrentCodec->second.isEncoder = encoder;
-        mCurrentCodec->second.order = mCodecCounter++;
+        codecIt->second.isEncoder = encoder;
+        codecIt->second.order = mData->mCodecMap.size();
     } else { // Existing codec name
-        if (!mUpdate) {
-            ALOGW("addMediaCodecFromAttributes: trying to add "
-                  "existing codec \"%s\"", name);
-            return ALREADY_EXISTS;
+        if (!updating) {
+            return { ALREADY_EXISTS, "MediaCodec: cannot add existing codec" };
         }
         if (type != nullptr) {
-            mCurrentType = mCurrentCodec->second.typeMap.find(type);
-            if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
-                ALOGE("addMediaCodecFromAttributes: cannot update "
-                      "non-existing type \"%s\" for codec \"%s\"",
-                        type, name);
-                return BAD_VALUE;
+            typeIt = codecIt->second.typeMap.find(type);
+            if (typeIt == codecIt->second.typeMap.end()) {
+                return { NAME_NOT_FOUND, "MediaCodec: cannot update non-existing type for codec" };
             }
         } else {
             // This should happen only when the codec has at most one type.
-            mCurrentType = mCurrentCodec->second.typeMap.begin();
-            if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
-                ALOGE("addMediaCodecFromAttributes: cannot update "
-                      "codec \"%s\" without type specified", name);
-                return BAD_VALUE;
+            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" };
             }
         }
     }
+    mCurrent.emplace_back(CodecAndType{name, codecIt, typeIt, updating});
+    return OK;
+}
+
+status_t MediaCodecsXmlParser::Impl::Parser::updateMediaCodec(
+        const char *rank, const StringSet &domains, const StringSet &variants,
+        const char *enabled) {
+    CHECK(mState->inCodec());
+    CodecProperties &codec = mState->codec();
 
     if (rank != nullptr) {
-        if (!mCurrentCodec->second.rank.empty() && mCurrentCodec->second.rank != rank) {
-            ALOGE("addMediaCodecFromAttributes: code \"%s\" rank changed from \"%s\" to \"%s\"",
-                    name, mCurrentCodec->second.rank.c_str(), rank);
-            return BAD_VALUE;
-        }
-        mCurrentCodec->second.rank = rank;
+        ALOGD_IF(!codec.rank.empty() && codec.rank != rank,
+                "codec '%s' rank changed from '%s' to '%s'",
+                mState->codecName().c_str(), codec.rank.c_str(), rank);
+        codec.rank = rank;
     }
 
+    codec.variantSet = variants;
+
+    for (const std::string &domain : domains) {
+        if (domain.size() && domain.at(0) == '!') {
+            codec.domainSet.erase(domain.substr(1));
+        } else {
+            codec.domainSet.emplace(domain);
+        }
+    }
+
+    if (enabled != nullptr) {
+        if (parseBoolean(enabled)) {
+            codec.quirkSet.erase("attribute::disabled");
+            ALOGD("enabling %s", mState->codecName().c_str());
+        } else {
+            codec.quirkSet.emplace("attribute::disabled");
+            ALOGD("disabling %s", mState->codecName().c_str());
+        }
+    }
     return OK;
 }
 
-status_t MediaCodecsXmlParser::addQuirk(const char **attrs, const char *tag) {
-    if (mCurrentCodec == mCodecMap.end()) {
-        return BAD_VALUE;
-    }
-
-    const char *name = nullptr;
+status_t MediaCodecsXmlParser::Impl::Parser::addQuirk(const char **attrs, const char *prefix) {
+    CHECK(mState->inCodec());
+    const char *a_name = nullptr;
 
     size_t i = 0;
     while (attrs[i] != nullptr) {
-        if (strEq(attrs[i], "name")) {
-            if (attrs[++i] == nullptr) {
-                ALOGE("addQuirk: name is null");
-                return BAD_VALUE;
-            }
-            name = attrs[i];
-        } else {
-            ALOGE("addQuirk: unrecognized attribute: %s", attrs[i]);
+        CHECK((i & 1) == 0);
+        if (attrs[i + 1] == nullptr) {
+            PLOGD("Quirk: attribute '%s' is null", attrs[i]);
             return BAD_VALUE;
         }
+
+        if (strEq(attrs[i], "name")) {
+            a_name = attrs[++i];
+        } else {
+            PLOGD("Quirk: ignoring unrecognized attribute '%s'", attrs[i]);
+            ++i;
+        }
         ++i;
     }
 
-    if (name == nullptr) {
-        ALOGE("addQuirk: name not found");
+    if (a_name == nullptr) {
+        PLOGD("Quirk with no 'name' attribute");
         return BAD_VALUE;
     }
 
-    std::string tagString = tag;
-    std::transform(tagString.begin(), tagString.end(), tagString.begin(), ::tolower);
-    tagString.append("::");
-    tagString.append(name);
-    mCurrentCodec->second.quirkSet.emplace(tagString.c_str());
-    ALOGI("adding %s to %s", tagString.c_str(), mCurrentCodec->first.c_str());
+    std::string key = std::string(prefix ? : "") + a_name;
+    mState->codec().quirkSet.emplace(key);
+    ALOGV("adding %s to %s", key.c_str(), mState->codecName().c_str());
     return OK;
 }
 
-status_t MediaCodecsXmlParser::addTypeFromAttributes(const char **attrs, bool encoder) {
-    if (mCurrentCodec == mCodecMap.end()) {
-        return BAD_VALUE;
-    }
+status_t MediaCodecsXmlParser::Impl::Parser::enterType(const char **attrs) {
+    CHECK(mState->inCodec());
 
-    const char *name = nullptr;
-    const char *update = nullptr;
+    const char *a_name = nullptr;
+    const char *a_update = nullptr;
 
     size_t i = 0;
     while (attrs[i] != nullptr) {
+        CHECK((i & 1) == 0);
+        if (attrs[i + 1] == nullptr) {
+            PLOGD("Type: attribute '%s' is null", attrs[i]);
+            return BAD_VALUE;
+        }
+
         if (strEq(attrs[i], "name")) {
-            if (attrs[++i] == nullptr) {
-                ALOGE("addTypeFromAttributes: name is null");
-                return BAD_VALUE;
-            }
-            name = attrs[i];
+            a_name = attrs[++i];
         } else if (strEq(attrs[i], "update")) {
-            if (attrs[++i] == nullptr) {
-                ALOGE("addTypeFromAttributes: update is null");
-                return BAD_VALUE;
-            }
-            update = attrs[i];
+            a_update = attrs[++i];
         } else {
-            ALOGE("addTypeFromAttributes: unrecognized attribute: %s", attrs[i]);
-            return BAD_VALUE;
+            PLOGD("Type: ignoring unrecognized attribute '%s'", attrs[i]);
+            ++i;
         }
         ++i;
     }
 
-    if (name == nullptr) {
+    if (a_name == nullptr) {
+        PLOGD("Type with no 'name' attribute");
         return BAD_VALUE;
     }
 
-    mCurrentCodec->second.isEncoder = encoder;
-    mCurrentType = mCurrentCodec->second.typeMap.find(name);
-    if (!mUpdate) {
-        if (mCurrentType != mCurrentCodec->second.typeMap.end()) {
-            ALOGW("addTypeFromAttributes: trying to update "
-                  "existing type \"%s\"", name);
-            return ALREADY_EXISTS;
+    bool update = (a_update != nullptr) && parseBoolean(a_update);
+    return mState->enterType(a_name, update);
+}
+
+MediaCodecsXmlParser::Impl::Result
+MediaCodecsXmlParser::Impl::State::enterType(const char *name, bool update) {
+    update = update || updating(); // handle parent
+
+    CodecMap::iterator codecIt = mCurrent.back().mCodec;
+    TypeMap::iterator typeIt = codecIt->second.typeMap.find(name);
+    if (!update) {
+        if (typeIt != codecIt->second.typeMap.end()) {
+            return { ALREADY_EXISTS, "trying to update existing type '" + std::string(name) + "'" };
         }
-        mCurrentType = mCurrentCodec->second.typeMap.insert(
-                Type(name, AttributeMap())).first;
-    } else if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
-        ALOGE("addTypeFromAttributes: updating non-existing type");
-        return BAD_VALUE;
+        typeIt = codecIt->second.typeMap.insert(Type(name, AttributeMap())).first;
+    } else if (typeIt == codecIt->second.typeMap.end()) {
+        return { NAME_NOT_FOUND, "addType: updating non-existing type" };
     }
+    mCurrent.push_back({ codecName(), codecIt, typeIt, update });
+    CHECK(inType());
     return OK;
 }
 
-status_t MediaCodecsXmlParser::addLimit(const char **attrs) {
-    if (mCurrentCodec == mCodecMap.end()) {
-        return BAD_VALUE;
-    }
-    if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
-        return BAD_VALUE;
-    }
-
+status_t MediaCodecsXmlParser::Impl::Parser::addLimit(const char **attrs) {
+    CHECK(mState->inType());
     const char* a_name = nullptr;
     const char* a_default = nullptr;
     const char* a_in = nullptr;
@@ -704,69 +1132,39 @@
 
     size_t i = 0;
     while (attrs[i] != nullptr) {
-        if (strEq(attrs[i], "name")) {
-            if (attrs[++i] == nullptr) {
-                ALOGE("addLimit: name is null");
-                return BAD_VALUE;
-            }
-            a_name = attrs[i];
-        } else if (strEq(attrs[i], "default")) {
-            if (attrs[++i] == nullptr) {
-                ALOGE("addLimit: default is null");
-                return BAD_VALUE;
-            }
-            a_default = attrs[i];
-        } else if (strEq(attrs[i], "in")) {
-            if (attrs[++i] == nullptr) {
-                ALOGE("addLimit: in is null");
-                return BAD_VALUE;
-            }
-            a_in = attrs[i];
-        } else if (strEq(attrs[i], "max")) {
-            if (attrs[++i] == nullptr) {
-                ALOGE("addLimit: max is null");
-                return BAD_VALUE;
-            }
-            a_max = attrs[i];
-        } else if (strEq(attrs[i], "min")) {
-            if (attrs[++i] == nullptr) {
-                ALOGE("addLimit: min is null");
-                return BAD_VALUE;
-            }
-            a_min = attrs[i];
-        } else if (strEq(attrs[i], "range")) {
-            if (attrs[++i] == nullptr) {
-                ALOGE("addLimit: range is null");
-                return BAD_VALUE;
-            }
-            a_range = attrs[i];
-        } else if (strEq(attrs[i], "ranges")) {
-            if (attrs[++i] == nullptr) {
-                ALOGE("addLimit: ranges is null");
-                return BAD_VALUE;
-            }
-            a_ranges = attrs[i];
-        } else if (strEq(attrs[i], "scale")) {
-            if (attrs[++i] == nullptr) {
-                ALOGE("addLimit: scale is null");
-                return BAD_VALUE;
-            }
-            a_scale = attrs[i];
-        } else if (strEq(attrs[i], "value")) {
-            if (attrs[++i] == nullptr) {
-                ALOGE("addLimit: value is null");
-                return BAD_VALUE;
-            }
-            a_value = attrs[i];
-        } else {
-            ALOGE("addLimit: unrecognized limit: %s", attrs[i]);
+        CHECK((i & 1) == 0);
+        if (attrs[i + 1] == nullptr) {
+            PLOGD("Limit: attribute '%s' is null", attrs[i]);
             return BAD_VALUE;
         }
+
+        if (strEq(attrs[i], "name")) {
+            a_name = attrs[++i];
+        } else if (strEq(attrs[i], "default")) {
+            a_default = attrs[++i];
+        } else if (strEq(attrs[i], "in")) {
+            a_in = attrs[++i];
+        } else if (strEq(attrs[i], "max")) {
+            a_max = attrs[++i];
+        } else if (strEq(attrs[i], "min")) {
+            a_min = attrs[++i];
+        } else if (strEq(attrs[i], "range")) {
+            a_range = attrs[++i];
+        } else if (strEq(attrs[i], "ranges")) {
+            a_ranges = attrs[++i];
+        } else if (strEq(attrs[i], "scale")) {
+            a_scale = attrs[++i];
+        } else if (strEq(attrs[i], "value")) {
+            a_value = attrs[++i];
+        } else {
+            PLOGD("Limit: ignoring unrecognized limit: %s", attrs[i]);
+            ++i;
+        }
         ++i;
     }
 
     if (a_name == nullptr) {
-        ALOGE("limit with no 'name' attribute");
+        PLOGD("Limit with no 'name' attribute");
         return BAD_VALUE;
     }
 
@@ -774,8 +1172,50 @@
     // measured-frame-rate, measured-blocks-per-second: range
     // quality: range + default + [scale]
     // complexity: range + default
-    std::string range;
-    if (strEq(a_name, "aspect-ratio") ||
+    std::string key = a_name, value;
+
+    // don't allow specifying more than one of value, range or min/max
+    if ((a_value != nullptr) + (a_range != nullptr) + (a_ranges != nullptr)
+            + (a_min != nullptr || a_max != nullptr) > 1) {
+        PLOGD("Limit '%s' has multiple 'min'/'max', 'range', 'ranges' or 'value' attributes",
+                a_name);
+        return BAD_VALUE;
+    }
+
+    // Min/max limits (only containing min or max attribute)
+    //
+    // Current "max" limits are "channel-count", "concurrent-instances".
+    // There are no current "min" limits
+    //
+    // Range limits. "range" is specified in exactly one of the following forms:
+    // 1) min-max
+    // 2) value-value
+    // 3) range
+    //
+    // Current range limits are "aspect-ratio", "bitrate", "block-count", "blocks-per-second",
+    // "complexity", "frame-rate", "quality", "size", "measured-blocks-per-second",
+    // "performance-point-*", "measured-frame-rate-*"
+    //
+    // Other limits (containing only value or ranges)
+    //
+    // Current ranges limit is "sample-rate"
+    if ((a_min != nullptr) ^ (a_max != nullptr)) {
+        // min/max limit
+        if (a_max != nullptr) {
+            key = "max-" + key;
+            value = a_max;
+        } else if (a_min != nullptr) {
+            key = "min-" + key;
+            value = a_min;
+        }
+    } else if (a_min != nullptr && a_max != nullptr) {
+        // min-max
+        key += "-range";
+        value = a_min + std::string("-") + a_max;
+    } else if (a_value != nullptr) {
+        // value-value or value
+        value = a_value;
+        if (strEq(a_name, "aspect-ratio") ||
             strEq(a_name, "bitrate") ||
             strEq(a_name, "block-count") ||
             strEq(a_name, "blocks-per-second") ||
@@ -786,249 +1226,199 @@
             strEq(a_name, "measured-blocks-per-second") ||
             strHasPrefix(a_name, "performance-point-") ||
             strHasPrefix(a_name, "measured-frame-rate-")) {
-        // "range" is specified in exactly one of the following forms:
-        // 1) min-max
-        // 2) value-value
-        // 3) range
-        if (a_min != nullptr && a_max != nullptr) {
-            // min-max
-            if (a_range != nullptr || a_value != nullptr) {
-                return limitError(a_name, "has 'min' and 'max' as well as 'range' or "
-                        "'value' attributes");
-            }
-            range = a_min;
-            range += '-';
-            range += a_max;
-        } else if (a_min != nullptr || a_max != nullptr) {
-            return limitError(a_name, "has only 'min' or 'max' attribute");
-        } else if (a_value != nullptr) {
-            // value-value
-            if (a_range != nullptr) {
-                return limitError(a_name, "has both 'range' and 'value' attributes");
-            }
-            range = a_value;
-            range += '-';
-            range += a_value;
-        } else if (a_range == nullptr) {
-            return limitError(a_name, "with no 'range', 'value' or 'min'/'max' attributes");
-        } else {
-            // range
-            range = a_range;
+            key += "-range";
+            value += std::string("-") + a_value;
         }
-
-        // "aspect-ratio" requires some special treatment.
-        if (strEq(a_name, "aspect-ratio")) {
-            // "aspect-ratio" must have "in".
-            if (a_in == nullptr) {
-                return limitFoundMissingAttr(a_name, "in", false);
-            }
-            // "in" must be either "pixels" or "blocks".
-            if (!strEq(a_in, "pixels") && !strEq(a_in, "blocks")) {
-                return limitInvalidAttr(a_name, "in", a_in);
-            }
-            // name will be "pixel-aspect-ratio-range" or
-            // "block-aspect-ratio-range".
-            mCurrentType->second[
-                    std::string(a_in).substr(0, strlen(a_in) - 1) +
-                    "-aspect-ratio-range"] = range;
-        } else {
-            // For everything else (apart from "aspect-ratio"), simply append
-            // "-range" to the name for the range-type property.
-            mCurrentType->second[std::string(a_name) + "-range"] = range;
-
-            // Only "quality" may have "scale".
-            if (!strEq(a_name, "quality") && a_scale != nullptr) {
-                return limitFoundMissingAttr(a_name, "scale");
-            } else if (strEq(a_name, "quality")) {
-                // The default value of "quality-scale" is "linear".
-                mCurrentType->second["quality-scale"] = a_scale == nullptr ?
-                        "linear" : a_scale;
-            }
-
-            // "quality" and "complexity" must have "default".
-            // Other limits must not have "default".
-            if (strEq(a_name, "quality") || strEq(a_name, "complexity")) {
-                if (a_default == nullptr) {
-                    return limitFoundMissingAttr(a_name, "default", false);
-                }
-                // name will be "quality-default" or "complexity-default".
-                mCurrentType->second[std::string(a_name) + "-default"] = a_default;
-            } else if (a_default != nullptr) {
-                return limitFoundMissingAttr(a_name, "default", true);
-            }
-        }
+    } else if (a_range != nullptr) {
+        // range
+        key += "-range";
+        value = a_range;
+    } else if (a_ranges != nullptr) {
+        // ranges
+        key += "-ranges";
+        value = a_ranges;
     } else {
-        if (a_default != nullptr) {
-            return limitFoundMissingAttr(a_name, "default");
-        }
-        if (a_in != nullptr) {
-            return limitFoundMissingAttr(a_name, "in");
-        }
-        if (a_scale != nullptr) {
-            return limitFoundMissingAttr(a_name, "scale");
-        }
-        if (a_range != nullptr) {
-            return limitFoundMissingAttr(a_name, "range");
-        }
-        if (a_min != nullptr) {
-            return limitFoundMissingAttr(a_name, "min");
-        }
-
-        if (a_max != nullptr) {
-            // "max" must exist if and only if name is "channel-count" or
-            // "concurrent-instances".
-            // "min" is not ncessary.
-            if (strEq(a_name, "channel-count") ||
-                    strEq(a_name, "concurrent-instances")) {
-                mCurrentType->second[std::string("max-") + a_name] = a_max;
-            } else {
-                return limitFoundMissingAttr(a_name, "max", false);
-            }
-        } else if (strEq(a_name, "channel-count") ||
-                strEq(a_name, "concurrent-instances")) {
-            return limitFoundMissingAttr(a_name, "max");
-        }
-
-        if (a_ranges != nullptr) {
-            // "ranges" must exist if and only if name is "sample-rate".
-            if (strEq(a_name, "sample-rate")) {
-                mCurrentType->second["sample-rate-ranges"] = a_ranges;
-            } else {
-                return limitFoundMissingAttr(a_name, "ranges", false);
-            }
-        } else if (strEq(a_name, "sample-rate")) {
-            return limitFoundMissingAttr(a_name, "ranges");
-        }
-
-        if (a_value != nullptr) {
-            // "value" must exist if and only if name is "alignment" or
-            // "block-size".
-            if (strEq(a_name, "alignment") || strEq(a_name, "block-size")) {
-                mCurrentType->second[a_name] = a_value;
-            } else {
-                return limitFoundMissingAttr(a_name, "value", false);
-            }
-        } else if (strEq(a_name, "alignment") || strEq(a_name, "block-size")) {
-            return limitFoundMissingAttr(a_name, "value", false);
-        }
-
+        PLOGD("Limit '%s' with no 'range', 'value' or 'min'/'max' attributes", a_name);
+        return BAD_VALUE;
     }
 
+    // handle 'in' attribute - this changes the key
+    if (a_in != nullptr) {
+        // Currently "aspect-ratio" uses in attribute
+        const size_t a_in_len = strlen(a_in);
+        key = std::string(a_in, a_in_len - a_in[a_in_len] == 's') + '-' + key;
+    }
+
+    // handle 'scale' attribute - this adds a new detail
+    if (a_scale != nullptr) {
+        mState->addDetail(a_name + std::string("-scale"), a_scale);
+    } else if (strEq(a_name, "quality")) {
+        // The default value of "quality-scale" is "linear" even if unspecified.
+        mState->addDetail(a_name + std::string("-scale"), "linear");
+    }
+
+    // handle 'default' attribute - this adds a new detail
+    if (a_default != nullptr) {
+        mState->addDetail(a_name + std::string("-default"), a_default);
+    }
+
+    mState->addDetail(key, value);
     return OK;
 }
 
-status_t MediaCodecsXmlParser::addFeature(const char **attrs) {
-    if (mCurrentCodec == mCodecMap.end()) {
-        return BAD_VALUE;
+void MediaCodecsXmlParser::Impl::State::addDetail(
+        const std::string &key, const std::string &value) {
+    CHECK(inType());
+    ALOGI("limit: %s = %s", key.c_str(), value.c_str());
+    const StringSet &variants = mVariantsStack.back();
+    if (variants.empty()) {
+        type()[key] = value;
+    } else {
+        for (const std::string &variant : variants) {
+            type()[variant + ":::" + key] = value;
+        }
     }
-    if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
+}
+
+status_t MediaCodecsXmlParser::Impl::Parser::limitVariants(const char **attrs) {
+    const char* a_name = nullptr;
+
+    size_t i = 0;
+    while (attrs[i] != nullptr) {
+        CHECK((i & 1) == 0);
+        if (attrs[i + 1] == nullptr) {
+            PLOGD("Variant: attribute '%s' is null", attrs[i]);
+            return BAD_VALUE;
+        }
+        if (strEq(attrs[i], "name")) {
+            a_name = attrs[++i];
+        } else {
+            PLOGD("Variant: ignoring unrecognized attribute: %s", attrs[i]);
+            ++i;
+        }
+        ++i;
+    }
+
+    if (a_name == nullptr || *a_name == '\0') {
+        PLOGD("Variant with no or empty 'name' attribute");
         return BAD_VALUE;
     }
 
+    StringSet variants;
+    for (const std::string &variant : parseCommaSeparatedStringSet(a_name)) {
+        if (mState->variants().count(variant)) {
+            variants.emplace(variant);
+        } else {
+            PLOGD("Variant: variant '%s' not in parent variants", variant.c_str());
+            return BAD_VALUE;
+        }
+    }
+    mState->enterVariants(variants);
+    return OK;
+}
+
+status_t MediaCodecsXmlParser::Impl::Parser::addFeature(const char **attrs) {
+    CHECK(mState->inType());
     size_t i = 0;
-    const char *name = nullptr;
+    const char *a_name = nullptr;
     int32_t optional = -1;
     int32_t required = -1;
-    const char *value = nullptr;
+    const char *a_value = nullptr;
 
     while (attrs[i] != nullptr) {
-        if (strEq(attrs[i], "name")) {
-            if (attrs[++i] == nullptr) {
-                ALOGE("addFeature: name is null");
-                return BAD_VALUE;
-            }
-            name = attrs[i];
-        } else if (strEq(attrs[i], "optional")) {
-            if (attrs[++i] == nullptr) {
-                ALOGE("addFeature: optional is null");
-                return BAD_VALUE;
-            }
-            optional = parseBoolean(attrs[i]) ? 1 : 0;
-        } else if (strEq(attrs[i], "required")) {
-            if (attrs[++i] == nullptr) {
-                ALOGE("addFeature: required is null");
-                return BAD_VALUE;
-            }
-            required = parseBoolean(attrs[i]) ? 1 : 0;
-        } else if (strEq(attrs[i], "value")) {
-            if (attrs[++i] == nullptr) {
-                ALOGE("addFeature: value is null");
-                return BAD_VALUE;
-            }
-            value = attrs[i];
-        } else {
-            ALOGE("addFeature: unrecognized attribute: %s", attrs[i]);
+        CHECK((i & 1) == 0);
+        if (attrs[i + 1] == nullptr) {
+            PLOGD("Feature: attribute '%s' is null", attrs[i]);
             return BAD_VALUE;
         }
+
+        if (strEq(attrs[i], "name")) {
+            a_name = attrs[++i];
+        } else if (strEq(attrs[i], "optional")) {
+            optional = parseBoolean(attrs[++i]) ? 1 : 0;
+        } else if (strEq(attrs[i], "required")) {
+            required = parseBoolean(attrs[++i]) ? 1 : 0;
+        } else if (strEq(attrs[i], "value")) {
+            a_value = attrs[++i];
+        } else {
+            PLOGD("Feature: ignoring unrecognized attribute '%s'", attrs[i]);
+            ++i;
+        }
         ++i;
     }
 
     // Every feature must have a name.
-    if (name == nullptr) {
-        ALOGE("feature with no 'name' attribute");
+    if (a_name == nullptr) {
+        PLOGD("Feature with no 'name' attribute");
         return BAD_VALUE;
     }
 
-    if ((optional != -1) || (required != -1)) {
-        if (optional == required) {
-            ALOGE("feature '%s' is both/neither optional and required", name);
+    if (a_value != nullptr) {
+        if (optional != -1 || required != -1) {
+            PLOGD("Feature '%s' has both value and optional/required attributes", a_name);
             return BAD_VALUE;
         }
-        if ((optional == 1) || (required == 1)) {
-            if (value != nullptr) {
-                ALOGE("feature '%s' cannot have extra 'value'", name);
-                return BAD_VALUE;
-            }
-            mCurrentType->second[std::string("feature-") + name] =
-                    optional == 1 ? "0" : "1";
-            return OK;
+    } else {
+        if (optional == required && optional != -1) {
+            PLOGD("Feature '%s' is both/neither optional and required", a_name);
+            return BAD_VALUE;
         }
+        a_value = (required == 1 || optional == 0) ? "1" : "0";
     }
-    mCurrentType->second[std::string("feature-") + name] = value == nullptr ?
-            "0" : value;
+
+    mState->addDetail(std::string("feature-") + a_name, a_value ? : "0");
     return OK;
 }
 
-status_t MediaCodecsXmlParser::addAlias(const char **attrs) {
+status_t MediaCodecsXmlParser::Impl::Parser::addAlias(const char **attrs) {
+    CHECK(mState->inCodec());
     size_t i = 0;
-    const char *name = nullptr;
+    const char *a_name = nullptr;
 
     while (attrs[i] != nullptr) {
-        if (strEq(attrs[i], "name")) {
-            if (attrs[++i] == nullptr) {
-                ALOGE("addAlias: name is null");
-                return BAD_VALUE;
-            }
-            name = attrs[i];
-        } else {
-            ALOGE("addAlias: unrecognized attribute: %s", attrs[i]);
+        CHECK((i & 1) == 0);
+        if (attrs[i + 1] == nullptr) {
+            PLOGD("Alias: attribute '%s' is null", attrs[i]);
             return BAD_VALUE;
         }
+
+        if (strEq(attrs[i], "name")) {
+            a_name = attrs[++i];
+        } else {
+            PLOGD("Alias: ignoring unrecognized attribute '%s'", attrs[i]);
+            ++i;
+        }
         ++i;
     }
 
     // Every feature must have a name.
-    if (name == nullptr) {
-        ALOGE("alias with no 'name' attribute");
+    if (a_name == nullptr) {
+        PLOGD("Alias with no 'name' attribute");
         return BAD_VALUE;
     }
 
-    mCurrentCodec->second.aliases.emplace_back(name);
+    mState->codec().aliases.emplace_back(a_name);
     return OK;
 }
 
 const MediaCodecsXmlParser::AttributeMap&
-        MediaCodecsXmlParser::getServiceAttributeMap() const {
-    return mServiceAttributeMap;
+MediaCodecsXmlParser::getServiceAttributeMap() const {
+    return mImpl->getServiceAttributeMap();
 }
 
 const MediaCodecsXmlParser::CodecMap&
-        MediaCodecsXmlParser::getCodecMap() const {
-    return mCodecMap;
+MediaCodecsXmlParser::getCodecMap() const {
+    return mImpl->getCodecMap();
 }
 
 const MediaCodecsXmlParser::RoleMap&
-        MediaCodecsXmlParser::getRoleMap() const {
+MediaCodecsXmlParser::getRoleMap() const {
+    return mImpl->getRoleMap();
+}
+
+const MediaCodecsXmlParser::RoleMap&
+MediaCodecsXmlParser::Impl::getRoleMap() const {
+    std::lock_guard<std::mutex> guard(mLock);
     if (mRoleMap.empty()) {
         generateRoleMap();
     }
@@ -1036,6 +1426,11 @@
 }
 
 const char* MediaCodecsXmlParser::getCommonPrefix() const {
+    return mImpl->getCommonPrefix();
+}
+
+const char* MediaCodecsXmlParser::Impl::getCommonPrefix() const {
+    std::lock_guard<std::mutex> guard(mLock);
     if (mCommonPrefix.empty()) {
         generateCommonPrefix();
     }
@@ -1043,12 +1438,15 @@
 }
 
 status_t MediaCodecsXmlParser::getParsingStatus() const {
-    return mParsingStatus;
+    return mImpl->getParsingStatus();
 }
 
-void MediaCodecsXmlParser::generateRoleMap() const {
-    for (const auto& codec : mCodecMap) {
-        const auto& codecName = codec.first;
+void MediaCodecsXmlParser::Impl::generateRoleMap() const {
+    for (const auto& codec : mData.mCodecMap) {
+        const auto &codecName = codec.first;
+        if (codecName == "<dummy>") {
+            continue;
+        }
         bool isEncoder = codec.second.isEncoder;
         size_t order = codec.second.order;
         std::string rank = codec.second.rank;
@@ -1116,14 +1514,14 @@
     }
 }
 
-void MediaCodecsXmlParser::generateCommonPrefix() const {
-    if (mCodecMap.empty()) {
+void MediaCodecsXmlParser::Impl::generateCommonPrefix() const {
+    if (mData.mCodecMap.empty()) {
         return;
     }
-    auto i = mCodecMap.cbegin();
+    auto i = mData.mCodecMap.cbegin();
     auto first = i->first.cbegin();
     auto last = i->first.cend();
-    for (++i; i != mCodecMap.cend(); ++i) {
+    for (++i; i != mData.mCodecMap.cend(); ++i) {
         last = std::mismatch(
                 first, last, i->first.cbegin(), i->first.cend()).first;
     }
@@ -1131,4 +1529,3 @@
 }
 
 } // namespace android
-
diff --git a/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h b/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h
index 7a986b7..b666de4 100644
--- a/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h
+++ b/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h
@@ -19,34 +19,31 @@
 
 #include <sys/types.h>
 #include <utils/Errors.h>
-#include <utils/Vector.h>
-#include <utils/StrongPointer.h>
 
-#include <string>
-#include <set>
 #include <map>
+#include <mutex>
+#include <set>
+#include <string>
 #include <vector>
 
+struct XML_ParserStruct; // from expat library
+
 namespace android {
 
 class MediaCodecsXmlParser {
 public:
 
     // Treblized media codec list will be located in /odm/etc or /vendor/etc.
-    static constexpr char const* defaultSearchDirs[] =
-            {"/odm/etc", "/vendor/etc", "/etc", nullptr};
-    static constexpr char const* defaultMainXmlName =
-            "media_codecs.xml";
-    static constexpr char const* defaultPerformanceXmlName =
-            "media_codecs_performance.xml";
+    static std::vector<std::string> getDefaultSearchDirs() {
+            return { "/odm/etc", "/vendor/etc", "/etc" };
+    }
+    static std::vector<std::string> getDefaultXmlNames() {
+            return { "media_codecs.xml", "media_codecs_performance.xml" };
+    }
     static constexpr char const* defaultProfilingResultsXmlPath =
             "/data/misc/media/media_codecs_profiling_results.xml";
 
-    MediaCodecsXmlParser(
-            const char* const* searchDirs = defaultSearchDirs,
-            const char* mainXmlName = defaultMainXmlName,
-            const char* performanceXmlName = defaultPerformanceXmlName,
-            const char* profilingResultsXmlPath = defaultProfilingResultsXmlPath);
+    MediaCodecsXmlParser();
     ~MediaCodecsXmlParser();
 
     typedef std::pair<std::string, std::string> Attribute;
@@ -55,7 +52,7 @@
     typedef std::pair<std::string, AttributeMap> Type;
     typedef std::map<std::string, AttributeMap> TypeMap;
 
-    typedef std::set<std::string> QuirkSet;
+    typedef std::set<std::string> StringSet;
 
     /**
      * Properties of a codec (node)
@@ -63,7 +60,9 @@
     struct CodecProperties {
         bool isEncoder;    ///< Whether this codec is an encoder or a decoder
         size_t order;      ///< Order of appearance in the file (starting from 0)
-        QuirkSet quirkSet; ///< Set of quirks requested by this codec
+        StringSet quirkSet; ///< Set of quirks requested by this codec
+        StringSet domainSet; ///< Set of domains this codec is in
+        StringSet variantSet; ///< Set of variants this codec is enabled on
         TypeMap typeMap;   ///< Map of types supported by this codec
         std::vector<std::string> aliases; ///< Name aliases for this codec
         std::string rank;  ///< Rank of this codec. This is a numeric string.
@@ -119,70 +118,31 @@
 
     status_t getParsingStatus() const;
 
+    /**
+     * Parse top level XML files from a group of search directories.
+     *
+     * @param xmlFiles ordered list of XML file names (no paths)
+     * @param searchDirs ordered list of paths to consider
+     *
+     * @return parsing status
+     */
+    status_t parseXmlFilesInSearchDirs(
+            const std::vector<std::string> &xmlFiles = getDefaultXmlNames(),
+            const std::vector<std::string> &searchDirs = getDefaultSearchDirs());
+
+
+    /**
+     * Parse a top level XML file.
+     *
+     * @param path XML file path
+     *
+     * @return parsing status
+     */
+    status_t parseXmlPath(const std::string &path);
+
 private:
-    enum Section {
-        SECTION_TOPLEVEL,
-        SECTION_SETTINGS,
-        SECTION_DECODERS,
-        SECTION_DECODER,
-        SECTION_DECODER_TYPE,
-        SECTION_ENCODERS,
-        SECTION_ENCODER,
-        SECTION_ENCODER_TYPE,
-        SECTION_INCLUDE,
-    };
-
-    status_t mParsingStatus;
-    Section mCurrentSection;
-    bool mUpdate;
-    std::vector<Section> mSectionStack;
-    std::string mHrefBase;
-
-    // Service attributes
-    AttributeMap mServiceAttributeMap;
-
-    // Codec attributes
-    std::string mCurrentName;
-    std::set<std::string> mCodecSet;
-    Codec mCodecListTemp[2048];
-    CodecMap mCodecMap;
-    size_t mCodecCounter;
-    CodecMap::iterator mCurrentCodec;
-    TypeMap::iterator mCurrentType;
-
-    // Role map
-    mutable RoleMap mRoleMap;
-
-    // Computed longest common prefix
-    mutable std::string mCommonPrefix;
-
-    bool parseTopLevelXMLFile(const char *path, bool ignore_errors = false);
-
-    void parseXMLFile(const char *path);
-
-    static void StartElementHandlerWrapper(
-            void *me, const char *name, const char **attrs);
-
-    static void EndElementHandlerWrapper(void *me, const char *name);
-
-    void startElementHandler(const char *name, const char **attrs);
-    void endElementHandler(const char *name);
-
-    status_t includeXMLFile(const char **attrs);
-    status_t addSettingFromAttributes(const char **attrs);
-    status_t addMediaCodecFromAttributes(bool encoder, const char **attrs);
-    void addMediaCodec(bool encoder, const char *name,
-            const char *type = nullptr);
-
-    status_t addQuirk(const char **attrs, const char *tag);
-    status_t addTypeFromAttributes(const char **attrs, bool encoder);
-    status_t addAlias(const char **attrs);
-    status_t addLimit(const char **attrs);
-    status_t addFeature(const char **attrs);
-    void addType(const char *name);
-
-    void generateRoleMap() const;
-    void generateCommonPrefix() const;
+    struct Impl;
+    std::shared_ptr<Impl> mImpl;
 
     MediaCodecsXmlParser(const MediaCodecsXmlParser&) = delete;
     MediaCodecsXmlParser& operator=(const MediaCodecsXmlParser&) = delete;
@@ -191,4 +151,3 @@
 } // namespace android
 
 #endif // MEDIA_STAGEFRIGHT_XMLPARSER_H_
-
diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp
index 7d1c88b..a3cabd8 100644
--- a/media/ndk/Android.bp
+++ b/media/ndk/Android.bp
@@ -74,6 +74,7 @@
         "android.hardware.graphics.bufferqueue@1.0",
         "android.hidl.token@1.0-utils",
         "libandroid_runtime_lazy",
+        "libbase",
         "libbinder",
         "libmedia",
         "libmedia_omx",
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
index 2deb1a4..85dbffe 100644
--- a/media/ndk/NdkMediaDrm.cpp
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "NdkMediaDrm"
 
 #include <inttypes.h>
+#include <unistd.h>
 
 #include <media/NdkMediaDrm.h>
 
@@ -26,6 +27,8 @@
 #include <utils/StrongPointer.h>
 #include <gui/Surface.h>
 
+#include <android-base/properties.h>
+#include <binder/PermissionController.h>
 #include <media/IDrm.h>
 #include <media/IDrmClient.h>
 #include <media/stagefright/MediaErrors.h>
@@ -231,6 +234,39 @@
     return result;
 }
 
+static bool ShouldGetAppPackageName(void) {
+    // Check what this device's first API level was.
+    int32_t firstApiLevel = android::base::GetIntProperty<int32_t>("ro.product.first_api_level", 0);
+    if (firstApiLevel == 0) {
+        // First API Level is 0 on factory ROMs, but we can assume the current SDK
+        // version is the first if it's a factory ROM.
+        firstApiLevel = android::base::GetIntProperty<int32_t>("ro.build.version.sdk", 0);
+    }
+    return firstApiLevel >= 29;  // Android Q
+}
+
+static status_t GetAppPackageName(String8 *packageName) {
+    sp<IServiceManager> serviceManager = defaultServiceManager();
+    sp<IBinder> binder = serviceManager->getService(String16("permission"));
+
+    sp<IPermissionController> permissionContol = interface_cast<IPermissionController>(binder);
+    if (permissionContol == NULL) {
+        ALOGE("Failed to get permission service");
+        return UNKNOWN_ERROR;
+    }
+
+    Vector<String16> packages;
+    permissionContol->getPackagesForUid(getuid(), packages);
+
+    if (packages.isEmpty()) {
+        ALOGE("Unable to get package name for current UID");
+        return UNKNOWN_ERROR;
+    }
+
+    *packageName = String8(packages[0]);
+    return OK;
+}
+
 static sp<IDrm> CreateDrm() {
     sp<IServiceManager> sm = defaultServiceManager();
     sp<IBinder> binder = sm->getService(String16("media.drm"));
@@ -255,8 +291,16 @@
         return NULL;
     }
 
-    String8 nullPackageName;
-    status_t err = drm->createPlugin(uuid, nullPackageName);
+    String8 packageName;
+    if (ShouldGetAppPackageName()) {
+        status_t err = GetAppPackageName(&packageName);
+
+        if (err != OK) {
+            return NULL;
+        }
+    }
+
+    status_t err = drm->createPlugin(uuid, packageName);
 
     if (err != OK) {
         return NULL;
diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp
index 768cd1e..16fdeaf 100644
--- a/media/utils/ServiceUtilities.cpp
+++ b/media/utils/ServiceUtilities.cpp
@@ -35,6 +35,8 @@
 namespace android {
 
 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) {
@@ -162,9 +164,8 @@
 }
 
 bool modifyAudioRoutingAllowed() {
-    static const String16 sModifyAudioRoutingAllowed("android.permission.MODIFY_AUDIO_ROUTING");
     // IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
-    bool ok = PermissionCache::checkCallingPermission(sModifyAudioRoutingAllowed);
+    bool ok = PermissionCache::checkCallingPermission(sModifyAudioRouting);
     if (!ok) ALOGE("android.permission.MODIFY_AUDIO_ROUTING");
     return ok;
 }
@@ -200,9 +201,19 @@
 }
 
 bool modifyPhoneStateAllowed(pid_t pid, uid_t uid) {
-    static const String16 sModifyPhoneState("android.permission.MODIFY_PHONE_STATE");
     bool ok = PermissionCache::checkPermission(sModifyPhoneState, pid, uid);
-    if (!ok) ALOGE("Request requires android.permission.MODIFY_PHONE_STATE");
+    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) {
+    static const String16 sWriteSecureSettings("android.permission.WRITE_SECURE_SETTINGS");
+    bool ok = PermissionCache::checkPermission(sModifyPhoneState, pid, uid)
+        || PermissionCache::checkPermission(sWriteSecureSettings, pid, uid)
+        || PermissionCache::checkPermission(sModifyAudioRouting, pid, uid);
+    ALOGE_IF(!ok, "Request requires %s or %s",
+             String8(sModifyPhoneState).c_str(), String8(sWriteSecureSettings).c_str());
     return ok;
 }
 
diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h
index c5fe05f..2a6e609 100644
--- a/media/utils/include/mediautils/ServiceUtilities.h
+++ b/media/utils/include/mediautils/ServiceUtilities.h
@@ -81,6 +81,8 @@
 bool modifyDefaultAudioEffectsAllowed();
 bool dumpAllowed();
 bool modifyPhoneStateAllowed(pid_t pid, uid_t uid);
+bool bypassInterruptionPolicyAllowed(pid_t pid, uid_t uid);
+
 status_t checkIMemory(const sp<IMemory>& iMemory);
 
 class MediaPackageManager {
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 5e5ea11..0b745ac 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -296,13 +296,15 @@
     audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT;
     audio_io_handle_t io = AUDIO_IO_HANDLE_NONE;
     audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
+    audio_attributes_t localAttr = *attr;
     if (direction == MmapStreamInterface::DIRECTION_OUTPUT) {
         audio_config_t fullConfig = AUDIO_CONFIG_INITIALIZER;
         fullConfig.sample_rate = config->sample_rate;
         fullConfig.channel_mask = config->channel_mask;
         fullConfig.format = config->format;
         std::vector<audio_io_handle_t> secondaryOutputs;
-        ret = AudioSystem::getOutputForAttr(attr, &io,
+
+        ret = AudioSystem::getOutputForAttr(&localAttr, &io,
                                             actualSessionId,
                                             &streamType, client.clientPid, client.clientUid,
                                             &fullConfig,
@@ -312,7 +314,7 @@
         ALOGW_IF(!secondaryOutputs.empty(),
                  "%s does not support secondary outputs, ignoring them", __func__);
     } else {
-        ret = AudioSystem::getInputForAttr(attr, &io,
+        ret = AudioSystem::getInputForAttr(&localAttr, &io,
                                               RECORD_RIID_INVALID,
                                               actualSessionId,
                                               client.clientPid,
@@ -330,7 +332,7 @@
     sp<MmapThread> thread = mMmapThreads.valueFor(io);
     if (thread != 0) {
         interface = new MmapThreadHandle(thread);
-        thread->configure(attr, streamType, actualSessionId, callback, *deviceId, portId);
+        thread->configure(&localAttr, streamType, actualSessionId, callback, *deviceId, portId);
         *handle = portId;
         *sessionId = actualSessionId;
     } else {
@@ -691,7 +693,7 @@
     uid_t clientUid = input.clientInfo.clientUid;
     audio_io_handle_t effectThreadId = AUDIO_IO_HANDLE_NONE;
     std::vector<int> effectIds;
-
+    audio_attributes_t localAttr = input.attr;
 
     if (!isAudioServerOrMediaServerUid(callingUid)) {
         ALOGW_IF(clientUid != callingUid,
@@ -720,8 +722,7 @@
     output.sessionId = sessionId;
     output.outputId = AUDIO_IO_HANDLE_NONE;
     output.selectedDeviceId = input.selectedDeviceId;
-
-    lStatus = AudioSystem::getOutputForAttr(&input.attr, &output.outputId, sessionId, &streamType,
+    lStatus = AudioSystem::getOutputForAttr(&localAttr, &output.outputId, sessionId, &streamType,
                                             clientPid, clientUid, &input.config, input.flags,
                                             &output.selectedDeviceId, &portId, &secondaryOutputs);
 
@@ -782,7 +783,7 @@
         output.notificationFrameCount = input.notificationFrameCount;
         output.flags = input.flags;
 
-        track = thread->createTrack_l(client, streamType, input.attr, &output.sampleRate,
+        track = thread->createTrack_l(client, streamType, localAttr, &output.sampleRate,
                                       input.config.format, input.config.channel_mask,
                                       &output.frameCount, &output.notificationFrameCount,
                                       input.notificationsPerBuffer, input.speed,
@@ -827,6 +828,12 @@
                     ALOGE("Secondary output patchRecord init failed: %d", status);
                     continue;
                 }
+
+                // TODO: We could check compatibility of the secondaryThread with the PatchTrack
+                // for fast usage: thread has fast mixer, sample rate matches, etc.;
+                // for now, we exclude fast tracks by removing the Fast flag.
+                const audio_output_flags_t outputFlags =
+                        (audio_output_flags_t)(output.flags & ~AUDIO_OUTPUT_FLAG_FAST);
                 sp patchTrack = new PlaybackThread::PatchTrack(secondaryThread,
                                                                streamType,
                                                                output.sampleRate,
@@ -835,7 +842,7 @@
                                                                frameCount,
                                                                patchRecord->buffer(),
                                                                patchRecord->bufferSize(),
-                                                               output.flags,
+                                                               outputFlags,
                                                                0ns /* timeout */);
                 status = patchTrack->initCheck();
                 if (status != NO_ERROR) {
@@ -1900,12 +1907,16 @@
                                       input.opPackageName,
                                       &input.config,
                                       output.flags, &output.selectedDeviceId, &portId);
+    if (lStatus != NO_ERROR) {
+        ALOGE("createRecord() getInputForAttr return error %d", lStatus);
+        goto Exit;
+    }
 
     {
         Mutex::Autolock _l(mLock);
         RecordThread *thread = checkRecordThread_l(output.inputId);
         if (thread == NULL) {
-            ALOGE("createRecord() checkRecordThread_l failed");
+            ALOGE("createRecord() checkRecordThread_l failed, input handle %d", output.inputId);
             lStatus = BAD_VALUE;
             goto Exit;
         }
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index a210a6d..edb331d 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -506,7 +506,10 @@
     }
     if (mPlayback.thread()->hasFastMixer()) {
         // Create a fast track if the playback thread has fast mixer to get better performance.
+        // Note: we should have matching channel mask, sample rate, and format by the logic above.
         outputFlags = (audio_output_flags_t) (outputFlags | AUDIO_OUTPUT_FLAG_FAST);
+    } else {
+        outputFlags = (audio_output_flags_t) (outputFlags & ~AUDIO_OUTPUT_FLAG_FAST);
     }
 
     // create a special playback track to render to playback thread.
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index bb97f8d..7008cee 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -26,7 +26,7 @@
     bool hasOpPlayAudio() const;
 
     static sp<OpPlayAudioMonitor> createIfNeeded(
-            uid_t uid, audio_usage_t usage, int id, audio_stream_type_t streamType);
+            uid_t uid, const audio_attributes_t& attr, int id, audio_stream_type_t streamType);
 
 private:
     OpPlayAudioMonitor(uid_t uid, audio_usage_t usage, int id);
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 7daa929..1d99b88 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -4618,6 +4618,10 @@
 
         // process fast tracks
         if (track->isFastTrack()) {
+            LOG_ALWAYS_FATAL_IF(mFastMixer.get() == nullptr,
+                    "%s(%d): FastTrack(%d) present without FastMixer",
+                     __func__, id(), track->id());
+
             if (track->getHapticPlaybackEnabled()) {
                 noFastHapticTrack = false;
             }
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index e1f00c1..b0817ed 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -387,18 +387,24 @@
 // static
 sp<AudioFlinger::PlaybackThread::OpPlayAudioMonitor>
 AudioFlinger::PlaybackThread::OpPlayAudioMonitor::createIfNeeded(
-            uid_t uid, audio_usage_t usage, int id, audio_stream_type_t streamType)
+            uid_t uid, const audio_attributes_t& attr, int id, audio_stream_type_t streamType)
 {
     if (isAudioServerOrRootUid(uid)) {
-        ALOGD("OpPlayAudio: not muting track:%d usage:%d root or audioserver", id, usage);
+        ALOGD("OpPlayAudio: not muting track:%d usage:%d root or audioserver", id, attr.usage);
         return nullptr;
     }
     // stream type has been filtered by audio policy to indicate whether it can be muted
     if (streamType == AUDIO_STREAM_ENFORCED_AUDIBLE) {
-        ALOGD("OpPlayAudio: not muting track:%d usage:%d ENFORCED_AUDIBLE", id, usage);
+        ALOGD("OpPlayAudio: not muting track:%d usage:%d ENFORCED_AUDIBLE", id, attr.usage);
         return nullptr;
     }
-    return new OpPlayAudioMonitor(uid, usage, id);
+    if ((attr.flags & AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY)
+            == AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY) {
+        ALOGD("OpPlayAudio: not muting track:%d flags %#x have FLAG_BYPASS_INTERRUPTION_POLICY",
+            id, attr.flags);
+        return nullptr;
+    }
+    return new OpPlayAudioMonitor(uid, attr.usage, id);
 }
 
 AudioFlinger::PlaybackThread::OpPlayAudioMonitor::OpPlayAudioMonitor(
@@ -508,7 +514,7 @@
     mPresentationCompleteFrames(0),
     mFrameMap(16 /* sink-frame-to-track-frame map memory */),
     mVolumeHandler(new media::VolumeHandler(sampleRate)),
-    mOpPlayAudioMonitor(OpPlayAudioMonitor::createIfNeeded(uid, attr.usage, id(), streamType)),
+    mOpPlayAudioMonitor(OpPlayAudioMonitor::createIfNeeded(uid, attr, id(), streamType)),
     // mSinkTimestamp
     mFastIndex(-1),
     mCachedVolume(1.0),
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 49937f0..30f29d6 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -258,7 +258,7 @@
     virtual status_t getHwOffloadEncodingFormatsSupportedForA2DP(
                 std::vector<audio_format_t> *formats) = 0;
 
-    virtual void     setAppState(uid_t uid, app_state_t state);
+    virtual void     setAppState(uid_t uid, app_state_t state) = 0;
 
     virtual status_t listAudioProductStrategies(AudioProductStrategyVector &strategies) = 0;
 
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
index f2b51d9..12b5e7d 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
@@ -89,6 +89,16 @@
 
     status_t getInputMixForAttr(audio_attributes_t attr, sp<AudioPolicyMix> *policyMix);
 
+    /**
+     * Updates the mix rules in order to make streams associated with the given uid
+     * be routed to the given audio devices.
+     * @param uid the uid for which the device affinity is set
+     * @param devices the vector of devices that this uid may be routed to. A typical
+     *    use is to pass the devices associated with a given zone in a multi-zone setup.
+     * @return NO_ERROR if the update was successful, INVALID_OPERATION otherwise.
+     *    An example of failure is when there are already rules in place to restrict
+     *    a mix to the given uid (i.e. when a MATCH_UID rule was set for it).
+     */
     status_t setUidDeviceAffinities(uid_t uid, const Vector<AudioDeviceTypeAddr>& devices);
     status_t removeUidDeviceAffinities(uid_t uid);
     status_t getDevicesForUid(uid_t uid, Vector<AudioDeviceTypeAddr>& devices) const;
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index 26bb354..98a7800 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -400,13 +400,29 @@
 
 status_t AudioPolicyMixCollection::setUidDeviceAffinities(uid_t uid,
         const Vector<AudioDeviceTypeAddr>& devices) {
+    // verify feasibility: for each player mix: if it already contains a
+    //    "match uid" rule for this uid, return an error
+    //    (adding a uid-device affinity would result in contradictory rules)
+    for (size_t i = 0; i < size(); i++) {
+        const AudioPolicyMix* mix = valueAt(i).get();
+        if (!mix->isDeviceAffinityCompatible()) {
+            continue;
+        }
+        if (mix->hasUidRule(true /*match*/, uid)) {
+            return INVALID_OPERATION;
+        }
+    }
+
     // remove existing rules for this uid
     removeUidDeviceAffinities(uid);
 
-    // for each player mix: add a rule to match or exclude the uid based on the device
+    // for each player mix:
+    //   IF    device is not a target for the mix,
+    //     AND it doesn't have a "match uid" rule
+    //   THEN add a rule to exclude the uid
     for (size_t i = 0; i < size(); i++) {
         const AudioPolicyMix *mix = valueAt(i).get();
-        if (mix->mMixType != MIX_TYPE_PLAYERS) {
+        if (!mix->isDeviceAffinityCompatible()) {
             continue;
         }
         // check if this mix goes to a device in the list of devices
@@ -418,12 +434,14 @@
                 break;
             }
         }
-        if (deviceMatch) {
-            mix->setMatchUid(uid);
-        } else {
+        if (!deviceMatch && !mix->hasMatchUidRule()) {
             // this mix doesn't go to one of the listed devices for the given uid,
+            // and it's not already restricting the mix on a uid,
             // modify its rules to exclude the uid
-            mix->setExcludeUid(uid);
+            if (!mix->hasUidRule(false /*match*/, uid)) {
+                // no need to do it again if uid is already excluded
+                mix->setExcludeUid(uid);
+            }
         }
     }
 
@@ -435,14 +453,15 @@
     for (size_t i = 0; i < size(); i++) {
         bool foundUidRule = false;
         const AudioPolicyMix *mix = valueAt(i).get();
-        if (mix->mMixType != MIX_TYPE_PLAYERS) {
+        if (!mix->isDeviceAffinityCompatible()) {
             continue;
         }
         std::vector<size_t> criteriaToRemove;
         for (size_t j = 0; j < mix->mCriteria.size(); j++) {
             const uint32_t rule = mix->mCriteria[j].mRule;
-            // is this rule affecting the uid?
-            if ((rule == RULE_EXCLUDE_UID || rule == RULE_MATCH_UID)
+            // is this rule excluding the uid? (not considering uid match rules
+            // as those are not used for uid-device affinity)
+            if (rule == RULE_EXCLUDE_UID
                     && uid == mix->mCriteria[j].mValue.mUid) {
                 foundUidRule = true;
                 criteriaToRemove.insert(criteriaToRemove.begin(), j);
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp
index a645e02..69d6b0c 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp
@@ -178,6 +178,7 @@
     const bool isRecordThread = portType == AUDIO_PORT_TYPE_MIX && portRole == AUDIO_PORT_ROLE_SINK;
     const bool isIndex = audio_channel_mask_get_representation(channelMask)
             == AUDIO_CHANNEL_REPRESENTATION_INDEX;
+    const uint32_t channelCount = audio_channel_count_from_in_mask(channelMask);
     int bestMatch = 0;
     for (size_t i = 0; i < mChannelMasks.size(); i ++) {
         audio_channel_mask_t supported = mChannelMasks[i];
@@ -201,11 +202,15 @@
             // OR
             // match score += 100 if the channel mask representations match
             // match score += number of channels matched.
+            // match score += 100 if the channel mask representations DO NOT match
+            //   but the profile has positional channel mask and less than 2 channels.
+            //   This is for audio HAL convention to not list index masks for less than 2 channels
             //
             // If there are no matched channels, the mask may still be accepted
             // but the playback or record will be silent.
             const bool isSupportedIndex = (audio_channel_mask_get_representation(supported)
                     == AUDIO_CHANNEL_REPRESENTATION_INDEX);
+            const uint32_t supportedChannelCount = audio_channel_count_from_in_mask(supported);
             int match;
             if (isIndex && isSupportedIndex) {
                 // index equivalence
@@ -213,13 +218,14 @@
                         audio_channel_mask_get_bits(channelMask)
                             & audio_channel_mask_get_bits(supported));
             } else if (isIndex && !isSupportedIndex) {
-                const uint32_t equivalentBits =
-                        (1 << audio_channel_count_from_in_mask(supported)) - 1 ;
+                const uint32_t equivalentBits = (1 << supportedChannelCount) - 1 ;
                 match = __builtin_popcount(
                         audio_channel_mask_get_bits(channelMask) & equivalentBits);
+                if (supportedChannelCount <= FCC_2) {
+                    match += 100;
+                }
             } else if (!isIndex && isSupportedIndex) {
-                const uint32_t equivalentBits =
-                        (1 << audio_channel_count_from_in_mask(channelMask)) - 1;
+                const uint32_t equivalentBits = (1 << channelCount) - 1;
                 match = __builtin_popcount(
                         equivalentBits & audio_channel_mask_get_bits(supported));
             } else {
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 2dc7cad..c430488 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1019,7 +1019,7 @@
     }
     if (*output == AUDIO_IO_HANDLE_NONE) {
         *output = getOutputForDevices(outputDevices, session, *stream, config,
-                flags, attr->flags & AUDIO_FLAG_MUTE_HAPTIC);
+                flags, resultAttr->flags & AUDIO_FLAG_MUTE_HAPTIC);
     }
     if (*output == AUDIO_IO_HANDLE_NONE) {
         return INVALID_OPERATION;
@@ -1991,16 +1991,25 @@
             strncmp(attributes.tags, "addr=", strlen("addr=")) == 0) {
         status = mPolicyMixes.getInputMixForAttr(attributes, &policyMix);
         if (status != NO_ERROR) {
+            ALOGW("%s could not find input mix for attr %s",
+                    __func__, toString(attributes).c_str());
             goto error;
         }
+        device = mAvailableInputDevices.getDevice(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
+                                                  String8(attr->tags + strlen("addr=")),
+                                                  AUDIO_FORMAT_DEFAULT);
+        if (device == nullptr) {
+            ALOGW("%s could not find in Remote Submix device for source %d, tags %s",
+                    __func__, attributes.source, attributes.tags);
+            status = BAD_VALUE;
+            goto error;
+        }
+
         if (is_mix_loopback_render(policyMix->mRouteFlags)) {
             *inputType = API_INPUT_MIX_PUBLIC_CAPTURE_PLAYBACK;
         } else {
             *inputType = API_INPUT_MIX_EXT_POLICY_REROUTE;
         }
-        device = mAvailableInputDevices.getDevice(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
-                                                  String8(attr->tags + strlen("addr=")),
-                                                  AUDIO_FORMAT_DEFAULT);
     } else {
         if (explicitRoutingDevice != nullptr) {
             device = explicitRoutingDevice;
@@ -2042,7 +2051,7 @@
                 device->getId() : AUDIO_PORT_HANDLE_NONE;
 
     isSoundTrigger = attributes.source == AUDIO_SOURCE_HOTWORD &&
-        mSoundTriggerSessions.indexOfKey(session) > 0;
+        mSoundTriggerSessions.indexOfKey(session) >= 0;
     *portId = AudioPort::getNextUniqueId();
 
     clientDesc = new RecordClientDescriptor(*portId, riid, uid, session, attributes, *config,
@@ -2127,6 +2136,7 @@
         for (size_t i = 0; i < mInputs.size(); ) {
             sp <AudioInputDescriptor> desc = mInputs.valueAt(i);
             if (desc->mProfile != profile) {
+                i++;
                 continue;
             }
             // if sound trigger, reuse input if used by other sound trigger on same session
@@ -2867,14 +2877,12 @@
             rSubmixModule->addInputProfile(address, &inputConfig,
                     AUDIO_DEVICE_IN_REMOTE_SUBMIX, address);
 
-            if (mix.mMixType == MIX_TYPE_PLAYERS) {
-                setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
-                        AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
-                        address.string(), "remote-submix", AUDIO_FORMAT_DEFAULT);
-            } else {
-                setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
-                        AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
-                        address.string(), "remote-submix", AUDIO_FORMAT_DEFAULT);
+            if ((res = setDeviceConnectionStateInt(mix.mDeviceType,
+                    AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+                    address.string(), "remote-submix", AUDIO_FORMAT_DEFAULT)) != NO_ERROR) {
+                ALOGE("Failed to set remote submix device available, type %u, address %s",
+                        mix.mDeviceType, address.string());
+                break;
             }
         } else if ((mix.mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
             String8 address = mix.mDeviceAddress;
@@ -2950,17 +2958,17 @@
                 continue;
             }
 
-            if (getDeviceConnectionState(AUDIO_DEVICE_IN_REMOTE_SUBMIX, address.string()) ==
-                    AUDIO_POLICY_DEVICE_STATE_AVAILABLE)  {
-                setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
-                        AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
-                        address.string(), "remote-submix", AUDIO_FORMAT_DEFAULT);
-            }
-            if (getDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, address.string()) ==
-                    AUDIO_POLICY_DEVICE_STATE_AVAILABLE)  {
-                setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
-                        AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
-                        address.string(), "remote-submix", AUDIO_FORMAT_DEFAULT);
+            for (auto device : {AUDIO_DEVICE_IN_REMOTE_SUBMIX, AUDIO_DEVICE_OUT_REMOTE_SUBMIX}) {
+                if (getDeviceConnectionState(device, address.string()) ==
+                        AUDIO_POLICY_DEVICE_STATE_AVAILABLE)  {
+                    res = setDeviceConnectionStateInt(device, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+                                                      address.string(), "remote-submix",
+                                                      AUDIO_FORMAT_DEFAULT);
+                    if (res != OK) {
+                        ALOGE("Error making RemoteSubmix device unavailable for mix "
+                              "with type %d, address %s", device, address.string());
+                    }
+                }
             }
             rSubmixModule->removeOutputProfile(address);
             rSubmixModule->removeInputProfile(address);
@@ -5685,7 +5693,16 @@
         const float maxVoiceVolDb =
                 computeVolume(voiceCurves, callVolumeSrc, voiceVolumeIndex, device)
                 + IN_CALL_EARPIECE_HEADROOM_DB;
-        if (volumeDb > maxVoiceVolDb) {
+        // FIXME: Workaround for call screening applications until a proper audio mode is defined
+        // to support this scenario : Exempt the RING stream from the audio cap if the audio was
+        // programmatically muted.
+        // VOICE_CALL stream has minVolumeIndex > 0 : Users cannot set the volume of voice calls to
+        // 0. We don't want to cap volume when the system has programmatically muted the voice call
+        // stream. See setVolumeCurveIndex() for more information.
+        bool exemptFromCapping = (volumeSource == ringVolumeSrc) && (voiceVolumeIndex == 0);
+        ALOGV_IF(exemptFromCapping, "%s volume source %d at vol=%f not capped", __func__,
+                 volumeSource, volumeDb);
+        if ((volumeDb > maxVoiceVolDb) && !exemptFromCapping) {
             ALOGV("%s volume source %d at vol=%f overriden by volume group %d at vol=%f", __func__,
                   volumeSource, volumeDb, callVolumeSrc, maxVoiceVolDb);
             volumeDb = maxVoiceVolDb;
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 06e68a9..b0a8541 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -166,7 +166,7 @@
     return mAudioPolicyManager->getOutput(stream);
 }
 
-status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *originalAttr,
+status_t AudioPolicyService::getOutputForAttr(audio_attributes_t *attr,
                                               audio_io_handle_t *output,
                                               audio_session_t session,
                                               audio_stream_type_t *stream,
@@ -190,13 +190,15 @@
                 "%s uid %d tried to pass itself off as %d", __FUNCTION__, callingUid, uid);
         uid = callingUid;
     }
-    audio_attributes_t attr = *originalAttr;
     if (!mPackageManager.allowPlaybackCapture(uid)) {
-        attr.flags |= AUDIO_FLAG_NO_MEDIA_PROJECTION;
+        attr->flags |= AUDIO_FLAG_NO_MEDIA_PROJECTION;
+    }
+    if (!bypassInterruptionPolicyAllowed(pid, uid)) {
+        attr->flags &= ~(AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY|AUDIO_FLAG_BYPASS_MUTE);
     }
     audio_output_flags_t originalFlags = flags;
     AutoCallerClear acc;
-    status_t result = mAudioPolicyManager->getOutputForAttr(&attr, output, session, stream, uid,
+    status_t result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid,
                                                  config,
                                                  &flags, selectedDeviceId, portId,
                                                  secondaryOutputs);
@@ -212,14 +214,14 @@
         *selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
         *portId = AUDIO_PORT_HANDLE_NONE;
         secondaryOutputs->clear();
-        result = mAudioPolicyManager->getOutputForAttr(&attr, output, session, stream, uid, config,
+        result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid, config,
                                                        &flags, selectedDeviceId, portId,
                                                        secondaryOutputs);
     }
 
     if (result == NO_ERROR) {
         sp <AudioPlaybackClient> client =
-            new AudioPlaybackClient(attr, *output, uid, pid, session, *selectedDeviceId, *stream);
+            new AudioPlaybackClient(*attr, *output, uid, pid, session, *selectedDeviceId, *stream);
         mAudioPlaybackClients.add(*portId, client);
     }
     return result;
@@ -511,6 +513,7 @@
     }
 
     // including successes gets very verbose
+    // but once we cut over to westworld, log them all.
     if (status != NO_ERROR) {
 
         static constexpr char kAudioPolicy[] = "audiopolicy";
@@ -571,6 +574,9 @@
             delete item;
             item = NULL;
         }
+    }
+
+    if (status != NO_ERROR) {
         client->active = false;
         client->startTimeNs = 0;
         updateUidStates_l();
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 5389c08..e3c72ae 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -411,19 +411,19 @@
 //    OR The client is the assistant
 //        AND an accessibility service is on TOP
 //               AND the source is VOICE_RECOGNITION or HOTWORD
-//        OR uses VOICE_RECOGNITION AND is on TOP OR latest started
+//        OR uses VOICE_RECOGNITION AND is on TOP
 //               OR uses HOTWORD
 //            AND there is no active privacy sensitive capture or call
 //                OR client has CAPTURE_AUDIO_OUTPUT privileged permission
 //    OR The client is an accessibility service
-//        AND is on TOP OR latest started
+//        AND is on TOP
 //        AND the source is VOICE_RECOGNITION or HOTWORD
 //    OR the client source is virtual (remote submix, call audio TX or RX...)
-//    OR Any other client
+//    OR Any client
 //        AND The assistant is not on TOP
+//        AND is on TOP or latest started
 //        AND there is no active privacy sensitive capture or call
 //                OR client has CAPTURE_AUDIO_OUTPUT privileged permission
-//TODO: mamanage pre processing effects according to use case priority
 
     sp<AudioRecordClient> topActive;
     sp<AudioRecordClient> latestActive;
@@ -505,9 +505,11 @@
 
         // By default allow capture if:
         //     The assistant is not on TOP
+        //     AND is on TOP or latest started
         //     AND there is no active privacy sensitive capture or call
         //             OR client has CAPTURE_AUDIO_OUTPUT privileged permission
         bool allowCapture = !isAssistantOnTop
+                && ((isTopOrLatestActive && !isLatestSensitive) || isLatestSensitive)
                 && !(isSensitiveActive && !(isLatestSensitive || current->canCaptureOutput))
                 && !(isInCall && !current->canCaptureOutput);
 
@@ -518,7 +520,7 @@
             // For assistant allow capture if:
             //     An accessibility service is on TOP
             //            AND the source is VOICE_RECOGNITION or HOTWORD
-            //     OR is on TOP OR latest started AND uses VOICE_RECOGNITION
+            //     OR is on TOP AND uses VOICE_RECOGNITION
             //            OR uses HOTWORD
             //         AND there is no active privacy sensitive capture or call
             //             OR client has CAPTURE_AUDIO_OUTPUT privileged permission
@@ -527,7 +529,7 @@
                     allowCapture = true;
                 }
             } else {
-                if (((isTopOrLatestActive && source == AUDIO_SOURCE_VOICE_RECOGNITION) ||
+                if (((isAssistantOnTop && source == AUDIO_SOURCE_VOICE_RECOGNITION) ||
                         source == AUDIO_SOURCE_HOTWORD) &&
                         (!(isSensitiveActive || isInCall) || current->canCaptureOutput)) {
                     allowCapture = true;
@@ -535,9 +537,9 @@
             }
         } else if (mUidPolicy->isA11yUid(current->uid)) {
             // For accessibility service allow capture if:
-            //     Is on TOP OR latest started
+            //     Is on TOP
             //     AND the source is VOICE_RECOGNITION or HOTWORD
-            if (isTopOrLatestActive &&
+            if (isA11yOnTop &&
                     (source == AUDIO_SOURCE_VOICE_RECOGNITION || source == AUDIO_SOURCE_HOTWORD)) {
                 allowCapture = true;
             }
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 58256f7..8db63a5 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -75,7 +75,7 @@
     virtual status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config);
     virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage);
     virtual audio_io_handle_t getOutput(audio_stream_type_t stream);
-    status_t getOutputForAttr(const audio_attributes_t *attr,
+    status_t getOutputForAttr(audio_attributes_t *attr,
                               audio_io_handle_t *output,
                               audio_session_t session,
                               audio_stream_type_t *stream,
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index fc6d6be..a87ebdf 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -117,6 +117,11 @@
 
 static const String16 sManageCameraPermission("android.permission.MANAGE_CAMERA");
 
+// Matches with PERCEPTIBLE_APP_ADJ in ProcessList.java
+static constexpr int32_t kVendorClientScore = 200;
+// Matches with PROCESS_STATE_PERSISTENT_UI in ActivityManager.java
+static constexpr int32_t kVendorClientState = 1;
+
 Mutex CameraService::sProxyMutex;
 sp<hardware::ICameraServiceProxy> CameraService::sCameraServiceProxy;
 
@@ -1120,7 +1125,8 @@
         std::map<int,resource_policy::ClientPriority> pidToPriorityMap;
         for (size_t i = 0; i < ownerPids.size() - 1; i++) {
             pidToPriorityMap.emplace(ownerPids[i],
-                    resource_policy::ClientPriority(priorityScores[i], states[i]));
+                    resource_policy::ClientPriority(priorityScores[i], states[i],
+                            /* isVendorClient won't get copied over*/ false));
         }
         mActiveClientManager.updatePriorities(pidToPriorityMap);
 
@@ -2980,8 +2986,12 @@
         const std::set<String8>& conflictingKeys, int32_t score, int32_t ownerId,
         int32_t state) {
 
+    bool isVendorClient = hardware::IPCThreadState::self()->isServingCall();
+    int32_t score_adj = isVendorClient ? kVendorClientScore : score;
+    int32_t state_adj = isVendorClient ? kVendorClientState: state;
+
     return std::make_shared<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>(
-            key, value, cost, conflictingKeys, score, ownerId, state);
+            key, value, cost, conflictingKeys, score_adj, ownerId, state_adj, isVendorClient);
 }
 
 CameraService::DescriptorPtr CameraService::CameraClientManager::makeClientDescriptor(
diff --git a/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp b/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp
index 6d96163..fc79150 100644
--- a/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp
+++ b/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp
@@ -64,6 +64,10 @@
 namespace android {
 namespace camera3 {
 
+// Depth samples with low confidence can skew the
+// near/far values and impact the range inverse coding.
+static const float CONFIDENCE_THRESHOLD = .15f;
+
 ExifOrientation getExifOrientation(const unsigned char *jpegBuffer, size_t jpegBufferSize) {
     if ((jpegBuffer == nullptr) || (jpegBufferSize == 0)) {
         return ExifOrientation::ORIENTATION_UNDEFINED;
@@ -238,6 +242,9 @@
     auto conf = (value >> 13) & 0x7;
     float normConfidence = (conf == 0) ? 1.f : (static_cast<float>(conf) - 1) / 7.f;
     confidence->push_back(normConfidence);
+    if (normConfidence < CONFIDENCE_THRESHOLD) {
+        return;
+    }
 
     if (*near > point) {
         *near = point;
@@ -358,8 +365,12 @@
     auto pointIt = points.begin();
     auto confidenceIt = confidence.begin();
     while ((pointIt != points.end()) && (confidenceIt != confidence.end())) {
-        pointsQuantized.push_back(floorf(((far * (*pointIt - near)) /
-                (*pointIt * (far - near))) * 255.0f));
+        auto point = *pointIt;
+        if ((*confidenceIt) < CONFIDENCE_THRESHOLD) {
+            point = std::clamp(point, near, far);
+        }
+        pointsQuantized.push_back(floorf(((far * (point - near)) /
+                (point * (far - near))) * 255.0f));
         confidenceQuantized.push_back(floorf(*confidenceIt * 255.0f));
         confidenceIt++; pointIt++;
     }
diff --git a/services/camera/libcameraservice/common/FrameProcessorBase.cpp b/services/camera/libcameraservice/common/FrameProcessorBase.cpp
index 41c953b..3d56cd2 100644
--- a/services/camera/libcameraservice/common/FrameProcessorBase.cpp
+++ b/services/camera/libcameraservice/common/FrameProcessorBase.cpp
@@ -89,12 +89,27 @@
     write(fd, result.string(), result.size());
 
     CameraMetadata lastFrame;
+    std::map<std::string, CameraMetadata> lastPhysicalFrames;
     {
         // Don't race while dumping metadata
         Mutex::Autolock al(mLastFrameMutex);
         lastFrame = CameraMetadata(mLastFrame);
+
+        for (const auto& physicalFrame : mLastPhysicalFrames) {
+            lastPhysicalFrames.emplace(String8(physicalFrame.mPhysicalCameraId),
+                    physicalFrame.mPhysicalCameraMetadata);
+        }
     }
-    lastFrame.dump(fd, 2, 6);
+    lastFrame.dump(fd, /*verbosity*/2, /*indentation*/6);
+
+    for (const auto& physicalFrame : lastPhysicalFrames) {
+        result = String8::format("   Latest received frame for physical camera %s:\n",
+                physicalFrame.first.c_str());
+        write(fd, result.string(), result.size());
+        CameraMetadata lastPhysicalMetadata = CameraMetadata(physicalFrame.second);
+        lastPhysicalMetadata.sort();
+        lastPhysicalMetadata.dump(fd, /*verbosity*/2, /*indentation*/6);
+    }
 }
 
 bool FrameProcessorBase::threadLoop() {
@@ -145,6 +160,8 @@
         if (!result.mMetadata.isEmpty()) {
             Mutex::Autolock al(mLastFrameMutex);
             mLastFrame.acquire(result.mMetadata);
+
+            mLastPhysicalFrames = std::move(result.mPhysicalMetadatas);
         }
     }
     if (res != NOT_ENOUGH_DATA) {
diff --git a/services/camera/libcameraservice/common/FrameProcessorBase.h b/services/camera/libcameraservice/common/FrameProcessorBase.h
index 00763a4..ae6d15d 100644
--- a/services/camera/libcameraservice/common/FrameProcessorBase.h
+++ b/services/camera/libcameraservice/common/FrameProcessorBase.h
@@ -83,6 +83,8 @@
                               const sp<CameraDeviceBase> &device);
 
     CameraMetadata mLastFrame;
+    std::vector<PhysicalCaptureResultInfo> mLastPhysicalFrames;
+
 };
 
 
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 6f9f039..063fa08 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -1308,7 +1308,7 @@
 void Camera3Device::processOneCaptureResultLocked(
         const hardware::camera::device::V3_2::CaptureResult& result,
         const hardware::hidl_vec<
-                hardware::camera::device::V3_4::PhysicalCameraMetadata> physicalCameraMetadatas) {
+                hardware::camera::device::V3_4::PhysicalCameraMetadata> physicalCameraMetadata) {
     camera3_capture_result r;
     status_t res;
     r.frame_number = result.frameNumber;
@@ -1324,21 +1324,21 @@
     r.result = reinterpret_cast<const camera_metadata_t*>(resultMetadata.data());
 
     // Read and validate physical camera metadata
-    size_t physResultCount = physicalCameraMetadatas.size();
+    size_t physResultCount = physicalCameraMetadata.size();
     std::vector<const char*> physCamIds(physResultCount);
     std::vector<const camera_metadata_t *> phyCamMetadatas(physResultCount);
     std::vector<hardware::camera::device::V3_2::CameraMetadata> physResultMetadata;
     physResultMetadata.resize(physResultCount);
-    for (size_t i = 0; i < physicalCameraMetadatas.size(); i++) {
-        res = readOneCameraMetadataLocked(physicalCameraMetadatas[i].fmqMetadataSize,
-                physResultMetadata[i], physicalCameraMetadatas[i].metadata);
+    for (size_t i = 0; i < physicalCameraMetadata.size(); i++) {
+        res = readOneCameraMetadataLocked(physicalCameraMetadata[i].fmqMetadataSize,
+                physResultMetadata[i], physicalCameraMetadata[i].metadata);
         if (res != OK) {
             ALOGE("%s: Frame %d: Failed to read capture result metadata for camera %s",
                     __FUNCTION__, result.frameNumber,
-                    physicalCameraMetadatas[i].physicalCameraId.c_str());
+                    physicalCameraMetadata[i].physicalCameraId.c_str());
             return;
         }
-        physCamIds[i] = physicalCameraMetadatas[i].physicalCameraId.c_str();
+        physCamIds[i] = physicalCameraMetadata[i].physicalCameraId.c_str();
         phyCamMetadatas[i] = reinterpret_cast<const camera_metadata_t*>(
                 physResultMetadata[i].data());
     }
@@ -2524,6 +2524,9 @@
         CLOGE("Stream %d is unknown", streamId);
         return BAD_VALUE;
     }
+
+    // isConsumerConfigurationDeferred will be off after setConsumers
+    bool isDeferred = stream->isConsumerConfigurationDeferred();
     status_t res = stream->setConsumers(consumers);
     if (res != OK) {
         CLOGE("Stream %d set consumer failed (error %d %s) ", streamId, res, strerror(-res));
@@ -2539,7 +2542,7 @@
         surfaceIds->push_back(id);
     }
 
-    if (stream->isConsumerConfigurationDeferred()) {
+    if (isDeferred) {
         if (!stream->isConfiguring()) {
             CLOGE("Stream %d was already fully configured.", streamId);
             return INVALID_OPERATION;
@@ -2614,7 +2617,6 @@
 sp<Camera3Device::CaptureRequest> Camera3Device::createCaptureRequest(
         const PhysicalCameraSettingsList &request, const SurfaceMap &surfaceMap) {
     ATRACE_CALL();
-    status_t res;
 
     sp<CaptureRequest> newRequest = new CaptureRequest;
     newRequest->mSettingsList = request;
@@ -2628,16 +2630,11 @@
                     inputStreams.data.u8[0]);
             return NULL;
         }
-        // Lazy completion of stream configuration (allocation/registration)
-        // on first use
+
         if (mInputStream->isConfiguring()) {
-            res = mInputStream->finishConfiguration();
-            if (res != OK) {
-                SET_ERR_L("Unable to finish configuring input stream %d:"
-                        " %s (%d)",
-                        mInputStream->getId(), strerror(-res), res);
-                return NULL;
-            }
+            SET_ERR_L("%s: input stream %d is not configured!",
+                    __FUNCTION__, mInputStream->getId());
+            return NULL;
         }
         // Check if stream prepare is blocking requests.
         if (mInputStream->isBlockedByPrepare()) {
@@ -2677,15 +2674,9 @@
             newRequest->mOutputSurfaces[streams.data.i32[i]] = surfaces;
         }
 
-        // Lazy completion of stream configuration (allocation/registration)
-        // on first use
         if (stream->isConfiguring()) {
-            res = stream->finishConfiguration();
-            if (res != OK) {
-                SET_ERR_L("Unable to finish configuring stream %d: %s (%d)",
-                        stream->getId(), strerror(-res), res);
-                return NULL;
-            }
+            SET_ERR_L("%s: stream %d is not configured!", __FUNCTION__, stream->getId());
+            return NULL;
         }
         // Check if stream prepare is blocking requests.
         if (stream->isBlockedByPrepare()) {
@@ -2910,7 +2901,8 @@
     // faster
 
     if (mInputStream != NULL && mInputStream->isConfiguring()) {
-        res = mInputStream->finishConfiguration();
+        bool streamReConfigured = false;
+        res = mInputStream->finishConfiguration(&streamReConfigured);
         if (res != OK) {
             CLOGE("Can't finish configuring input stream %d: %s (%d)",
                     mInputStream->getId(), strerror(-res), res);
@@ -2920,12 +2912,16 @@
             }
             return BAD_VALUE;
         }
+        if (streamReConfigured) {
+            mInterface->onStreamReConfigured(mInputStream->getId());
+        }
     }
 
     for (size_t i = 0; i < mOutputStreams.size(); i++) {
         sp<Camera3OutputStreamInterface> outputStream = mOutputStreams[i];
         if (outputStream->isConfiguring() && !outputStream->isConsumerConfigurationDeferred()) {
-            res = outputStream->finishConfiguration();
+            bool streamReConfigured = false;
+            res = outputStream->finishConfiguration(&streamReConfigured);
             if (res != OK) {
                 CLOGE("Can't finish configuring output stream %d: %s (%d)",
                         outputStream->getId(), strerror(-res), res);
@@ -2935,6 +2931,9 @@
                 }
                 return BAD_VALUE;
             }
+            if (streamReConfigured) {
+                mInterface->onStreamReConfigured(outputStream->getId());
+            }
         }
     }
 
@@ -3422,6 +3421,14 @@
         return;
     }
 
+    // Update vendor tag id for physical metadata
+    for (auto& physicalMetadata : result->mPhysicalMetadatas) {
+        camera_metadata_t *pmeta = const_cast<camera_metadata_t *>(
+                physicalMetadata.mPhysicalCameraMetadata.getAndLock());
+        set_camera_metadata_vendor_id(pmeta, mVendorTagId);
+        physicalMetadata.mPhysicalCameraMetadata.unlock(pmeta);
+    }
+
     // Valid result, insert into queue
     List<CaptureResult>::iterator queuedResult =
             mResultQueue.insert(mResultQueue.end(), CaptureResult(*result));
@@ -3561,8 +3568,14 @@
         }
     }
 
+    std::unordered_map<std::string, CameraMetadata> monitoredPhysicalMetadata;
+    for (auto& m : physicalMetadatas) {
+        monitoredPhysicalMetadata.emplace(String8(m.mPhysicalCameraId).string(),
+                CameraMetadata(m.mPhysicalCameraMetadata));
+    }
     mTagMonitor.monitorMetadata(TagMonitor::RESULT,
-            frameNumber, timestamp.data.i64[0], captureResult.mMetadata);
+            frameNumber, timestamp.data.i64[0], captureResult.mMetadata,
+            monitoredPhysicalMetadata);
 
     insertResultLocked(&captureResult, frameNumber);
 }
@@ -3986,8 +3999,11 @@
 
 
 void Camera3Device::monitorMetadata(TagMonitor::eventSource source,
-        int64_t frameNumber, nsecs_t timestamp, const CameraMetadata& metadata) {
-    mTagMonitor.monitorMetadata(source, frameNumber, timestamp, metadata);
+        int64_t frameNumber, nsecs_t timestamp, const CameraMetadata& metadata,
+        const std::unordered_map<std::string, CameraMetadata>& physicalMetadata) {
+
+    mTagMonitor.monitorMetadata(source, frameNumber, timestamp, metadata,
+            physicalMetadata);
 }
 
 /**
@@ -4800,7 +4816,7 @@
                 __FUNCTION__, handle, streamId);
         return;
     } else {
-        bufferId =  it->second;
+        bufferId = it->second;
         bIdMap.erase(it);
         ALOGV("%s: stream %d now have %zu buffer caches after removing buf %p",
                 __FUNCTION__, streamId, bIdMap.size(), handle);
@@ -4808,6 +4824,22 @@
     mFreedBuffers.push_back(std::make_pair(streamId, bufferId));
 }
 
+void Camera3Device::HalInterface::onStreamReConfigured(int streamId) {
+    std::lock_guard<std::mutex> lock(mBufferIdMapLock);
+    auto mapIt = mBufferIdMaps.find(streamId);
+    if (mapIt == mBufferIdMaps.end()) {
+        ALOGE("%s: streamId %d not found!", __FUNCTION__, streamId);
+        return;
+    }
+
+    BufferIdMap& bIdMap = mapIt->second;
+    for (const auto& it : bIdMap) {
+        uint64_t bufferId = it.second;
+        mFreedBuffers.push_back(std::make_pair(streamId, bufferId));
+    }
+    bIdMap.clear();
+}
+
 /**
  * RequestThread inner class methods
  */
@@ -5127,28 +5159,7 @@
         NextRequest& nextRequest = mNextRequests.editItemAt(i);
         nextRequest.submitted = true;
 
-
-        // Update the latest request sent to HAL
-        if (nextRequest.halRequest.settings != NULL) { // Don't update if they were unchanged
-            Mutex::Autolock al(mLatestRequestMutex);
-
-            camera_metadata_t* cloned = clone_camera_metadata(nextRequest.halRequest.settings);
-            mLatestRequest.acquire(cloned);
-
-            sp<Camera3Device> parent = mParent.promote();
-            if (parent != NULL) {
-                parent->monitorMetadata(TagMonitor::REQUEST,
-                        nextRequest.halRequest.frame_number,
-                        0, mLatestRequest);
-            }
-        }
-
-        if (nextRequest.halRequest.settings != NULL) {
-            nextRequest.captureRequest->mSettingsList.begin()->metadata.unlock(
-                    nextRequest.halRequest.settings);
-        }
-
-        cleanupPhysicalSettings(nextRequest.captureRequest, &nextRequest.halRequest);
+        updateNextRequest(nextRequest);
 
         if (!triggerRemoveFailed) {
             // Remove any previously queued triggers (after unlock)
@@ -5203,26 +5214,7 @@
         // Mark that the request has be submitted successfully.
         nextRequest.submitted = true;
 
-        // Update the latest request sent to HAL
-        if (nextRequest.halRequest.settings != NULL) { // Don't update if they were unchanged
-            Mutex::Autolock al(mLatestRequestMutex);
-
-            camera_metadata_t* cloned = clone_camera_metadata(nextRequest.halRequest.settings);
-            mLatestRequest.acquire(cloned);
-
-            sp<Camera3Device> parent = mParent.promote();
-            if (parent != NULL) {
-                parent->monitorMetadata(TagMonitor::REQUEST, nextRequest.halRequest.frame_number,
-                        0, mLatestRequest);
-            }
-        }
-
-        if (nextRequest.halRequest.settings != NULL) {
-            nextRequest.captureRequest->mSettingsList.begin()->metadata.unlock(
-                    nextRequest.halRequest.settings);
-        }
-
-        cleanupPhysicalSettings(nextRequest.captureRequest, &nextRequest.halRequest);
+        updateNextRequest(nextRequest);
 
         // Remove any previously queued triggers (after unlock)
         res = removeTriggers(mPrevRequest);
@@ -5284,6 +5276,37 @@
     return false;
 }
 
+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
+        Mutex::Autolock al(mLatestRequestMutex);
+
+        camera_metadata_t* cloned = clone_camera_metadata(nextRequest.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],
+                    CameraMetadata(cloned));
+        }
+
+        sp<Camera3Device> parent = mParent.promote();
+        if (parent != NULL) {
+            parent->monitorMetadata(TagMonitor::REQUEST,
+                    nextRequest.halRequest.frame_number,
+                    0, mLatestRequest, mLatestPhysicalRequest);
+        }
+    }
+
+    if (nextRequest.halRequest.settings != NULL) {
+        nextRequest.captureRequest->mSettingsList.begin()->metadata.unlock(
+                nextRequest.halRequest.settings);
+    }
+
+    cleanupPhysicalSettings(nextRequest.captureRequest, &nextRequest.halRequest);
+}
+
 bool Camera3Device::RequestThread::updateSessionParameters(const CameraMetadata& settings) {
     ATRACE_CALL();
     bool updatesDetected = false;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index f32a90b..de5323e 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -336,6 +336,8 @@
         // Get a vector of bufferId of currently inflight buffers
         void getInflightRequestBufferKeys(std::vector<uint64_t>* out);
 
+        void onStreamReConfigured(int streamId);
+
         static const uint64_t BUFFER_ID_NO_BUFFER = 0;
       private:
         // Always valid
@@ -554,7 +556,7 @@
     void processOneCaptureResultLocked(
             const hardware::camera::device::V3_2::CaptureResult& result,
             const hardware::hidl_vec<
-            hardware::camera::device::V3_4::PhysicalCameraMetadata> physicalCameraMetadatas);
+            hardware::camera::device::V3_4::PhysicalCameraMetadata> physicalCameraMetadata);
     status_t readOneCameraMetadataLocked(uint64_t fmqResultSize,
             hardware::camera::device::V3_2::CameraMetadata& resultMetadata,
             const hardware::camera::device::V3_2::CameraMetadata& result);
@@ -910,8 +912,8 @@
         bool skipHFRTargetFPSUpdate(int32_t tag, const camera_metadata_ro_entry_t& newEntry,
                 const camera_metadata_entry_t& currentEntry);
 
-        // Re-configure camera using the latest session parameters.
-        bool reconfigureCamera();
+        // Update next request sent to HAL
+        void updateNextRequest(NextRequest& nextRequest);
 
         wp<Camera3Device>  mParent;
         wp<camera3::StatusTracker>  mStatusTracker;
@@ -955,6 +957,7 @@
         // android.request.id for latest process_capture_request
         int32_t            mLatestRequestId;
         CameraMetadata     mLatestRequest;
+        std::unordered_map<std::string, CameraMetadata> mLatestPhysicalRequest;
 
         typedef KeyedVector<uint32_t/*tag*/, RequestTrigger> TriggerMap;
         Mutex              mTriggerMutex;
@@ -1268,7 +1271,8 @@
     TagMonitor mTagMonitor;
 
     void monitorMetadata(TagMonitor::eventSource source, int64_t frameNumber,
-            nsecs_t timestamp, const CameraMetadata& metadata);
+            nsecs_t timestamp, const CameraMetadata& metadata,
+            const std::unordered_map<std::string, CameraMetadata>& physicalMetadata);
 
     metadata_vendor_id_t mVendorTagId;
 
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index 2e909a0..ef0d919 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -82,6 +82,9 @@
     lines.appendFormat("      Max size: %zu\n", mMaxSize);
     lines.appendFormat("      Combined usage: %" PRIu64 ", max HAL buffers: %d\n",
             mUsage | consumerUsage, camera3_stream::max_buffers);
+    if (strlen(camera3_stream::physical_camera_id) > 0) {
+        lines.appendFormat("      Physical camera id: %s\n", camera3_stream::physical_camera_id);
+    }
     lines.appendFormat("      Frames produced: %d, last timestamp: %" PRId64 " ns\n",
             mFrameCount, mLastTimestamp);
     lines.appendFormat("      Total buffers: %zu, currently dequeued: %zu\n",
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 12ff130..d73a2f9 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -287,8 +287,11 @@
     return (mState == STATE_IN_CONFIG) || (mState == STATE_IN_RECONFIG);
 }
 
-status_t Camera3Stream::finishConfiguration() {
+status_t Camera3Stream::finishConfiguration(/*out*/bool* streamReconfigured) {
     ATRACE_CALL();
+    if (streamReconfigured != nullptr) {
+        *streamReconfigured = false;
+    }
     Mutex::Autolock l(mLock);
     switch (mState) {
         case STATE_ERROR:
@@ -313,7 +316,7 @@
 
     // Register for idle tracking
     sp<StatusTracker> statusTracker = mStatusTracker.promote();
-    if (statusTracker != 0) {
+    if (statusTracker != 0 && mStatusId == StatusTracker::NO_STATUS_ID) {
         mStatusId = statusTracker->addComponent();
     }
 
@@ -332,6 +335,7 @@
     mPrepareBlockRequest = true;
     mStreamUnpreparable = false;
 
+    bool reconfiguring = (mState == STATE_IN_RECONFIG);
     status_t res;
     res = configureQueueLocked();
     // configureQueueLocked could return error in case of abandoned surface.
@@ -348,6 +352,9 @@
         return res;
     }
 
+    if (reconfiguring && streamReconfigured != nullptr) {
+        *streamReconfigured = true;
+    }
     mState = STATE_CONFIGURED;
 
     return res;
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 3d21029..c916fe8 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -197,6 +197,8 @@
      * after this call, but can still be read until the destruction of the
      * stream.
      *
+     * streamReconfigured: set to true when a stream is being reconfigured.
+     *
      * Returns:
      *   OK on a successful configuration
      *   NO_INIT in case of a serious error from the HAL device
@@ -204,7 +206,7 @@
      *   INVALID_OPERATION in case connecting to the consumer failed or consumer
      *       doesn't exist yet.
      */
-    status_t         finishConfiguration();
+    status_t         finishConfiguration(/*out*/bool* streamReconfigured = nullptr);
 
     /**
      * Cancels the stream configuration process. This returns the stream to the
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index 5cd11b7..73f501a 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -130,13 +130,15 @@
      * modified after this call, but can still be read until the destruction of
      * the stream.
      *
+     * streamReconfigured: set to true when a stream is being reconfigured.
+     *
      * Returns:
      *   OK on a successful configuration
      *   NO_INIT in case of a serious error from the HAL device
      *   NO_MEMORY in case of an error registering buffers
      *   INVALID_OPERATION in case connecting to the consumer failed
      */
-    virtual status_t finishConfiguration() = 0;
+    virtual status_t finishConfiguration(/*out*/bool* streamReconfigured = nullptr) = 0;
 
     /**
      * Cancels the stream configuration process. This returns the stream to the
diff --git a/services/camera/libcameraservice/utils/ClientManager.h b/services/camera/libcameraservice/utils/ClientManager.h
index d7135f1..ec6f01c 100644
--- a/services/camera/libcameraservice/utils/ClientManager.h
+++ b/services/camera/libcameraservice/utils/ClientManager.h
@@ -33,12 +33,38 @@
 
 class ClientPriority {
 public:
-    ClientPriority(int32_t score, int32_t state) :
-        mScore(score), mState(state) {}
+    /**
+     * Choosing to set mIsVendorClient through a parameter instead of calling
+     * hardware::IPCThreadState::self()->isServingCall() to protect against the
+     * case where the construction is offloaded to another thread which isn't a
+     * hwbinder thread.
+     */
+    ClientPriority(int32_t score, int32_t state, bool isVendorClient) :
+            mScore(score), mState(state), mIsVendorClient(isVendorClient) { }
 
     int32_t getScore() const { return mScore; }
     int32_t getState() const { return mState; }
 
+    void setScore(int32_t score) {
+        // For vendor clients, the score is set once and for all during
+        // construction. Otherwise, it can get reset each time cameraserver
+        // queries ActivityManagerService for oom_adj scores / states .
+        if (!mIsVendorClient) {
+            mScore = score;
+        }
+    }
+
+    void setState(int32_t state) {
+      // For vendor clients, the score is set once and for all during
+      // construction. Otherwise, it can get reset each time cameraserver
+      // queries ActivityManagerService for oom_adj scores / states
+      // (ActivityManagerService returns a vendor process' state as
+      // PROCESS_STATE_NONEXISTENT.
+      if (!mIsVendorClient) {
+          mState = state;
+      }
+    }
+
     bool operator==(const ClientPriority& rhs) const {
         return (this->mScore == rhs.mScore) && (this->mState == rhs.mState);
     }
@@ -66,6 +92,7 @@
 private:
         int32_t mScore;
         int32_t mState;
+        bool mIsVendorClient = false;
 };
 
 // --------------------------------------------------------------------------------
@@ -82,9 +109,10 @@
 class ClientDescriptor final {
 public:
     ClientDescriptor(const KEY& key, const VALUE& value, int32_t cost,
-            const std::set<KEY>& conflictingKeys, int32_t score, int32_t ownerId, int32_t state);
+            const std::set<KEY>& conflictingKeys, int32_t score, int32_t ownerId, int32_t state,
+            bool isVendorClient);
     ClientDescriptor(KEY&& key, VALUE&& value, int32_t cost, std::set<KEY>&& conflictingKeys,
-            int32_t score, int32_t ownerId, int32_t state);
+            int32_t score, int32_t ownerId, int32_t state, bool isVendorClient);
 
     ~ClientDescriptor();
 
@@ -148,17 +176,19 @@
 
 template<class KEY, class VALUE>
 ClientDescriptor<KEY, VALUE>::ClientDescriptor(const KEY& key, const VALUE& value, int32_t cost,
-        const std::set<KEY>& conflictingKeys, int32_t score, int32_t ownerId, int32_t state) :
+        const std::set<KEY>& conflictingKeys, int32_t score, int32_t ownerId, int32_t state,
+        bool isVendorClient) :
         mKey{key}, mValue{value}, mCost{cost}, mConflicting{conflictingKeys},
-        mPriority(score, state),
+        mPriority(score, state, isVendorClient),
         mOwnerId{ownerId} {}
 
 template<class KEY, class VALUE>
 ClientDescriptor<KEY, VALUE>::ClientDescriptor(KEY&& key, VALUE&& value, int32_t cost,
-        std::set<KEY>&& conflictingKeys, int32_t score, int32_t ownerId, int32_t state) :
+        std::set<KEY>&& conflictingKeys, int32_t score, int32_t ownerId, int32_t state,
+        bool isVendorClient) :
         mKey{std::forward<KEY>(key)}, mValue{std::forward<VALUE>(value)}, mCost{cost},
         mConflicting{std::forward<std::set<KEY>>(conflictingKeys)},
-        mPriority(score, state), mOwnerId{ownerId} {}
+        mPriority(score, state, isVendorClient), mOwnerId{ownerId} {}
 
 template<class KEY, class VALUE>
 ClientDescriptor<KEY, VALUE>::~ClientDescriptor() {}
@@ -204,7 +234,13 @@
 
 template<class KEY, class VALUE>
 void ClientDescriptor<KEY, VALUE>::setPriority(const ClientPriority& priority) {
-    mPriority = priority;
+    // We don't use the usual copy constructor here since we want to remember
+    // whether a client is a vendor client or not. This could have been wiped
+    // off in the incoming priority argument since an AIDL thread might have
+    // called hardware::IPCThreadState::self()->isServingCall() after refreshing
+    // priorities for old clients through ProcessInfoService::getProcessStatesScoresFromPids().
+    mPriority.setScore(priority.getScore());
+    mPriority.setState(priority.getState());
 }
 
 // --------------------------------------------------------------------------------
diff --git a/services/camera/libcameraservice/utils/TagMonitor.cpp b/services/camera/libcameraservice/utils/TagMonitor.cpp
index f4c49ec..4037a66 100644
--- a/services/camera/libcameraservice/utils/TagMonitor.cpp
+++ b/services/camera/libcameraservice/utils/TagMonitor.cpp
@@ -100,10 +100,13 @@
     mMonitoringEnabled = false;
     mLastMonitoredRequestValues.clear();
     mLastMonitoredResultValues.clear();
+    mLastMonitoredPhysicalRequestKeys.clear();
+    mLastMonitoredPhysicalResultKeys.clear();
 }
 
 void TagMonitor::monitorMetadata(eventSource source, int64_t frameNumber, nsecs_t timestamp,
-        const CameraMetadata& metadata) {
+        const CameraMetadata& metadata,
+        const std::unordered_map<std::string, CameraMetadata>& physicalMetadata) {
     if (!mMonitoringEnabled) return;
 
     std::lock_guard<std::mutex> lock(mMonitorMutex);
@@ -112,62 +115,77 @@
         timestamp = systemTime(SYSTEM_TIME_BOOTTIME);
     }
 
+    std::string emptyId;
     for (auto tag : mMonitoredTagList) {
-        camera_metadata_ro_entry entry = metadata.find(tag);
-        CameraMetadata &lastValues = (source == REQUEST) ?
-                mLastMonitoredRequestValues : mLastMonitoredResultValues;
-        if (lastValues.isEmpty()) {
-            lastValues = CameraMetadata(mMonitoredTagList.size());
-            const camera_metadata_t *metaBuffer =
-                    lastValues.getAndLock();
-            set_camera_metadata_vendor_id(
-                    const_cast<camera_metadata_t *> (metaBuffer), mVendorTagId);
-            lastValues.unlock(metaBuffer);
+        monitorSingleMetadata(source, frameNumber, timestamp, emptyId, tag, metadata);
+
+        for (auto& m : physicalMetadata) {
+            monitorSingleMetadata(source, frameNumber, timestamp, m.first, tag, m.second);
         }
+    }
+}
 
-        camera_metadata_entry lastEntry = lastValues.find(tag);
+void TagMonitor::monitorSingleMetadata(eventSource source, int64_t frameNumber, nsecs_t timestamp,
+        const std::string& cameraId, uint32_t tag, const CameraMetadata& metadata) {
 
-        if (entry.count > 0) {
-            bool isDifferent = false;
-            if (lastEntry.count > 0) {
-                // Have a last value, compare to see if changed
-                if (lastEntry.type == entry.type &&
-                        lastEntry.count == entry.count) {
-                    // Same type and count, compare values
-                    size_t bytesPerValue = camera_metadata_type_size[lastEntry.type];
-                    size_t entryBytes = bytesPerValue * lastEntry.count;
-                    int cmp = memcmp(entry.data.u8, lastEntry.data.u8, entryBytes);
-                    if (cmp != 0) {
-                        isDifferent = true;
-                    }
-                } else {
-                    // Count or type has changed
+    CameraMetadata &lastValues = (source == REQUEST) ?
+            (cameraId.empty() ? mLastMonitoredRequestValues :
+                    mLastMonitoredPhysicalRequestKeys[cameraId]) :
+            (cameraId.empty() ? mLastMonitoredResultValues :
+                    mLastMonitoredPhysicalResultKeys[cameraId]);
+
+    camera_metadata_ro_entry entry = metadata.find(tag);
+    if (lastValues.isEmpty()) {
+        lastValues = CameraMetadata(mMonitoredTagList.size());
+        const camera_metadata_t *metaBuffer =
+                lastValues.getAndLock();
+        set_camera_metadata_vendor_id(
+                const_cast<camera_metadata_t *> (metaBuffer), mVendorTagId);
+        lastValues.unlock(metaBuffer);
+    }
+
+    camera_metadata_entry lastEntry = lastValues.find(tag);
+
+    if (entry.count > 0) {
+        bool isDifferent = false;
+        if (lastEntry.count > 0) {
+            // Have a last value, compare to see if changed
+            if (lastEntry.type == entry.type &&
+                    lastEntry.count == entry.count) {
+                // Same type and count, compare values
+                size_t bytesPerValue = camera_metadata_type_size[lastEntry.type];
+                size_t entryBytes = bytesPerValue * lastEntry.count;
+                int cmp = memcmp(entry.data.u8, lastEntry.data.u8, entryBytes);
+                if (cmp != 0) {
                     isDifferent = true;
                 }
             } else {
-                // No last entry, so always consider to be different
+                // Count or type has changed
                 isDifferent = true;
             }
+        } else {
+            // No last entry, so always consider to be different
+            isDifferent = true;
+        }
 
-            if (isDifferent) {
-                ALOGV("%s: Tag %s changed", __FUNCTION__,
-                      get_local_camera_metadata_tag_name_vendor_id(
-                              tag, mVendorTagId));
-                lastValues.update(entry);
-                mMonitoringEvents.emplace(source, frameNumber, timestamp, entry);
-            }
-        } else if (lastEntry.count > 0) {
-            // Value has been removed
-            ALOGV("%s: Tag %s removed", __FUNCTION__,
+        if (isDifferent) {
+            ALOGV("%s: Tag %s changed", __FUNCTION__,
                   get_local_camera_metadata_tag_name_vendor_id(
                           tag, mVendorTagId));
-            lastValues.erase(tag);
-            entry.tag = tag;
-            entry.type = get_local_camera_metadata_tag_type_vendor_id(tag,
-                    mVendorTagId);
-            entry.count = 0;
-            mMonitoringEvents.emplace(source, frameNumber, timestamp, entry);
+            lastValues.update(entry);
+            mMonitoringEvents.emplace(source, frameNumber, timestamp, entry, cameraId);
         }
+    } else if (lastEntry.count > 0) {
+        // Value has been removed
+        ALOGV("%s: Tag %s removed", __FUNCTION__,
+              get_local_camera_metadata_tag_name_vendor_id(
+                      tag, mVendorTagId));
+        lastValues.erase(tag);
+        entry.tag = tag;
+        entry.type = get_local_camera_metadata_tag_type_vendor_id(tag,
+                mVendorTagId);
+        entry.count = 0;
+        mMonitoringEvents.emplace(source, frameNumber, timestamp, entry, cameraId);
     }
 }
 
@@ -190,8 +208,9 @@
         dprintf(fd, "     Monitored tag event log:\n");
         for (const auto& event : mMonitoringEvents) {
             int indentation = (event.source == REQUEST) ? 15 : 30;
-            dprintf(fd, "        f%d:%" PRId64 "ns: %*s%s.%s: ",
+            dprintf(fd, "        f%d:%" PRId64 "ns:%*s%*s%s.%s: ",
                     event.frameNumber, event.timestamp,
+                    2, event.cameraId.c_str(),
                     indentation,
                     event.source == REQUEST ? "REQ:" : "RES:",
                     get_local_camera_metadata_section_name_vendor_id(event.tag,
@@ -296,13 +315,14 @@
 
 template<typename T>
 TagMonitor::MonitorEvent::MonitorEvent(eventSource src, uint32_t frameNumber, nsecs_t timestamp,
-        const T &value) :
+        const T &value, const std::string& cameraId) :
         source(src),
         frameNumber(frameNumber),
         timestamp(timestamp),
         tag(value.tag),
         type(value.type),
-        newData(value.data.u8, value.data.u8 + camera_metadata_type_size[value.type] * value.count) {
+        newData(value.data.u8, value.data.u8 + camera_metadata_type_size[value.type] * value.count),
+        cameraId(cameraId) {
 }
 
 TagMonitor::MonitorEvent::~MonitorEvent() {
diff --git a/services/camera/libcameraservice/utils/TagMonitor.h b/services/camera/libcameraservice/utils/TagMonitor.h
index 2dece62..1b7b033 100644
--- a/services/camera/libcameraservice/utils/TagMonitor.h
+++ b/services/camera/libcameraservice/utils/TagMonitor.h
@@ -20,6 +20,7 @@
 #include <vector>
 #include <atomic>
 #include <mutex>
+#include <unordered_map>
 
 #include <utils/RefBase.h>
 #include <utils/String8.h>
@@ -62,7 +63,8 @@
 
     // Scan through the metadata and update the monitoring information
     void monitorMetadata(eventSource source, int64_t frameNumber,
-            nsecs_t timestamp, const CameraMetadata& metadata);
+            nsecs_t timestamp, const CameraMetadata& metadata,
+            const std::unordered_map<std::string, CameraMetadata>& physicalMetadata);
 
     // Dump current event log to the provided fd
     void dumpMonitoredMetadata(int fd);
@@ -72,6 +74,10 @@
     static void printData(int fd, const uint8_t *data_ptr, uint32_t tag,
             int type, int count, int indentation);
 
+    void monitorSingleMetadata(TagMonitor::eventSource source, int64_t frameNumber,
+            nsecs_t timestamp, const std::string& cameraId, uint32_t tag,
+            const CameraMetadata& metadata);
+
     std::atomic<bool> mMonitoringEnabled;
     std::mutex mMonitorMutex;
 
@@ -82,6 +88,9 @@
     CameraMetadata mLastMonitoredRequestValues;
     CameraMetadata mLastMonitoredResultValues;
 
+    std::unordered_map<std::string, CameraMetadata> mLastMonitoredPhysicalRequestKeys;
+    std::unordered_map<std::string, CameraMetadata> mLastMonitoredPhysicalResultKeys;
+
     /**
      * A monitoring event
      * Stores a new metadata field value and the timestamp at which it changed.
@@ -90,7 +99,7 @@
     struct MonitorEvent {
         template<typename T>
         MonitorEvent(eventSource src, uint32_t frameNumber, nsecs_t timestamp,
-                const T &newValue);
+                const T &newValue, const std::string& cameraId);
         ~MonitorEvent();
 
         eventSource source;
@@ -99,6 +108,7 @@
         uint32_t tag;
         uint8_t type;
         std::vector<uint8_t> newData;
+        std::string cameraId;
     };
 
     // A ring buffer for tracking the last kMaxMonitorEvents metadata changes
diff --git a/services/mediaanalytics/Android.bp b/services/mediaanalytics/Android.bp
index c93c120..72f4b52 100644
--- a/services/mediaanalytics/Android.bp
+++ b/services/mediaanalytics/Android.bp
@@ -7,8 +7,22 @@
     srcs: [
         "main_mediametrics.cpp",
         "MediaAnalyticsService.cpp",
+        "iface_statsd.cpp",
+        "statsd_audiopolicy.cpp",
+        "statsd_audiorecord.cpp",
+        "statsd_audiothread.cpp",
+        "statsd_audiotrack.cpp",
+        "statsd_codec.cpp",
+        "statsd_drm.cpp",
+        "statsd_extractor.cpp",
+        "statsd_nuplayer.cpp",
+        "statsd_recorder.cpp",
     ],
 
+    proto: {
+        type: "lite",
+    },
+
     shared_libs: [
         "libcutils",
         "liblog",
@@ -21,10 +35,15 @@
         "libmediautils",
         "libmediametrics",
         "libstagefright_foundation",
+        "libstatslog",
         "libutils",
+        "libprotobuf-cpp-lite",
     ],
 
-    static_libs: ["libregistermsext"],
+    static_libs: [
+        "libplatformprotos",
+        "libregistermsext",
+    ],
 
     include_dirs: [
         "frameworks/av/media/libstagefright/include",
diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp
index 06baac9..3626ad1 100644
--- a/services/mediaanalytics/MediaAnalyticsService.cpp
+++ b/services/mediaanalytics/MediaAnalyticsService.cpp
@@ -210,21 +210,24 @@
 
     // XXX: if we have a sessionid in the new record, look to make
     // sure it doesn't appear in the finalized list.
-    // XXX: this is for security / DOS prevention.
-    // may also require that we persist the unique sessionIDs
-    // across boots [instead of within a single boot]
 
     if (item->count() == 0) {
-        // drop empty records
+        ALOGV("dropping empty record...");
         delete item;
         item = NULL;
         return MediaAnalyticsItem::SessionIDInvalid;
     }
 
     // save the new record
+    //
+    // send a copy to statsd
+    dump2Statsd(item);
+
+    // and keep our copy for dumpsys
     MediaAnalyticsItem::SessionID_t id = item->getSessionID();
     saveItem(item);
     mItemsFinalized++;
+
     return id;
 }
 
diff --git a/services/mediaanalytics/MediaAnalyticsService.h b/services/mediaanalytics/MediaAnalyticsService.h
index 632c692..6c9cbaa 100644
--- a/services/mediaanalytics/MediaAnalyticsService.h
+++ b/services/mediaanalytics/MediaAnalyticsService.h
@@ -112,6 +112,9 @@
 
 };
 
+// hook to send things off to the statsd subsystem
+extern bool dump2Statsd(MediaAnalyticsItem *item);
+
 // ----------------------------------------------------------------------------
 
 }; // namespace android
diff --git a/services/mediaanalytics/iface_statsd.cpp b/services/mediaanalytics/iface_statsd.cpp
new file mode 100644
index 0000000..6845f06
--- /dev/null
+++ b/services/mediaanalytics/iface_statsd.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2019 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 "iface_statsd"
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <dirent.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#include <string.h>
+#include <pwd.h>
+
+#include "MediaAnalyticsService.h"
+#include "iface_statsd.h"
+
+#include <statslog.h>
+
+namespace android {
+
+// set of routines that crack a MediaAnalyticsItem
+// and send it off to statsd with the appropriate hooks
+//
+// each MediaAnalyticsItem type (extractor, codec, nuplayer, etc)
+// has its own routine to handle this.
+//
+
+bool enabled_statsd = true;
+
+struct statsd_hooks {
+    const char *key;
+    bool (*handler)(MediaAnalyticsItem *);
+};
+
+// keep this sorted, so we can do binary searches
+struct statsd_hooks  statsd_handlers[] =
+{
+    { "audiopolicy", statsd_audiopolicy },
+    { "audiorecord", statsd_audiorecord },
+    { "audiothread", statsd_audiothread },
+    { "audiotrack", statsd_audiotrack },
+    { "codec", statsd_codec},
+    { "drm.vendor.Google.WidevineCDM", statsd_widevineCDM },
+    { "extractor", statsd_extractor },
+    { "mediadrm", statsd_mediadrm },
+    { "nuplayer", statsd_nuplayer },
+    { "nuplayer2", statsd_nuplayer },
+    { "recorder", statsd_recorder },
+};
+
+
+// give me a record, i'll look at the type and upload appropriately
+bool dump2Statsd(MediaAnalyticsItem *item) {
+    if (item == NULL) return false;
+
+    // get the key
+    std::string key = item->getKey();
+
+    if (!enabled_statsd) {
+        ALOGV("statsd logging disabled for record key=%s", key.c_str());
+        return false;
+    }
+
+    int i;
+    for(i = 0;i < sizeof(statsd_handlers) / sizeof(statsd_handlers[0]) ; i++) {
+        if (key == statsd_handlers[i].key) {
+            return (*statsd_handlers[i].handler)(item);
+        }
+    }
+    return false;
+}
+
+} // namespace android
diff --git a/services/mediaanalytics/iface_statsd.h b/services/mediaanalytics/iface_statsd.h
new file mode 100644
index 0000000..f85d303
--- /dev/null
+++ b/services/mediaanalytics/iface_statsd.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+namespace android {
+
+extern bool enabled_statsd;
+
+// component specific dumpers
+extern bool statsd_audiopolicy(MediaAnalyticsItem *);
+extern bool statsd_audiorecord(MediaAnalyticsItem *);
+extern bool statsd_audiothread(MediaAnalyticsItem *);
+extern bool statsd_audiotrack(MediaAnalyticsItem *);
+extern bool statsd_codec(MediaAnalyticsItem *);
+extern bool statsd_extractor(MediaAnalyticsItem *);
+extern bool statsd_nuplayer(MediaAnalyticsItem *);
+extern bool statsd_recorder(MediaAnalyticsItem *);
+
+extern bool statsd_mediadrm(MediaAnalyticsItem *);
+extern bool statsd_widevineCDM(MediaAnalyticsItem *);
+
+} // namespace android
diff --git a/services/mediaanalytics/statsd_audiopolicy.cpp b/services/mediaanalytics/statsd_audiopolicy.cpp
new file mode 100644
index 0000000..06c4dde
--- /dev/null
+++ b/services/mediaanalytics/statsd_audiopolicy.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2019 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 "statsd_audiopolicy"
+#include <utils/Log.h>
+
+#include <dirent.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <statslog.h>
+
+#include "MediaAnalyticsService.h"
+#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
+#include "iface_statsd.h"
+
+namespace android {
+
+bool statsd_audiopolicy(MediaAnalyticsItem *item)
+{
+    if (item == NULL) return false;
+
+    // these go into the statsd wrapper
+    nsecs_t timestamp = item->getTimestamp();
+    std::string pkgName = item->getPkgName();
+    int64_t pkgVersionCode = item->getPkgVersionCode();
+    int64_t mediaApexVersion = 0;
+
+
+    // the rest into our own proto
+    //
+    ::android::stats::mediametrics::AudioPolicyData metrics_proto;
+
+    // flesh out the protobuf we'll hand off with our data
+    //
+    //int32 char kAudioPolicyStatus[] = "android.media.audiopolicy.status";
+    int32_t status = -1;
+    if (item->getInt32("android.media.audiopolicy.status", &status)) {
+        metrics_proto.set_status(status);
+    }
+    //string char kAudioPolicyRqstSrc[] = "android.media.audiopolicy.rqst.src";
+    char *rqst_src = NULL;
+    if (item->getCString("android.media.audiopolicy.rqst.src", &rqst_src)) {
+        metrics_proto.set_request_source(rqst_src);
+    }
+    //string char kAudioPolicyRqstPkg[] = "android.media.audiopolicy.rqst.pkg";
+    char *rqst_pkg = NULL;
+    if (item->getCString("android.media.audiopolicy.rqst.pkg", &rqst_pkg)) {
+        metrics_proto.set_request_package(rqst_pkg);
+    }
+    //int32 char kAudioPolicyRqstSession[] = "android.media.audiopolicy.rqst.session";
+    int32_t rqst_session = -1;
+    if (item->getInt32("android.media.audiopolicy.rqst.session", &rqst_session)) {
+        metrics_proto.set_request_session(rqst_session);
+    }
+    //string char kAudioPolicyRqstDevice[] = "android.media.audiopolicy.rqst.device";
+    char *rqst_device = NULL;
+    if (item->getCString("android.media.audiopolicy.rqst.device", &rqst_device)) {
+        metrics_proto.set_request_device(rqst_device);
+    }
+
+    //string char kAudioPolicyActiveSrc[] = "android.media.audiopolicy.active.src";
+    char *active_src = NULL;
+    if (item->getCString("android.media.audiopolicy.active.src", &active_src)) {
+        metrics_proto.set_active_source(active_src);
+    }
+    //string char kAudioPolicyActivePkg[] = "android.media.audiopolicy.active.pkg";
+    char *active_pkg = NULL;
+    if (item->getCString("android.media.audiopolicy.active.pkg", &active_pkg)) {
+        metrics_proto.set_active_package(active_pkg);
+    }
+    //int32 char kAudioPolicyActiveSession[] = "android.media.audiopolicy.active.session";
+    int32_t active_session = -1;
+    if (item->getInt32("android.media.audiopolicy.active.session", &active_session)) {
+        metrics_proto.set_active_session(active_session);
+    }
+    //string char kAudioPolicyActiveDevice[] = "android.media.audiopolicy.active.device";
+    char *active_device = NULL;
+    if (item->getCString("android.media.audiopolicy.active.device", &active_device)) {
+        metrics_proto.set_active_device(active_device);
+    }
+
+
+    std::string serialized;
+    if (!metrics_proto.SerializeToString(&serialized)) {
+        ALOGE("Failed to serialize audipolicy metrics");
+        return false;
+    }
+
+    if (enabled_statsd) {
+        android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
+        (void)android::util::stats_write(android::util::MEDIAMETRICS_AUDIOPOLICY_REPORTED,
+                                   timestamp, pkgName.c_str(), pkgVersionCode,
+                                   mediaApexVersion,
+                                   bf_serialized);
+
+    } else {
+        ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str()));
+    }
+
+    // must free the strings that we were given
+    free(rqst_src);
+    free(rqst_pkg);
+    free(rqst_device);
+    free(active_src);
+    free(active_pkg);
+    free(active_device);
+
+    return true;
+}
+
+};
diff --git a/services/mediaanalytics/statsd_audiorecord.cpp b/services/mediaanalytics/statsd_audiorecord.cpp
new file mode 100644
index 0000000..c9edb27
--- /dev/null
+++ b/services/mediaanalytics/statsd_audiorecord.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2019 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 "statsd_audiorecord"
+#include <utils/Log.h>
+
+#include <dirent.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <statslog.h>
+
+#include "MediaAnalyticsService.h"
+#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
+#include "iface_statsd.h"
+
+namespace android {
+
+bool statsd_audiorecord(MediaAnalyticsItem *item)
+{
+    if (item == NULL) return false;
+
+    // these go into the statsd wrapper
+    nsecs_t timestamp = item->getTimestamp();
+    std::string pkgName = item->getPkgName();
+    int64_t pkgVersionCode = item->getPkgVersionCode();
+    int64_t mediaApexVersion = 0;
+
+
+    // the rest into our own proto
+    //
+    ::android::stats::mediametrics::AudioRecordData metrics_proto;
+
+    // flesh out the protobuf we'll hand off with our data
+    //
+    char *encoding = NULL;
+    if (item->getCString("android.media.audiorecord.encoding", &encoding)) {
+        metrics_proto.set_encoding(encoding);
+    }
+
+    char *source = NULL;
+    if (item->getCString("android.media.audiorecord.source", &source)) {
+        metrics_proto.set_source(source);
+    }
+
+    int32_t latency = -1;
+    if (item->getInt32("android.media.audiorecord.latency", &latency)) {
+        metrics_proto.set_latency(latency);
+    }
+
+    int32_t samplerate = -1;
+    if (item->getInt32("android.media.audiorecord.samplerate", &samplerate)) {
+        metrics_proto.set_samplerate(samplerate);
+    }
+
+    int32_t channels = -1;
+    if (item->getInt32("android.media.audiorecord.channels", &channels)) {
+        metrics_proto.set_channels(channels);
+    }
+
+    int64_t createdMs = -1;
+    if (item->getInt64("android.media.audiorecord.createdMs", &createdMs)) {
+        metrics_proto.set_created_millis(createdMs);
+    }
+
+    int64_t durationMs = -1;
+    if (item->getInt64("android.media.audiorecord.durationMs", &durationMs)) {
+        metrics_proto.set_duration_millis(durationMs);
+    }
+
+    int32_t count = -1;
+    if (item->getInt32("android.media.audiorecord.n", &count)) {
+        metrics_proto.set_count(count);
+    }
+
+    int32_t errcode = -1;
+    if (item->getInt32("android.media.audiorecord.errcode", &errcode)) {
+        metrics_proto.set_error_code(errcode);
+    } else if (item->getInt32("android.media.audiorecord.lastError.code", &errcode)) {
+        metrics_proto.set_error_code(errcode);
+    }
+
+    char *errfunc = NULL;
+    if (item->getCString("android.media.audiorecord.errfunc", &errfunc)) {
+        metrics_proto.set_error_function(errfunc);
+    } else if (item->getCString("android.media.audiorecord.lastError.at", &errfunc)) {
+        metrics_proto.set_error_function(errfunc);
+    }
+
+    // portId (int32)
+    int32_t port_id = -1;
+    if (item->getInt32("android.media.audiorecord.portId", &port_id)) {
+        metrics_proto.set_port_id(count);
+    }
+    // frameCount (int32)
+    int32_t frameCount = -1;
+    if (item->getInt32("android.media.audiorecord.frameCount", &frameCount)) {
+        metrics_proto.set_frame_count(frameCount);
+    }
+    // attributes (string)
+    char *attributes = NULL;
+    if (item->getCString("android.media.audiorecord.attributes", &attributes)) {
+        metrics_proto.set_attributes(attributes);
+    }
+    // channelMask (int64)
+    int64_t channelMask = -1;
+    if (item->getInt64("android.media.audiorecord.channelMask", &channelMask)) {
+        metrics_proto.set_channel_mask(channelMask);
+    }
+    // startcount (int64)
+    int64_t startcount = -1;
+    if (item->getInt64("android.media.audiorecord.startcount", &startcount)) {
+        metrics_proto.set_start_count(startcount);
+    }
+
+
+    std::string serialized;
+    if (!metrics_proto.SerializeToString(&serialized)) {
+        ALOGE("Failed to serialize audiorecord metrics");
+        return false;
+    }
+
+    if (enabled_statsd) {
+        android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
+        (void)android::util::stats_write(android::util::MEDIAMETRICS_AUDIORECORD_REPORTED,
+                                   timestamp, pkgName.c_str(), pkgVersionCode,
+                                   mediaApexVersion,
+                                   bf_serialized);
+
+    } else {
+        ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str()));
+    }
+
+    // must free the strings that we were given
+    free(encoding);
+    free(source);
+    free(errfunc);
+    free(attributes);
+
+    return true;
+}
+
+};
diff --git a/services/mediaanalytics/statsd_audiothread.cpp b/services/mediaanalytics/statsd_audiothread.cpp
new file mode 100644
index 0000000..8232424
--- /dev/null
+++ b/services/mediaanalytics/statsd_audiothread.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2019 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 "statsd_audiothread"
+#include <utils/Log.h>
+
+#include <dirent.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <statslog.h>
+
+#include "MediaAnalyticsService.h"
+#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
+#include "iface_statsd.h"
+
+namespace android {
+
+bool statsd_audiothread(MediaAnalyticsItem *item)
+{
+    if (item == NULL) return false;
+
+    // these go into the statsd wrapper
+    nsecs_t timestamp = item->getTimestamp();
+    std::string pkgName = item->getPkgName();
+    int64_t pkgVersionCode = item->getPkgVersionCode();
+    int64_t mediaApexVersion = 0;
+
+
+    // the rest into our own proto
+    //
+    ::android::stats::mediametrics::AudioThreadData metrics_proto;
+
+#define	MM_PREFIX "android.media.audiothread."
+
+    // flesh out the protobuf we'll hand off with our data
+    //
+    char *mytype = NULL;
+    if (item->getCString(MM_PREFIX "type", &mytype)) {
+        metrics_proto.set_type(mytype);
+    }
+    int32_t framecount = -1;
+    if (item->getInt32(MM_PREFIX "framecount", &framecount)) {
+        metrics_proto.set_framecount(framecount);
+    }
+    int32_t samplerate = -1;
+    if (item->getInt32(MM_PREFIX "samplerate", &samplerate)) {
+        metrics_proto.set_samplerate(samplerate);
+    }
+    char *workhist = NULL;
+    if (item->getCString(MM_PREFIX "workMs.hist", &workhist)) {
+        metrics_proto.set_work_millis_hist(workhist);
+    }
+    char *latencyhist = NULL;
+    if (item->getCString(MM_PREFIX "latencyMs.hist", &latencyhist)) {
+        metrics_proto.set_latency_millis_hist(latencyhist);
+    }
+    char *warmuphist = NULL;
+    if (item->getCString(MM_PREFIX "warmupMs.hist", &warmuphist)) {
+        metrics_proto.set_warmup_millis_hist(warmuphist);
+    }
+    int64_t underruns = -1;
+    if (item->getInt64(MM_PREFIX "underruns", &underruns)) {
+        metrics_proto.set_underruns(underruns);
+    }
+    int64_t overruns = -1;
+    if (item->getInt64(MM_PREFIX "overruns", &overruns)) {
+        metrics_proto.set_overruns(overruns);
+    }
+    int64_t activeMs = -1;
+    if (item->getInt64(MM_PREFIX "activeMs", &activeMs)) {
+        metrics_proto.set_active_millis(activeMs);
+    }
+    int64_t durationMs = -1;
+    if (item->getInt64(MM_PREFIX "durationMs", &durationMs)) {
+        metrics_proto.set_duration_millis(durationMs);
+    }
+
+    // item->setInt32(MM_PREFIX "id", (int32_t)mId); // IO handle
+    int32_t id = -1;
+    if (item->getInt32(MM_PREFIX "id", &id)) {
+        metrics_proto.set_id(id);
+    }
+    // item->setInt32(MM_PREFIX "portId", (int32_t)mPortId);
+    int32_t port_id = -1;
+    if (item->getInt32(MM_PREFIX "portId", &id)) {
+        metrics_proto.set_port_id(port_id);
+    }
+    // item->setCString(MM_PREFIX "type", threadTypeToString(mType));
+    char *type = NULL;
+    if (item->getCString(MM_PREFIX "type", &type)) {
+        metrics_proto.set_type(type);
+    }
+    // item->setInt32(MM_PREFIX "sampleRate", (int32_t)mSampleRate);
+    int32_t sample_rate = -1;
+    if (item->getInt32(MM_PREFIX "sampleRate", &sample_rate)) {
+        metrics_proto.set_sample_rate(sample_rate);
+    }
+    // item->setInt64(MM_PREFIX "channelMask", (int64_t)mChannelMask);
+    int32_t channel_mask = -1;
+    if (item->getInt32(MM_PREFIX "channelMask", &channel_mask)) {
+        metrics_proto.set_channel_mask(channel_mask);
+    }
+    // item->setCString(MM_PREFIX "encoding", toString(mFormat).c_str());
+    char *encoding = NULL;
+    if (item->getCString(MM_PREFIX "encoding", &encoding)) {
+        metrics_proto.set_encoding(encoding);
+    }
+    // item->setInt32(MM_PREFIX "frameCount", (int32_t)mFrameCount);
+    int32_t frame_count = -1;
+    if (item->getInt32(MM_PREFIX "frameCount", &frame_count)) {
+        metrics_proto.set_frame_count(frame_count);
+    }
+    // item->setCString(MM_PREFIX "outDevice", toString(mOutDevice).c_str());
+    char *outDevice = NULL;
+    if (item->getCString(MM_PREFIX "outDevice", &outDevice)) {
+        metrics_proto.set_output_device(outDevice);
+    }
+    // item->setCString(MM_PREFIX "inDevice", toString(mInDevice).c_str());
+    char *inDevice = NULL;
+    if (item->getCString(MM_PREFIX "inDevice", &inDevice)) {
+        metrics_proto.set_input_device(inDevice);
+    }
+    // item->setDouble(MM_PREFIX "ioJitterMs.mean", mIoJitterMs.getMean());
+    double iojitters_ms_mean = -1;
+    if (item->getDouble(MM_PREFIX "ioJitterMs.mean", &iojitters_ms_mean)) {
+        metrics_proto.set_io_jitter_mean_millis(iojitters_ms_mean);
+    }
+    // item->setDouble(MM_PREFIX "ioJitterMs.std", mIoJitterMs.getStdDev());
+    double iojitters_ms_std = -1;
+    if (item->getDouble(MM_PREFIX "ioJitterMs.std", &iojitters_ms_std)) {
+        metrics_proto.set_io_jitter_stddev_millis(iojitters_ms_std);
+    }
+    // item->setDouble(MM_PREFIX "processTimeMs.mean", mProcessTimeMs.getMean());
+    double process_time_ms_mean = -1;
+    if (item->getDouble(MM_PREFIX "processTimeMs.mean", &process_time_ms_mean)) {
+        metrics_proto.set_process_time_mean_millis(process_time_ms_mean);
+    }
+    // item->setDouble(MM_PREFIX "processTimeMs.std", mProcessTimeMs.getStdDev());
+    double process_time_ms_std = -1;
+    if (item->getDouble(MM_PREFIX "processTimeMs.std", &process_time_ms_std)) {
+        metrics_proto.set_process_time_stddev_millis(process_time_ms_std);
+    }
+    // item->setDouble(MM_PREFIX "timestampJitterMs.mean", tsjitter.getMean());
+    double timestamp_jitter_ms_mean = -1;
+    if (item->getDouble(MM_PREFIX "timestampJitterMs.mean", &timestamp_jitter_ms_mean)) {
+        metrics_proto.set_timestamp_jitter_mean_millis(timestamp_jitter_ms_mean);
+    }
+    // item->setDouble(MM_PREFIX "timestampJitterMs.std", tsjitter.getStdDev());
+    double timestamp_jitter_ms_stddev = -1;
+    if (item->getDouble(MM_PREFIX "timestampJitterMs.std", &timestamp_jitter_ms_stddev)) {
+        metrics_proto.set_timestamp_jitter_stddev_millis(timestamp_jitter_ms_stddev);
+    }
+    // item->setDouble(MM_PREFIX "latencyMs.mean", mLatencyMs.getMean());
+    double latency_ms_mean = -1;
+    if (item->getDouble(MM_PREFIX "latencyMs.mean", &latency_ms_mean)) {
+        metrics_proto.set_latency_mean_millis(latency_ms_mean);
+    }
+    // item->setDouble(MM_PREFIX "latencyMs.std", mLatencyMs.getStdDev());
+    double latency_ms_stddev = -1;
+    if (item->getDouble(MM_PREFIX "latencyMs.std", &latency_ms_stddev)) {
+        metrics_proto.set_latency_stddev_millis(latency_ms_stddev);
+    }
+
+
+    std::string serialized;
+    if (!metrics_proto.SerializeToString(&serialized)) {
+        ALOGE("Failed to serialize audiothread metrics");
+        return false;
+    }
+
+    if (enabled_statsd) {
+        android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
+        (void)android::util::stats_write(android::util::MEDIAMETRICS_AUDIOTHREAD_REPORTED,
+                                   timestamp, pkgName.c_str(), pkgVersionCode,
+                                   mediaApexVersion,
+                                   bf_serialized);
+
+    } else {
+        ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str()));
+    }
+
+    // must free the strings that we were given
+    free(mytype);
+    free(workhist);
+    free(latencyhist);
+    free(warmuphist);
+    free(type);
+    free(encoding);
+    free(inDevice);
+    free(outDevice);
+
+    return true;
+}
+
+};
diff --git a/services/mediaanalytics/statsd_audiotrack.cpp b/services/mediaanalytics/statsd_audiotrack.cpp
new file mode 100644
index 0000000..f250ced
--- /dev/null
+++ b/services/mediaanalytics/statsd_audiotrack.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2019 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 "statsd_audiotrack"
+#include <utils/Log.h>
+
+#include <dirent.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <statslog.h>
+
+#include "MediaAnalyticsService.h"
+#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
+#include "iface_statsd.h"
+
+namespace android {
+
+bool statsd_audiotrack(MediaAnalyticsItem *item)
+{
+    if (item == NULL) return false;
+
+    // these go into the statsd wrapper
+    nsecs_t timestamp = item->getTimestamp();
+    std::string pkgName = item->getPkgName();
+    int64_t pkgVersionCode = item->getPkgVersionCode();
+    int64_t mediaApexVersion = 0;
+
+
+    // the rest into our own proto
+    //
+    ::android::stats::mediametrics::AudioTrackData metrics_proto;
+
+    // flesh out the protobuf we'll hand off with our data
+    //
+
+    // static constexpr char kAudioTrackStreamType[] = "android.media.audiotrack.streamtype";
+    // optional string streamType;
+    char *streamtype = NULL;
+    if (item->getCString("android.media.audiotrack.streamtype", &streamtype)) {
+        metrics_proto.set_stream_type(streamtype);
+    }
+
+    // static constexpr char kAudioTrackContentType[] = "android.media.audiotrack.type";
+    // optional string contentType;
+    char *contenttype = NULL;
+    if (item->getCString("android.media.audiotrack.type", &contenttype)) {
+        metrics_proto.set_content_type(contenttype);
+    }
+
+    // static constexpr char kAudioTrackUsage[] = "android.media.audiotrack.usage";
+    // optional string trackUsage;
+    char *trackusage = NULL;
+    if (item->getCString("android.media.audiotrack.usage", &trackusage)) {
+        metrics_proto.set_track_usage(trackusage);
+    }
+
+    // static constexpr char kAudioTrackSampleRate[] = "android.media.audiotrack.samplerate";
+    // optional int32 samplerate;
+    int32_t samplerate = -1;
+    if (item->getInt32("android.media.audiotrack.samplerate", &samplerate)) {
+        metrics_proto.set_sample_rate(samplerate);
+    }
+
+    // static constexpr char kAudioTrackChannelMask[] = "android.media.audiotrack.channelmask";
+    // optional int64 channelMask;
+    int64_t channelMask = -1;
+    if (item->getInt64("android.media.audiotrack.channelmask", &channelMask)) {
+        metrics_proto.set_channel_mask(channelMask);
+    }
+
+    // NB: These are not yet exposed as public Java API constants.
+    // static constexpr char kAudioTrackUnderrunFrames[] = "android.media.audiotrack.underrunframes";
+    // optional int32 underrunframes;
+    int32_t underrunframes = -1;
+    if (item->getInt32("android.media.audiotrack.underrunframes", &underrunframes)) {
+        metrics_proto.set_underrun_frames(underrunframes);
+    }
+
+    // static constexpr char kAudioTrackStartupGlitch[] = "android.media.audiotrack.glitch.startup";
+    // optional int32 startupglitch;
+    int32_t startupglitch = -1;
+    if (item->getInt32("android.media.audiotrack.glitch.startup", &startupglitch)) {
+        metrics_proto.set_startup_glitch(startupglitch);
+    }
+
+    // portId (int32)
+    int32_t port_id = -1;
+    if (item->getInt32("android.media.audiotrack.portId", &port_id)) {
+        metrics_proto.set_port_id(port_id);
+    }
+    // encoding (string)
+    char *encoding = NULL;
+    if (item->getCString("android.media.audiotrack.encoding", &encoding)) {
+        metrics_proto.set_encoding(encoding);
+    }
+    // frameCount (int32)
+    int32_t frame_count = -1;
+    if (item->getInt32("android.media.audiotrack.frameCount", &frame_count)) {
+        metrics_proto.set_frame_count(frame_count);
+    }
+    // attributes (string)
+    char *attributes = NULL;
+    if (item->getCString("android.media.audiotrack.attributes", &attributes)) {
+        metrics_proto.set_attributes(attributes);
+    }
+
+    std::string serialized;
+    if (!metrics_proto.SerializeToString(&serialized)) {
+        ALOGE("Failed to serialize audiotrack metrics");
+        return false;
+    }
+
+    if (enabled_statsd) {
+        android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
+        (void)android::util::stats_write(android::util::MEDIAMETRICS_AUDIOTRACK_REPORTED,
+                                   timestamp, pkgName.c_str(), pkgVersionCode,
+                                   mediaApexVersion,
+                                   bf_serialized);
+
+    } else {
+        ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str()));
+    }
+
+    // must free the strings that we were given
+    free(streamtype);
+    free(contenttype);
+    free(trackusage);
+    free(encoding);
+    free(attributes);
+
+    return true;
+}
+
+};
diff --git a/services/mediaanalytics/statsd_codec.cpp b/services/mediaanalytics/statsd_codec.cpp
new file mode 100644
index 0000000..dc8e4ef
--- /dev/null
+++ b/services/mediaanalytics/statsd_codec.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2019 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 "statsd_codec"
+#include <utils/Log.h>
+
+#include <dirent.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <statslog.h>
+
+#include "MediaAnalyticsService.h"
+#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
+#include "iface_statsd.h"
+
+namespace android {
+
+bool statsd_codec(MediaAnalyticsItem *item)
+{
+    if (item == NULL) return false;
+
+    // these go into the statsd wrapper
+    nsecs_t timestamp = item->getTimestamp();
+    std::string pkgName = item->getPkgName();
+    int64_t pkgVersionCode = item->getPkgVersionCode();
+    int64_t mediaApexVersion = 0;
+
+
+    // the rest into our own proto
+    //
+    ::android::stats::mediametrics::CodecData metrics_proto;
+
+    // flesh out the protobuf we'll hand off with our data
+    //
+    // android.media.mediacodec.codec   string
+    char *codec = NULL;
+    if (item->getCString("android.media.mediacodec.codec", &codec)) {
+        metrics_proto.set_codec(codec);
+    }
+    // android.media.mediacodec.mime    string
+    char *mime = NULL;
+    if (item->getCString("android.media.mediacodec.mime", &mime)) {
+        metrics_proto.set_mime(mime);
+    }
+    // android.media.mediacodec.mode    string
+    char *mode = NULL;
+    if ( item->getCString("android.media.mediacodec.mode", &mode)) {
+        metrics_proto.set_mode(mode);
+    }
+    // android.media.mediacodec.encoder int32
+    int32_t encoder = -1;
+    if ( item->getInt32("android.media.mediacodec.encoder", &encoder)) {
+        metrics_proto.set_encoder(encoder);
+    }
+    // android.media.mediacodec.secure  int32
+    int32_t secure = -1;
+    if ( item->getInt32("android.media.mediacodec.secure", &secure)) {
+        metrics_proto.set_secure(secure);
+    }
+    // android.media.mediacodec.width   int32
+    int32_t width = -1;
+    if ( item->getInt32("android.media.mediacodec.width", &width)) {
+        metrics_proto.set_width(width);
+    }
+    // android.media.mediacodec.height  int32
+    int32_t height = -1;
+    if ( item->getInt32("android.media.mediacodec.height", &height)) {
+        metrics_proto.set_height(height);
+    }
+    // android.media.mediacodec.rotation-degrees        int32
+    int32_t rotation = -1;
+    if ( item->getInt32("android.media.mediacodec.rotation-degrees", &rotation)) {
+        metrics_proto.set_rotation(rotation);
+    }
+    // android.media.mediacodec.crypto  int32 (although missing if not needed
+    int32_t crypto = -1;
+    if ( item->getInt32("android.media.mediacodec.crypto", &crypto)) {
+        metrics_proto.set_crypto(crypto);
+    }
+    // android.media.mediacodec.profile int32
+    int32_t profile = -1;
+    if ( item->getInt32("android.media.mediacodec.profile", &profile)) {
+        metrics_proto.set_profile(profile);
+    }
+    // android.media.mediacodec.level   int32
+    int32_t level = -1;
+    if ( item->getInt32("android.media.mediacodec.level", &level)) {
+        metrics_proto.set_level(level);
+    }
+    // android.media.mediacodec.maxwidth        int32
+    int32_t maxwidth = -1;
+    if ( item->getInt32("android.media.mediacodec.maxwidth", &maxwidth)) {
+        metrics_proto.set_max_width(maxwidth);
+    }
+    // android.media.mediacodec.maxheight       int32
+    int32_t maxheight = -1;
+    if ( item->getInt32("android.media.mediacodec.maxheight", &maxheight)) {
+        metrics_proto.set_max_height(maxheight);
+    }
+    // android.media.mediacodec.errcode         int32
+    int32_t errcode = -1;
+    if ( item->getInt32("android.media.mediacodec.errcode", &errcode)) {
+        metrics_proto.set_error_code(errcode);
+    }
+    // android.media.mediacodec.errstate        string
+    char *errstate = NULL;
+    if ( item->getCString("android.media.mediacodec.errstate", &errstate)) {
+        metrics_proto.set_error_state(errstate);
+    }
+    // android.media.mediacodec.latency.max  int64
+    int64_t latency_max = -1;
+    if ( item->getInt64("android.media.mediacodec.latency.max", &latency_max)) {
+        metrics_proto.set_latency_max(latency_max);
+    }
+    // android.media.mediacodec.latency.min  int64
+    int64_t latency_min = -1;
+    if ( item->getInt64("android.media.mediacodec.latency.min", &latency_min)) {
+        metrics_proto.set_latency_min(latency_min);
+    }
+    // android.media.mediacodec.latency.avg  int64
+    int64_t latency_avg = -1;
+    if ( item->getInt64("android.media.mediacodec.latency.avg", &latency_avg)) {
+        metrics_proto.set_latency_avg(latency_avg);
+    }
+    // android.media.mediacodec.latency.n    int64
+    int64_t latency_count = -1;
+    if ( item->getInt64("android.media.mediacodec.latency.n", &latency_count)) {
+        metrics_proto.set_latency_count(latency_count);
+    }
+    // android.media.mediacodec.latency.unknown    int64
+    int64_t latency_unknown = -1;
+    if ( item->getInt64("android.media.mediacodec.latency.unknown", &latency_unknown)) {
+        metrics_proto.set_latency_unknown(latency_unknown);
+    }
+    // android.media.mediacodec.latency.hist    NOT EMITTED
+
+    std::string serialized;
+    if (!metrics_proto.SerializeToString(&serialized)) {
+        ALOGE("Failed to serialize codec metrics");
+        return false;
+    }
+
+    if (enabled_statsd) {
+        android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
+        (void)android::util::stats_write(android::util::MEDIAMETRICS_CODEC_REPORTED,
+                                   timestamp, pkgName.c_str(), pkgVersionCode,
+                                   mediaApexVersion,
+                                   bf_serialized);
+
+    } else {
+        ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str()));
+    }
+
+    // must free the strings that we were given
+    free(codec);
+    free(mime);
+    free(mode);
+    free(errstate);
+
+    return true;
+}
+
+};
diff --git a/services/mediaanalytics/statsd_drm.cpp b/services/mediaanalytics/statsd_drm.cpp
new file mode 100644
index 0000000..902483a
--- /dev/null
+++ b/services/mediaanalytics/statsd_drm.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2019 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 "statsd_drm"
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <dirent.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#include <string.h>
+#include <pwd.h>
+
+#include "MediaAnalyticsService.h"
+#include "iface_statsd.h"
+
+#include <statslog.h>
+
+namespace android {
+
+// mediadrm
+bool statsd_mediadrm(MediaAnalyticsItem *item)
+{
+    if (item == NULL) return false;
+
+    nsecs_t timestamp = item->getTimestamp();
+    std::string pkgName = item->getPkgName();
+    int64_t pkgVersionCode = item->getPkgVersionCode();
+    int64_t mediaApexVersion = 0;
+
+    char *vendor = NULL;
+    (void) item->getCString("vendor", &vendor);
+    char *description = NULL;
+    (void) item->getCString("description", &description);
+    char *serialized_metrics = NULL;
+    (void) item->getCString("serialized_metrics", &serialized_metrics);
+
+    if (enabled_statsd) {
+        android::util::BytesField bf_serialized(serialized_metrics ? serialized_metrics : NULL,
+                                                serialized_metrics ? strlen(serialized_metrics)
+                                                                   : 0);
+        android::util::stats_write(android::util::MEDIAMETRICS_MEDIADRM_REPORTED,
+                                   timestamp, pkgName.c_str(), pkgVersionCode,
+                                   mediaApexVersion,
+                                   vendor, description,
+                                   bf_serialized);
+    } else {
+        ALOGV("NOT sending: mediadrm private data (len=%zu)",
+              serialized_metrics ? strlen(serialized_metrics) : 0);
+    }
+
+    free(vendor);
+    free(description);
+    free(serialized_metrics);
+    return true;
+}
+
+// widevineCDM
+bool statsd_widevineCDM(MediaAnalyticsItem *item)
+{
+    if (item == NULL) return false;
+
+    nsecs_t timestamp = item->getTimestamp();
+    std::string pkgName = item->getPkgName();
+    int64_t pkgVersionCode = item->getPkgVersionCode();
+    int64_t mediaApexVersion = 0;
+
+    char *serialized_metrics = NULL;
+    (void) item->getCString("serialized_metrics", &serialized_metrics);
+
+    if (enabled_statsd) {
+        android::util::BytesField bf_serialized(serialized_metrics ? serialized_metrics : NULL,
+                                                serialized_metrics ? strlen(serialized_metrics)
+                                                                   : 0);
+        android::util::stats_write(android::util::MEDIAMETRICS_DRM_WIDEVINE_REPORTED,
+                                   timestamp, pkgName.c_str(), pkgVersionCode,
+                                   mediaApexVersion,
+                                   bf_serialized);
+    } else {
+        ALOGV("NOT sending: widevine private data (len=%zu)",
+              serialized_metrics ? strlen(serialized_metrics) : 0);
+    }
+
+    free(serialized_metrics);
+    return true;
+}
+
+} // namespace android
diff --git a/services/mediaanalytics/statsd_extractor.cpp b/services/mediaanalytics/statsd_extractor.cpp
new file mode 100644
index 0000000..395c912
--- /dev/null
+++ b/services/mediaanalytics/statsd_extractor.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2019 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 "statsd_extractor"
+#include <utils/Log.h>
+
+#include <dirent.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <statslog.h>
+
+#include "MediaAnalyticsService.h"
+#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
+#include "iface_statsd.h"
+
+namespace android {
+
+bool statsd_extractor(MediaAnalyticsItem *item)
+{
+    if (item == NULL) return false;
+
+    // these go into the statsd wrapper
+    nsecs_t timestamp = item->getTimestamp();
+    std::string pkgName = item->getPkgName();
+    int64_t pkgVersionCode = item->getPkgVersionCode();
+    int64_t mediaApexVersion = 0;
+
+
+    // the rest into our own proto
+    //
+    ::android::stats::mediametrics::ExtractorData metrics_proto;
+
+    // flesh out the protobuf we'll hand off with our data
+    //
+
+    // android.media.mediaextractor.fmt         string
+    char *fmt = NULL;
+    if (item->getCString("android.media.mediaextractor.fmt", &fmt)) {
+        metrics_proto.set_format(fmt);
+    }
+    // android.media.mediaextractor.mime        string
+    char *mime = NULL;
+    if (item->getCString("android.media.mediaextractor.mime", &mime)) {
+        metrics_proto.set_mime(mime);
+    }
+    // android.media.mediaextractor.ntrk        int32
+    int32_t ntrk = -1;
+    if (item->getInt32("android.media.mediaextractor.ntrk", &ntrk)) {
+        metrics_proto.set_tracks(ntrk);
+    }
+
+    std::string serialized;
+    if (!metrics_proto.SerializeToString(&serialized)) {
+        ALOGE("Failed to serialize extractor metrics");
+        return false;
+    }
+
+    if (enabled_statsd) {
+        android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
+        (void)android::util::stats_write(android::util::MEDIAMETRICS_EXTRACTOR_REPORTED,
+                                   timestamp, pkgName.c_str(), pkgVersionCode,
+                                   mediaApexVersion,
+                                   bf_serialized);
+
+    } else {
+        ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str()));
+    }
+
+    // must free the strings that we were given
+    free(fmt);
+    free(mime);
+
+    return true;
+}
+
+};
diff --git a/services/mediaanalytics/statsd_nuplayer.cpp b/services/mediaanalytics/statsd_nuplayer.cpp
new file mode 100644
index 0000000..5ec118a
--- /dev/null
+++ b/services/mediaanalytics/statsd_nuplayer.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2019 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 "statsd_nuplayer"
+#include <utils/Log.h>
+
+#include <dirent.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <statslog.h>
+
+#include "MediaAnalyticsService.h"
+#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
+#include "iface_statsd.h"
+
+namespace android {
+
+/*
+ *  handles nuplayer AND nuplayer2
+ *  checks for the union of what the two players generate
+ */
+bool statsd_nuplayer(MediaAnalyticsItem *item)
+{
+    if (item == NULL) return false;
+
+    // these go into the statsd wrapper
+    nsecs_t timestamp = item->getTimestamp();
+    std::string pkgName = item->getPkgName();
+    int64_t pkgVersionCode = item->getPkgVersionCode();
+    int64_t mediaApexVersion = 0;
+
+
+    // the rest into our own proto
+    //
+    ::android::stats::mediametrics::NuPlayerData metrics_proto;
+
+    // flesh out the protobuf we'll hand off with our data
+    //
+
+    // differentiate between nuplayer and nuplayer2
+    metrics_proto.set_whichplayer(item->getKey().c_str());
+
+    char *video_mime = NULL;
+    if (item->getCString("android.media.mediaplayer.video.mime", &video_mime)) {
+        metrics_proto.set_video_mime(video_mime);
+    }
+    char *video_codec = NULL;
+    if (item->getCString("android.media.mediaplayer.video.codec", &video_codec)) {
+        metrics_proto.set_video_codec(video_codec);
+    }
+
+    int32_t width = -1;
+    if (item->getInt32("android.media.mediaplayer.width", &width)) {
+        metrics_proto.set_width(width);
+    }
+    int32_t height = -1;
+    if (item->getInt32("android.media.mediaplayer.height", &height)) {
+        metrics_proto.set_height(height);
+    }
+
+    int64_t frames = -1;
+    if (item->getInt64("android.media.mediaplayer.frames", &frames)) {
+        metrics_proto.set_frames(frames);
+    }
+    int64_t frames_dropped = -1;
+    if (item->getInt64("android.media.mediaplayer.dropped", &frames_dropped)) {
+        metrics_proto.set_frames_dropped(frames_dropped);
+    }
+    int64_t frames_dropped_startup = -1;
+    if (item->getInt64("android.media.mediaplayer.startupdropped", &frames_dropped_startup)) {
+        metrics_proto.set_frames_dropped_startup(frames_dropped_startup);
+    }
+    double fps = -1.0;
+    if (item->getDouble("android.media.mediaplayer.fps", &fps)) {
+        metrics_proto.set_framerate(fps);
+    }
+
+    char *audio_mime = NULL;
+    if (item->getCString("android.media.mediaplayer.audio.mime", &audio_mime)) {
+        metrics_proto.set_audio_mime(audio_mime);
+    }
+    char *audio_codec = NULL;
+    if (item->getCString("android.media.mediaplayer.audio.codec", &audio_codec)) {
+        metrics_proto.set_audio_codec(audio_codec);
+    }
+
+    int64_t duration_ms = -1;
+    if (item->getInt64("android.media.mediaplayer.durationMs", &duration_ms)) {
+        metrics_proto.set_duration_millis(duration_ms);
+    }
+    int64_t playing_ms = -1;
+    if (item->getInt64("android.media.mediaplayer.playingMs", &playing_ms)) {
+        metrics_proto.set_playing_millis(playing_ms);
+    }
+
+    int32_t err = -1;
+    if (item->getInt32("android.media.mediaplayer.err", &err)) {
+        metrics_proto.set_error(err);
+    }
+    int32_t error_code = -1;
+    if (item->getInt32("android.media.mediaplayer.errcode", &error_code)) {
+        metrics_proto.set_error_code(error_code);
+    }
+    char *error_state = NULL;
+    if (item->getCString("android.media.mediaplayer.errstate", &error_state)) {
+        metrics_proto.set_error_state(error_state);
+    }
+
+    char *data_source_type = NULL;
+    if (item->getCString("android.media.mediaplayer.dataSource", &data_source_type)) {
+        metrics_proto.set_data_source_type(data_source_type);
+    }
+
+    int64_t rebufferingMs = -1;
+    if (item->getInt64("android.media.mediaplayer.rebufferingMs", &rebufferingMs)) {
+        metrics_proto.set_rebuffering_millis(rebufferingMs);
+    }
+    int32_t rebuffers = -1;
+    if (item->getInt32("android.media.mediaplayer.rebuffers", &rebuffers)) {
+        metrics_proto.set_rebuffers(rebuffers);
+    }
+    int32_t rebufferExit = -1;
+    if (item->getInt32("android.media.mediaplayer.rebufferExit", &rebufferExit)) {
+        metrics_proto.set_rebuffer_at_exit(rebufferExit);
+    }
+
+
+    std::string serialized;
+    if (!metrics_proto.SerializeToString(&serialized)) {
+        ALOGE("Failed to serialize nuplayer metrics");
+        return false;
+    }
+
+    if (enabled_statsd) {
+        android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
+        (void)android::util::stats_write(android::util::MEDIAMETRICS_NUPLAYER_REPORTED,
+                                   timestamp, pkgName.c_str(), pkgVersionCode,
+                                   mediaApexVersion,
+                                   bf_serialized);
+
+    } else {
+        ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str()));
+    }
+
+    // must free the strings that we were given
+    free(video_mime);
+    free(video_codec);
+    free(audio_mime);
+    free(audio_codec);
+    free(error_state);
+    free(data_source_type);
+
+    return true;
+}
+
+};
diff --git a/services/mediaanalytics/statsd_recorder.cpp b/services/mediaanalytics/statsd_recorder.cpp
new file mode 100644
index 0000000..4d981b4
--- /dev/null
+++ b/services/mediaanalytics/statsd_recorder.cpp
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2019 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 "statsd_recorder"
+#include <utils/Log.h>
+
+#include <dirent.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <statslog.h>
+
+#include "MediaAnalyticsService.h"
+#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
+#include "iface_statsd.h"
+
+namespace android {
+
+bool statsd_recorder(MediaAnalyticsItem *item)
+{
+    if (item == NULL) return false;
+
+    // these go into the statsd wrapper
+    nsecs_t timestamp = item->getTimestamp();
+    std::string pkgName = item->getPkgName();
+    int64_t pkgVersionCode = item->getPkgVersionCode();
+    int64_t mediaApexVersion = 0;
+
+
+    // the rest into our own proto
+    //
+    ::android::stats::mediametrics::RecorderData metrics_proto;
+
+    // flesh out the protobuf we'll hand off with our data
+    //
+
+    // string kRecorderAudioMime = "android.media.mediarecorder.audio.mime";
+    char *audio_mime = NULL;
+    if (item->getCString("android.media.mediarecorder.audio.mime", &audio_mime)) {
+        metrics_proto.set_audio_mime(audio_mime);
+    }
+    // string kRecorderVideoMime = "android.media.mediarecorder.video.mime";
+    char *video_mime = NULL;
+    if (item->getCString("android.media.mediarecorder.video.mime", &video_mime)) {
+        metrics_proto.set_video_mime(video_mime);
+    }
+    // int32 kRecorderVideoProfile = "android.media.mediarecorder.video-encoder-profile";
+    int32_t videoProfile = -1;
+    if (item->getInt32("android.media.mediarecorder.video-encoder-profile", &videoProfile)) {
+        metrics_proto.set_video_profile(videoProfile);
+    }
+    // int32 kRecorderVideoLevel = "android.media.mediarecorder.video-encoder-level";
+    int32_t videoLevel = -1;
+    if (item->getInt32("android.media.mediarecorder.video-encoder-level", &videoLevel)) {
+        metrics_proto.set_video_level(videoLevel);
+    }
+    // int32 kRecorderWidth = "android.media.mediarecorder.width";
+    int32_t width = -1;
+    if (item->getInt32("android.media.mediarecorder.width", &width)) {
+        metrics_proto.set_width(width);
+    }
+    // int32 kRecorderHeight = "android.media.mediarecorder.height";
+    int32_t height = -1;
+    if (item->getInt32("android.media.mediarecorder.height", &height)) {
+        metrics_proto.set_height(height);
+    }
+    // int32 kRecorderRotation = "android.media.mediarecorder.rotation";
+    int32_t rotation = -1;                      // default to 0?
+    if (item->getInt32("android.media.mediarecorder.rotation", &rotation)) {
+        metrics_proto.set_rotation(rotation);
+    }
+    // int32 kRecorderFrameRate = "android.media.mediarecorder.frame-rate";
+    int32_t framerate = -1;
+    if (item->getInt32("android.media.mediarecorder.frame-rate", &framerate)) {
+        metrics_proto.set_framerate(framerate);
+    }
+
+    // int32 kRecorderCaptureFps = "android.media.mediarecorder.capture-fps";
+    int32_t captureFps = -1;
+    if (item->getInt32("android.media.mediarecorder.capture-fps", &captureFps)) {
+        metrics_proto.set_capture_fps(captureFps);
+    }
+    // double kRecorderCaptureFpsEnable = "android.media.mediarecorder.capture-fpsenable";
+    double captureFpsEnable = -1;
+    if (item->getDouble("android.media.mediarecorder.capture-fpsenable", &captureFpsEnable)) {
+        metrics_proto.set_capture_fps_enable(captureFpsEnable);
+    }
+
+    // int64 kRecorderDurationMs = "android.media.mediarecorder.durationMs";
+    int64_t durationMs = -1;
+    if (item->getInt64("android.media.mediarecorder.durationMs", &durationMs)) {
+        metrics_proto.set_duration_millis(durationMs);
+    }
+    // int64 kRecorderPaused = "android.media.mediarecorder.pausedMs";
+    int64_t pausedMs = -1;
+    if (item->getInt64("android.media.mediarecorder.pausedMs", &pausedMs)) {
+        metrics_proto.set_paused_millis(pausedMs);
+    }
+    // int32 kRecorderNumPauses = "android.media.mediarecorder.NPauses";
+    int32_t pausedCount = -1;
+    if (item->getInt32("android.media.mediarecorder.NPauses", &pausedCount)) {
+        metrics_proto.set_paused_count(pausedCount);
+    }
+
+    // int32 kRecorderAudioBitrate = "android.media.mediarecorder.audio-bitrate";
+    int32_t audioBitrate = -1;
+    if (item->getInt32("android.media.mediarecorder.audio-bitrate", &audioBitrate)) {
+        metrics_proto.set_audio_bitrate(audioBitrate);
+    }
+    // int32 kRecorderAudioChannels = "android.media.mediarecorder.audio-channels";
+    int32_t audioChannels = -1;
+    if (item->getInt32("android.media.mediarecorder.audio-channels", &audioChannels)) {
+        metrics_proto.set_audio_channels(audioChannels);
+    }
+    // int32 kRecorderAudioSampleRate = "android.media.mediarecorder.audio-samplerate";
+    int32_t audioSampleRate = -1;
+    if (item->getInt32("android.media.mediarecorder.audio-samplerate", &audioSampleRate)) {
+        metrics_proto.set_audio_samplerate(audioSampleRate);
+    }
+
+    // int32 kRecorderMovieTimescale = "android.media.mediarecorder.movie-timescale";
+    int32_t movieTimescale = -1;
+    if (item->getInt32("android.media.mediarecorder.movie-timescale", &movieTimescale)) {
+        metrics_proto.set_movie_timescale(movieTimescale);
+    }
+    // int32 kRecorderAudioTimescale = "android.media.mediarecorder.audio-timescale";
+    int32_t audioTimescale = -1;
+    if (item->getInt32("android.media.mediarecorder.audio-timescale", &audioTimescale)) {
+        metrics_proto.set_audio_timescale(audioTimescale);
+    }
+    // int32 kRecorderVideoTimescale = "android.media.mediarecorder.video-timescale";
+    int32_t videoTimescale = -1;
+    if (item->getInt32("android.media.mediarecorder.video-timescale", &videoTimescale)) {
+        metrics_proto.set_video_timescale(videoTimescale);
+    }
+
+    // int32 kRecorderVideoBitrate = "android.media.mediarecorder.video-bitrate";
+    int32_t videoBitRate = -1;
+    if (item->getInt32("android.media.mediarecorder.video-bitrate", &videoBitRate)) {
+        metrics_proto.set_video_bitrate(videoBitRate);
+    }
+    // int32 kRecorderVideoIframeInterval = "android.media.mediarecorder.video-iframe-interval";
+    int32_t iFrameInterval = -1;
+    if (item->getInt32("android.media.mediarecorder.video-iframe-interval", &iFrameInterval)) {
+        metrics_proto.set_iframe_interval(iFrameInterval);
+    }
+
+    std::string serialized;
+    if (!metrics_proto.SerializeToString(&serialized)) {
+        ALOGE("Failed to serialize recorder metrics");
+        return false;
+    }
+
+    if (enabled_statsd) {
+        android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
+        (void)android::util::stats_write(android::util::MEDIAMETRICS_RECORDER_REPORTED,
+                                   timestamp, pkgName.c_str(), pkgVersionCode,
+                                   mediaApexVersion,
+                                   bf_serialized);
+
+    } else {
+        ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str()));
+    }
+
+    // must free the strings that we were given
+    free(audio_mime);
+    free(video_mime);
+
+    return true;
+}
+
+};
diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk
index 473e21c..ecd437b 100644
--- a/services/mediacodec/Android.mk
+++ b/services/mediacodec/Android.mk
@@ -22,6 +22,7 @@
     libstagefright_soft_vorbisdec \
     libstagefright_soft_vpxdec \
     libstagefright_soft_vpxenc \
+    libstagefright_softomx_plugin \
 
 # service executable
 include $(CLEAR_VARS)
diff --git a/services/mediacodec/main_codecservice.cpp b/services/mediacodec/main_codecservice.cpp
index 6ffbd26..f668c33 100644
--- a/services/mediacodec/main_codecservice.cpp
+++ b/services/mediacodec/main_codecservice.cpp
@@ -49,12 +49,6 @@
 
     // Default codec services
     using namespace ::android::hardware::media::omx::V1_0;
-    sp<IOmxStore> omxStore = new implementation::OmxStore();
-    if (omxStore == nullptr) {
-        LOG(ERROR) << "Cannot create IOmxStore HAL service.";
-    } else if (omxStore->registerAsService() != OK) {
-        LOG(ERROR) << "Cannot register IOmxStore HAL service.";
-    }
     sp<IOmx> omx = new implementation::Omx();
     if (omx == nullptr) {
         LOG(ERROR) << "Cannot create IOmx HAL service.";
@@ -63,6 +57,12 @@
     } else {
         LOG(INFO) << "IOmx HAL service created.";
     }
+    sp<IOmxStore> omxStore = new implementation::OmxStore(omx);
+    if (omxStore == nullptr) {
+        LOG(ERROR) << "Cannot create IOmxStore HAL service.";
+    } else if (omxStore->registerAsService() != OK) {
+        LOG(ERROR) << "Cannot register IOmxStore HAL service.";
+    }
 
     ::android::hardware::joinRpcThreadpool();
 }
diff --git a/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy b/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy
index f75515a..9042cd7 100644
--- a/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy
+++ b/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy
@@ -42,6 +42,7 @@
 lseek: 1
 rt_sigprocmask: 1
 openat: 1
+open: 1
 fstat64: 1
 write: 1
 nanosleep: 1
@@ -49,6 +50,7 @@
 set_tid_address: 1
 getdents64: 1
 readlinkat: 1
+readlink: 1
 read: 1
 pread64: 1
 fstatfs64: 1
diff --git a/services/mediaextractor/Android.bp b/services/mediaextractor/Android.bp
new file mode 100644
index 0000000..b812244
--- /dev/null
+++ b/services/mediaextractor/Android.bp
@@ -0,0 +1,71 @@
+// service library
+cc_library_shared {
+    name: "libmediaextractorservice",
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    srcs: ["MediaExtractorService.cpp"],
+
+    shared_libs: [
+        "libmedia",
+        "libstagefright",
+        "libbinder",
+        "libutils",
+    ],
+}
+
+// service executable
+cc_binary {
+    name: "mediaextractor",
+
+    srcs: ["main_extractorservice.cpp"],
+    shared_libs: [
+        "libmedia",
+        "libmediaextractorservice",
+        "libbinder",
+        "libutils",
+        "liblog",
+        "libavservices_minijail",
+    ],
+    target: {
+        android: {
+            product_variables: {
+                malloc_not_svelte: {
+                    // Scudo increases memory footprint, so only enable on
+                    // non-svelte devices.
+                    shared_libs: ["libc_scudo"],
+                },
+            },
+        },
+    },
+    init_rc: ["mediaextractor.rc"],
+
+    include_dirs: ["frameworks/av/media/libmedia"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
+
+prebuilt_etc {
+    name: "mediaextractor.policy",
+    sub_dir: "seccomp_policy",
+    arch: {
+        arm: {
+            src: "seccomp_policy/mediaextractor-arm.policy",
+        },
+        arm64: {
+            src: "seccomp_policy/mediaextractor-arm64.policy",
+        },
+        x86: {
+            src: "seccomp_policy/mediaextractor-x86.policy",
+        },
+        x86_64: {
+            src: "seccomp_policy/mediaextractor-x86_64.policy",
+        },
+    },
+    required: ["crash_dump.policy"],
+}
+
diff --git a/services/mediaextractor/Android.mk b/services/mediaextractor/Android.mk
deleted file mode 100644
index 9db6ed1..0000000
--- a/services/mediaextractor/Android.mk
+++ /dev/null
@@ -1,43 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-# service library
-include $(CLEAR_VARS)
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_SRC_FILES := \
-    MediaExtractorService.cpp
-
-LOCAL_SHARED_LIBRARIES := libmedia libstagefright libbinder libutils
-LOCAL_MODULE:= libmediaextractorservice
-include $(BUILD_SHARED_LIBRARY)
-
-
-# service executable
-include $(CLEAR_VARS)
-# seccomp filters are defined for the following architectures:
-LOCAL_REQUIRED_MODULES_arm := crash_dump.policy mediaextractor.policy
-LOCAL_REQUIRED_MODULES_arm64 := crash_dump.policy mediaextractor.policy
-LOCAL_REQUIRED_MODULES_x86 := crash_dump.policy mediaextractor.policy
-LOCAL_REQUIRED_MODULES_x86_64 := crash_dump.policy mediaextractor.policy
-
-LOCAL_SRC_FILES := main_extractorservice.cpp
-ifneq (true, $(filter true, $(MALLOC_SVELTE)))
-# Scudo increases memory footprint, so only use on non-svelte configs.
-LOCAL_SHARED_LIBRARIES := libc_scudo
-endif
-LOCAL_SHARED_LIBRARIES += libmedia libmediaextractorservice libbinder libutils \
-    liblog libandroidicu libavservices_minijail
-LOCAL_MODULE:= mediaextractor
-LOCAL_INIT_RC := mediaextractor.rc
-LOCAL_C_INCLUDES := frameworks/av/media/libmedia
-LOCAL_CFLAGS := -Wall -Werror
-include $(BUILD_EXECUTABLE)
-
-# service seccomp filter
-ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), arm arm64 x86 x86_64))
-include $(CLEAR_VARS)
-LOCAL_MODULE := mediaextractor.policy
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/seccomp_policy
-LOCAL_SRC_FILES := seccomp_policy/mediaextractor-$(TARGET_ARCH).policy
-include $(BUILD_PREBUILT)
-endif
diff --git a/services/mediaextractor/MediaExtractorService.cpp b/services/mediaextractor/MediaExtractorService.cpp
index de5c3e4..36e084b 100644
--- a/services/mediaextractor/MediaExtractorService.cpp
+++ b/services/mediaextractor/MediaExtractorService.cpp
@@ -30,7 +30,9 @@
 namespace android {
 
 MediaExtractorService::MediaExtractorService()
-        : BnMediaExtractorService() { }
+        : BnMediaExtractorService() {
+    MediaExtractorFactory::LoadExtractors();
+}
 
 sp<IMediaExtractor> MediaExtractorService::makeExtractor(
         const sp<IDataSource> &remoteSource, const char *mime) {
diff --git a/services/mediaextractor/main_extractorservice.cpp b/services/mediaextractor/main_extractorservice.cpp
index bb9a56b..3c4125b 100644
--- a/services/mediaextractor/main_extractorservice.cpp
+++ b/services/mediaextractor/main_extractorservice.cpp
@@ -15,7 +15,6 @@
 ** limitations under the License.
 */
 
-#include <aicu/AIcu.h>
 #include <fcntl.h>
 #include <sys/prctl.h>
 #include <sys/wait.h>
@@ -37,7 +36,7 @@
 using namespace android;
 
 static const char kSystemSeccompPolicyPath[] =
-        "/system/etc/seccomp_policy/mediaextractor.policy";
+        "/apex/com.android.media/etc/seccomp_policy/mediaextractor.policy";
 static const char kVendorSeccompPolicyPath[] =
         "/vendor/etc/seccomp_policy/mediaextractor.policy";
 
@@ -58,8 +57,6 @@
 
     SetUpMinijail(kSystemSeccompPolicyPath, kVendorSeccompPolicyPath);
 
-    AIcu_initializeIcuOrDie();
-
     strcpy(argv[0], "media.extractor");
     sp<ProcessState> proc(ProcessState::self());
     sp<IServiceManager> sm = defaultServiceManager();