Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8024460e authored by Arun Johnson's avatar Arun Johnson
Browse files

Implementing CryptoAsync

Decryption is done in a seperate thread when configured
with CONFIGURE_FLAG_USE_CRYPTO_ASYNC

Bug: 254050543

Change-Id: Ib192a5da27f28335b3ed00025b0084e99a511e9c
parent 28fbbc02
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -237,6 +237,7 @@ cc_library {
        "CallbackMediaSource.cpp",
        "CameraSource.cpp",
        "CameraSourceTimeLapse.cpp",
        "CryptoAsync.cpp",
        "FrameDecoder.cpp",
        "HevcUtils.cpp",
        "InterfaceUtils.cpp",
+289 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 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 "CryptoAsync"

#include <log/log.h>

#include "hidl/HidlSupport.h"
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>

#include <media/MediaCodecBuffer.h>
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/CryptoAsync.h>

namespace android {

CryptoAsync::~CryptoAsync() {
}

status_t CryptoAsync::decrypt(sp<AMessage> &msg) {
    int32_t decryptAction;
    CHECK(msg->findInt32("action", &decryptAction));
    if (mCallback == nullptr) {
       ALOGE("Crypto callback channel is not set");
       return -ENOSYS;
    }
    bool shouldPost = false;
    Mutexed<std::list<sp<AMessage>>>::Locked pendingBuffers(mPendingBuffers);
    if (mState != kCryptoAsyncActive) {
       ALOGE("Cannot decrypt in errored state");
       return -ENOSYS;
    }
    shouldPost = pendingBuffers->size() == 0 ? true : false;
    pendingBuffers->push_back(std::move(msg));
    if (shouldPost) {
       sp<AMessage> decryptMsg = new AMessage(kWhatDecrypt, this);
       decryptMsg->post();
    }
    return OK;
}

void CryptoAsync::stop(std::list<sp<AMessage>> * const buffers) {
    sp<AMessage>  stopMsg = new AMessage(kWhatStop, this);
    stopMsg->setPointer("remaining", static_cast<void*>(buffers));
    sp<AMessage> response;
    status_t err = stopMsg->postAndAwaitResponse(&response);
    if (err == OK && response != NULL) {
        CHECK(response->findInt32("err", &err));
    } else {
        ALOGE("Error handling stop in CryptoAsync");
        //TODO: handle the error here.
    }
}

status_t CryptoAsync::decryptAndQueue(sp<AMessage> & msg) {
    std::shared_ptr<BufferChannelBase> channel = mBufferChannel.lock();
    status_t err = OK;
    sp<RefBase> obj;
    size_t numSubSamples = 0;
    int32_t secure = 0;
    CryptoPlugin::Mode mode;
    CryptoPlugin::Pattern pattern;
    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());
    sp<MediaCodecBuffer> buffer = static_cast<MediaCodecBuffer *>(obj.get());
    err = channel->queueSecureInputBuffer(buffer, secure, key, iv, mode,
        pattern, subSamples, numSubSamples, &errorDetailMsg);
    if (err != OK) {
        std::list<sp<AMessage>> errorList;
        msg->removeEntryByName("buffer");
        msg->setInt32("err", err);
        msg->setInt32("actionCode", ACTION_CODE_FATAL);
        msg->setString("errorDetail", errorDetailMsg);
        errorList.push_back(std::move(msg));
        mCallback->onDecryptError(errorList);
   }
   return err;
}

status_t CryptoAsync::attachEncryptedBufferAndQueue(sp<AMessage> & msg) {
    std::shared_ptr<BufferChannelBase> channel = mBufferChannel.lock();
    status_t err = OK;
    sp<RefBase> obj;
    sp<RefBase> mem_obj;
    sp<hardware::HidlMemory> memory;
    size_t numSubSamples = 0;
    int32_t secure = 0;
    size_t offset;
    size_t size;
    CryptoPlugin::Mode mode;
    CryptoPlugin::Pattern pattern;
    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);
    CHECK(msg->findObject("memory", &mem_obj));
    CHECK(msg->findSize("offset", (size_t*)&offset));
    AString errorDetailMsg;
    // get key info
    const uint8_t * key = keyBuffer.get() != nullptr ? keyBuffer.get()->data() : nullptr;
    // get iv info
    const uint8_t * iv = ivBuffer.get() != nullptr ? ivBuffer.get()->data() : nullptr;

    const CryptoPlugin::SubSample * subSamples =
     (CryptoPlugin::SubSample *)(subSamplesBuffer.get()->data());

    // get MediaCodecBuffer
    sp<MediaCodecBuffer> buffer = static_cast<MediaCodecBuffer *>(obj.get());

    // get HidlMemory
    memory = static_cast<MediaCodec::WrapperObject<sp<hardware::HidlMemory>> *>
        (mem_obj.get())->value;

    // attach buffer
    err = channel->attachEncryptedBuffer(
        memory, secure, key, iv, mode, pattern,
        offset, subSamples, numSubSamples, buffer);

    // a generic error
    auto handleError = [this, &err, &msg]() {
        std::list<sp<AMessage>> errorList;
        msg->removeEntryByName("buffer");
        msg->setInt32("err", err);
        msg->setInt32("actionCode", ACTION_CODE_FATAL);
        errorList.push_back(std::move(msg));
        mCallback->onDecryptError(errorList);
    };
    if (err != OK) {
        handleError();
        return err;
     }
     offset = buffer->offset();
     size = buffer->size();

    if (offset + size > buffer->capacity()) {
        err = -ENOSYS;
        handleError();
        return err;
    }
    buffer->setRange(offset, size);
    err = channel->queueInputBuffer(buffer);
    if (err != OK) {
        handleError();
        return err;
    }
   return err;
}

void CryptoAsync::onMessageReceived(const sp<AMessage> & msg) {
    status_t err = OK;
    auto getCurrentAndNextTask =
        [this](sp<AMessage> * const  current, uint32_t & nextTask) -> status_t {
        sp<AMessage> obj;
        Mutexed<std::list<sp<AMessage>>>::Locked pendingBuffers(mPendingBuffers);
        if ((pendingBuffers->size() == 0) || (mState != kCryptoAsyncActive)) {
           return -ENOMSG;
        }
        *current = std::move(*(pendingBuffers->begin()));
        pendingBuffers->pop_front();
        //Try to see if we will be able to process next buffer
        while((nextTask == kWhatDoNothing) && pendingBuffers->size() > 0)
        {
            sp<AMessage> & nextBuffer = pendingBuffers->front();
            if (nextBuffer == nullptr) {
                pendingBuffers->pop_front();
                continue;
            }
            nextTask = kWhatDecrypt;
        }
        return OK;
    };
    switch(msg->what()) {
        case kWhatDecrypt:
        {
            sp<AMessage> thisMsg;
            uint32_t nextTask = kWhatDoNothing;
            if(OK != getCurrentAndNextTask(&thisMsg, nextTask)) {
                return;
            }
            if (thisMsg != nullptr) {
                int32_t action;
                err = OK;
                CHECK(thisMsg->findInt32("action", &action));
                switch(action) {
                    case kActionDecrypt:
                    {
                        err = decryptAndQueue(thisMsg);
                        break;
                    }

                    case kActionAttachEncryptedBuffer:
                    {
                        err = attachEncryptedBufferAndQueue(thisMsg);
                        break;
                    }

                    default:
                    {
                        ALOGE("Unrecognized action in decrypt");
                    }
                }
                if (err != OK) {
                    Mutexed<std::list<sp<AMessage>>>::Locked pendingBuffers(mPendingBuffers);
                    mState = kCryptoAsyncError;
                }
            }
            // we won't take  next buffers if buffer caused
            // an error. We want the caller to deal with the error first
            // Expected behahiour is that the caller acknowledge the error
            // with a call to stop() which clear the queues.
            // Then move forward with processing of next set of buffers.
            if (mState == kCryptoAsyncActive && nextTask != kWhatDoNothing) {
                sp<AMessage> nextMsg = new AMessage(nextTask,this);
                nextMsg->post();
            }
            break;
        }

        case kWhatStop:
        {
            typedef std::list<sp<AMessage>> ReturnListType;
            ReturnListType * returnList = nullptr;
            sp<AReplyToken> replyID;
            CHECK(msg->senderAwaitsResponse(&replyID));
            sp<AMessage> response = new AMessage;
            msg->findPointer("remaining", (void**)(&returnList));
            Mutexed<std::list<sp<AMessage>>>::Locked pendingBuffers(mPendingBuffers);
            if (returnList) {
                returnList->clear();
                returnList->splice(returnList->end(), std::move(*pendingBuffers));
            }
            pendingBuffers->clear();
            mState = kCryptoAsyncActive;
            response->setInt32("err", OK);
            response->postReply(replyID);

            break;
        }

        default:
        {
            status_t err = OK;
            //TODO: do something with error here.
            (void)err;
            break;
        }
    }
}

}  // namespace android
+190 −63

File changed.

Preview size limit exceeded, changes collapsed.

+139 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef CRYPTO_ASYNC_H_
#define CRYPTO_ASYNC_H_

#include <media/stagefright/CodecBase.h>
#include <media/stagefright/foundation/Mutexed.h>
namespace android {

class CryptoAsync: public AHandler {
public:

    class CryptoAsyncCallback {
    public:

        virtual ~CryptoAsyncCallback() = default;

        /*
         * Callback with result for queuing the decrypted buffer to the
         * underlying codec. Cannot block this function
         */
        virtual void onDecryptComplete(const sp<AMessage>& result) = 0;

        /*
         * Callback with error information while decryption. Cannot block
         * this call. The return should contain the error information
         * and the buffer the caused the error.
         */
        virtual void onDecryptError(const std::list<sp<AMessage>>& errorMsg) = 0;
    };

    // Ideally we should be returning the output of the decryption in
    // onDecryptComple() calback and let the next module take over the
    // rest of the processing. In the current state, the next step will
    // be to queue the output the codec which is done using BufferChannel

    // In order to prevent thread hop to just do that, we have created
    // a dependency on BufferChannel here to queue the buffer to the codec
    // immediately after decryption.
    CryptoAsync(std::weak_ptr<BufferChannelBase> bufferChannel)
        :mState(kCryptoAsyncActive) {
        mBufferChannel = std::move(bufferChannel);
    }

    // Destructor
    virtual ~CryptoAsync();

    inline void setCallback(std::unique_ptr<CryptoAsyncCallback>&& callback) {
        mCallback = std::move(callback);
    }

    // Call this function to decrypt the buffer in the message.
    status_t decrypt(sp<AMessage>& msg);

    // This function stops further processing in the thread and returns
    // with any unprocessed buffers from the queue.
    // We can use this method in case of flush or clearing the queue
    // upon error. When the processing hits an error, the self processing
    // in this looper stops and in-fact., there is a need to clear (call stop())
    // for the queue to become operational again. Also acts like a rest.
    void stop(std::list<sp<AMessage>> * const buffers = nullptr);

    // Describes two actions for decrypt();
    // kActionDecrypt - decrypts the buffer and queues to codec
    // kActionAttachEncryptedBuffer - decrypts and attaches the buffer
    //                               and queues to the codec.
    // TODO: kActionAttachEncryptedBuffer is meant to work with
    // BLOCK_MODEL which is not yet implemented.
    enum : uint32_t {
        // decryption types
        kActionDecrypt                 = (1 <<  0),
        kActionAttachEncryptedBuffer   = (1 <<  1)
    };
protected:

    // Message types for the looper
    enum : uint32_t {
        // used with decrypt()
        // Exact decryption type as described by the above enum
        // decides what "action" to take. The "action" should be
        // part of this message
        kWhatDecrypt         = 1,
        // used with stop()
        kWhatStop            = 2,
        // place holder
        kWhatDoNothing       = 10
    };

    // Defines the staste of this thread.
    typedef enum : uint32_t {
        // kCryptoAsyncActive as long as we have not encountered
        // any errors during processing. Any errors will
        // put the state to error and the thread now refuses to
        // do further processing until the error state is cleared
        // with a call to stop()

        kCryptoAsyncActive  = (0 <<  0),
        // state of the looper when encountered with error during
        // processing
        kCryptoAsyncError   = (1 <<  8)
    } CryptoAsyncState;

    // Implements kActionDecrypt
    status_t decryptAndQueue(sp<AMessage>& msg);

    // Implements kActionAttachEncryptedBuffer
    status_t attachEncryptedBufferAndQueue(sp<AMessage>& msg);

    // Implements the Looper
    void onMessageReceived(const sp<AMessage>& msg) override;

    std::unique_ptr<CryptoAsyncCallback> mCallback;
private:

    CryptoAsyncState mState;

    // Queue holding any pending buffers
    Mutexed<std::list<sp<AMessage>>> mPendingBuffers;

    std::weak_ptr<BufferChannelBase> mBufferChannel;
};

}  // namespace android

#endif  // CRYPTO_ASYNC_H_
+7 −0
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ struct CodecBase;
struct CodecParameterDescriptor;
class IBatteryStats;
struct ICrypto;
class CryptoAsync;
class MediaCodecBuffer;
class IMemory;
struct PersistentSurface;
@@ -82,6 +83,7 @@ struct MediaCodec : public AHandler {
    enum ConfigureFlags {
        CONFIGURE_FLAG_ENCODE           = 1,
        CONFIGURE_FLAG_USE_BLOCK_MODEL  = 2,
        CONFIGURE_FLAG_USE_CRYPTO_ASYNC = 4,
    };

    enum BufferFlags {
@@ -106,6 +108,7 @@ struct MediaCodec : public AHandler {
        CB_ERROR = 3,
        CB_OUTPUT_FORMAT_CHANGED = 4,
        CB_RESOURCE_RECLAIMED = 5,
        CB_CRYPTO_ERROR = 6,
    };

    static const pid_t kNoPid = -1;
@@ -376,6 +379,7 @@ private:
        kFlagIsComponentAllocated       = 2048,
        kFlagPushBlankBuffersOnShutdown = 4096,
        kFlagUseBlockModel              = 8192,
        kFlagUseCryptoAsync             = 16384,
    };

    struct BufferInfo {
@@ -530,6 +534,8 @@ private:
    bool mCpuBoostRequested;

    std::shared_ptr<BufferChannelBase> mBufferChannel;
    sp<CryptoAsync> mCryptoAsync;
    sp<ALooper> mCryptoLooper;

    std::unique_ptr<PlaybackDurationAccumulator> mPlaybackDurationAccumulator;
    bool mIsSurfaceToScreen;
@@ -583,6 +589,7 @@ private:

    void onInputBufferAvailable();
    void onOutputBufferAvailable();
    void onCryptoError(const sp<AMessage> &msg);
    void onError(status_t err, int32_t actionCode, const char *detail = NULL);
    void onOutputFormatChanged();