Merge "Fix android.media.cts.ImageReaderDecoderTest fail on MT6580 project" into qt-aml-media-dev
diff --git a/camera/ndk/impl/ACameraCaptureSession.cpp b/camera/ndk/impl/ACameraCaptureSession.cpp
index d6f1412..68db233 100644
--- a/camera/ndk/impl/ACameraCaptureSession.cpp
+++ b/camera/ndk/impl/ACameraCaptureSession.cpp
@@ -33,7 +33,9 @@
         dev->unlockDevice();
     }
     // Fire onClosed callback
-    (*mUserSessionCallback.onClosed)(mUserSessionCallback.context, this);
+    if (mUserSessionCallback.onClosed != nullptr) {
+        (*mUserSessionCallback.onClosed)(mUserSessionCallback.context, this);
+    }
     ALOGV("~ACameraCaptureSession: %p is deleted", this);
 }
 
diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp
index d24cb81..46a8dae 100644
--- a/camera/ndk/impl/ACameraDevice.cpp
+++ b/camera/ndk/impl/ACameraDevice.cpp
@@ -29,7 +29,7 @@
 #include "ACameraCaptureSession.inc"
 
 ACameraDevice::~ACameraDevice() {
-    mDevice->stopLooper();
+    mDevice->stopLooperAndDisconnect();
 }
 
 namespace android {
@@ -112,19 +112,7 @@
     }
 }
 
-// Device close implementaiton
-CameraDevice::~CameraDevice() {
-    sp<ACameraCaptureSession> session = mCurrentSession.promote();
-    {
-        Mutex::Autolock _l(mDeviceLock);
-        if (!isClosed()) {
-            disconnectLocked(session);
-        }
-        LOG_ALWAYS_FATAL_IF(mCbLooper != nullptr,
-                "CameraDevice looper should've been stopped before ~CameraDevice");
-        mCurrentSession = nullptr;
-    }
-}
+CameraDevice::~CameraDevice() { }
 
 void
 CameraDevice::postSessionMsgAndCleanup(sp<AMessage>& msg) {
@@ -892,8 +880,14 @@
     return;
 }
 
-void CameraDevice::stopLooper() {
+void CameraDevice::stopLooperAndDisconnect() {
     Mutex::Autolock _l(mDeviceLock);
+    sp<ACameraCaptureSession> session = mCurrentSession.promote();
+    if (!isClosed()) {
+        disconnectLocked(session);
+    }
+    mCurrentSession = nullptr;
+
     if (mCbLooper != nullptr) {
       mCbLooper->unregisterHandler(mHandler->id());
       mCbLooper->stop();
diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h
index 7a35bf0..6c2ceb3 100644
--- a/camera/ndk/impl/ACameraDevice.h
+++ b/camera/ndk/impl/ACameraDevice.h
@@ -40,6 +40,7 @@
 
 #include <camera/NdkCameraManager.h>
 #include <camera/NdkCameraCaptureSession.h>
+
 #include "ACameraMetadata.h"
 
 namespace android {
@@ -110,7 +111,7 @@
     inline ACameraDevice* getWrapper() const { return mWrapper; };
 
     // Stop the looper thread and unregister the handler
-    void stopLooper();
+    void stopLooperAndDisconnect();
 
   private:
     friend ACameraCaptureSession;
diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
index 35c8355..e511a3f 100644
--- a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
+++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
@@ -45,7 +45,7 @@
 using namespace android;
 
 ACameraDevice::~ACameraDevice() {
-    mDevice->stopLooper();
+    mDevice->stopLooperAndDisconnect();
 }
 
 namespace android {
@@ -125,19 +125,7 @@
     }
 }
 
-// Device close implementaiton
-CameraDevice::~CameraDevice() {
-    sp<ACameraCaptureSession> session = mCurrentSession.promote();
-    {
-        Mutex::Autolock _l(mDeviceLock);
-        if (!isClosed()) {
-            disconnectLocked(session);
-        }
-        mCurrentSession = nullptr;
-        LOG_ALWAYS_FATAL_IF(mCbLooper != nullptr,
-            "CameraDevice looper should've been stopped before ~CameraDevice");
-    }
-}
+CameraDevice::~CameraDevice() { }
 
 void
 CameraDevice::postSessionMsgAndCleanup(sp<AMessage>& msg) {
@@ -1388,6 +1376,7 @@
             // before cbh goes out of scope and causing we call the session
             // destructor while holding device lock
             cbh.mSession.clear();
+
             postSessionMsgAndCleanup(msg);
         }
 
@@ -1400,8 +1389,13 @@
     }
 }
 
-void CameraDevice::stopLooper() {
+void CameraDevice::stopLooperAndDisconnect() {
     Mutex::Autolock _l(mDeviceLock);
+    sp<ACameraCaptureSession> session = mCurrentSession.promote();
+    if (!isClosed()) {
+        disconnectLocked(session);
+    }
+    mCurrentSession = nullptr;
     if (mCbLooper != nullptr) {
       mCbLooper->unregisterHandler(mHandler->id());
       mCbLooper->stop();
diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.h b/camera/ndk/ndk_vendor/impl/ACameraDevice.h
index 9e034c4..7fc699e 100644
--- a/camera/ndk/ndk_vendor/impl/ACameraDevice.h
+++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.h
@@ -36,6 +36,7 @@
 
 #include <camera/NdkCameraManager.h>
 #include <camera/NdkCameraCaptureSession.h>
+
 #include "ACameraMetadata.h"
 #include "utils.h"
 
@@ -134,7 +135,7 @@
     inline ACameraDevice* getWrapper() const { return mWrapper; };
 
     // Stop the looper thread and unregister the handler
-    void stopLooper();
+    void stopLooperAndDisconnect();
 
   private:
     friend ACameraCaptureSession;
diff --git a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp
index 7ab0124..938b5f5 100644
--- a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp
+++ b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp
@@ -253,21 +253,9 @@
         return true;
     }
 
-    static void onDeviceDisconnected(void* /*obj*/, ACameraDevice* /*device*/) {}
-
-    static void onDeviceError(void* /*obj*/, ACameraDevice* /*device*/, int /*errorCode*/) {}
-
-    static void onSessionClosed(void* /*obj*/, ACameraCaptureSession* /*session*/) {}
-
-    static void onSessionReady(void* /*obj*/, ACameraCaptureSession* /*session*/) {}
-
-    static void onSessionActive(void* /*obj*/, ACameraCaptureSession* /*session*/) {}
-
    private:
-    ACameraDevice_StateCallbacks mDeviceCb{this, onDeviceDisconnected,
-                                           onDeviceError};
-    ACameraCaptureSession_stateCallbacks mSessionCb{
-        this, onSessionClosed, onSessionReady, onSessionActive};
+    ACameraDevice_StateCallbacks mDeviceCb{this, nullptr, nullptr};
+    ACameraCaptureSession_stateCallbacks mSessionCb{ this, nullptr, nullptr, nullptr};
 
     native_handle_t* mImgReaderAnw = nullptr;  // not owned by us.
 
diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
index d74bc53..71bb218 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
@@ -797,37 +797,55 @@
 }
 
 Return<Status> DrmPlugin::releaseSecureStops(const SecureStopRelease& ssRelease) {
-    if (ssRelease.opaqueData.size() == 0) {
+    // OpaqueData starts with 4 byte decimal integer string
+    const size_t kFourBytesOffset = 4;
+    if (ssRelease.opaqueData.size() < kFourBytesOffset) {
+        ALOGE("Invalid secureStopRelease length");
         return Status::BAD_VALUE;
     }
 
     Status status = Status::OK;
     std::vector<uint8_t> input = toVector(ssRelease.opaqueData);
 
+    if (input.size() < kSecureStopIdSize + kFourBytesOffset) {
+        // The minimum size of SecureStopRelease has to contain
+        // a 4 bytes count and one secureStop id
+        ALOGE("Total size of secureStops is too short");
+        return Status::BAD_VALUE;
+    }
+
     // The format of opaqueData is shared between the server
     // and the drm service. The clearkey implementation consists of:
     //    count - number of secure stops
     //    list of fixed length secure stops
-    size_t countBufferSize = sizeof(uint32_t);
     uint32_t count = 0;
     sscanf(reinterpret_cast<char*>(input.data()), "%04" PRIu32, &count);
 
     // Avoid divide by 0 below.
     if (count == 0) {
+        ALOGE("Invalid 0 secureStop count");
         return Status::BAD_VALUE;
     }
 
-    size_t secureStopSize = (input.size() - countBufferSize) / count;
-    uint8_t buffer[secureStopSize];
-    size_t offset = countBufferSize; // skip the count
+    // Computes the fixed length secureStop size
+    size_t secureStopSize = (input.size() - kFourBytesOffset) / count;
+    if (secureStopSize < kSecureStopIdSize) {
+        // A valid secureStop contains the id plus data
+        ALOGE("Invalid secureStop size");
+        return Status::BAD_VALUE;
+    }
+    uint8_t* buffer = new uint8_t[secureStopSize];
+    size_t offset = kFourBytesOffset; // skip the count
     for (size_t i = 0; i < count; ++i, offset += secureStopSize) {
         memcpy(buffer, input.data() + offset, secureStopSize);
-        std::vector<uint8_t> id(buffer, buffer + kSecureStopIdSize);
 
+        // A secureStop contains id+data, we only use the id for removal
+        std::vector<uint8_t> id(buffer, buffer + kSecureStopIdSize);
         status = removeSecureStop(toHidlVec(id));
         if (Status::OK != status) break;
     }
 
+    delete[] buffer;
     return status;
 }
 
diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp
index 8c8e6d1..0773387 100644
--- a/media/extractors/mp4/ItemTable.cpp
+++ b/media/extractors/mp4/ItemTable.cpp
@@ -700,8 +700,8 @@
     }
 
 private:
-    uint32_t mWidth;
-    uint32_t mHeight;
+    int32_t mWidth;
+    int32_t mHeight;
 };
 
 status_t IspeBox::parse(off64_t offset, size_t size) {
@@ -715,12 +715,19 @@
     if (size < 8) {
         return ERROR_MALFORMED;
     }
-    if (!source()->getUInt32(offset, &mWidth)
-            || !source()->getUInt32(offset + 4, &mHeight)) {
+    if (!source()->getUInt32(offset, (uint32_t *)&mWidth)
+            || !source()->getUInt32(offset + 4, (uint32_t *)&mHeight)) {
         return ERROR_IO;
     }
-    ALOGV("property ispe: %dx%d", mWidth, mHeight);
 
+    // Validate that the dimension doesn't cause overflow on calculated max input size.
+    // Max input size is width*height*1.5, restrict width*height to 1<<29 so that
+    // we don't need to cast to int64_t when doing mults.
+    if (mWidth <= 0 || mHeight <= 0 || mWidth > (1 << 29) / mHeight) {
+        return ERROR_MALFORMED;
+    }
+
+    ALOGV("property ispe: %dx%d", mWidth, mHeight);
     return OK;
 }
 
@@ -1524,8 +1531,9 @@
             default: break; // don't set if invalid
         }
     }
+    // we validated no overflow in IspeBox::parse()
     AMediaFormat_setInt32(meta,
-            AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 1.5);
+            AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 3 / 2);
 
     if (!image->thumbnails.empty()) {
         ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(image->thumbnails[0]);
@@ -1561,8 +1569,9 @@
                 AMEDIAFORMAT_KEY_TILE_WIDTH, image->width);
         AMediaFormat_setInt32(meta,
                 AMEDIAFORMAT_KEY_TILE_HEIGHT, image->height);
+        // we validated no overflow in IspeBox::parse()
         AMediaFormat_setInt32(meta,
-                AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 1.5);
+                AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 3 / 2);
     }
 
     if (image->hvcc == NULL) {
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 12982ed..86ed610 100755
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -1561,8 +1561,12 @@
         {
             *offset += chunk_size;
 
-            if (mLastTrack == NULL)
+            // the absolute minimum size of a compliant mett box is 11 bytes:
+            // 6 byte reserved, 2 byte index, null byte, one char mime_format, null byte
+            // The resulting mime_format would be invalid at that size though.
+            if (mLastTrack == NULL || chunk_data_size < 11) {
                 return ERROR_MALFORMED;
+            }
 
             auto buffer = heapbuffer<uint8_t>(chunk_data_size);
             if (buffer.get() == NULL) {
@@ -1574,10 +1578,24 @@
                 return ERROR_IO;
             }
 
+            // ISO-14496-12:
+            // int8 reserved[6];               // should be all zeroes
+            // int16_t data_reference_index;
+            // char content_encoding[];        // null terminated, optional (= just the null byte)
+            // char mime_format[];             // null terminated, mandatory
+            // optional other boxes
+            //
+            // API < 29:
+            // char mime_format[];             // null terminated
+            //
+            // API >= 29
+            // char mime_format[];             // null terminated
+            // char mime_format[];             // null terminated
+
             // Prior to API 29, the metadata track was not compliant with ISO/IEC
             // 14496-12-2015. This led to some ISO-compliant parsers failing to read the
             // metatrack. As of API 29 and onwards, a change was made to metadata track to
-            // make it compliant with the standard. The workaround is to write the
+            // make it somewhat compatible with the standard. The workaround is to write the
             // null-terminated mime_format string twice. This allows compliant parsers to
             // read the missing reserved, data_reference_index, and content_encoding fields
             // from the first mime_type string. The actual mime_format field would then be
@@ -1586,27 +1604,27 @@
             // as it would only read the first null-terminated mime_format string. To enable
             // reading metadata tracks generated from both the non-compliant and compliant
             // formats, a check needs to be done to see which format is used.
-            int null_pos = 0;
-            const unsigned char *str = buffer.get();
-            while (null_pos < chunk_data_size) {
-              if (*(str + null_pos) == '\0') {
-                break;
-              }
-              ++null_pos;
-            }
+            const char *str = (const char*) buffer.get();
+            size_t string_length = strnlen(str, chunk_data_size);
 
-            if (null_pos == chunk_data_size - 1) {
-              // This is not a standard ompliant metadata track.
-              String8 mimeFormat((const char *)(buffer.get()), chunk_data_size);
-              AMediaFormat_setString(mLastTrack->meta,
-                  AMEDIAFORMAT_KEY_MIME, mimeFormat.string());
+            if (string_length == chunk_data_size - 1) {
+                // This is likely a pre API 29 file, since it's a single null terminated
+                // string filling the entire box.
+                AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, str);
             } else {
-              // This is a standard compliant metadata track.
-              String8 contentEncoding((const char *)(buffer.get() + 8));
-              String8 mimeFormat((const char *)(buffer.get() + 8 + contentEncoding.size() + 1),
-                  chunk_data_size - 8 - contentEncoding.size() - 1);
-              AMediaFormat_setString(mLastTrack->meta,
-                  AMEDIAFORMAT_KEY_MIME, mimeFormat.string());
+                // This might be a fully compliant metadata track, a "double mime" compatibility
+                // track, or anything else, including a single non-terminated string, so we need
+                // to determine the length of each string we want to parse out of the box.
+                size_t encoding_length = strnlen(str + 8, chunk_data_size - 8);
+                if (encoding_length + 8 >= chunk_data_size - 2) {
+                    // the encoding extends to the end of the box, so there's no mime_format
+                    return ERROR_MALFORMED;
+                }
+                String8 contentEncoding(str + 8, encoding_length);
+                String8 mimeFormat(str + 8 + encoding_length + 1,
+                        chunk_data_size - 8 - encoding_length - 1);
+                AMediaFormat_setString(mLastTrack->meta,
+                        AMEDIAFORMAT_KEY_MIME, mimeFormat.string());
             }
             break;
         }
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index 9b77223..f5c97d8 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -473,7 +473,7 @@
 
 void AudioStream::MyPlayerBase::registerWithAudioManager() {
     if (!mRegistered) {
-        init(android::PLAYER_TYPE_AAUDIO, AUDIO_USAGE_MEDIA);
+        init(android::PLAYER_TYPE_AAUDIO, AAudioConvert_usageToInternal(mParent->getUsage()));
         mRegistered = true;
     }
 }
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index dd95e34..efa0512 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -1339,10 +1339,14 @@
         }
         case GET_EFFECT_DESCRIPTOR: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
-            effect_uuid_t uuid;
-            data.read(&uuid, sizeof(effect_uuid_t));
-            effect_uuid_t type;
-            data.read(&type, sizeof(effect_uuid_t));
+            effect_uuid_t uuid = {};
+            if (data.read(&uuid, sizeof(effect_uuid_t)) != NO_ERROR) {
+                android_errorWriteLog(0x534e4554, "139417189");
+            }
+            effect_uuid_t type = {};
+            if (data.read(&type, sizeof(effect_uuid_t)) != NO_ERROR) {
+                android_errorWriteLog(0x534e4554, "139417189");
+            }
             uint32_t preferredTypeFlag = data.readUint32();
             effect_descriptor_t desc = {};
             status_t status = getEffectDescriptor(&uuid, &type, preferredTypeFlag, &desc);
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
index afdcd37..f21d2b3 100644
--- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
@@ -130,29 +130,32 @@
         } else if (n < 0) {
             break;
         } else {
-            if (buffer[0] == 0x00) {
+            if (buffer[0] == 0x00) { // OK to access buffer[0] since n must be > 0 here
                 // XXX legacy
 
                 if (extra == NULL) {
                     extra = new AMessage;
                 }
 
-                uint8_t type = buffer[1];
+                uint8_t type = 0;
+                if (n > 1) {
+                    type = buffer[1];
 
-                if (type & 2) {
-                    int64_t mediaTimeUs;
-                    memcpy(&mediaTimeUs, &buffer[2], sizeof(mediaTimeUs));
+                    if ((type & 2) && (n >= 2 + sizeof(int64_t))) {
+                        int64_t mediaTimeUs;
+                        memcpy(&mediaTimeUs, &buffer[2], sizeof(mediaTimeUs));
 
-                    extra->setInt64(kATSParserKeyMediaTimeUs, mediaTimeUs);
+                        extra->setInt64(kATSParserKeyMediaTimeUs, mediaTimeUs);
+                    }
                 }
 
                 mTSParser->signalDiscontinuity(
                         ((type & 1) == 0)
-                            ? ATSParser::DISCONTINUITY_TIME
-                            : ATSParser::DISCONTINUITY_FORMATCHANGE,
+                                ? ATSParser::DISCONTINUITY_TIME
+                                : ATSParser::DISCONTINUITY_FORMATCHANGE,
                         extra);
             } else {
-                status_t err = mTSParser->feedTSPacket(buffer, sizeof(buffer));
+                status_t err = mTSParser->feedTSPacket(buffer, n);
 
                 if (err != OK) {
                     ALOGE("TS Parser returned error %d", err);
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index acb8b3c..e1d35e8 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -54,9 +54,8 @@
         mState = STATE_ERROR;
     }
 
-    if (setId > CAMERA3_STREAM_SET_ID_INVALID) {
-        mBufferReleasedListener = new BufferReleasedListener(this);
-    }
+    bool needsReleaseNotify = setId > CAMERA3_STREAM_SET_ID_INVALID;
+    mBufferProducerListener = new BufferProducerListener(this, needsReleaseNotify);
 }
 
 Camera3OutputStream::Camera3OutputStream(int id,
@@ -87,9 +86,8 @@
         mState = STATE_ERROR;
     }
 
-    if (setId > CAMERA3_STREAM_SET_ID_INVALID) {
-        mBufferReleasedListener = new BufferReleasedListener(this);
-    }
+    bool needsReleaseNotify = setId > CAMERA3_STREAM_SET_ID_INVALID;
+    mBufferProducerListener = new BufferProducerListener(this, needsReleaseNotify);
 }
 
 Camera3OutputStream::Camera3OutputStream(int id,
@@ -124,10 +122,8 @@
     }
 
     mConsumerName = String8("Deferred");
-    if (setId > CAMERA3_STREAM_SET_ID_INVALID) {
-        mBufferReleasedListener = new BufferReleasedListener(this);
-    }
-
+    bool needsReleaseNotify = setId > CAMERA3_STREAM_SET_ID_INVALID;
+    mBufferProducerListener = new BufferProducerListener(this, needsReleaseNotify);
 }
 
 Camera3OutputStream::Camera3OutputStream(int id, camera3_stream_type_t type,
@@ -151,9 +147,8 @@
         mDropBuffers(false),
         mDequeueBufferLatency(kDequeueLatencyBinSize) {
 
-    if (setId > CAMERA3_STREAM_SET_ID_INVALID) {
-        mBufferReleasedListener = new BufferReleasedListener(this);
-    }
+    bool needsReleaseNotify = setId > CAMERA3_STREAM_SET_ID_INVALID;
+    mBufferProducerListener = new BufferProducerListener(this, needsReleaseNotify);
 
     // Subclasses expected to initialize mConsumer themselves
 }
@@ -261,7 +256,7 @@
         notifyBufferReleased(anwBuffer);
         if (mUseBufferManager) {
             // Return this buffer back to buffer manager.
-            mBufferReleasedListener->onBufferReleased();
+            mBufferProducerListener->onBufferReleased();
         }
     } else {
         if (mTraceFirstBuffer && (stream_type == CAMERA3_STREAM_OUTPUT)) {
@@ -387,8 +382,8 @@
     // Configure consumer-side ANativeWindow interface. The listener may be used
     // to notify buffer manager (if it is used) of the returned buffers.
     res = mConsumer->connect(NATIVE_WINDOW_API_CAMERA,
-            /*listener*/mBufferReleasedListener,
-            /*reportBufferRemoval*/true);
+            /*reportBufferRemoval*/true,
+            /*listener*/mBufferProducerListener);
     if (res != OK) {
         ALOGE("%s: Unable to connect to native window for stream %d",
                 __FUNCTION__, mId);
@@ -790,7 +785,7 @@
     return INVALID_OPERATION;
 }
 
-void Camera3OutputStream::BufferReleasedListener::onBufferReleased() {
+void Camera3OutputStream::BufferProducerListener::onBufferReleased() {
     sp<Camera3OutputStream> stream = mParent.promote();
     if (stream == nullptr) {
         ALOGV("%s: Parent camera3 output stream was destroyed", __FUNCTION__);
@@ -823,6 +818,25 @@
     }
 }
 
+void Camera3OutputStream::BufferProducerListener::onBuffersDiscarded(
+        const std::vector<sp<GraphicBuffer>>& buffers) {
+    sp<Camera3OutputStream> stream = mParent.promote();
+    if (stream == nullptr) {
+        ALOGV("%s: Parent camera3 output stream was destroyed", __FUNCTION__);
+        return;
+    }
+
+    if (buffers.size() > 0) {
+        Mutex::Autolock l(stream->mLock);
+        stream->onBuffersRemovedLocked(buffers);
+        if (stream->mUseBufferManager) {
+            stream->mBufferManager->onBuffersRemoved(stream->getId(),
+                    stream->getStreamSetId(), buffers.size());
+        }
+        ALOGV("Stream %d: %zu Buffers discarded.", stream->getId(), buffers.size());
+    }
+}
+
 void Camera3OutputStream::onBuffersRemovedLocked(
         const std::vector<sp<GraphicBuffer>>& removedBuffers) {
     sp<Camera3StreamBufferFreedListener> callback = mBufferFreedListener.promote();
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 729c655..b4e49f9 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -146,18 +146,22 @@
      */
     virtual status_t setConsumers(const std::vector<sp<Surface>>& consumers);
 
-    class BufferReleasedListener : public BnProducerListener {
+    class BufferProducerListener : public SurfaceListener {
         public:
-          BufferReleasedListener(wp<Camera3OutputStream> parent) : mParent(parent) {}
+            BufferProducerListener(wp<Camera3OutputStream> parent, bool needsReleaseNotify)
+                    : mParent(parent), mNeedsReleaseNotify(needsReleaseNotify) {}
 
-          /**
-          * Implementation of IProducerListener, used to notify this stream that the consumer
-          * has returned a buffer and it is ready to return to Camera3BufferManager for reuse.
-          */
-          virtual void onBufferReleased();
+            /**
+            * Implementation of IProducerListener, used to notify this stream that the consumer
+            * has returned a buffer and it is ready to return to Camera3BufferManager for reuse.
+            */
+            virtual void onBufferReleased();
+            virtual bool needsReleaseNotify() { return mNeedsReleaseNotify; }
+            virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& buffers);
 
         private:
-          wp<Camera3OutputStream> mParent;
+            wp<Camera3OutputStream> mParent;
+            bool mNeedsReleaseNotify;
     };
 
     virtual status_t detachBuffer(sp<GraphicBuffer>* buffer, int* fenceFd);
@@ -262,10 +266,10 @@
     sp<Camera3BufferManager> mBufferManager;
 
     /**
-     * Buffer released listener, used to notify the buffer manager that a buffer is released
-     * from consumer side.
+     * Buffer producer listener, used to handle notification when a buffer is released
+     * from consumer side, or a set of buffers are discarded by the consumer.
      */
-    sp<BufferReleasedListener> mBufferReleasedListener;
+    sp<BufferProducerListener> mBufferProducerListener;
 
     /**
      * Flag indicating if the buffer manager is used to allocate the stream buffers
diff --git a/services/mediacodec/registrant/Android.bp b/services/mediacodec/registrant/Android.bp
index fa5bc4a..765ac99 100644
--- a/services/mediacodec/registrant/Android.bp
+++ b/services/mediacodec/registrant/Android.bp
@@ -14,6 +14,7 @@
         "libbase",
         "libcodec2_hidl@1.0",
         "libcodec2_vndk",
+        "libhidlbase",
         "libutils",
     ],
 
diff --git a/services/mediacodec/registrant/CodecServiceRegistrant.cpp b/services/mediacodec/registrant/CodecServiceRegistrant.cpp
index 706ebee..7f7ceca 100644
--- a/services/mediacodec/registrant/CodecServiceRegistrant.cpp
+++ b/services/mediacodec/registrant/CodecServiceRegistrant.cpp
@@ -18,21 +18,411 @@
 #define LOG_TAG "CodecServiceRegistrant"
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 
+#include <C2Component.h>
 #include <C2PlatformSupport.h>
 #include <codec2/hidl/1.0/ComponentStore.h>
+#include <codec2/hidl/1.0/Configurable.h>
+#include <codec2/hidl/1.0/types.h>
+#include <hidl/HidlSupport.h>
 #include <media/CodecServiceRegistrant.h>
 
+namespace /* unnamed */ {
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::hidl_string;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+using namespace ::android::hardware::media::c2::V1_0;
+using namespace ::android::hardware::media::c2::V1_0::utils;
+
+constexpr c2_status_t C2_TRANSACTION_FAILED = C2_CORRUPTED;
+
+// Converter from IComponentStore to C2ComponentStore.
+class H2C2ComponentStore : public C2ComponentStore {
+protected:
+    sp<IComponentStore> mStore;
+    sp<IConfigurable> mConfigurable;
+public:
+    explicit H2C2ComponentStore(sp<IComponentStore> const& store)
+          : mStore{store},
+            mConfigurable{[store]() -> sp<IConfigurable>{
+                if (!store) {
+                    return nullptr;
+                }
+                Return<sp<IConfigurable>> transResult =
+                    store->getConfigurable();
+                return transResult.isOk() ?
+                        static_cast<sp<IConfigurable>>(transResult) :
+                        nullptr;
+            }()} {
+        if (!mConfigurable) {
+            LOG(ERROR) << "Preferred store is corrupted.";
+        }
+    }
+
+    virtual ~H2C2ComponentStore() override = default;
+
+    virtual c2_status_t config_sm(
+            std::vector<C2Param*> const &params,
+            std::vector<std::unique_ptr<C2SettingResult>>* const failures
+            ) override {
+        Params hidlParams;
+        if (!createParamsBlob(&hidlParams, params)) {
+            LOG(ERROR) << "config -- bad input.";
+            return C2_TRANSACTION_FAILED;
+        }
+        c2_status_t status{};
+        Return<void> transResult = mConfigurable->config(
+                hidlParams,
+                true,
+                [&status, &params, failures](
+                        Status s,
+                        const hidl_vec<SettingResult> f,
+                        const Params& o) {
+                    status = static_cast<c2_status_t>(s);
+                    if (status != C2_OK && status != C2_BAD_INDEX) {
+                        LOG(DEBUG) << "config -- call failed: "
+                                   << status << ".";
+                    }
+                    size_t i = failures->size();
+                    failures->resize(i + f.size());
+                    for (const SettingResult& sf : f) {
+                        if (!objcpy(&(*failures)[i++], sf)) {
+                            LOG(ERROR) << "config -- "
+                                       << "invalid SettingResult returned.";
+                            return;
+                        }
+                    }
+                    if (!updateParamsFromBlob(params, o)) {
+                        LOG(ERROR) << "config -- "
+                                   << "failed to parse returned params.";
+                        status = C2_CORRUPTED;
+                    }
+                });
+        if (!transResult.isOk()) {
+            LOG(ERROR) << "config -- transaction failed.";
+            return C2_TRANSACTION_FAILED;
+        }
+        return status;
+    };
+
+    virtual c2_status_t copyBuffer(
+            std::shared_ptr<C2GraphicBuffer>,
+            std::shared_ptr<C2GraphicBuffer>) override {
+        LOG(ERROR) << "copyBuffer -- not supported.";
+        return C2_OMITTED;
+    }
+
+    virtual c2_status_t createComponent(
+            C2String, std::shared_ptr<C2Component> *const component) override {
+        component->reset();
+        LOG(ERROR) << "createComponent -- not supported.";
+        return C2_OMITTED;
+    }
+
+    virtual c2_status_t createInterface(
+            C2String, std::shared_ptr<C2ComponentInterface> *const interface) {
+        interface->reset();
+        LOG(ERROR) << "createInterface -- not supported.";
+        return C2_OMITTED;
+    }
+
+    virtual c2_status_t query_sm(
+            const std::vector<C2Param *> &stackParams,
+            const std::vector<C2Param::Index> &heapParamIndices,
+            std::vector<std::unique_ptr<C2Param>> *const heapParams) const
+            override {
+        hidl_vec<ParamIndex> indices(
+                stackParams.size() + heapParamIndices.size());
+        size_t numIndices = 0;
+        for (C2Param* const& stackParam : stackParams) {
+            if (!stackParam) {
+                LOG(WARNING) << "query -- null stack param encountered.";
+                continue;
+            }
+            indices[numIndices++] = static_cast<ParamIndex>(stackParam->index());
+        }
+        size_t numStackIndices = numIndices;
+        for (const C2Param::Index& index : heapParamIndices) {
+            indices[numIndices++] =
+                    static_cast<ParamIndex>(static_cast<uint32_t>(index));
+        }
+        indices.resize(numIndices);
+        if (heapParams) {
+            heapParams->reserve(heapParams->size() + numIndices);
+        }
+        c2_status_t status;
+        Return<void> transResult = mConfigurable->query(
+                indices,
+                true,
+                [&status, &numStackIndices, &stackParams, heapParams](
+                        Status s, const Params& p) {
+                    status = static_cast<c2_status_t>(s);
+                    if (status != C2_OK && status != C2_BAD_INDEX) {
+                        LOG(DEBUG) << "query -- call failed: "
+                                   << status << ".";
+                        return;
+                    }
+                    std::vector<C2Param*> paramPointers;
+                    if (!parseParamsBlob(&paramPointers, p)) {
+                        LOG(ERROR) << "query -- error while parsing params.";
+                        status = C2_CORRUPTED;
+                        return;
+                    }
+                    size_t i = 0;
+                    for (auto it = paramPointers.begin();
+                            it != paramPointers.end(); ) {
+                        C2Param* paramPointer = *it;
+                        if (numStackIndices > 0) {
+                            --numStackIndices;
+                            if (!paramPointer) {
+                                LOG(WARNING) << "query -- null stack param.";
+                                ++it;
+                                continue;
+                            }
+                            for (; i < stackParams.size() && !stackParams[i]; ) {
+                                ++i;
+                            }
+                            if (i >= stackParams.size()) {
+                                LOG(ERROR) << "query -- unexpected error.";
+                                status = C2_CORRUPTED;
+                                return;
+                            }
+                            if (stackParams[i]->index() != paramPointer->index()) {
+                                LOG(WARNING) << "query -- param skipped: "
+                                                "index = "
+                                             << stackParams[i]->index() << ".";
+                                stackParams[i++]->invalidate();
+                                continue;
+                            }
+                            if (!stackParams[i++]->updateFrom(*paramPointer)) {
+                                LOG(WARNING) << "query -- param update failed: "
+                                                "index = "
+                                             << paramPointer->index() << ".";
+                            }
+                        } else {
+                            if (!paramPointer) {
+                                LOG(WARNING) << "query -- null heap param.";
+                                ++it;
+                                continue;
+                            }
+                            if (!heapParams) {
+                                LOG(WARNING) << "query -- "
+                                                "unexpected extra stack param.";
+                            } else {
+                                heapParams->emplace_back(
+                                        C2Param::Copy(*paramPointer));
+                            }
+                        }
+                        ++it;
+                    }
+                });
+        if (!transResult.isOk()) {
+            LOG(ERROR) << "query -- transaction failed.";
+            return C2_TRANSACTION_FAILED;
+        }
+        return status;
+    }
+
+    virtual c2_status_t querySupportedParams_nb(
+            std::vector<std::shared_ptr<C2ParamDescriptor>> *const params) const {
+        c2_status_t status;
+        Return<void> transResult = mConfigurable->querySupportedParams(
+                std::numeric_limits<uint32_t>::min(),
+                std::numeric_limits<uint32_t>::max(),
+                [&status, params](
+                        Status s,
+                        const hidl_vec<ParamDescriptor>& p) {
+                    status = static_cast<c2_status_t>(s);
+                    if (status != C2_OK) {
+                        LOG(DEBUG) << "querySupportedParams -- call failed: "
+                                   << status << ".";
+                        return;
+                    }
+                    size_t i = params->size();
+                    params->resize(i + p.size());
+                    for (const ParamDescriptor& sp : p) {
+                        if (!objcpy(&(*params)[i++], sp)) {
+                            LOG(ERROR) << "querySupportedParams -- "
+                                       << "invalid returned ParamDescriptor.";
+                            return;
+                        }
+                    }
+                });
+        if (!transResult.isOk()) {
+            LOG(ERROR) << "querySupportedParams -- transaction failed.";
+            return C2_TRANSACTION_FAILED;
+        }
+        return status;
+    }
+
+    virtual c2_status_t querySupportedValues_sm(
+            std::vector<C2FieldSupportedValuesQuery> &fields) const {
+        hidl_vec<FieldSupportedValuesQuery> inFields(fields.size());
+        for (size_t i = 0; i < fields.size(); ++i) {
+            if (!objcpy(&inFields[i], fields[i])) {
+                LOG(ERROR) << "querySupportedValues -- bad input";
+                return C2_TRANSACTION_FAILED;
+            }
+        }
+
+        c2_status_t status;
+        Return<void> transResult = mConfigurable->querySupportedValues(
+                inFields,
+                true,
+                [&status, &inFields, &fields](
+                        Status s,
+                        const hidl_vec<FieldSupportedValuesQueryResult>& r) {
+                    status = static_cast<c2_status_t>(s);
+                    if (status != C2_OK) {
+                        LOG(DEBUG) << "querySupportedValues -- call failed: "
+                                   << status << ".";
+                        return;
+                    }
+                    if (r.size() != fields.size()) {
+                        LOG(ERROR) << "querySupportedValues -- "
+                                      "input and output lists "
+                                      "have different sizes.";
+                        status = C2_CORRUPTED;
+                        return;
+                    }
+                    for (size_t i = 0; i < fields.size(); ++i) {
+                        if (!objcpy(&fields[i], inFields[i], r[i])) {
+                            LOG(ERROR) << "querySupportedValues -- "
+                                          "invalid returned value.";
+                            status = C2_CORRUPTED;
+                            return;
+                        }
+                    }
+                });
+        if (!transResult.isOk()) {
+            LOG(ERROR) << "querySupportedValues -- transaction failed.";
+            return C2_TRANSACTION_FAILED;
+        }
+        return status;
+    }
+
+    virtual C2String getName() const {
+        C2String outName;
+        Return<void> transResult = mConfigurable->getName(
+                [&outName](const hidl_string& name) {
+                    outName = name.c_str();
+                });
+        if (!transResult.isOk()) {
+            LOG(ERROR) << "getName -- transaction failed.";
+        }
+        return outName;
+    }
+
+    virtual std::shared_ptr<C2ParamReflector> getParamReflector() const
+            override {
+        struct SimpleParamReflector : public C2ParamReflector {
+            virtual std::unique_ptr<C2StructDescriptor> describe(
+                    C2Param::CoreIndex coreIndex) const {
+                hidl_vec<ParamIndex> indices(1);
+                indices[0] = static_cast<ParamIndex>(coreIndex.coreIndex());
+                std::unique_ptr<C2StructDescriptor> descriptor;
+                Return<void> transResult = mBase->getStructDescriptors(
+                        indices,
+                        [&descriptor](
+                                Status s,
+                                const hidl_vec<StructDescriptor>& sd) {
+                            c2_status_t status = static_cast<c2_status_t>(s);
+                            if (status != C2_OK) {
+                                LOG(DEBUG) << "SimpleParamReflector -- "
+                                              "getStructDescriptors() failed: "
+                                           << status << ".";
+                                descriptor.reset();
+                                return;
+                            }
+                            if (sd.size() != 1) {
+                                LOG(DEBUG) << "SimpleParamReflector -- "
+                                              "getStructDescriptors() "
+                                              "returned vector of size "
+                                           << sd.size() << ". "
+                                              "It should be 1.";
+                                descriptor.reset();
+                                return;
+                            }
+                            if (!objcpy(&descriptor, sd[0])) {
+                                LOG(DEBUG) << "SimpleParamReflector -- "
+                                              "getStructDescriptors() returned "
+                                              "corrupted data.";
+                                descriptor.reset();
+                                return;
+                            }
+                        });
+                return descriptor;
+            }
+
+            explicit SimpleParamReflector(sp<IComponentStore> base)
+                : mBase(base) { }
+
+            sp<IComponentStore> mBase;
+        };
+
+        return std::make_shared<SimpleParamReflector>(mStore);
+    }
+
+    virtual std::vector<std::shared_ptr<const C2Component::Traits>>
+            listComponents() override {
+        LOG(ERROR) << "listComponents -- not supported.";
+        return {};
+    }
+};
+
+bool ionPropertiesDefined() {
+    using namespace ::android::base;
+    std::string heapMask =
+        GetProperty("ro.com.android.media.swcodec.ion.heapmask", "undefined");
+    std::string flags =
+        GetProperty("ro.com.android.media.swcodec.ion.flags", "undefined");
+    std::string align =
+        GetProperty("ro.com.android.media.swcodec.ion.align", "undefined");
+    if (heapMask != "undefined" ||
+            flags != "undefined" ||
+            align != "undefined") {
+        LOG(INFO)
+                << "Some system properties for mediaswcodec ION usage are set: "
+                << "heapmask = " << heapMask << ", "
+                << "flags = " << flags << ", "
+                << "align = " << align << ". "
+                << "Preferred Codec2 store is defaulted to \"software\".";
+        return true;
+    }
+    return false;
+}
+
+} // unnamed namespace
+
 extern "C" void RegisterCodecServices() {
-    using namespace ::android::hardware::media::c2::V1_0;
+    using ComponentStore = ::android::hardware::media::c2::V1_0::utils::
+            ComponentStore;
     LOG(INFO) << "Creating software Codec2 service...";
-    android::sp<IComponentStore> store =
-        new utils::ComponentStore(
-                android::GetCodec2PlatformComponentStore());
+    sp<ComponentStore> store =
+        new ComponentStore(::android::GetCodec2PlatformComponentStore());
     if (store == nullptr) {
         LOG(ERROR) <<
                 "Cannot create software Codec2 service.";
     } else {
+        if (!ionPropertiesDefined()) {
+            std::string preferredStoreName = "default";
+            sp<IComponentStore> preferredStore =
+                IComponentStore::getService(preferredStoreName.c_str());
+            if (preferredStore) {
+                ::android::SetPreferredCodec2ComponentStore(
+                        std::make_shared<H2C2ComponentStore>(preferredStore));
+                LOG(INFO) <<
+                        "Preferred Codec2 store is set to \"" <<
+                        preferredStoreName << "\".";
+            } else {
+                LOG(INFO) <<
+                        "Preferred Codec2 store is defaulted to \"software\".";
+            }
+        }
         if (store->registerAsService("software") != android::OK) {
             LOG(ERROR) <<
                     "Cannot register software Codec2 service.";