/*
* Copyright (C) 2012 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 "ICrypto"
#include <utils/Log.h>
#include <binder/Parcel.h>
#include <binder/IMemory.h>
#include <media/ICrypto.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AString.h>
namespace android {
enum {
INIT_CHECK = IBinder::FIRST_CALL_TRANSACTION,
IS_CRYPTO_SUPPORTED,
CREATE_PLUGIN,
DESTROY_PLUGIN,
REQUIRES_SECURE_COMPONENT,
DECRYPT,
NOTIFY_RESOLUTION,
SET_MEDIADRM_SESSION,
};
struct BpCrypto : public BpInterface<ICrypto> {
BpCrypto(const sp<IBinder> &impl)
: BpInterface<ICrypto>(impl) {
}
virtual status_t initCheck() const {
Parcel data, reply;
data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
remote()->transact(INIT_CHECK, data, &reply);
return reply.readInt32();
}
virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]) {
Parcel data, reply;
data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
data.write(uuid, 16);
remote()->transact(IS_CRYPTO_SUPPORTED, data, &reply);
return reply.readInt32() != 0;
}
virtual status_t createPlugin(
const uint8_t uuid[16], const void *opaqueData, size_t opaqueSize) {
Parcel data, reply;
data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
data.write(uuid, 16);
data.writeInt32(opaqueSize);
if (opaqueSize > 0) {
data.write(opaqueData, opaqueSize);
}
remote()->transact(CREATE_PLUGIN, data, &reply);
return reply.readInt32();
}
virtual status_t destroyPlugin() {
Parcel data, reply;
data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
remote()->transact(DESTROY_PLUGIN, data, &reply);
return reply.readInt32();
}
virtual bool requiresSecureDecoderComponent(
const char *mime) const {
Parcel data, reply;
data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
data.writeCString(mime);
remote()->transact(REQUIRES_SECURE_COMPONENT, data, &reply);
return reply.readInt32() != 0;
}
virtual ssize_t decrypt(
bool secure,
const uint8_t key[16],
const uint8_t iv[16],
CryptoPlugin::Mode mode,
const sp<IMemory> &sharedBuffer, size_t offset,
const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
void *dstPtr,
AString *errorDetailMsg) {
Parcel data, reply;
data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
data.writeInt32(secure);
data.writeInt32(mode);
static const uint8_t kDummy[16] = { 0 };
if (key == NULL) {
key = kDummy;
}
if (iv == NULL) {
iv = kDummy;
}
data.write(key, 16);
data.write(iv, 16);
size_t totalSize = 0;
for (size_t i = 0; i < numSubSamples; ++i) {
totalSize += subSamples[i].mNumBytesOfEncryptedData;
totalSize += subSamples[i].mNumBytesOfClearData;
}
data.writeInt32(totalSize);
data.writeStrongBinder(IInterface::asBinder(sharedBuffer));
data.writeInt32(offset);
data.writeInt32(numSubSamples);
data.write(subSamples, sizeof(CryptoPlugin::SubSample) * numSubSamples);
if (secure) {
data.writeInt64(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(dstPtr)));
}
remote()->transact(DECRYPT, data, &reply);
ssize_t result = reply.readInt32();
if (isCryptoError(result)) {
errorDetailMsg->setTo(reply.readCString());
}
if (!secure && result >= 0) {
reply.read(dstPtr, result);
}
return result;
}
virtual void notifyResolution(
uint32_t width, uint32_t height) {
Parcel data, reply;
data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
data.writeInt32(width);
data.writeInt32(height);
remote()->transact(NOTIFY_RESOLUTION, data, &reply);
}
virtual status_t setMediaDrmSession(const Vector<uint8_t> &sessionId) {
Parcel data, reply;
data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
writeVector(data, sessionId);
remote()->transact(SET_MEDIADRM_SESSION, data, &reply);
return reply.readInt32();
}
private:
void readVector(Parcel &reply, Vector<uint8_t> &vector) const {
uint32_t size = reply.readInt32();
vector.insertAt((size_t)0, size);
reply.read(vector.editArray(), size);
}
void writeVector(Parcel &data, Vector<uint8_t> const &vector) const {
data.writeInt32(vector.size());
data.write(vector.array(), vector.size());
}
DISALLOW_EVIL_CONSTRUCTORS(BpCrypto);
};
IMPLEMENT_META_INTERFACE(Crypto, "android.hardware.ICrypto");
////////////////////////////////////////////////////////////////////////////////
void BnCrypto::readVector(const Parcel &data, Vector<uint8_t> &vector) const {
uint32_t size = data.readInt32();
vector.insertAt((size_t)0, size);
data.read(vector.editArray(), size);
}
void BnCrypto::writeVector(Parcel *reply, Vector<uint8_t> const &vector) const {
reply->writeInt32(vector.size());
reply->write(vector.array(), vector.size());
}
status_t BnCrypto::onTransact(
uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
switch (code) {
case INIT_CHECK:
{
CHECK_INTERFACE(ICrypto, data, reply);
reply->writeInt32(initCheck());
return OK;
}
case IS_CRYPTO_SUPPORTED:
{
CHECK_INTERFACE(ICrypto, data, reply);
uint8_t uuid[16];
data.read(uuid, sizeof(uuid));
reply->writeInt32(isCryptoSchemeSupported(uuid));
return OK;
}
case CREATE_PLUGIN:
{
CHECK_INTERFACE(ICrypto, data, reply);
uint8_t uuid[16];
data.read(uuid, sizeof(uuid));
size_t opaqueSize = data.readInt32();
void *opaqueData = NULL;
if (opaqueSize > 0) {
opaqueData = malloc(opaqueSize);
data.read(opaqueData, opaqueSize);
}
reply->writeInt32(createPlugin(uuid, opaqueData, opaqueSize));
if (opaqueData != NULL) {
free(opaqueData);
opaqueData = NULL;
}
return OK;
}
case DESTROY_PLUGIN:
{
CHECK_INTERFACE(ICrypto, data, reply);
reply->writeInt32(destroyPlugin());
return OK;
}
case REQUIRES_SECURE_COMPONENT:
{
CHECK_INTERFACE(ICrypto, data, reply);
const char *mime = data.readCString();
reply->writeInt32(requiresSecureDecoderComponent(mime));
return OK;
}
case DECRYPT:
{
CHECK_INTERFACE(ICrypto, data, reply);
bool secure = data.readInt32() != 0;
CryptoPlugin::Mode mode = (CryptoPlugin::Mode)data.readInt32();
uint8_t key[16];
data.read(key, sizeof(key));
uint8_t iv[16];
data.read(iv, sizeof(iv));
size_t totalSize = data.readInt32();
sp<IMemory> sharedBuffer =
interface_cast<IMemory>(data.readStrongBinder());
int32_t offset = data.readInt32();
int32_t numSubSamples = data.readInt32();
CryptoPlugin::SubSample *subSamples =
new CryptoPlugin::SubSample[numSubSamples];
data.read(
subSamples,
sizeof(CryptoPlugin::SubSample) * numSubSamples);
void *secureBufferId, *dstPtr;
if (secure) {
secureBufferId = reinterpret_cast<void *>(static_cast<uintptr_t>(data.readInt64()));
} else {
dstPtr = calloc(1, totalSize);
}
AString errorDetailMsg;
ssize_t result;
size_t sumSubsampleSizes = 0;
bool overflow = false;
for (int32_t i = 0; i < numSubSamples; ++i) {
CryptoPlugin::SubSample &ss = subSamples[i];
if (sumSubsampleSizes <= SIZE_MAX - ss.mNumBytesOfEncryptedData) {
sumSubsampleSizes += ss.mNumBytesOfEncryptedData;
} else {
overflow = true;
}
if (sumSubsampleSizes <= SIZE_MAX - ss.mNumBytesOfClearData) {
sumSubsampleSizes += ss.mNumBytesOfClearData;
} else {
overflow = true;
}
}
if (overflow || sumSubsampleSizes != totalSize) {
result = -EINVAL;
} else if (offset + totalSize > sharedBuffer->size()) {
result = -EINVAL;
} else {
result = decrypt(
secure,
key,
iv,
mode,
sharedBuffer, offset,
subSamples, numSubSamples,
secure ? secureBufferId : dstPtr,
&errorDetailMsg);
}
reply->writeInt32(result);
if (isCryptoError(result)) {
reply->writeCString(errorDetailMsg.c_str());
}
if (!secure) {
if (result >= 0) {
CHECK_LE(result, static_cast<ssize_t>(totalSize));
reply->write(dstPtr, result);
}
free(dstPtr);
dstPtr = NULL;
}
delete[] subSamples;
subSamples = NULL;
return OK;
}
case NOTIFY_RESOLUTION:
{
CHECK_INTERFACE(ICrypto, data, reply);
int32_t width = data.readInt32();
int32_t height = data.readInt32();
notifyResolution(width, height);
return OK;
}
case SET_MEDIADRM_SESSION:
{
CHECK_INTERFACE(IDrm, data, reply);
Vector<uint8_t> sessionId;
readVector(data, sessionId);
reply->writeInt32(setMediaDrmSession(sessionId));
return OK;
}
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
} // namespace android