Source code
Revision control
Copy as Markdown
Other Tools
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
#include "MediaDrmProxySupport.h"
#include "MediaDrmCDMCallbackProxy.h"
#include "mozilla/EMEUtils.h"
#include "mozilla/java/MediaDrmProxyNatives.h"
#include "mozilla/java/SessionKeyInfoWrappers.h"
#include "MediaCodec.h" // For MediaDrm::KeyStatus
namespace mozilla {
LogModule* GetMDRMNLog() {
static LazyLogModule log("MediaDrmProxySupport");
return log;
}
class MediaDrmJavaCallbacksSupport
: public java::MediaDrmProxy::NativeMediaDrmProxyCallbacks::Natives<
MediaDrmJavaCallbacksSupport> {
public:
typedef java::MediaDrmProxy::NativeMediaDrmProxyCallbacks::Natives<
MediaDrmJavaCallbacksSupport>
MediaDrmProxyNativeCallbacks;
using MediaDrmProxyNativeCallbacks::AttachNative;
using MediaDrmProxyNativeCallbacks::DisposeNative;
explicit MediaDrmJavaCallbacksSupport(
UniquePtr<MediaDrmCDMCallbackProxy>&& aDecryptorProxyCallback)
: mDecryptorProxyCallback(std::move(aDecryptorProxyCallback)) {
MOZ_ASSERT(mDecryptorProxyCallback);
}
/*
* Native implementation, called by Java.
*/
void OnSessionCreated(int aCreateSessionToken, int aPromiseId,
jni::ByteArray::Param aSessionId,
jni::ByteArray::Param aRequest);
void OnSessionUpdated(int aPromiseId, jni::ByteArray::Param aSessionId);
void OnSessionClosed(int aPromiseId, jni::ByteArray::Param aSessionId);
void OnSessionMessage(
jni::ByteArray::Param aSessionId,
int /*mozilla::dom::MediaKeyMessageType*/ aSessionMessageType,
jni::ByteArray::Param aRequest);
void OnSessionError(jni::ByteArray::Param aSessionId,
jni::String::Param aMessage);
void OnSessionBatchedKeyChanged(jni::ByteArray::Param,
jni::ObjectArray::Param);
void OnRejectPromise(int aPromiseId, jni::String::Param aMessage);
private:
UniquePtr<MediaDrmCDMCallbackProxy> mDecryptorProxyCallback;
}; // MediaDrmJavaCallbacksSupport
void MediaDrmJavaCallbacksSupport::OnSessionCreated(
int aCreateSessionToken, int aPromiseId, jni::ByteArray::Param aSessionId,
jni::ByteArray::Param aRequest) {
MOZ_ASSERT(NS_IsMainThread());
auto reqDataArray = aRequest->GetElements();
nsCString sessionId(
reinterpret_cast<char*>(aSessionId->GetElements().Elements()),
aSessionId->Length());
MDRMN_LOG("SessionId(%s) closed", sessionId.get());
mDecryptorProxyCallback->SetSessionId(aCreateSessionToken, sessionId);
mDecryptorProxyCallback->ResolvePromise(aPromiseId);
}
void MediaDrmJavaCallbacksSupport::OnSessionUpdated(
int aPromiseId, jni::ByteArray::Param aSessionId) {
MOZ_ASSERT(NS_IsMainThread());
MDRMN_LOG(
"SessionId(%s) closed",
nsCString(reinterpret_cast<char*>(aSessionId->GetElements().Elements()),
aSessionId->Length())
.get());
mDecryptorProxyCallback->ResolvePromise(aPromiseId);
}
void MediaDrmJavaCallbacksSupport::OnSessionClosed(
int aPromiseId, jni::ByteArray::Param aSessionId) {
MOZ_ASSERT(NS_IsMainThread());
nsCString sessionId(
reinterpret_cast<char*>(aSessionId->GetElements().Elements()),
aSessionId->Length());
MDRMN_LOG("SessionId(%s) closed", sessionId.get());
mDecryptorProxyCallback->ResolvePromise(aPromiseId);
mDecryptorProxyCallback->SessionClosed(sessionId);
}
void MediaDrmJavaCallbacksSupport::OnSessionMessage(
jni::ByteArray::Param aSessionId,
int /*mozilla::dom::MediaKeyMessageType*/ aMessageType,
jni::ByteArray::Param aRequest) {
MOZ_ASSERT(NS_IsMainThread());
nsCString sessionId(
reinterpret_cast<char*>(aSessionId->GetElements().Elements()),
aSessionId->Length());
auto reqDataArray = aRequest->GetElements();
nsTArray<uint8_t> retRequest;
retRequest.AppendElements(reinterpret_cast<uint8_t*>(reqDataArray.Elements()),
reqDataArray.Length());
mDecryptorProxyCallback->SessionMessage(
sessionId, static_cast<dom::MediaKeyMessageType>(aMessageType),
retRequest);
}
void MediaDrmJavaCallbacksSupport::OnSessionError(
jni::ByteArray::Param aSessionId, jni::String::Param aMessage) {
MOZ_ASSERT(NS_IsMainThread());
nsCString sessionId(
reinterpret_cast<char*>(aSessionId->GetElements().Elements()),
aSessionId->Length());
nsCString errorMessage = aMessage->ToCString();
MDRMN_LOG("SessionId(%s)", sessionId.get());
// TODO: We cannot get system error code from media drm API.
// Currently use -1 as an error code.
mDecryptorProxyCallback->SessionError(
sessionId, NS_ERROR_DOM_INVALID_STATE_ERR, -1, errorMessage);
}
// TODO: MediaDrm.KeyStatus defined the status code not included
// dom::MediaKeyStatus::Released and dom::MediaKeyStatus::Output_downscaled.
// Should keep tracking for this if it will be changed in the future.
static dom::MediaKeyStatus MediaDrmKeyStatusToMediaKeyStatus(int aStatusCode) {
using mozilla::java::sdk::MediaDrm;
switch (aStatusCode) {
case MediaDrm::KeyStatus::STATUS_USABLE:
return dom::MediaKeyStatus::Usable;
case MediaDrm::KeyStatus::STATUS_EXPIRED:
return dom::MediaKeyStatus::Expired;
case MediaDrm::KeyStatus::STATUS_OUTPUT_NOT_ALLOWED:
return dom::MediaKeyStatus::Output_restricted;
case MediaDrm::KeyStatus::STATUS_INTERNAL_ERROR:
return dom::MediaKeyStatus::Internal_error;
case MediaDrm::KeyStatus::STATUS_PENDING:
return dom::MediaKeyStatus::Status_pending;
default:
return dom::MediaKeyStatus::Internal_error;
}
}
void MediaDrmJavaCallbacksSupport::OnSessionBatchedKeyChanged(
jni::ByteArray::Param aSessionId, jni::ObjectArray::Param aKeyInfos) {
MOZ_ASSERT(NS_IsMainThread());
nsCString sessionId(
reinterpret_cast<char*>(aSessionId->GetElements().Elements()),
aSessionId->Length());
nsTArray<jni::Object::LocalRef> keyInfosObjectArray(aKeyInfos->GetElements());
nsTArray<CDMKeyInfo> keyInfosArray;
for (auto&& keyInfoObject : keyInfosObjectArray) {
java::SessionKeyInfo::LocalRef keyInfo(std::move(keyInfoObject));
mozilla::jni::ByteArray::LocalRef keyIdByteArray = keyInfo->KeyId();
nsTArray<int8_t> keyIdInt8Array = keyIdByteArray->GetElements();
// Cast nsTArray<int8_t> to nsTArray<uint8_t>
nsTArray<uint8_t>* keyId =
reinterpret_cast<nsTArray<uint8_t>*>(&keyIdInt8Array);
auto keyStatus = keyInfo->Status(); // int32_t
keyInfosArray.AppendElement(
CDMKeyInfo(*keyId, dom::Optional<dom::MediaKeyStatus>(
MediaDrmKeyStatusToMediaKeyStatus(keyStatus))));
}
mDecryptorProxyCallback->BatchedKeyStatusChanged(sessionId, keyInfosArray);
}
void MediaDrmJavaCallbacksSupport::OnRejectPromise(
int aPromiseId, jni::String::Param aMessage) {
MOZ_ASSERT(NS_IsMainThread());
nsCString reason = aMessage->ToCString();
MDRMN_LOG("OnRejectPromise aMessage(%s) ", reason.get());
// Current implementation assume all the reject from MediaDrm is due to
// invalid state. Other cases should be handled before calling into
// MediaDrmProxy API.
ErrorResult rv;
rv.ThrowInvalidStateError(reason);
mDecryptorProxyCallback->RejectPromise(aPromiseId, std::move(rv), reason);
}
MediaDrmProxySupport::MediaDrmProxySupport(const nsAString& aKeySystem)
: mKeySystem(aKeySystem), mDestroyed(false) {
mJavaCallbacks = java::MediaDrmProxy::NativeMediaDrmProxyCallbacks::New();
mBridgeProxy = java::MediaDrmProxy::Create(mKeySystem, mJavaCallbacks);
MOZ_ASSERT(mBridgeProxy, "mBridgeProxy should not be null");
mMediaDrmStubId = mBridgeProxy->GetStubId()->ToString();
}
MediaDrmProxySupport::~MediaDrmProxySupport() {
MOZ_ASSERT(mDestroyed, "Shutdown() should be called before !!");
MediaDrmJavaCallbacksSupport::DisposeNative(mJavaCallbacks);
}
nsresult MediaDrmProxySupport::Init(
UniquePtr<MediaDrmCDMCallbackProxy>&& aCallback) {
MOZ_ASSERT(mJavaCallbacks);
MediaDrmJavaCallbacksSupport::AttachNative(
mJavaCallbacks,
mozilla::MakeUnique<MediaDrmJavaCallbacksSupport>(std::move(aCallback)));
return mBridgeProxy != nullptr ? NS_OK : NS_ERROR_FAILURE;
}
void MediaDrmProxySupport::CreateSession(uint32_t aCreateSessionToken,
uint32_t aPromiseId,
const nsCString& aInitDataType,
const nsTArray<uint8_t>& aInitData,
MediaDrmSessionType aSessionType) {
MOZ_ASSERT(mBridgeProxy);
auto initDataBytes = mozilla::jni::ByteArray::New(
reinterpret_cast<const int8_t*>(&aInitData[0]), aInitData.Length());
// TODO: aSessionType is not used here.
// Refer to
// it is hard code to streaming type.
mBridgeProxy->CreateSession(aCreateSessionToken, aPromiseId,
NS_ConvertUTF8toUTF16(aInitDataType),
initDataBytes);
}
void MediaDrmProxySupport::UpdateSession(uint32_t aPromiseId,
const nsCString& aSessionId,
const nsTArray<uint8_t>& aResponse) {
MOZ_ASSERT(mBridgeProxy);
auto response = mozilla::jni::ByteArray::New(
reinterpret_cast<const int8_t*>(aResponse.Elements()),
aResponse.Length());
mBridgeProxy->UpdateSession(aPromiseId, NS_ConvertUTF8toUTF16(aSessionId),
response);
}
void MediaDrmProxySupport::CloseSession(uint32_t aPromiseId,
const nsCString& aSessionId) {
MOZ_ASSERT(mBridgeProxy);
mBridgeProxy->CloseSession(aPromiseId, NS_ConvertUTF8toUTF16(aSessionId));
}
void MediaDrmProxySupport::Shutdown() {
MOZ_ASSERT(mBridgeProxy);
if (mDestroyed) {
return;
}
mBridgeProxy->Destroy();
mDestroyed = true;
}
bool MediaDrmProxySupport::SetServerCertificate(
const nsTArray<uint8_t>& aCert) {
jni::ByteArray::LocalRef cert = jni::ByteArray::New(
reinterpret_cast<const int8_t*>(aCert.Elements()), aCert.Length());
return mBridgeProxy->SetServerCertificate(cert);
}
} // namespace mozilla