blob: 8b5c8ed36bf63498b30b9dd19a93e1cb8b80780b [file] [log] [blame]
/*
* 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, &errorDetailMsg);
// 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