summaryrefslogtreecommitdiffstats
path: root/dom/media/eme/mediadrm
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/eme/mediadrm')
-rw-r--r--dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.cpp115
-rw-r--r--dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.h63
-rw-r--r--dom/media/eme/mediadrm/MediaDrmCDMProxy.cpp453
-rw-r--r--dom/media/eme/mediadrm/MediaDrmCDMProxy.h186
-rw-r--r--dom/media/eme/mediadrm/MediaDrmProxySupport.cpp272
-rw-r--r--dom/media/eme/mediadrm/MediaDrmProxySupport.h68
-rw-r--r--dom/media/eme/mediadrm/moz.build19
7 files changed, 1176 insertions, 0 deletions
diff --git a/dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.cpp b/dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.cpp
new file mode 100644
index 0000000000..e5431f50fd
--- /dev/null
+++ b/dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.cpp
@@ -0,0 +1,115 @@
+/* -*- 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,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MediaDrmCDMCallbackProxy.h"
+#include "MediaDrmCDMProxy.h"
+#include "nsString.h"
+#include "mozilla/dom/MediaKeys.h"
+#include "mozilla/dom/MediaKeySession.h"
+#include "nsContentCID.h"
+#include "nsServiceManagerUtils.h"
+#include "MainThreadUtils.h"
+#include "mozilla/EMEUtils.h"
+
+namespace mozilla {
+
+MediaDrmCDMCallbackProxy::MediaDrmCDMCallbackProxy(MediaDrmCDMProxy* aProxy)
+ : mProxy(aProxy) {}
+
+MediaDrmCDMCallbackProxy::~MediaDrmCDMCallbackProxy() = default;
+
+void MediaDrmCDMCallbackProxy::SetSessionId(uint32_t aToken,
+ const nsCString& aSessionId) {
+ MOZ_ASSERT(NS_IsMainThread());
+ mProxy->OnSetSessionId(aToken, NS_ConvertUTF8toUTF16(aSessionId));
+}
+
+void MediaDrmCDMCallbackProxy::ResolveLoadSessionPromise(uint32_t aPromiseId,
+ bool aSuccess) {
+ MOZ_ASSERT(NS_IsMainThread());
+ mProxy->OnResolveLoadSessionPromise(aPromiseId, aSuccess);
+}
+
+void MediaDrmCDMCallbackProxy::ResolvePromise(uint32_t aPromiseId) {
+ // Note: CDMProxy proxies this from non-main threads to main thread.
+ mProxy->ResolvePromise(aPromiseId);
+}
+
+void MediaDrmCDMCallbackProxy::RejectPromise(uint32_t aPromiseId,
+ ErrorResult&& aException,
+ const nsCString& aMessage) {
+ MOZ_ASSERT(NS_IsMainThread());
+ mProxy->OnRejectPromise(aPromiseId, std::move(aException), aMessage);
+}
+
+void MediaDrmCDMCallbackProxy::SessionMessage(
+ const nsCString& aSessionId, dom::MediaKeyMessageType aMessageType,
+ const nsTArray<uint8_t>& aMessage) {
+ MOZ_ASSERT(NS_IsMainThread());
+ // For removing constness
+ nsTArray<uint8_t> message(aMessage.Clone());
+ mProxy->OnSessionMessage(NS_ConvertUTF8toUTF16(aSessionId), aMessageType,
+ message);
+}
+
+void MediaDrmCDMCallbackProxy::ExpirationChange(const nsCString& aSessionId,
+ UnixTime aExpiryTime) {
+ MOZ_ASSERT(NS_IsMainThread());
+ mProxy->OnExpirationChange(NS_ConvertUTF8toUTF16(aSessionId), aExpiryTime);
+}
+
+void MediaDrmCDMCallbackProxy::SessionClosed(const nsCString& aSessionId) {
+ MOZ_ASSERT(NS_IsMainThread());
+ bool keyStatusesChange = false;
+ {
+ auto caps = mProxy->Capabilites().Lock();
+ keyStatusesChange =
+ caps->RemoveKeysForSession(NS_ConvertUTF8toUTF16(aSessionId));
+ }
+ if (keyStatusesChange) {
+ mProxy->OnKeyStatusesChange(NS_ConvertUTF8toUTF16(aSessionId));
+ }
+ mProxy->OnSessionClosed(NS_ConvertUTF8toUTF16(aSessionId));
+}
+
+void MediaDrmCDMCallbackProxy::SessionError(const nsCString& aSessionId,
+ nsresult aException,
+ uint32_t aSystemCode,
+ const nsCString& aMessage) {
+ MOZ_ASSERT(NS_IsMainThread());
+ mProxy->OnSessionError(NS_ConvertUTF8toUTF16(aSessionId), aException,
+ aSystemCode, NS_ConvertUTF8toUTF16(aMessage));
+}
+
+void MediaDrmCDMCallbackProxy::BatchedKeyStatusChanged(
+ const nsCString& aSessionId, const nsTArray<CDMKeyInfo>& aKeyInfos) {
+ MOZ_ASSERT(NS_IsMainThread());
+ BatchedKeyStatusChangedInternal(aSessionId, aKeyInfos);
+}
+
+void MediaDrmCDMCallbackProxy::BatchedKeyStatusChangedInternal(
+ const nsCString& aSessionId, const nsTArray<CDMKeyInfo>& aKeyInfos) {
+ bool keyStatusesChange = false;
+ {
+ auto caps = mProxy->Capabilites().Lock();
+ for (size_t i = 0; i < aKeyInfos.Length(); i++) {
+ keyStatusesChange |= caps->SetKeyStatus(aKeyInfos[i].mKeyId,
+ NS_ConvertUTF8toUTF16(aSessionId),
+ aKeyInfos[i].mStatus);
+ }
+ }
+ if (keyStatusesChange) {
+ mProxy->OnKeyStatusesChange(NS_ConvertUTF8toUTF16(aSessionId));
+ }
+}
+
+void MediaDrmCDMCallbackProxy::Decrypted(
+ uint32_t aId, DecryptStatus aResult,
+ const nsTArray<uint8_t>& aDecryptedData) {
+ MOZ_ASSERT_UNREACHABLE("Fennec could not handle decrypted event");
+}
+
+} // namespace mozilla
diff --git a/dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.h b/dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.h
new file mode 100644
index 0000000000..e963e21e11
--- /dev/null
+++ b/dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.h
@@ -0,0 +1,63 @@
+/* -*- 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,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MediaDrmCDMCallbackProxy_h_
+#define MediaDrmCDMCallbackProxy_h_
+
+#include "mozilla/CDMProxy.h"
+#include "mozilla/DecryptorProxyCallback.h"
+
+namespace mozilla {
+class CDMProxy;
+class ErrorResult;
+class MediaDrmCDMProxy;
+
+// Proxies call backs from the MediaDrmProxy -> MediaDrmProxySupport back to the
+// MediaKeys object on the main thread. We used annotation calledFrom = "gecko"
+// to ensure running on main thread.
+class MediaDrmCDMCallbackProxy final : public DecryptorProxyCallback {
+ public:
+ void SetSessionId(uint32_t aCreateSessionToken,
+ const nsCString& aSessionId) override;
+
+ void ResolveLoadSessionPromise(uint32_t aPromiseId, bool aSuccess) override;
+
+ void ResolvePromise(uint32_t aPromiseId) override;
+
+ void RejectPromise(uint32_t aPromiseId, ErrorResult&& aException,
+ const nsCString& aSessionId) override;
+
+ void SessionMessage(const nsCString& aSessionId,
+ dom::MediaKeyMessageType aMessageType,
+ const nsTArray<uint8_t>& aMessage) override;
+
+ void ExpirationChange(const nsCString& aSessionId,
+ UnixTime aExpiryTime) override;
+
+ void SessionClosed(const nsCString& aSessionId) override;
+
+ void SessionError(const nsCString& aSessionId, nsresult aException,
+ uint32_t aSystemCode, const nsCString& aMessage) override;
+
+ void Decrypted(uint32_t aId, DecryptStatus aResult,
+ const nsTArray<uint8_t>& aDecryptedData) override;
+
+ void BatchedKeyStatusChanged(const nsCString& aSessionId,
+ const nsTArray<CDMKeyInfo>& aKeyInfos) override;
+
+ ~MediaDrmCDMCallbackProxy();
+
+ private:
+ friend class MediaDrmCDMProxy;
+ explicit MediaDrmCDMCallbackProxy(MediaDrmCDMProxy* aProxy);
+
+ void BatchedKeyStatusChangedInternal(const nsCString& aSessionId,
+ const nsTArray<CDMKeyInfo>& aKeyInfos);
+ const RefPtr<MediaDrmCDMProxy> mProxy;
+};
+
+} // namespace mozilla
+#endif
diff --git a/dom/media/eme/mediadrm/MediaDrmCDMProxy.cpp b/dom/media/eme/mediadrm/MediaDrmCDMProxy.cpp
new file mode 100644
index 0000000000..6ef47a84c3
--- /dev/null
+++ b/dom/media/eme/mediadrm/MediaDrmCDMProxy.cpp
@@ -0,0 +1,453 @@
+/* -*- 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,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/MediaKeySession.h"
+#include "mozilla/MediaDrmCDMProxy.h"
+#include "MediaDrmCDMCallbackProxy.h"
+
+namespace mozilla {
+
+MediaDrmSessionType ToMediaDrmSessionType(
+ dom::MediaKeySessionType aSessionType) {
+ switch (aSessionType) {
+ case dom::MediaKeySessionType::Temporary:
+ return kKeyStreaming;
+ case dom::MediaKeySessionType::Persistent_license:
+ return kKeyOffline;
+ default:
+ return kKeyStreaming;
+ };
+}
+
+MediaDrmCDMProxy::MediaDrmCDMProxy(dom::MediaKeys* aKeys,
+ const nsAString& aKeySystem,
+ bool aDistinctiveIdentifierRequired,
+ bool aPersistentStateRequired)
+ : CDMProxy(aKeys, aKeySystem, aDistinctiveIdentifierRequired,
+ aPersistentStateRequired),
+ mCDM(nullptr),
+ mShutdownCalled(false) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_COUNT_CTOR(MediaDrmCDMProxy);
+}
+
+MediaDrmCDMProxy::~MediaDrmCDMProxy() { MOZ_COUNT_DTOR(MediaDrmCDMProxy); }
+
+void MediaDrmCDMProxy::Init(PromiseId aPromiseId, const nsAString& aOrigin,
+ const nsAString& aTopLevelOrigin,
+ const nsAString& aName) {
+ MOZ_ASSERT(NS_IsMainThread());
+ NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
+
+ EME_LOG("MediaDrmCDMProxy::Init (%s, %s) %s",
+ NS_ConvertUTF16toUTF8(aOrigin).get(),
+ NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
+ NS_ConvertUTF16toUTF8(aName).get());
+
+ // Create a thread to work with cdm.
+ if (!mOwnerThread) {
+ nsresult rv =
+ NS_NewNamedThread("MDCDMThread", getter_AddRefs(mOwnerThread));
+ if (NS_FAILED(rv)) {
+ RejectPromiseWithStateError(
+ aPromiseId, nsLiteralCString(
+ "Couldn't create CDM thread MediaDrmCDMProxy::Init"));
+ return;
+ }
+ }
+
+ mCDM = mozilla::MakeUnique<MediaDrmProxySupport>(mKeySystem);
+ nsCOMPtr<nsIRunnable> task(
+ NewRunnableMethod<uint32_t>("MediaDrmCDMProxy::md_Init", this,
+ &MediaDrmCDMProxy::md_Init, aPromiseId));
+ mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
+}
+
+void MediaDrmCDMProxy::CreateSession(uint32_t aCreateSessionToken,
+ MediaKeySessionType aSessionType,
+ PromiseId aPromiseId,
+ const nsAString& aInitDataType,
+ nsTArray<uint8_t>& aInitData) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mOwnerThread);
+
+ UniquePtr<CreateSessionData> data(new CreateSessionData());
+ data->mSessionType = aSessionType;
+ data->mCreateSessionToken = aCreateSessionToken;
+ data->mPromiseId = aPromiseId;
+ data->mInitDataType = NS_ConvertUTF16toUTF8(aInitDataType);
+ data->mInitData = std::move(aInitData);
+
+ nsCOMPtr<nsIRunnable> task(NewRunnableMethod<UniquePtr<CreateSessionData>&&>(
+ "MediaDrmCDMProxy::md_CreateSession", this,
+ &MediaDrmCDMProxy::md_CreateSession, std::move(data)));
+ mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
+}
+
+void MediaDrmCDMProxy::LoadSession(PromiseId aPromiseId,
+ dom::MediaKeySessionType aSessionType,
+ const nsAString& aSessionId) {
+ // TODO: Implement LoadSession.
+ RejectPromiseWithStateError(
+ aPromiseId, "Currently Fennec does not support LoadSession"_ns);
+}
+
+void MediaDrmCDMProxy::SetServerCertificate(PromiseId aPromiseId,
+ nsTArray<uint8_t>& aCert) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mOwnerThread);
+
+ mOwnerThread->Dispatch(NewRunnableMethod<PromiseId, const nsTArray<uint8_t>>(
+ "MediaDrmCDMProxy::md_SetServerCertificate", this,
+ &MediaDrmCDMProxy::md_SetServerCertificate,
+ aPromiseId, std::move(aCert)),
+ NS_DISPATCH_NORMAL);
+}
+
+void MediaDrmCDMProxy::UpdateSession(const nsAString& aSessionId,
+ PromiseId aPromiseId,
+ nsTArray<uint8_t>& aResponse) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mOwnerThread);
+ NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
+
+ UniquePtr<UpdateSessionData> data(new UpdateSessionData());
+ data->mPromiseId = aPromiseId;
+ data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
+ data->mResponse = std::move(aResponse);
+
+ nsCOMPtr<nsIRunnable> task(NewRunnableMethod<UniquePtr<UpdateSessionData>&&>(
+ "MediaDrmCDMProxy::md_UpdateSession", this,
+ &MediaDrmCDMProxy::md_UpdateSession, std::move(data)));
+ mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
+}
+
+void MediaDrmCDMProxy::CloseSession(const nsAString& aSessionId,
+ PromiseId aPromiseId) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mOwnerThread);
+ NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
+
+ UniquePtr<SessionOpData> data(new SessionOpData());
+ data->mPromiseId = aPromiseId;
+ data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
+
+ nsCOMPtr<nsIRunnable> task(NewRunnableMethod<UniquePtr<SessionOpData>&&>(
+ "MediaDrmCDMProxy::md_CloseSession", this,
+ &MediaDrmCDMProxy::md_CloseSession, std::move(data)));
+ mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
+}
+
+void MediaDrmCDMProxy::RemoveSession(const nsAString& aSessionId,
+ PromiseId aPromiseId) {
+ // TODO: Implement RemoveSession.
+ RejectPromiseWithStateError(
+ aPromiseId, "Currently Fennec does not support RemoveSession"_ns);
+}
+
+void MediaDrmCDMProxy::QueryOutputProtectionStatus() {
+ // TODO(bryce): determine if this is needed for Android and implement as
+ // needed. See also `NotifyOutputProtectionStatus`.
+}
+
+void MediaDrmCDMProxy::NotifyOutputProtectionStatus(
+ OutputProtectionCheckStatus aCheckStatus,
+ OutputProtectionCaptureStatus aCaptureStatus) {
+ // TODO(bryce): determine if this is needed for Android and implement as
+ // needed. See also `QueryOutputProtectionStatus`.
+}
+
+void MediaDrmCDMProxy::Shutdown() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mOwnerThread);
+ nsCOMPtr<nsIRunnable> task(NewRunnableMethod(
+ "MediaDrmCDMProxy::md_Shutdown", this, &MediaDrmCDMProxy::md_Shutdown));
+
+ mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
+ mOwnerThread->Shutdown();
+ mOwnerThread = nullptr;
+ mKeys.Clear();
+}
+
+void MediaDrmCDMProxy::Terminated() {
+ // TODO: Implement Terminated.
+ // Should find a way to handle the case when remote side MediaDrm crashed.
+}
+
+void MediaDrmCDMProxy::OnSetSessionId(uint32_t aCreateSessionToken,
+ const nsAString& aSessionId) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mKeys.IsNull()) {
+ return;
+ }
+
+ RefPtr<dom::MediaKeySession> session(
+ mKeys->GetPendingSession(aCreateSessionToken));
+ if (session) {
+ session->SetSessionId(aSessionId);
+ }
+}
+
+void MediaDrmCDMProxy::OnResolveLoadSessionPromise(uint32_t aPromiseId,
+ bool aSuccess) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mKeys.IsNull()) {
+ return;
+ }
+ mKeys->OnSessionLoaded(aPromiseId, aSuccess);
+}
+
+void MediaDrmCDMProxy::OnSessionMessage(const nsAString& aSessionId,
+ dom::MediaKeyMessageType aMessageType,
+ const nsTArray<uint8_t>& aMessage) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mKeys.IsNull()) {
+ return;
+ }
+ RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
+ if (session) {
+ session->DispatchKeyMessage(aMessageType, aMessage);
+ }
+}
+
+void MediaDrmCDMProxy::OnExpirationChange(const nsAString& aSessionId,
+ UnixTime aExpiryTime) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mKeys.IsNull()) {
+ return;
+ }
+ RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
+ if (session) {
+ session->SetExpiration(static_cast<double>(aExpiryTime));
+ }
+}
+
+void MediaDrmCDMProxy::OnSessionClosed(const nsAString& aSessionId) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mKeys.IsNull()) {
+ return;
+ }
+ RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
+ if (session) {
+ session->OnClosed();
+ }
+}
+
+void MediaDrmCDMProxy::OnSessionError(const nsAString& aSessionId,
+ nsresult aException, uint32_t aSystemCode,
+ const nsAString& aMsg) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mKeys.IsNull()) {
+ return;
+ }
+ RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
+ if (session) {
+ session->DispatchKeyError(aSystemCode);
+ }
+}
+
+void MediaDrmCDMProxy::OnRejectPromise(uint32_t aPromiseId,
+ ErrorResult&& aException,
+ const nsCString& aMsg) {
+ MOZ_ASSERT(NS_IsMainThread());
+ RejectPromise(aPromiseId, std::move(aException), aMsg);
+}
+
+RefPtr<DecryptPromise> MediaDrmCDMProxy::Decrypt(MediaRawData* aSample) {
+ MOZ_ASSERT_UNREACHABLE("Fennec could not handle decrypting individually");
+ return nullptr;
+}
+
+void MediaDrmCDMProxy::OnDecrypted(uint32_t aId, DecryptStatus aResult,
+ const nsTArray<uint8_t>& aDecryptedData) {
+ MOZ_ASSERT_UNREACHABLE("Fennec could not handle decrypted event");
+}
+
+void MediaDrmCDMProxy::RejectPromise(PromiseId aId, ErrorResult&& aException,
+ const nsCString& aReason) {
+ if (NS_IsMainThread()) {
+ if (!mKeys.IsNull()) {
+ mKeys->RejectPromise(aId, std::move(aException), aReason);
+ }
+ } else {
+ nsCOMPtr<nsIRunnable> task(
+ new RejectPromiseTask(this, aId, std::move(aException), aReason));
+ mMainThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
+ }
+}
+
+void MediaDrmCDMProxy::RejectPromiseWithStateError(PromiseId aId,
+ const nsCString& aReason) {
+ ErrorResult rv;
+ rv.ThrowInvalidStateError(aReason);
+ RejectPromise(aId, std::move(rv), aReason);
+}
+
+void MediaDrmCDMProxy::ResolvePromise(PromiseId aId) {
+ if (NS_IsMainThread()) {
+ if (!mKeys.IsNull()) {
+ mKeys->ResolvePromise(aId);
+ } else {
+ NS_WARNING("MediaDrmCDMProxy unable to resolve promise!");
+ }
+ } else {
+ nsCOMPtr<nsIRunnable> task;
+ task =
+ NewRunnableMethod<PromiseId>("MediaDrmCDMProxy::ResolvePromise", this,
+ &MediaDrmCDMProxy::ResolvePromise, aId);
+ mMainThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
+ }
+}
+
+template <typename T>
+void MediaDrmCDMProxy::ResolvePromiseWithResult(PromiseId aId,
+ const T& aResult) {
+ if (NS_IsMainThread()) {
+ if (!mKeys.IsNull()) {
+ mKeys->ResolvePromiseWithResult(aId, aResult);
+ } else {
+ NS_WARNING("MediaDrmCDMProxy unable to resolve promise!");
+ }
+ return;
+ }
+
+ nsCOMPtr<nsIRunnable> task;
+ task = NewRunnableMethod<PromiseId, T>(
+ "MediaDrmCDMProxy::ResolvePromiseWithResult", this,
+ &MediaDrmCDMProxy::ResolvePromiseWithResult<T>, aId, aResult);
+ mMainThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
+}
+
+void MediaDrmCDMProxy::OnKeyStatusesChange(const nsAString& aSessionId) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mKeys.IsNull()) {
+ return;
+ }
+ RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
+ if (session) {
+ session->DispatchKeyStatusesChange();
+ }
+}
+
+void MediaDrmCDMProxy::GetStatusForPolicy(
+ PromiseId aPromiseId, const dom::HDCPVersion& aMinHdcpVersion) {
+ // TODO: Implement GetStatusForPolicy.
+ constexpr auto err =
+ "Currently Fennec does not support GetStatusForPolicy"_ns;
+
+ ErrorResult rv;
+ rv.ThrowNotSupportedError(err);
+ RejectPromise(aPromiseId, std::move(rv), err);
+}
+
+#ifdef DEBUG
+bool MediaDrmCDMProxy::IsOnOwnerThread() {
+ return NS_GetCurrentThread() == mOwnerThread;
+}
+#endif
+
+const nsString& MediaDrmCDMProxy::GetMediaDrmStubId() const {
+ MOZ_ASSERT(mCDM);
+ return mCDM->GetMediaDrmStubId();
+}
+
+void MediaDrmCDMProxy::OnCDMCreated(uint32_t aPromiseId) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mKeys.IsNull()) {
+ return;
+ }
+
+ if (mCDM) {
+ mKeys->OnCDMCreated(aPromiseId, 0);
+ return;
+ }
+
+ // No CDM? Just reject the promise.
+ constexpr auto err = "Null CDM in OnCDMCreated()"_ns;
+ ErrorResult rv;
+ rv.ThrowInvalidStateError(err);
+ mKeys->RejectPromise(aPromiseId, std::move(rv), err);
+}
+
+void MediaDrmCDMProxy::md_Init(uint32_t aPromiseId) {
+ MOZ_ASSERT(IsOnOwnerThread());
+ MOZ_ASSERT(mCDM);
+
+ UniquePtr<MediaDrmCDMCallbackProxy> callback(
+ new MediaDrmCDMCallbackProxy(this));
+ mCDM->Init(std::move(callback));
+ nsCOMPtr<nsIRunnable> task(
+ NewRunnableMethod<uint32_t>("MediaDrmCDMProxy::OnCDMCreated", this,
+ &MediaDrmCDMProxy::OnCDMCreated, aPromiseId));
+ mMainThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
+}
+
+void MediaDrmCDMProxy::md_CreateSession(UniquePtr<CreateSessionData>&& aData) {
+ MOZ_ASSERT(IsOnOwnerThread());
+
+ if (!mCDM) {
+ RejectPromiseWithStateError(aData->mPromiseId,
+ "Null CDM in md_CreateSession"_ns);
+ return;
+ }
+
+ mCDM->CreateSession(aData->mCreateSessionToken, aData->mPromiseId,
+ aData->mInitDataType, aData->mInitData,
+ ToMediaDrmSessionType(aData->mSessionType));
+}
+
+void MediaDrmCDMProxy::md_SetServerCertificate(PromiseId aPromiseId,
+ const nsTArray<uint8_t>& aCert) {
+ MOZ_ASSERT(IsOnOwnerThread());
+
+ if (!mCDM) {
+ RejectPromiseWithStateError(aPromiseId,
+ "Null CDM in md_SetServerCertificate"_ns);
+ return;
+ }
+
+ if (mCDM->SetServerCertificate(aCert)) {
+ ResolvePromiseWithResult(aPromiseId, true);
+ } else {
+ RejectPromiseWithStateError(
+ aPromiseId, "MediaDrmCDMProxy unable to set server certificate"_ns);
+ }
+}
+
+void MediaDrmCDMProxy::md_UpdateSession(UniquePtr<UpdateSessionData>&& aData) {
+ MOZ_ASSERT(IsOnOwnerThread());
+
+ if (!mCDM) {
+ RejectPromiseWithStateError(aData->mPromiseId,
+ "Null CDM in md_UpdateSession"_ns);
+ return;
+ }
+ mCDM->UpdateSession(aData->mPromiseId, aData->mSessionId, aData->mResponse);
+}
+
+void MediaDrmCDMProxy::md_CloseSession(UniquePtr<SessionOpData>&& aData) {
+ MOZ_ASSERT(IsOnOwnerThread());
+
+ if (!mCDM) {
+ RejectPromiseWithStateError(aData->mPromiseId,
+ "Null CDM in md_CloseSession"_ns);
+ return;
+ }
+ mCDM->CloseSession(aData->mPromiseId, aData->mSessionId);
+}
+
+void MediaDrmCDMProxy::md_Shutdown() {
+ MOZ_ASSERT(IsOnOwnerThread());
+ MOZ_ASSERT(mCDM);
+ if (mShutdownCalled) {
+ return;
+ }
+ mShutdownCalled = true;
+ mCDM->Shutdown();
+ mCDM = nullptr;
+}
+
+} // namespace mozilla
diff --git a/dom/media/eme/mediadrm/MediaDrmCDMProxy.h b/dom/media/eme/mediadrm/MediaDrmCDMProxy.h
new file mode 100644
index 0000000000..89d562eac0
--- /dev/null
+++ b/dom/media/eme/mediadrm/MediaDrmCDMProxy.h
@@ -0,0 +1,186 @@
+/* -*- 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,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MediaDrmCDMProxy_h_
+#define MediaDrmCDMProxy_h_
+
+#include <jni.h>
+#include "mozilla/jni/Types.h"
+#include "mozilla/CDMProxy.h"
+#include "mozilla/CDMCaps.h"
+#include "mozilla/dom/MediaKeys.h"
+#include "mozilla/dom/MediaKeySession.h"
+#include "mozilla/MediaDrmProxySupport.h"
+#include "mozilla/UniquePtr.h"
+
+#include "MediaCodec.h"
+#include "nsString.h"
+
+namespace mozilla {
+
+class MediaDrmCDMCallbackProxy;
+class MediaDrmCDMProxy final : public CDMProxy {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDrmCDMProxy, override)
+
+ MediaDrmCDMProxy(dom::MediaKeys* aKeys, const nsAString& aKeySystem,
+ bool aDistinctiveIdentifierRequired,
+ bool aPersistentStateRequired);
+
+ void Init(PromiseId aPromiseId, const nsAString& aOrigin,
+ const nsAString& aTopLevelOrigin,
+ const nsAString& aGMPName) override;
+
+ void CreateSession(uint32_t aCreateSessionToken,
+ MediaKeySessionType aSessionType, PromiseId aPromiseId,
+ const nsAString& aInitDataType,
+ nsTArray<uint8_t>& aInitData) override;
+
+ void LoadSession(PromiseId aPromiseId, dom::MediaKeySessionType aSessionType,
+ const nsAString& aSessionId) override;
+
+ void SetServerCertificate(PromiseId aPromiseId,
+ nsTArray<uint8_t>& aCert) override;
+
+ void UpdateSession(const nsAString& aSessionId, PromiseId aPromiseId,
+ nsTArray<uint8_t>& aResponse) override;
+
+ void CloseSession(const nsAString& aSessionId, PromiseId aPromiseId) override;
+
+ void RemoveSession(const nsAString& aSessionId,
+ PromiseId aPromiseId) override;
+
+ void QueryOutputProtectionStatus() override;
+
+ void NotifyOutputProtectionStatus(
+ OutputProtectionCheckStatus aCheckStatus,
+ OutputProtectionCaptureStatus aCaptureStatus) override;
+
+ void Shutdown() override;
+
+ void Terminated() override;
+
+ void OnSetSessionId(uint32_t aCreateSessionToken,
+ const nsAString& aSessionId) override;
+
+ void OnResolveLoadSessionPromise(uint32_t aPromiseId, bool aSuccess) override;
+
+ void OnSessionMessage(const nsAString& aSessionId,
+ dom::MediaKeyMessageType aMessageType,
+ const nsTArray<uint8_t>& aMessage) override;
+
+ void OnExpirationChange(const nsAString& aSessionId,
+ UnixTime aExpiryTime) override;
+
+ void OnSessionClosed(const nsAString& aSessionId) override;
+
+ void OnSessionError(const nsAString& aSessionId, nsresult aException,
+ uint32_t aSystemCode, const nsAString& aMsg) override;
+
+ void OnRejectPromise(uint32_t aPromiseId, ErrorResult&& aException,
+ const nsCString& aMsg) override;
+
+ RefPtr<DecryptPromise> Decrypt(MediaRawData* aSample) override;
+ void OnDecrypted(uint32_t aId, DecryptStatus aResult,
+ const nsTArray<uint8_t>& aDecryptedData) override;
+
+ void RejectPromise(PromiseId aId, ErrorResult&& aException,
+ const nsCString& aReason) override;
+ // Reject promise with an InvalidStateError and the given message.
+ void RejectPromiseWithStateError(PromiseId aId, const nsCString& aReason);
+
+ // Resolves promise with "undefined".
+ // Can be called from any thread.
+ void ResolvePromise(PromiseId aId) override;
+
+ void OnKeyStatusesChange(const nsAString& aSessionId) override;
+
+ void GetStatusForPolicy(PromiseId aPromiseId,
+ const dom::HDCPVersion& aMinHdcpVersion) override;
+
+#ifdef DEBUG
+ bool IsOnOwnerThread() override;
+#endif
+
+ const nsString& GetMediaDrmStubId() const;
+
+ private:
+ virtual ~MediaDrmCDMProxy();
+
+ void OnCDMCreated(uint32_t aPromiseId);
+
+ template <typename T>
+ void ResolvePromiseWithResult(PromiseId aId, const T& aResult);
+
+ struct CreateSessionData {
+ MediaKeySessionType mSessionType;
+ uint32_t mCreateSessionToken;
+ PromiseId mPromiseId;
+ nsCString mInitDataType;
+ nsTArray<uint8_t> mInitData;
+ };
+
+ struct UpdateSessionData {
+ PromiseId mPromiseId;
+ nsCString mSessionId;
+ nsTArray<uint8_t> mResponse;
+ };
+
+ struct SessionOpData {
+ PromiseId mPromiseId;
+ nsCString mSessionId;
+ };
+
+ class RejectPromiseTask : public Runnable {
+ public:
+ RejectPromiseTask(MediaDrmCDMProxy* aProxy, PromiseId aId,
+ ErrorResult&& aException, const nsCString& aReason)
+ : Runnable("RejectPromiseTask"),
+ mProxy(aProxy),
+ mId(aId),
+ mException(std::move(aException)),
+ mReason(aReason) {}
+ NS_IMETHOD Run() override {
+ // Moving into or out of a non-copyable ErrorResult will assert that both
+ // ErorResults are from our current thread. Avoid the assertion by moving
+ // into a current-thread CopyableErrorResult first. Note that this is
+ // safe, because CopyableErrorResult never holds state that can't move
+ // across threads.
+ CopyableErrorResult rv(std::move(mException));
+ mProxy->RejectPromise(mId, std::move(rv), mReason);
+ return NS_OK;
+ }
+
+ private:
+ RefPtr<MediaDrmCDMProxy> mProxy;
+ PromiseId mId;
+ // We use a CopyableErrorResult here, because we're going to dispatch to a
+ // different thread and normal ErrorResult doesn't support that.
+ // CopyableErrorResult ensures that it only stores values that are safe to
+ // move across threads.
+ CopyableErrorResult mException;
+ nsCString mReason;
+ };
+
+ nsCString mNodeId;
+ UniquePtr<MediaDrmProxySupport> mCDM;
+ bool mShutdownCalled;
+
+ // =====================================================================
+ // For MediaDrmProxySupport
+ void md_Init(uint32_t aPromiseId);
+ void md_CreateSession(UniquePtr<CreateSessionData>&& aData);
+ void md_SetServerCertificate(PromiseId aPromiseId,
+ const nsTArray<uint8_t>& aCert);
+ void md_UpdateSession(UniquePtr<UpdateSessionData>&& aData);
+ void md_CloseSession(UniquePtr<SessionOpData>&& aData);
+ void md_Shutdown();
+ // =====================================================================
+};
+
+} // namespace mozilla
+
+#endif // MediaDrmCDMProxy_h_
diff --git a/dom/media/eme/mediadrm/MediaDrmProxySupport.cpp b/dom/media/eme/mediadrm/MediaDrmProxySupport.cpp
new file mode 100644
index 0000000000..717a57df35
--- /dev/null
+++ b/dom/media/eme/mediadrm/MediaDrmProxySupport.cpp
@@ -0,0 +1,272 @@
+/* -*- 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,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#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
+ // http://androidxref.com/5.1.1_r6/xref/external/chromium_org/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java#420
+ // 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
diff --git a/dom/media/eme/mediadrm/MediaDrmProxySupport.h b/dom/media/eme/mediadrm/MediaDrmProxySupport.h
new file mode 100644
index 0000000000..e994036f69
--- /dev/null
+++ b/dom/media/eme/mediadrm/MediaDrmProxySupport.h
@@ -0,0 +1,68 @@
+/* -*- 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,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MediaDrmProxySupport_H
+#define MediaDrmProxySupport_H
+
+#include "mozilla/DecryptorProxyCallback.h"
+#include "mozilla/java/MediaDrmProxyWrappers.h"
+#include "mozilla/Logging.h"
+#include "mozilla/UniquePtr.h"
+#include "nsString.h"
+
+namespace mozilla {
+
+enum MediaDrmSessionType {
+ kKeyStreaming = 1,
+ kKeyOffline = 2,
+ kKeyRelease = 3,
+};
+
+#ifndef MDRMN_LOG
+LogModule* GetMDRMNLog();
+# define MDRMN_LOG(x, ...) \
+ MOZ_LOG(GetMDRMNLog(), mozilla::LogLevel::Debug, \
+ ("[MediaDrmProxySupport][%s]" x, __FUNCTION__, ##__VA_ARGS__))
+#endif
+
+class MediaDrmCDMCallbackProxy;
+
+class MediaDrmProxySupport final {
+ public:
+ explicit MediaDrmProxySupport(const nsAString& aKeySystem);
+ ~MediaDrmProxySupport();
+
+ /*
+ * APIs to act as GMPDecryptorAPI, discarding unnecessary calls.
+ */
+ nsresult Init(UniquePtr<MediaDrmCDMCallbackProxy>&& aCallback);
+
+ void CreateSession(uint32_t aCreateSessionToken, uint32_t aPromiseId,
+ const nsCString& aInitDataType,
+ const nsTArray<uint8_t>& aInitData,
+ MediaDrmSessionType aSessionType);
+
+ void UpdateSession(uint32_t aPromiseId, const nsCString& aSessionId,
+ const nsTArray<uint8_t>& aResponse);
+
+ void CloseSession(uint32_t aPromiseId, const nsCString& aSessionId);
+
+ void Shutdown();
+
+ const nsString& GetMediaDrmStubId() const { return mMediaDrmStubId; }
+
+ bool SetServerCertificate(const nsTArray<uint8_t>& aCert);
+
+ private:
+ const nsString mKeySystem;
+ java::MediaDrmProxy::GlobalRef mBridgeProxy;
+ java::MediaDrmProxy::NativeMediaDrmProxyCallbacks::GlobalRef mJavaCallbacks;
+ bool mDestroyed;
+ nsString mMediaDrmStubId;
+};
+
+} // namespace mozilla
+#endif // MediaDrmProxySupport_H
diff --git a/dom/media/eme/mediadrm/moz.build b/dom/media/eme/mediadrm/moz.build
new file mode 100644
index 0000000000..c8f433f25f
--- /dev/null
+++ b/dom/media/eme/mediadrm/moz.build
@@ -0,0 +1,19 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS.mozilla += [
+ "MediaDrmCDMCallbackProxy.h",
+ "MediaDrmCDMProxy.h",
+ "MediaDrmProxySupport.h",
+]
+
+UNIFIED_SOURCES += [
+ "MediaDrmCDMCallbackProxy.cpp",
+ "MediaDrmCDMProxy.cpp",
+ "MediaDrmProxySupport.cpp",
+]
+
+FINAL_LIBRARY = "xul"