Bug 1306572 - Part3 - Implement MediaCDMProxy and the callback proxy. draft
authorJames Cheng <jacheng@mozilla.com>
Tue, 01 Nov 2016 14:39:34 +0800
changeset 433973 4591c3a1fff7c144c91bf89eaff10a287209d9ce
parent 433972 01f90920eec0cb7cd471fa1ae58f03c11e486bb3
child 535996 9c1d9a3e064793d4947bec0095167d8aef964a06
push id34695
push userbmo:jacheng@mozilla.com
push dateFri, 04 Nov 2016 15:45:45 +0000
bugs1306572
milestone52.0a1
Bug 1306572 - Part3 - Implement MediaCDMProxy and the callback proxy. MozReview-Commit-ID: 3dFawuvhfWu
dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.cpp
dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.h
dom/media/eme/mediadrm/MediaDrmCDMProxy.cpp
dom/media/eme/mediadrm/MediaDrmCDMProxy.h
dom/media/eme/mediadrm/moz.build
dom/media/eme/moz.build
new file mode 100644
--- /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
new file mode 100644
--- /dev/null
+++ b/dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.h
@@ -0,0 +1,66 @@
+/* -*- 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 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
new file mode 100644
--- /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
new file mode 100644
--- /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
new file mode 100644
--- /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
--- a/dom/media/eme/moz.build
+++ b/dom/media/eme/moz.build
@@ -32,11 +32,14 @@ UNIFIED_SOURCES += [
     'MediaKeyMessageEvent.cpp',
     'MediaKeys.cpp',
     'MediaKeySession.cpp',
     'MediaKeyStatusMap.cpp',
     'MediaKeySystemAccess.cpp',
     'MediaKeySystemAccessManager.cpp',
 ]
 
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
+  DIRS += ['mediadrm']
+
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'