Implement secure API functions for large audio frames

 Functions added
 - queueSecureInputBuffers
 - attachEncryptedBuffers
 - cryptoAsync for multiple access units

Bug: 298052174
Test: atest android.media.drmframework.cts.MediaDrmCodecMultiAccessUnitTest

Change-Id: I8053f40c2e15d29132441de06bacf2fa2f9ac1f2
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 7b1721e..40656ff 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -483,6 +483,130 @@
     return heapSeqNum;
 }
 
+typedef WrapperObject<std::vector<AccessUnitInfo>> BufferInfosWrapper;
+typedef WrapperObject<std::vector<std::unique_ptr<CodecCryptoInfo>>> CryptoInfosWrapper;
+status_t CCodecBufferChannel::attachEncryptedBuffers(
+        const sp<hardware::HidlMemory> &memory,
+        size_t offset,
+        const sp<MediaCodecBuffer> &buffer,
+        bool secure,
+        AString* errorDetailMsg) {
+    static const C2MemoryUsage kDefaultReadWriteUsage{
+        C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
+    if (!hasCryptoOrDescrambler()) {
+        ALOGE("attachEncryptedBuffers requires Crypto/descrambler object");
+        return -ENOSYS;
+    }
+    size_t size = 0;
+    CHECK(buffer->meta()->findSize("ssize", &size));
+    if (size == 0) {
+        buffer->setRange(0, 0);
+        return OK;
+    }
+    sp<RefBase> obj;
+    CHECK(buffer->meta()->findObject("cryptoInfos", &obj));
+    sp<CryptoInfosWrapper> cryptoInfos{(CryptoInfosWrapper *)obj.get()};
+    CHECK(buffer->meta()->findObject("accessUnitInfo", &obj));
+    sp<BufferInfosWrapper> bufferInfos{(BufferInfosWrapper *)obj.get()};
+    if (secure || (mCrypto == nullptr)) {
+        if (cryptoInfos->value.size() != 1) {
+            ALOGE("Cannot decrypt multiple access units");
+            return -ENOSYS;
+        }
+        // we are dealing with just one cryptoInfo or descrambler.
+        std::unique_ptr<CodecCryptoInfo> info = std::move(cryptoInfos->value[0]);
+        if (info == nullptr) {
+            ALOGE("Cannot decrypt, CryptoInfos are null.");
+            return -ENOSYS;
+        }
+        return attachEncryptedBuffer(
+                memory,
+                secure,
+                info->mKey,
+                info->mIv,
+                info->mMode,
+                info->mPattern,
+                offset,
+                info->mSubSamples,
+                info->mNumSubSamples,
+                buffer,
+                errorDetailMsg);
+    }
+    std::shared_ptr<C2BlockPool> pool = mBlockPools.lock()->inputPool;
+    std::shared_ptr<C2LinearBlock> block;
+    c2_status_t err = pool->fetchLinearBlock(
+            size,
+            kDefaultReadWriteUsage,
+            &block);
+    if (err != C2_OK) {
+        ALOGI("[%s] attachEncryptedBuffers: fetchLinearBlock failed: size = %zu (%s) err = %d",
+              mName, size, secure ? "secure" : "non-secure", err);
+        return NO_MEMORY;
+    }
+    ensureDecryptDestination(size);
+    C2WriteView wView = block->map().get();
+    if (wView.error() != C2_OK) {
+        ALOGI("[%s] attachEncryptedBuffers: block map error: %d (non-secure)",
+              mName, wView.error());
+        return UNKNOWN_ERROR;
+    }
+
+    ssize_t result = -1;
+    ssize_t codecDataOffset = 0;
+    size_t inBufferOffset = 0;
+    size_t outBufferSize = 0;
+    uint32_t cryptoInfoIdx = 0;
+    int32_t heapSeqNum = getHeapSeqNum(memory);
+    hardware::drm::V1_0::SharedBuffer src{(uint32_t)heapSeqNum, offset, size};
+    hardware::drm::V1_0::DestinationBuffer dst;
+    dst.type = DrmBufferType::SHARED_MEMORY;
+    IMemoryToSharedBuffer(
+            mDecryptDestination, mHeapSeqNum, &dst.nonsecureMemory);
+    for (int i = 0; i < bufferInfos->value.size(); i++) {
+        if (bufferInfos->value[i].mSize > 0) {
+            std::unique_ptr<CodecCryptoInfo> info = std::move(cryptoInfos->value[cryptoInfoIdx++]);
+            result = mCrypto->decrypt(
+                    (uint8_t*)info->mKey,
+                    (uint8_t*)info->mIv,
+                    info->mMode,
+                    info->mPattern,
+                    src,
+                    inBufferOffset,
+                    info->mSubSamples,
+                    info->mNumSubSamples,
+                    dst,
+                    errorDetailMsg);
+            inBufferOffset += bufferInfos->value[i].mSize;
+            if (result < 0) {
+                ALOGI("[%s] attachEncryptedBuffers: decrypt failed: result = %zd",
+                        mName, result);
+                return result;
+            }
+            if (wView.error() == C2_OK) {
+                if (wView.size() < result) {
+                    ALOGI("[%s] attachEncryptedBuffers: block size too small:"
+                            "size=%u result=%zd (non-secure)", mName, wView.size(), result);
+                    return UNKNOWN_ERROR;
+                }
+                memcpy(wView.data(), mDecryptDestination->unsecurePointer(), result);
+                bufferInfos->value[i].mSize = result;
+                wView.setOffset(wView.offset() + result);
+            }
+            outBufferSize += result;
+        }
+    }
+    if (wView.error() == C2_OK) {
+        wView.setOffset(0);
+    }
+    std::shared_ptr<C2Buffer> c2Buffer{C2Buffer::CreateLinearBuffer(
+            block->share(codecDataOffset, outBufferSize - codecDataOffset, C2Fence{}))};
+    if (!buffer->copy(c2Buffer)) {
+        ALOGI("[%s] attachEncryptedBuffers: buffer copy failed", mName);
+        return -ENOSYS;
+    }
+    return OK;
+}
+
 status_t CCodecBufferChannel::attachEncryptedBuffer(
         const sp<hardware::HidlMemory> &memory,
         bool secure,
@@ -777,6 +901,138 @@
     return queueInputBufferInternal(buffer, block, bufferSize);
 }
 
+status_t CCodecBufferChannel::queueSecureInputBuffers(
+        const sp<MediaCodecBuffer> &buffer,
+        bool secure,
+        AString *errorDetailMsg) {
+    QueueGuard guard(mSync);
+    if (!guard.isRunning()) {
+        ALOGD("[%s] No more buffers should be queued at current state.", mName);
+        return -ENOSYS;
+    }
+
+    if (!hasCryptoOrDescrambler()) {
+        ALOGE("queueSecureInputBuffers requires a Crypto/descrambler Object");
+        return -ENOSYS;
+    }
+    sp<RefBase> obj;
+    CHECK(buffer->meta()->findObject("cryptoInfos", &obj));
+    sp<CryptoInfosWrapper> cryptoInfos{(CryptoInfosWrapper *)obj.get()};
+    CHECK(buffer->meta()->findObject("accessUnitInfo", &obj));
+    sp<BufferInfosWrapper> bufferInfos{(BufferInfosWrapper *)obj.get()};
+    if (secure || mCrypto == nullptr) {
+        if (cryptoInfos->value.size() != 1) {
+            ALOGE("Cannot decrypt multiple access units on native handles");
+            return -ENOSYS;
+        }
+        std::unique_ptr<CodecCryptoInfo> info = std::move(cryptoInfos->value[0]);
+        if (info == nullptr) {
+            ALOGE("Cannot decrypt, CryptoInfos are null");
+            return -ENOSYS;
+        }
+        return queueSecureInputBuffer(
+                buffer,
+                secure,
+                info->mKey,
+                info->mIv,
+                info->mMode,
+                info->mPattern,
+                info->mSubSamples,
+                info->mNumSubSamples,
+                errorDetailMsg);
+    }
+    sp<EncryptedLinearBlockBuffer> encryptedBuffer((EncryptedLinearBlockBuffer *)buffer.get());
+
+    std::shared_ptr<C2LinearBlock> block;
+    size_t allocSize = buffer->size();
+    size_t bufferSize = 0;
+    c2_status_t blockRes = C2_OK;
+    bool copied = false;
+    ScopedTrace trace(ATRACE_TAG, android::base::StringPrintf(
+            "CCodecBufferChannel::decrypt(%s)", mName).c_str());
+    if (mSendEncryptedInfoBuffer) {
+        static const C2MemoryUsage kDefaultReadWriteUsage{
+            C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
+        constexpr int kAllocGranule0 = 1024 * 64;
+        constexpr int kAllocGranule1 = 1024 * 1024;
+        std::shared_ptr<C2BlockPool> pool = mBlockPools.lock()->inputPool;
+        // round up encrypted sizes to limit fragmentation and encourage buffer reuse
+        if (allocSize <= kAllocGranule1) {
+            bufferSize = align(allocSize, kAllocGranule0);
+        } else {
+            bufferSize = align(allocSize, kAllocGranule1);
+        }
+        blockRes = pool->fetchLinearBlock(
+                bufferSize, kDefaultReadWriteUsage, &block);
+
+        if (blockRes == C2_OK) {
+            C2WriteView view = block->map().get();
+            if (view.error() == C2_OK && view.size() == bufferSize) {
+                copied = true;
+                // TODO: only copy clear sections
+                memcpy(view.data(), buffer->data(), allocSize);
+            }
+        }
+    }
+
+    if (!copied) {
+        block.reset();
+    }
+    // size of cryptoInfo and accessUnitInfo should be the same?
+    ssize_t result = -1;
+    ssize_t codecDataOffset = 0;
+    size_t inBufferOffset = 0;
+    size_t outBufferSize = 0;
+    uint32_t cryptoInfoIdx = 0;
+    {
+        // scoped this block to enable destruction of mappedBlock
+        std::unique_ptr<EncryptedLinearBlockBuffer::MappedBlock> mappedBlock = nullptr;
+        hardware::drm::V1_0::DestinationBuffer destination;
+        destination.type = DrmBufferType::SHARED_MEMORY;
+        IMemoryToSharedBuffer(
+                mDecryptDestination, mHeapSeqNum, &destination.nonsecureMemory);
+        encryptedBuffer->getMappedBlock(&mappedBlock);
+        hardware::drm::V1_0::SharedBuffer source;
+        encryptedBuffer->fillSourceBuffer(&source);
+        for (int i = 0 ; i < bufferInfos->value.size(); i++) {
+            if (bufferInfos->value[i].mSize > 0) {
+                std::unique_ptr<CodecCryptoInfo> info =
+                        std::move(cryptoInfos->value[cryptoInfoIdx++]);
+                if (info->mNumSubSamples == 1
+                        && info->mSubSamples[0].mNumBytesOfClearData == 0
+                        && info->mSubSamples[0].mNumBytesOfEncryptedData == 0) {
+                    // no data so we only populate the bufferInfo
+                    result = 0;
+                } else {
+                    result = mCrypto->decrypt(
+                            (uint8_t*)info->mKey,
+                            (uint8_t*)info->mIv,
+                            info->mMode,
+                            info->mPattern,
+                            source,
+                            inBufferOffset,
+                            info->mSubSamples,
+                            info->mNumSubSamples,
+                            destination,
+                            errorDetailMsg);
+                    inBufferOffset += bufferInfos->value[i].mSize;
+                    if (result < 0) {
+                        ALOGI("[%s] decrypt failed: result=%zd", mName, result);
+                        return result;
+                    }
+                    if (destination.type == DrmBufferType::SHARED_MEMORY && mappedBlock) {
+                        mappedBlock->copyDecryptedContent(mDecryptDestination, result);
+                    }
+                    bufferInfos->value[i].mSize = result;
+                    outBufferSize += result;
+                }
+            }
+        }
+        buffer->setRange(codecDataOffset, outBufferSize - codecDataOffset);
+    }
+    return queueInputBufferInternal(buffer, block, bufferSize);
+}
+
 void CCodecBufferChannel::feedInputBufferIfAvailable() {
     QueueGuard guard(mSync);
     if (!guard.isRunning()) {
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h
index 8dc9fb6..b470655 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.h
+++ b/media/codec2/sfplugin/CCodecBufferChannel.h
@@ -73,6 +73,10 @@
             const CryptoPlugin::SubSample *subSamples,
             size_t numSubSamples,
             AString *errorDetailMsg) override;
+    status_t queueSecureInputBuffers(
+            const sp<MediaCodecBuffer> &buffer,
+            bool secure,
+            AString *errorDetailMsg) override;
     status_t attachBuffer(
             const std::shared_ptr<C2Buffer> &c2Buffer,
             const sp<MediaCodecBuffer> &buffer) override;
@@ -88,6 +92,12 @@
             size_t numSubSamples,
             const sp<MediaCodecBuffer> &buffer,
             AString* errorDetailMsg) override;
+    status_t attachEncryptedBuffers(
+            const sp<hardware::HidlMemory> &memory,
+            size_t offset,
+            const sp<MediaCodecBuffer> &buffer,
+            bool secure,
+            AString* errorDetailMsg) override;
     status_t renderOutputBuffer(
             const sp<MediaCodecBuffer> &buffer, int64_t timestampNs) override;
     void pollForRenderedBuffers() override;
diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp
index 4f466c5..9c514f2 100644
--- a/media/codec2/sfplugin/Codec2Buffer.cpp
+++ b/media/codec2/sfplugin/Codec2Buffer.cpp
@@ -1036,6 +1036,37 @@
     return const_cast<native_handle_t *>(mBlock->handle());
 }
 
+void EncryptedLinearBlockBuffer::getMappedBlock(
+        std::unique_ptr<MappedBlock> * const mappedBlock) const {
+    if (mappedBlock) {
+        mappedBlock->reset(new EncryptedLinearBlockBuffer::MappedBlock(mBlock));
+    }
+    return;
+}
+
+EncryptedLinearBlockBuffer::MappedBlock::MappedBlock(
+        const std::shared_ptr<C2LinearBlock> &block) : mView(block->map().get()) {
+}
+
+bool EncryptedLinearBlockBuffer::MappedBlock::copyDecryptedContent(
+        const sp<IMemory> &decrypted, size_t length) {
+    if (mView.error() != C2_OK) {
+        return false;
+    }
+    if (mView.size() < length) {
+        ALOGE("View size(%d) less than decrypted length(%zu)",
+                mView.size(), length);
+        return false;
+    }
+    memcpy(mView.data(), decrypted->unsecurePointer(), length);
+    mView.setOffset(mView.offset() + length);
+    return true;
+}
+
+EncryptedLinearBlockBuffer::MappedBlock::~MappedBlock() {
+    mView.setOffset(0);
+}
+
 using ::aidl::android::hardware::graphics::common::Cta861_3;
 using ::aidl::android::hardware::graphics::common::Smpte2086;
 
diff --git a/media/codec2/sfplugin/Codec2Buffer.h b/media/codec2/sfplugin/Codec2Buffer.h
index b73acab..5e96921 100644
--- a/media/codec2/sfplugin/Codec2Buffer.h
+++ b/media/codec2/sfplugin/Codec2Buffer.h
@@ -384,6 +384,17 @@
      */
     native_handle_t *handle() const;
 
+    class MappedBlock {
+    public:
+        explicit MappedBlock(const std::shared_ptr<C2LinearBlock> &block);
+        virtual ~MappedBlock();
+        bool copyDecryptedContent(const sp<IMemory> &decrypted, size_t length);
+    private:
+        C2WriteView mView;
+    };
+
+    void getMappedBlock(std::unique_ptr<MappedBlock> * const mappedBlock) const;
+
 private:
 
     std::shared_ptr<C2LinearBlock> mBlock;
diff --git a/media/libstagefright/CryptoAsync.cpp b/media/libstagefright/CryptoAsync.cpp
index 8b5c8ed..0fc78ec 100644
--- a/media/libstagefright/CryptoAsync.cpp
+++ b/media/libstagefright/CryptoAsync.cpp
@@ -30,6 +30,36 @@
 
 namespace android {
 
+CryptoAsync::CryptoAsyncInfo::CryptoAsyncInfo(const std::unique_ptr<CodecCryptoInfo> &info) {
+    if (info == nullptr) {
+        return;
+    }
+    size_t key_len = (info->mKey != nullptr)? 16 : 0;
+    size_t iv_len = (info->mIv != nullptr)? 16 : 0;
+    mNumSubSamples = info->mNumSubSamples;
+    mMode = info->mMode;
+    mPattern = info->mPattern;
+    if (key_len > 0) {
+        mKeyBuffer = ABuffer::CreateAsCopy((void*)info->mKey, key_len);
+        mKey = (uint8_t*)(mKeyBuffer.get() != nullptr ? mKeyBuffer.get()->data() : nullptr);
+    }
+    if (iv_len > 0) {
+        mIvBuffer = ABuffer::CreateAsCopy((void*)info->mIv, iv_len);
+        mIv = (uint8_t*)(mIvBuffer.get() != nullptr ? mIvBuffer.get()->data() : nullptr);
+    }
+    mSubSamplesBuffer =
+        new ABuffer(sizeof(CryptoPlugin::SubSample) * mNumSubSamples);
+    if (mSubSamplesBuffer.get()) {
+        CryptoPlugin::SubSample * samples =
+           (CryptoPlugin::SubSample *)(mSubSamplesBuffer.get()->data());
+        for (int s = 0 ; s < mNumSubSamples ; s++) {
+            samples[s].mNumBytesOfClearData = info->mSubSamples[s].mNumBytesOfClearData;
+            samples[s].mNumBytesOfEncryptedData = info->mSubSamples[s].mNumBytesOfEncryptedData;
+        }
+        mSubSamples = (CryptoPlugin::SubSample *)mSubSamplesBuffer.get()->data();
+    }
+}
+
 CryptoAsync::~CryptoAsync() {
 }
 
@@ -79,23 +109,27 @@
     sp<ABuffer> keyBuffer;
     sp<ABuffer> ivBuffer;
     sp<ABuffer> subSamplesBuffer;
-    msg->findInt32("encryptBlocks", (int32_t*)&pattern.mEncryptBlocks);
-    msg->findInt32("skipBlocks", (int32_t*)&pattern.mSkipBlocks);
-    msg->findBuffer("key", &keyBuffer);
-    msg->findBuffer("iv", &ivBuffer);
-    msg->findBuffer("subSamples", &subSamplesBuffer);
-    msg->findInt32("secure", &secure);
-    msg->findSize("numSubSamples", &numSubSamples);
-    msg->findObject("buffer", &obj);
-    msg->findInt32("mode", (int32_t*)&mode);
     AString errorDetailMsg;
-    const uint8_t * key = keyBuffer.get() != nullptr ? keyBuffer.get()->data() : nullptr;
-    const uint8_t * iv = ivBuffer.get() != nullptr ? ivBuffer.get()->data() : nullptr;
-    const CryptoPlugin::SubSample * subSamples =
-       (CryptoPlugin::SubSample *)(subSamplesBuffer.get()->data());
+    msg->findObject("buffer", &obj);
+    msg->findInt32("secure", &secure);
     sp<MediaCodecBuffer> buffer = static_cast<MediaCodecBuffer *>(obj.get());
-    err = channel->queueSecureInputBuffer(buffer, secure, key, iv, mode,
-        pattern, subSamples, numSubSamples, &errorDetailMsg);
+    if (buffer->meta()->findObject("cryptoInfos", &obj)) {
+        err = channel->queueSecureInputBuffers(buffer, secure, &errorDetailMsg);
+    } else {
+        msg->findInt32("encryptBlocks", (int32_t*)&pattern.mEncryptBlocks);
+        msg->findInt32("skipBlocks", (int32_t*)&pattern.mSkipBlocks);
+        msg->findBuffer("key", &keyBuffer);
+        msg->findBuffer("iv", &ivBuffer);
+        msg->findBuffer("subSamples", &subSamplesBuffer);
+        msg->findSize("numSubSamples", &numSubSamples);
+        msg->findInt32("mode", (int32_t*)&mode);
+        const uint8_t * key = keyBuffer.get() != nullptr ? keyBuffer.get()->data() : nullptr;
+        const uint8_t * iv = ivBuffer.get() != nullptr ? ivBuffer.get()->data() : nullptr;
+        const CryptoPlugin::SubSample * subSamples =
+           (CryptoPlugin::SubSample *)(subSamplesBuffer.get()->data());
+        err = channel->queueSecureInputBuffer(buffer, secure, key, iv, mode,
+            pattern, subSamples, numSubSamples, &errorDetailMsg);
+    }
     if (err != OK) {
         std::list<sp<AMessage>> errorList;
         msg->removeEntryByName("buffer");
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 770da65..305d42f 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -300,7 +300,6 @@
             return -EINVAL;
         }
         msg->setInt32("flags", bufferFlags);
-        msg->setObject("accessUnitInfo", bufferInfos);
     }
     return OK;
 }
@@ -3299,6 +3298,58 @@
     return err;
 }
 
+status_t MediaCodec::queueSecureInputBuffers(
+        size_t index,
+        size_t offset,
+        size_t size,
+        const sp<BufferInfosWrapper> &auInfo,
+        const sp<CryptoInfosWrapper> &cryptoInfos,
+        AString *errorDetailMsg) {
+    if (errorDetailMsg != NULL) {
+        errorDetailMsg->clear();
+    }
+    sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
+    uint32_t bufferFlags = 0;
+    uint32_t flagsinAllAU = BUFFER_FLAG_DECODE_ONLY | BUFFER_FLAG_CODECCONFIG;
+    uint32_t andFlags = flagsinAllAU;
+    if (auInfo == nullptr
+            || auInfo->value.empty()
+            || cryptoInfos == nullptr
+            || cryptoInfos->value.empty()) {
+        ALOGE("ERROR: Large Audio frame with no BufferInfo/CryptoInfo");
+        return BAD_VALUE;
+    }
+    int infoIdx = 0;
+    std::vector<AccessUnitInfo> &accessUnitInfo = auInfo->value;
+    int64_t minTimeUs = accessUnitInfo.front().mTimestamp;
+    bool foundEndOfStream = false;
+    for ( ; infoIdx < accessUnitInfo.size() && !foundEndOfStream; ++infoIdx) {
+        bufferFlags |= accessUnitInfo[infoIdx].mFlags;
+        andFlags &= accessUnitInfo[infoIdx].mFlags;
+        if (bufferFlags & BUFFER_FLAG_END_OF_STREAM) {
+            foundEndOfStream = true;
+        }
+    }
+    bufferFlags = bufferFlags & (andFlags | (~flagsinAllAU));
+    if (infoIdx != accessUnitInfo.size()) {
+        ALOGE("queueInputBuffers has incorrect access-units");
+        return -EINVAL;
+    }
+    msg->setSize("index", index);
+    msg->setSize("offset", offset);
+    msg->setSize("ssize", size);
+    msg->setInt64("timeUs", minTimeUs);
+    msg->setInt32("flags", bufferFlags);
+    msg->setObject("accessUnitInfo", auInfo);
+    msg->setObject("cryptoInfos", cryptoInfos);
+    msg->setPointer("errorDetailMsg", errorDetailMsg);
+
+    sp<AMessage> response;
+    status_t err = PostAndAwaitResponse(msg, &response);
+
+    return err;
+}
+
 status_t MediaCodec::queueBuffer(
         size_t index,
         const std::shared_ptr<C2Buffer> &buffer,
@@ -3320,6 +3371,7 @@
     if (OK != (err = generateFlagsFromAccessUnitInfo(msg, bufferInfos))) {
         return err;
     }
+    msg->setObject("accessUnitInfo", bufferInfos);
     if (tunings && tunings->countEntries() > 0) {
         msg->setMessage("tunings", tunings);
     }
@@ -3334,13 +3386,9 @@
         size_t index,
         const sp<hardware::HidlMemory> &buffer,
         size_t offset,
-        const CryptoPlugin::SubSample *subSamples,
-        size_t numSubSamples,
-        const uint8_t key[16],
-        const uint8_t iv[16],
-        CryptoPlugin::Mode mode,
-        const CryptoPlugin::Pattern &pattern,
+        size_t size,
         const sp<BufferInfosWrapper> &bufferInfos,
+        const sp<CryptoInfosWrapper> &cryptoInfos,
         const sp<AMessage> &tunings,
         AString *errorDetailMsg) {
     if (errorDetailMsg != NULL) {
@@ -3349,6 +3397,9 @@
     if (bufferInfos == nullptr || bufferInfos->value.empty()) {
         return BAD_VALUE;
     }
+    if (cryptoInfos == nullptr || cryptoInfos->value.empty()) {
+        return BAD_VALUE;
+    }
     status_t err = OK;
     sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
     msg->setSize("index", index);
@@ -3356,13 +3407,9 @@
         new WrapperObject<sp<hardware::HidlMemory>>{buffer}};
     msg->setObject("memory", memory);
     msg->setSize("offset", offset);
-    msg->setPointer("subSamples", (void *)subSamples);
-    msg->setSize("numSubSamples", numSubSamples);
-    msg->setPointer("key", (void *)key);
-    msg->setPointer("iv", (void *)iv);
-    msg->setInt32("mode", mode);
-    msg->setInt32("encryptBlocks", pattern.mEncryptBlocks);
-    msg->setInt32("skipBlocks", pattern.mSkipBlocks);
+    msg->setSize("ssize", size);
+    msg->setObject("cryptoInfos", cryptoInfos);
+    msg->setObject("accessUnitInfo", bufferInfos);
     if (OK != (err = generateFlagsFromAccessUnitInfo(msg, bufferInfos))) {
         return err;
     }
@@ -6072,22 +6119,26 @@
             mErrorLog.log(LOG_TAG, "queuing secure buffer without mCrypto or mDescrambler!");
             return -EINVAL;
         }
-        CHECK(msg->findPointer("subSamples", (void **)&subSamples));
-        CHECK(msg->findSize("numSubSamples", &numSubSamples));
-        CHECK(msg->findPointer("key", (void **)&key));
-        CHECK(msg->findPointer("iv", (void **)&iv));
-        CHECK(msg->findInt32("encryptBlocks", (int32_t *)&pattern.mEncryptBlocks));
-        CHECK(msg->findInt32("skipBlocks", (int32_t *)&pattern.mSkipBlocks));
+        sp<RefBase> obj;
+        if (msg->findObject("cryptoInfos", &obj)) {
+            CHECK(msg->findSize("ssize", &size));
+        } else {
+            CHECK(msg->findPointer("subSamples", (void **)&subSamples));
+            CHECK(msg->findSize("numSubSamples", &numSubSamples));
+            CHECK(msg->findPointer("key", (void **)&key));
+            CHECK(msg->findPointer("iv", (void **)&iv));
+            CHECK(msg->findInt32("encryptBlocks", (int32_t *)&pattern.mEncryptBlocks));
+            CHECK(msg->findInt32("skipBlocks", (int32_t *)&pattern.mSkipBlocks));
 
-        int32_t tmp;
-        CHECK(msg->findInt32("mode", &tmp));
+            int32_t tmp;
+            CHECK(msg->findInt32("mode", &tmp));
 
-        mode = (CryptoPlugin::Mode)tmp;
-
-        size = 0;
-        for (size_t i = 0; i < numSubSamples; ++i) {
-            size += subSamples[i].mNumBytesOfClearData;
-            size += subSamples[i].mNumBytesOfEncryptedData;
+            mode = (CryptoPlugin::Mode)tmp;
+            size = 0;
+            for (size_t i = 0; i < numSubSamples; ++i) {
+                size += subSamples[i].mNumBytesOfClearData;
+                size += subSamples[i].mNumBytesOfEncryptedData;
+            }
         }
     }
 
@@ -6114,7 +6165,7 @@
         status_t err = OK;
         sp<RefBase> obj;
         if (msg->findObject("accessUnitInfo", &obj)) {
-                buffer->meta()->setObject("accessUnitInfo", obj);
+            buffer->meta()->setObject("accessUnitInfo", obj);
         }
         buffer->meta()->setInt64("timeUs", timeUs);
         if (flags & BUFFER_FLAG_EOS) {
@@ -6152,35 +6203,48 @@
      return err;
     };
     auto buildCryptoInfoAMessage = [&](const sp<AMessage> & cryptoInfo, int32_t action) {
-        size_t key_len = (key != nullptr)? 16 : 0;
-        size_t iv_len = (iv != nullptr)? 16 : 0;
-        sp<ABuffer> shared_key;
-        sp<ABuffer> shared_iv;
-        if (key_len > 0) {
-            shared_key = ABuffer::CreateAsCopy((void*)key, key_len);
-        }
-        if (iv_len > 0) {
-            shared_iv = ABuffer::CreateAsCopy((void*)iv, iv_len);
-        }
-        sp<ABuffer> subSamples_buffer =
-            new ABuffer(sizeof(CryptoPlugin::SubSample) * numSubSamples);
-        CryptoPlugin::SubSample * samples =
-           (CryptoPlugin::SubSample *)(subSamples_buffer.get()->data());
-        for (int s = 0 ; s < numSubSamples ; s++) {
-            samples[s].mNumBytesOfClearData = subSamples[s].mNumBytesOfClearData;
-            samples[s].mNumBytesOfEncryptedData = subSamples[s].mNumBytesOfEncryptedData;
-        }
         // set decrypt Action
         cryptoInfo->setInt32("action", action);
         cryptoInfo->setObject("buffer", buffer);
         cryptoInfo->setInt32("secure", mFlags & kFlagIsSecure);
-        cryptoInfo->setBuffer("key", shared_key);
-        cryptoInfo->setBuffer("iv", shared_iv);
-        cryptoInfo->setInt32("mode", (int)mode);
-        cryptoInfo->setInt32("encryptBlocks", pattern.mEncryptBlocks);
-        cryptoInfo->setInt32("skipBlocks", pattern.mSkipBlocks);
-        cryptoInfo->setBuffer("subSamples", subSamples_buffer);
-        cryptoInfo->setSize("numSubSamples", numSubSamples);
+        sp<RefBase> obj;
+        if (msg->findObject("cryptoInfos", &obj)) {
+            sp<CryptoInfosWrapper> infos{(CryptoInfosWrapper*)obj.get()};
+            sp<CryptoInfosWrapper> asyncInfos{
+                    new CryptoInfosWrapper(std::vector<std::unique_ptr<CodecCryptoInfo>>())};
+            for (std::unique_ptr<CodecCryptoInfo> &info : infos->value) {
+                if (info) {
+                    asyncInfos->value.emplace_back(new CryptoAsync::CryptoAsyncInfo(info));
+                }
+            }
+            buffer->meta()->setObject("cryptoInfos", asyncInfos);
+        } else {
+            size_t key_len = (key != nullptr)? 16 : 0;
+            size_t iv_len = (iv != nullptr)? 16 : 0;
+            sp<ABuffer> shared_key;
+            sp<ABuffer> shared_iv;
+            if (key_len > 0) {
+                shared_key = ABuffer::CreateAsCopy((void*)key, key_len);
+            }
+            if (iv_len > 0) {
+                shared_iv = ABuffer::CreateAsCopy((void*)iv, iv_len);
+            }
+            sp<ABuffer> subSamples_buffer =
+                new ABuffer(sizeof(CryptoPlugin::SubSample) * numSubSamples);
+            CryptoPlugin::SubSample * samples =
+               (CryptoPlugin::SubSample *)(subSamples_buffer.get()->data());
+            for (int s = 0 ; s < numSubSamples ; s++) {
+                samples[s].mNumBytesOfClearData = subSamples[s].mNumBytesOfClearData;
+                samples[s].mNumBytesOfEncryptedData = subSamples[s].mNumBytesOfEncryptedData;
+            }
+            cryptoInfo->setBuffer("key", shared_key);
+            cryptoInfo->setBuffer("iv", shared_iv);
+            cryptoInfo->setInt32("mode", (int)mode);
+            cryptoInfo->setInt32("encryptBlocks", pattern.mEncryptBlocks);
+            cryptoInfo->setInt32("skipBlocks", pattern.mSkipBlocks);
+            cryptoInfo->setBuffer("subSamples", subSamples_buffer);
+            cryptoInfo->setSize("numSubSamples", numSubSamples);
+        }
     };
     if (c2Buffer || memory) {
         sp<AMessage> tunings = NULL;
@@ -6190,15 +6254,37 @@
         status_t err = OK;
         if (c2Buffer) {
             err = mBufferChannel->attachBuffer(c2Buffer, buffer);
+            // to prevent unnecessary copy for single info case.
+            if (msg->findObject("accessUnitInfo", &obj)) {
+                sp<BufferInfosWrapper> infos{(BufferInfosWrapper*)(obj.get())};
+                if (infos->value.size() == 1) {
+                   msg->removeEntryByName("accessUnitInfo");
+                }
+            }
         } else if (memory) {
             AString errorDetailMsg;
-            err = mBufferChannel->attachEncryptedBuffer(
-                    memory, (mFlags & kFlagIsSecure), key, iv, mode, pattern,
-                    offset, subSamples, numSubSamples, buffer, &errorDetailMsg);
+            if (msg->findObject("cryptoInfos", &obj)) {
+                buffer->meta()->setSize("ssize", size);
+                buffer->meta()->setObject("cryptoInfos", obj);
+                if (msg->findObject("accessUnitInfo", &obj)) {
+                    // the reference will be same here and
+                    // setBufferParams
+                    buffer->meta()->setObject("accessUnitInfo", obj);
+                }
+                err = mBufferChannel->attachEncryptedBuffers(
+                    memory,
+                    offset,
+                    buffer,
+                    (mFlags & kFlagIsSecure),
+                    &errorDetailMsg);
+            } else {
+                err = mBufferChannel->attachEncryptedBuffer(
+                        memory, (mFlags & kFlagIsSecure), key, iv, mode, pattern,
+                        offset, subSamples, numSubSamples, buffer, &errorDetailMsg);
+            }
             if (err != OK && hasCryptoOrDescrambler()
                     && (mFlags & kFlagUseCryptoAsync)) {
                 // create error detail
-                AString errorDetailMsg;
                 sp<AMessage> cryptoErrorInfo = new AMessage();
                 buildCryptoInfoAMessage(cryptoErrorInfo, CryptoAsync::kActionDecrypt);
                 cryptoErrorInfo->setInt32("err", err);
@@ -6270,10 +6356,17 @@
             }
         }
         if (mCryptoAsync) {
+            // TODO b/316565675 - enable async path for audio
             // prepare a message and enqueue
             sp<AMessage> cryptoInfo = new AMessage();
             buildCryptoInfoAMessage(cryptoInfo, CryptoAsync::kActionDecrypt);
             mCryptoAsync->decrypt(cryptoInfo);
+        } else if (msg->findObject("cryptoInfos", &obj)) {
+                buffer->meta()->setObject("cryptoInfos", obj);
+                err = mBufferChannel->queueSecureInputBuffers(
+                        buffer,
+                        (mFlags & kFlagIsSecure),
+                        errorDetailMsg);
         } else {
             err = mBufferChannel->queueSecureInputBuffer(
                 buffer,
@@ -6647,7 +6740,7 @@
         if (accessUnitInfoObj) {
             outputCallbackID = CB_LARGE_FRAME_OUTPUT_AVAILABLE;
             msg->setObject("accessUnitInfo", accessUnitInfoObj);
-             sp<BufferInfosWrapper> auInfo(
+            sp<BufferInfosWrapper> auInfo(
                     (decltype(auInfo.get()))accessUnitInfoObj.get());
              auInfo->value.back().mFlags |= flags & BUFFER_FLAG_END_OF_STREAM;
         }
diff --git a/media/libstagefright/include/media/stagefright/CodecBase.h b/media/libstagefright/include/media/stagefright/CodecBase.h
index 8741daa..bffb294 100644
--- a/media/libstagefright/include/media/stagefright/CodecBase.h
+++ b/media/libstagefright/include/media/stagefright/CodecBase.h
@@ -71,6 +71,26 @@
     ~AccessUnitInfo() {}
 };
 
+struct CodecCryptoInfo {
+    size_t mNumSubSamples{0};
+    CryptoPlugin::SubSample *mSubSamples{nullptr};
+    uint8_t *mIv{nullptr};
+    uint8_t *mKey{nullptr};
+    enum CryptoPlugin::Mode mMode;
+    CryptoPlugin::Pattern mPattern;
+
+    virtual ~CodecCryptoInfo() {}
+protected:
+    CodecCryptoInfo():
+            mNumSubSamples(0),
+            mSubSamples(nullptr),
+            mIv(nullptr),
+            mKey(nullptr),
+            mMode{CryptoPlugin::kMode_Unencrypted},
+            mPattern{0, 0} {
+    }
+};
+
 struct CodecParameterDescriptor {
     std::string name;
     AMessage::Type type;
@@ -372,6 +392,30 @@
             const CryptoPlugin::SubSample *subSamples,
             size_t numSubSamples,
             AString *errorDetailMsg) = 0;
+
+    /**
+     * Queue a secure input buffer with multiple access units into the buffer channel.
+     *
+     * @param buffer The buffer to queue. The access unit delimiters and crypto
+     *               subsample information is included in the buffer metadata.
+     * @param secure Whether the buffer is secure.
+     * @param errorDetailMsg The error message to be set in case of error.
+     * @return OK if successful;
+     *         -ENOENT of the buffer is not known
+     *         -ENOSYS if mCrypto is not set so that decryption is not
+     *         possible;
+     *         other errors if decryption failed.
+     */
+     virtual status_t queueSecureInputBuffers(
+            const sp<MediaCodecBuffer> &buffer,
+            bool secure,
+            AString *errorDetailMsg) {
+        (void)buffer;
+        (void)secure;
+        (void)errorDetailMsg;
+        return -ENOSYS;
+     }
+
     /**
      * Attach a Codec 2.0 buffer to MediaCodecBuffer.
      *
@@ -418,6 +462,34 @@
         (void)errorDetailMsg;
         return -ENOSYS;
     }
+
+    /**
+     * Attach an encrypted HidlMemory buffer containing multiple access units to an index
+     *
+     * @param memory The memory to attach.
+     * @param offset index???
+     * @param buffer The MediaCodecBuffer to attach the memory to. The access
+     *               unit delimiters and crypto subsample information is included
+     *               in the buffer metadata.
+     * @param secure Whether the buffer is secure.
+     * @param errorDetailMsg The error message to be set if an error occurs.
+     * @return    OK if successful;
+     *            -ENOENT if index is not recognized
+     *            -ENOSYS if attaching buffer is not possible or not supported
+     */
+    virtual status_t attachEncryptedBuffers(
+            const sp<hardware::HidlMemory> &memory,
+            size_t offset,
+            const sp<MediaCodecBuffer> &buffer,
+            bool secure,
+            AString* errorDetailMsg) {
+        (void)memory;
+        (void)offset;
+        (void)buffer;
+        (void)secure;
+        (void)errorDetailMsg;
+        return -ENOSYS;
+    }
     /**
      * Request buffer rendering at specified time.
      *
diff --git a/media/libstagefright/include/media/stagefright/CryptoAsync.h b/media/libstagefright/include/media/stagefright/CryptoAsync.h
index b675518..acb3dae 100644
--- a/media/libstagefright/include/media/stagefright/CryptoAsync.h
+++ b/media/libstagefright/include/media/stagefright/CryptoAsync.h
@@ -85,6 +85,18 @@
         kActionDecrypt                 = (1 <<  0),
         kActionAttachEncryptedBuffer   = (1 <<  1)
     };
+
+    // This struct is meant to copy the mapped contents from the original info.
+    struct CryptoAsyncInfo : public CodecCryptoInfo {
+        public:
+            explicit CryptoAsyncInfo(const std::unique_ptr<CodecCryptoInfo> &info);
+            virtual ~CryptoAsyncInfo() = default;
+        protected:
+            // all backup buffers for the base object.
+            sp<ABuffer> mKeyBuffer;
+            sp<ABuffer> mIvBuffer;
+            sp<ABuffer> mSubSamplesBuffer;
+    };
 protected:
 
     // Message types for the looper
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index b0b1427..9ecb12e 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -59,6 +59,7 @@
 class BufferChannelBase;
 struct AccessUnitInfo;
 struct CodecBase;
+struct CodecCryptoInfo;
 struct CodecParameterDescriptor;
 class IBatteryStats;
 struct ICrypto;
@@ -81,6 +82,7 @@
 using aidl::android::media::ClientConfigParcel;
 
 typedef WrapperObject<std::vector<AccessUnitInfo>> BufferInfosWrapper;
+typedef WrapperObject<std::vector<std::unique_ptr<CodecCryptoInfo>>> CryptoInfosWrapper;
 
 struct MediaCodec : public AHandler {
     enum Domain {
@@ -210,6 +212,14 @@
             uint32_t flags,
             AString *errorDetailMsg = NULL);
 
+    status_t queueSecureInputBuffers(
+            size_t index,
+            size_t offset,
+            size_t size,
+            const sp<BufferInfosWrapper> &accessUnitInfo,
+            const sp<CryptoInfosWrapper> &cryptoInfos,
+            AString *errorDetailMsg = NULL);
+
     status_t queueBuffer(
             size_t index,
             const std::shared_ptr<C2Buffer> &buffer,
@@ -221,13 +231,9 @@
             size_t index,
             const sp<hardware::HidlMemory> &memory,
             size_t offset,
-            const CryptoPlugin::SubSample *subSamples,
-            size_t numSubSamples,
-            const uint8_t key[16],
-            const uint8_t iv[16],
-            CryptoPlugin::Mode mode,
-            const CryptoPlugin::Pattern &pattern,
+            size_t size,
             const sp<BufferInfosWrapper> &bufferInfos,
+            const sp<CryptoInfosWrapper> &cryptoInfos,
             const sp<AMessage> &tunings,
             AString *errorDetailMsg = NULL);