diff options
Diffstat (limited to 'dom/media/eme/mediadrm')
-rw-r--r-- | dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.cpp | 140 | ||||
-rw-r--r-- | dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.h | 69 | ||||
-rw-r--r-- | dom/media/eme/mediadrm/MediaDrmCDMProxy.cpp | 467 | ||||
-rw-r--r-- | dom/media/eme/mediadrm/MediaDrmCDMProxy.h | 184 | ||||
-rw-r--r-- | dom/media/eme/mediadrm/MediaDrmProxySupport.cpp | 284 | ||||
-rw-r--r-- | dom/media/eme/mediadrm/MediaDrmProxySupport.h | 67 | ||||
-rw-r--r-- | dom/media/eme/mediadrm/moz.build | 19 |
7 files changed, 1230 insertions, 0 deletions
diff --git a/dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.cpp b/dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.cpp new file mode 100644 index 000000000..ee57afae9 --- /dev/null +++ b/dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.cpp @@ -0,0 +1,140 @@ +/* -*- 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 "mozilla/CDMProxy.h" +#include "nsString.h" +#include "mozilla/dom/MediaKeys.h" +#include "mozilla/dom/MediaKeySession.h" +#include "mozIGeckoMediaPluginService.h" +#include "nsContentCID.h" +#include "nsServiceManagerUtils.h" +#include "MainThreadUtils.h" +#include "mozilla/EMEUtils.h" + +namespace mozilla { + +MediaDrmCDMCallbackProxy::MediaDrmCDMCallbackProxy(CDMProxy* aProxy) + : mProxy(aProxy) +{ + +} + +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, + nsresult aException, + const nsCString& aMessage) +{ + MOZ_ASSERT(NS_IsMainThread()); + mProxy->OnRejectPromise(aPromiseId, 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); + 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; + { + CDMCaps::AutoLock caps(mProxy->Capabilites()); + 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; + { + CDMCaps::AutoLock caps(mProxy->Capabilites()); + 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
\ No newline at end of file diff --git a/dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.h b/dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.h new file mode 100644 index 000000000..29f9c8192 --- /dev/null +++ b/dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.h @@ -0,0 +1,69 @@ +/* -*- 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; +// 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 : public DecryptorProxyCallback { +public: + + void SetDecryptorId(uint32_t aId) override {} + + 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, + nsresult 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(CDMProxy* aProxy); + + void BatchedKeyStatusChangedInternal(const nsCString& aSessionId, + const nsTArray<CDMKeyInfo>& aKeyInfos); + // Warning: Weak ref. + CDMProxy* mProxy; +}; + +} // namespace mozilla +#endif
\ No newline at end of file diff --git a/dom/media/eme/mediadrm/MediaDrmCDMProxy.cpp b/dom/media/eme/mediadrm/MediaDrmCDMProxy.cpp new file mode 100644 index 000000000..a57d764e7 --- /dev/null +++ b/dom/media/eme/mediadrm/MediaDrmCDMProxy.cpp @@ -0,0 +1,467 @@ +/* -*- 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" + +using namespace mozilla::java::sdk; + +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, + bool aInPrivateBrowsing) +{ + 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(), + (aInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing")); + + // Create a thread to work with cdm. + if (!mOwnerThread) { + nsresult rv = NS_NewNamedThread("MDCDMThread", getter_AddRefs(mOwnerThread)); + if (NS_FAILED(rv)) { + RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR, + NS_LITERAL_CSTRING("Couldn't create CDM thread MediaDrmCDMProxy::Init")); + return; + } + } + + mCDM = mozilla::MakeUnique<MediaDrmProxySupport>(mKeySystem); + nsCOMPtr<nsIRunnable> task(NewRunnableMethod<uint32_t>(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); + + nsAutoPtr<CreateSessionData> data(new CreateSessionData()); + data->mSessionType = aSessionType; + data->mCreateSessionToken = aCreateSessionToken; + data->mPromiseId = aPromiseId; + data->mInitDataType = NS_ConvertUTF16toUTF8(aInitDataType); + data->mInitData = Move(aInitData); + + nsCOMPtr<nsIRunnable> task( + NewRunnableMethod<nsAutoPtr<CreateSessionData>>(this, + &MediaDrmCDMProxy::md_CreateSession, + data)); + mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL); +} + +void +MediaDrmCDMProxy::LoadSession(PromiseId aPromiseId, + const nsAString& aSessionId) +{ + // TODO: Implement LoadSession. + RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR, + NS_LITERAL_CSTRING("Currently Fennec did not support LoadSession")); +} + +void +MediaDrmCDMProxy::SetServerCertificate(PromiseId aPromiseId, + nsTArray<uint8_t>& aCert) +{ + // TODO: Implement SetServerCertificate. + RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR, + NS_LITERAL_CSTRING("Currently Fennec did not support SetServerCertificate")); +} + +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()); + + nsAutoPtr<UpdateSessionData> data(new UpdateSessionData()); + data->mPromiseId = aPromiseId; + data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId); + data->mResponse = Move(aResponse); + + nsCOMPtr<nsIRunnable> task( + NewRunnableMethod<nsAutoPtr<UpdateSessionData>>(this, + &MediaDrmCDMProxy::md_UpdateSession, + 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()); + + nsAutoPtr<SessionOpData> data(new SessionOpData()); + data->mPromiseId = aPromiseId; + data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId); + + nsCOMPtr<nsIRunnable> task( + NewRunnableMethod<nsAutoPtr<SessionOpData>>(this, + &MediaDrmCDMProxy::md_CloseSession, + data)); + mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL); +} + +void +MediaDrmCDMProxy::RemoveSession(const nsAString& aSessionId, + PromiseId aPromiseId) +{ + // TODO: Implement RemoveSession. + RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR, + NS_LITERAL_CSTRING("Currently Fennec did not support RemoveSession")); +} + +void +MediaDrmCDMProxy::Shutdown() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mOwnerThread); + nsCOMPtr<nsIRunnable> task( + NewRunnableMethod(this, &MediaDrmCDMProxy::md_Shutdown)); + + mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL); + mOwnerThread->Shutdown(); + mOwnerThread = nullptr; +} + +void +MediaDrmCDMProxy::Terminated() +{ + // TODO: Implement Terminated. + // Should find a way to handle the case when remote side MediaDrm crashed. +} + +const nsCString& +MediaDrmCDMProxy::GetNodeId() const +{ + return mNodeId; +} + +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, + 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, + nsresult aDOMException, + const nsCString& aMsg) +{ + MOZ_ASSERT(NS_IsMainThread()); + RejectPromise(aPromiseId, aDOMException, aMsg); +} + +RefPtr<MediaDrmCDMProxy::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, nsresult aCode, + const nsCString& aReason) +{ + if (NS_IsMainThread()) { + if (!mKeys.IsNull()) { + mKeys->RejectPromise(aId, aCode, aReason); + } + } else { + nsCOMPtr<nsIRunnable> task(new RejectPromiseTask(this, aId, aCode, + aReason)); + NS_DispatchToMainThread(task); + } +} + +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>(this, + &MediaDrmCDMProxy::ResolvePromise, + aId); + NS_DispatchToMainThread(task); + } +} + +const nsString& +MediaDrmCDMProxy::KeySystem() const +{ + return mKeySystem; +} + +CDMCaps& +MediaDrmCDMProxy::Capabilites() +{ + return mCapabilites; +} + +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::GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId, + nsTArray<nsCString>& aSessionIds) +{ + CDMCaps::AutoLock caps(Capabilites()); + caps.GetSessionIdsForKeyId(aKeyId, aSessionIds); +} + +#ifdef DEBUG +bool +MediaDrmCDMProxy::IsOnOwnerThread() +{ + return NS_GetCurrentThread() == mOwnerThread; +} +#endif + +void +MediaDrmCDMProxy::OnCDMCreated(uint32_t aPromiseId) +{ + MOZ_ASSERT(NS_IsMainThread()); + if (mKeys.IsNull()) { + return; + } + + if (mCDM) { + mKeys->OnCDMCreated(aPromiseId, GetNodeId(), 0); + return; + } + + // No CDM? Just reject the promise. + mKeys->RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR, + NS_LITERAL_CSTRING("Null CDM in OnCDMCreated()")); +} + +void +MediaDrmCDMProxy::md_Init(uint32_t aPromiseId) +{ + MOZ_ASSERT(IsOnOwnerThread()); + MOZ_ASSERT(mCDM); + + mCallback = new MediaDrmCDMCallbackProxy(this); + mCDM->Init(mCallback); + nsCOMPtr<nsIRunnable> task( + NewRunnableMethod<uint32_t>(this, + &MediaDrmCDMProxy::OnCDMCreated, + aPromiseId)); + NS_DispatchToMainThread(task); +} + +void +MediaDrmCDMProxy::md_CreateSession(nsAutoPtr<CreateSessionData> aData) +{ + MOZ_ASSERT(IsOnOwnerThread()); + + if (!mCDM) { + RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR, + NS_LITERAL_CSTRING("Null CDM in md_CreateSession")); + return; + } + + mCDM->CreateSession(aData->mCreateSessionToken, + aData->mPromiseId, + aData->mInitDataType, + aData->mInitData, + ToMediaDrmSessionType(aData->mSessionType)); +} + +void +MediaDrmCDMProxy::md_UpdateSession(nsAutoPtr<UpdateSessionData> aData) +{ + MOZ_ASSERT(IsOnOwnerThread()); + + if (!mCDM) { + RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR, + NS_LITERAL_CSTRING("Null CDM in md_UpdateSession")); + return; + } + mCDM->UpdateSession(aData->mPromiseId, + aData->mSessionId, + aData->mResponse); +} + +void +MediaDrmCDMProxy::md_CloseSession(nsAutoPtr<SessionOpData> aData) +{ + MOZ_ASSERT(IsOnOwnerThread()); + + if (!mCDM) { + RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR, + NS_LITERAL_CSTRING("Null CDM in md_CloseSession")); + 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
\ No newline at end of file diff --git a/dom/media/eme/mediadrm/MediaDrmCDMProxy.h b/dom/media/eme/mediadrm/MediaDrmCDMProxy.h new file mode 100644 index 000000000..c7cb3000f --- /dev/null +++ b/dom/media/eme/mediadrm/MediaDrmCDMProxy.h @@ -0,0 +1,184 @@ +/* -*- 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 "GeneratedJNINatives.h" + +#include "mozilla/CDMProxy.h" +#include "mozilla/CDMCaps.h" +#include "mozilla/dom/MediaKeys.h" +#include "mozilla/MediaDrmProxySupport.h" +#include "mozilla/UniquePtr.h" + +#include "MediaCodec.h" +#include "nsString.h" +#include "nsAutoPtr.h" + +using namespace mozilla::java; + +namespace mozilla { +class MediaDrmCDMCallbackProxy; +class MediaDrmCDMProxy : public CDMProxy { +public: + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDrmCDMProxy) + + MediaDrmCDMProxy(dom::MediaKeys* aKeys, + const nsAString& aKeySystem, + bool aDistinctiveIdentifierRequired, + bool aPersistentStateRequired); + + void Init(PromiseId aPromiseId, + const nsAString& aOrigin, + const nsAString& aTopLevelOrigin, + const nsAString& aGMPName, + bool aInPrivateBrowsing) override; + + void CreateSession(uint32_t aCreateSessionToken, + MediaKeySessionType aSessionType, + PromiseId aPromiseId, + const nsAString& aInitDataType, + nsTArray<uint8_t>& aInitData) override; + + void LoadSession(PromiseId aPromiseId, + 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 Shutdown() override; + + void Terminated() override; + + const nsCString& GetNodeId() const 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, + 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, + nsresult aCode, + 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, nsresult aCode, + const nsCString& aReason) override; + + // Resolves promise with "undefined". + // Can be called from any thread. + void ResolvePromise(PromiseId aId) override; + + // Threadsafe. + const nsString& KeySystem() const override; + + CDMCaps& Capabilites() override; + + void OnKeyStatusesChange(const nsAString& aSessionId) override; + + void GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId, + nsTArray<nsCString>& aSessionIds) override; + +#ifdef DEBUG + bool IsOnOwnerThread() override; +#endif + +private: + virtual ~MediaDrmCDMProxy(); + + void OnCDMCreated(uint32_t aPromiseId); + + 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, + nsresult aCode, + const nsCString& aReason) + : mProxy(aProxy) + , mId(aId) + , mCode(aCode) + , mReason(aReason) + { + } + NS_METHOD Run() { + mProxy->RejectPromise(mId, mCode, mReason); + return NS_OK; + } + private: + RefPtr<MediaDrmCDMProxy> mProxy; + PromiseId mId; + nsresult mCode; + nsCString mReason; + }; + + nsCString mNodeId; + mozilla::UniquePtr<MediaDrmProxySupport> mCDM; + nsAutoPtr<MediaDrmCDMCallbackProxy> mCallback; + bool mShutdownCalled; + +// ===================================================================== +// For MediaDrmProxySupport + void md_Init(uint32_t aPromiseId); + void md_CreateSession(nsAutoPtr<CreateSessionData> aData); + void md_UpdateSession(nsAutoPtr<UpdateSessionData> aData); + void md_CloseSession(nsAutoPtr<SessionOpData> aData); + void md_Shutdown(); +// ===================================================================== +}; + +} // namespace mozilla +#endif // MediaDrmCDMProxy_h_
\ No newline at end of file diff --git a/dom/media/eme/mediadrm/MediaDrmProxySupport.cpp b/dom/media/eme/mediadrm/MediaDrmProxySupport.cpp new file mode 100644 index 000000000..192769470 --- /dev/null +++ b/dom/media/eme/mediadrm/MediaDrmProxySupport.cpp @@ -0,0 +1,284 @@ +/* -*- 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 "mozilla/EMEUtils.h" +#include "FennecJNINatives.h" +#include "MediaCodec.h" // For MediaDrm::KeyStatus +#include "MediaPrefs.h" + +using namespace mozilla::java; + +namespace mozilla { + +LogModule* GetMDRMNLog() { + static LazyLogModule log("MediaDrmProxySupport"); + return log; +} + +class MediaDrmJavaCallbacksSupport + : public MediaDrmProxy::NativeMediaDrmProxyCallbacks::Natives<MediaDrmJavaCallbacksSupport> +{ +public: + typedef MediaDrmProxy::NativeMediaDrmProxyCallbacks::Natives<MediaDrmJavaCallbacksSupport> MediaDrmProxyNativeCallbacks; + using MediaDrmProxyNativeCallbacks::DisposeNative; + using MediaDrmProxyNativeCallbacks::AttachNative; + + MediaDrmJavaCallbacksSupport(DecryptorProxyCallback* aDecryptorProxyCallback) + : mDecryptorProxyCallback(aDecryptorProxyCallback) + { + MOZ_ASSERT(aDecryptorProxyCallback); + } + /* + * 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: + DecryptorProxyCallback* 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::KeyStatus; + switch (aStatusCode) { + case KeyStatus::STATUS_USABLE: return dom::MediaKeyStatus::Usable; + case KeyStatus::STATUS_EXPIRED: return dom::MediaKeyStatus::Expired; + case KeyStatus::STATUS_OUTPUT_NOT_ALLOWED: return dom::MediaKeyStatus::Output_restricted; + case KeyStatus::STATUS_INTERNAL_ERROR: return dom::MediaKeyStatus::Internal_error; + case 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(mozilla::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. + mDecryptorProxyCallback->RejectPromise(aPromiseId, + NS_ERROR_DOM_INVALID_STATE_ERR, + reason); +} + +MediaDrmProxySupport::MediaDrmProxySupport(const nsAString& aKeySystem) + : mKeySystem(aKeySystem), mDestroyed(false) +{ + mJavaCallbacks = MediaDrmProxy::NativeMediaDrmProxyCallbacks::New(); + + mBridgeProxy = + MediaDrmProxy::Create(mKeySystem, + mJavaCallbacks, + MediaPrefs::PDMAndroidRemoteCodecEnabled()); +} + +MediaDrmProxySupport::~MediaDrmProxySupport() +{ + MOZ_ASSERT(mDestroyed, "Shutdown() should be called before !!"); + MediaDrmJavaCallbacksSupport::DisposeNative(mJavaCallbacks); +} + +nsresult +MediaDrmProxySupport::Init(DecryptorProxyCallback* aCallback) +{ + MOZ_ASSERT(mJavaCallbacks); + + mCallback = aCallback; + MediaDrmJavaCallbacksSupport::AttachNative(mJavaCallbacks, + mozilla::MakeUnique<MediaDrmJavaCallbacksSupport>(mCallback)); + 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; +} + +} // namespace mozilla
\ No newline at end of file diff --git a/dom/media/eme/mediadrm/MediaDrmProxySupport.h b/dom/media/eme/mediadrm/MediaDrmProxySupport.h new file mode 100644 index 000000000..e43b71bc1 --- /dev/null +++ b/dom/media/eme/mediadrm/MediaDrmProxySupport.h @@ -0,0 +1,67 @@ +/* -*- 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/Logging.h" +#include "FennecJNIWrappers.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 MediaDrmProxySupport final +{ +public: + + MediaDrmProxySupport(const nsAString& aKeySystem); + ~MediaDrmProxySupport(); + + /* + * APIs to act as GMPDecryptorAPI, discarding unnecessary calls. + */ + nsresult Init(DecryptorProxyCallback* 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(); + +private: + const nsString mKeySystem; + java::MediaDrmProxy::GlobalRef mBridgeProxy; + java::MediaDrmProxy::NativeMediaDrmProxyCallbacks::GlobalRef mJavaCallbacks; + DecryptorProxyCallback* mCallback; + bool mDestroyed; + +}; + +} // namespace mozilla +#endif // MediaDrmProxySupport_H
\ No newline at end of file diff --git a/dom/media/eme/mediadrm/moz.build b/dom/media/eme/mediadrm/moz.build new file mode 100644 index 000000000..c01f7a52e --- /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'
\ No newline at end of file |