Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/media/eme/mediadrm/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 32 kB image not shown  

Quelle  MediaDrmRemoteCDMParent.cpp

  Sprache: C
 

/* 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 "MediaDrmRemoteCDMParent.h"

#include <limits>

#include "mozilla/CheckedInt.h"
#include "mozilla/EMEUtils.h"
#include "mozilla/RemoteMediaManagerParent.h"

namespace mozilla {

StaticAutoPtr<MediaDrmRemoteCDMParent::DrmCallbackMap>
    MediaDrmRemoteCDMParent::sCbMap;

/* static */
void MediaDrmRemoteCDMParent::InitializeStatics() {
  if (sCbMap) {
    return;
  }

  sCbMap = new DrmCallbackMap();
}

/* static */
void MediaDrmRemoteCDMParent::HandleEventCb(
    AMediaDrm* aDrm, const AMediaDrmSessionId* aSessionId,
    AMediaDrmEventType aEventType, int aExtra, const uint8_t* aData,
    size_t aDataSize) {
  EME_LOG("MediaDrmRemoteCDMParent::HandleEventCb -- drm {}, event {}",
          fmt::ptr(aDrm), static_cast<int>(aEventType));

  // Called from an internal NDK thread. We need to dispatch to the owning
  // thread of the actor with the same AMediaDrm object.
  NS_ConvertUTF8toUTF16 sessionIdStr(
      reinterpret_cast<const char*>(aSessionId->ptr), aSessionId->length);

  size_t dataSize = aData ? aDataSize : 0;
  nsTArray<uint8_t> data(dataSize);
  if (dataSize) {
    data.AppendElements(aData, dataSize);
  }

  RemoteMediaManagerParent::Dispatch(NS_NewRunnableFunction(
      __func__,
      [aDrm, aEventType, aExtra, sessionIdStr = std::move(sessionIdStr),
       data = std::move(data)]() mutable {
        auto i = sCbMap->find(aDrm);
        if (i == sCbMap->end()) {
          return;
        }

        i->second->HandleEvent(std::move(sessionIdStr), aEventType, aExtra,
                               std::move(data));
      }));
}

/* static */
void MediaDrmRemoteCDMParent::HandleExpirationUpdateCb(
    AMediaDrm* aDrm, const AMediaDrmSessionId* aSessionId,
    int64_t aExpiryTimeInMS) {
  EME_LOG("MediaDrmRemoteCDMParent::HandleExpirationUpdateCb -- drm {}",
          fmt::ptr(aDrm));

  // Called from an internal NDK thread. We need to dispatch to the owning
  // thread of the actor with the same AMediaDrm object.
  NS_ConvertUTF8toUTF16 sessionIdStr(
      reinterpret_cast<const char*>(aSessionId->ptr), aSessionId->length);

  RemoteMediaManagerParent::Dispatch(NS_NewRunnableFunction(
      __func__, [aDrm, aExpiryTimeInMS,
                 sessionIdStr = std::move(sessionIdStr)]() mutable {
        auto i = sCbMap->find(aDrm);
        if (i == sCbMap->end()) {
          return;
        }

        i->second->HandleExpirationUpdate(std::move(sessionIdStr),
                                          aExpiryTimeInMS);
      }));
}

/* static */
void MediaDrmRemoteCDMParent::HandleKeysChangeCb(
    AMediaDrm* aDrm, const AMediaDrmSessionId* aSessionId,
    const AMediaDrmKeyStatus* aKeyStatus, size_t aNumKeys,
    bool aHasNewUsableKey) {
  EME_LOG("MediaDrmRemoteCDMParent::HandleKeysChangeCb -- drm {}",
          fmt::ptr(aDrm));

  // Called from an internal NDK thread. We need to dispatch to the owning
  // thread of the actor with the same AMediaDrm object.
  NS_ConvertUTF8toUTF16 sessionIdStr(
      reinterpret_cast<const char*>(aSessionId->ptr), aSessionId->length);

  nsTArray<CDMKeyInfo> keyInfo(aNumKeys);
  if (aKeyStatus) {
    for (size_t i = 0; i < aNumKeys; ++i) {
      nsTArray<uint8_t> keyId(aKeyStatus[i].keyId.ptr,
                              aKeyStatus[i].keyId.length);

      dom::Optional<dom::MediaKeyStatus> keyStatus;
      switch (aKeyStatus[i].keyType) {
        case AMediaKeyStatusType::KEY_STATUS_TYPE_USABLE:
          keyStatus.Construct(dom::MediaKeyStatus::Usable);
          break;
        case AMediaKeyStatusType::KEY_STATUS_TYPE_EXPIRED:
          keyStatus.Construct(dom::MediaKeyStatus::Expired);
          break;
        case AMediaKeyStatusType::KEY_STATUS_TYPE_OUTPUTNOTALLOWED:
          keyStatus.Construct(dom::MediaKeyStatus::Output_restricted);
          break;
        case AMediaKeyStatusType::KEY_STATUS_TYPE_STATUSPENDING:
          keyStatus.Construct(dom::MediaKeyStatus::Status_pending);
          break;
        default:
          MOZ_FALLTHROUGH_ASSERT("Unhandled AMediaKeyStatusType!");
        case AMediaKeyStatusType::KEY_STATUS_TYPE_INTERNALERROR:
          keyStatus.Construct(dom::MediaKeyStatus::Internal_error);
          break;
      }

      keyInfo.AppendElement(CDMKeyInfo(std::move(keyId), std::move(keyStatus)));
    }
  }

  RemoteMediaManagerParent::Dispatch(NS_NewRunnableFunction(
      __func__, [aDrm, aHasNewUsableKey, sessionIdStr = std::move(sessionIdStr),
                 keyInfo = std::move(keyInfo)]() mutable {
        auto i = sCbMap->find(aDrm);
        if (i == sCbMap->end()) {
          return;
        }

        i->second->HandleKeysChange(std::move(sessionIdStr), aHasNewUsableKey,
                                    std::move(keyInfo));
      }));
}

MediaDrmRemoteCDMParent::MediaDrmRemoteCDMParent(const nsAString& aKeySystem)
    : mMutex("MediaDrmRemoteCDMParent::mMutex") {
  EME_LOG("[{}] MediaDrmRemoteCDMParent::MediaDrmRemoteCDMParent",
          fmt::ptr(this));
  if (IsWidevineKeySystem(aKeySystem)) {
    mUuid = WIDEVINE_UUID;
  } else if (IsClearkeyKeySystem(aKeySystem)) {
    mUuid = CLEARKEY_UUID;
  }
}

MediaDrmRemoteCDMParent::~MediaDrmRemoteCDMParent() {
  EME_LOG("[{}] MediaDrmRemoteCDMParent::~MediaDrmRemoteCDMParent",
          fmt::ptr(this));
  Destroy();
}

void MediaDrmRemoteCDMParent::ActorDestroy(ActorDestroyReason aWhy) {
  EME_LOG("[{}] MediaDrmRemoteCDMParent::ActorDestroy", fmt::ptr(this));
  Destroy();
}

void MediaDrmRemoteCDMParent::Destroy() {
  EME_LOG("[{}] MediaDrmRemoteCDMParent::Destroy -- drm {}", fmt::ptr(this),
          fmt::ptr(mDrm));

  mProvisionPromise.RejectIfExists(
      MediaResult(NS_ERROR_DOM_ABORT_ERR, "Destroyed"_ns), __func__);

  for (const auto& i : mSessions) {
    AMediaDrm_closeSession(mDrm, &i.second.id);
  }
  mSessions.clear();

  {
    MutexAutoLock lock(mMutex);
    if (mCrypto) {
      mCrypto = nullptr;
      mCryptoSessionId = {};
    }
  }

  if (mDrm) {
    auto i = sCbMap->find(mDrm);
    if (i != sCbMap->end()) {
      sCbMap->erase(i);
    } else {
      MOZ_ASSERT_UNREACHABLE("Missing MediaDrm in sCbMap");
    }
    AMediaDrm_release(mDrm);
    mDrm = nullptr;
  }
}

mozilla::ipc::IPCResult MediaDrmRemoteCDMParent::RecvInit(
    const RemoteCDMInitRequestIPDL& request, InitResolver&& aResolver) {
  EME_LOG("[{}] MediaDrmRemoteCDMParent::RecvInit", fmt::ptr(this));

  if (NS_WARN_IF(!mUuid)) {
    aResolver(MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, "Invalid uuid"_ns));
    return IPC_OK();
  }

  if (NS_WARN_IF(mDrm)) {
    aResolver(MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR,
                          "AMediaDrm already initialized"_ns));
    return IPC_OK();
  }

  if (NS_WARN_IF(!AMediaCrypto_isCryptoSchemeSupported(mUuid))) {
    aResolver(MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR,
                          "AMediaCrypto_isCryptoSchemeSupported failed"_ns));
    return IPC_OK();
  }

  mDrm = AMediaDrm_createByUUID(mUuid);
  if (NS_WARN_IF(!mDrm)) {
    aResolver(MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR,
                          "AMediaDrm_createByUUID failed"_ns));
    return IPC_OK();
  }

  media_status_t status =
      AMediaDrm_setPropertyString(mDrm, "securityLevel""L3");
  if (NS_WARN_IF(status != AMEDIA_OK)) {
    aResolver(MediaResult(
        NS_ERROR_DOM_INVALID_STATE_ERR,
        RESULT_DETAIL("AMediaDrm_setPropertyString securityLevel failed %d",
                      status)));
    return IPC_OK();
  }

  status = AMediaDrm_setPropertyString(mDrm, "privacyMode""enable");
  if (NS_WARN_IF(status != AMEDIA_OK)) {
    aResolver(MediaResult(
        NS_ERROR_DOM_INVALID_STATE_ERR,
        RESULT_DETAIL("AMediaDrm_setPropertyString privateMode failed %d",
                      status)));
    return IPC_OK();
  }

  status = AMediaDrm_setPropertyString(mDrm, "sessionSharing""enable");
  if (NS_WARN_IF(status != AMEDIA_OK)) {
    aResolver(MediaResult(
        NS_ERROR_DOM_INVALID_STATE_ERR,
        RESULT_DETAIL("AMediaDrm_setPropertyString sessionSharing failed %d",
                      status)));
    return IPC_OK();
  }

  // Set per-origin ID if available.
  if (!request.originID().IsEmpty()) {
    EME_LOG(
        "[{}] MediaDrmRemoteCDMParent::RecvInit -- "
        "setting origin ID property ({:.4}...)",
        fmt::ptr(this), request.originID().get());
    media_status_t originStatus =
        AMediaDrm_setPropertyString(mDrm, "origin", request.originID().get());
    if (originStatus != AMEDIA_OK) {
      EME_LOG(
          "[{}] MediaDrmRemoteCDMParent::RecvInit -- "
          "AMediaDrm_setPropertyString origin ID failed: {}",
          fmt::ptr(this), static_cast<int>(originStatus));
    }
  }

  status = AMediaDrm_setOnEventListener(mDrm, HandleEventCb);
  if (NS_WARN_IF(status != AMEDIA_OK)) {
    aResolver(MediaResult(
        NS_ERROR_DOM_INVALID_STATE_ERR,
        RESULT_DETAIL("AMediaDrm_setOnEventListener failed %d", status)));
    return IPC_OK();
  }

  if (__builtin_available(android 29, *)) {
    status =
        AMediaDrm_setOnExpirationUpdateListener(mDrm, HandleExpirationUpdateCb);
    if (NS_WARN_IF(status != AMEDIA_OK)) {
      aResolver(MediaResult(
          NS_ERROR_DOM_INVALID_STATE_ERR,
          RESULT_DETAIL("AMediaDrm_setOnExpirationUpdateListener failed %d",
                        status)));
      return IPC_OK();
    }

    status = AMediaDrm_setOnKeysChangeListener(mDrm, HandleKeysChangeCb);
    if (NS_WARN_IF(status != AMEDIA_OK)) {
      aResolver(MediaResult(
          NS_ERROR_DOM_INVALID_STATE_ERR,
          RESULT_DETAIL("AMediaDrm_setOnKeysChangeListener failed %d",
                        status)));
      return IPC_OK();
    }
  }

  EME_LOG("[{}] MediaDrmRemoteCDMParent::RecvInit -- drm {}", fmt::ptr(this),
          fmt::ptr(mDrm));
  InitializeStatics();
  sCbMap->insert(std::pair{mDrm, this});
  aResolver(MediaResult(NS_OK));

  EnsureHasAMediaCrypto();
  return IPC_OK();
}

RefPtr<MediaDrmRemoteCDMParent::InternalPromise>
MediaDrmRemoteCDMParent::EnsureHasAMediaCrypto() {
  if (NS_WARN_IF(!mDrm)) {
    return InternalPromise::CreateAndReject(
        MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, "Missing AMediaDrm"_ns),
        __func__);
  }

  if (HasCrypto()) {
    return InternalPromise::CreateAndResolve(true, __func__);
  }

  if (mCryptoError) {
    return InternalPromise::CreateAndReject(*mCryptoError, __func__);
  }

  EME_LOG("[{}] MediaDrmRemoteCDMParent::EnsureHasAMediaCrypto -- open session",
          fmt::ptr(this));

  media_status_t status = AMediaDrm_openSession(mDrm, &mCryptoSessionId);
  if (status == AMEDIA_DRM_NOT_PROVISIONED) {
    return EnsureProvisioned()->Then(
        GetCurrentSerialEventTarget(), __func__,
        [self = RefPtr{this}](const InternalPromise::ResolveOrRejectValue&) {
          return self->EnsureHasAMediaCrypto();
        });
  }

  if (NS_WARN_IF(status != AMEDIA_OK)) {
    mCryptoError.emplace(
        NS_ERROR_DOM_INVALID_STATE_ERR,
        RESULT_DETAIL("AMediaDrm_openSession failed for crypto %d", status));
    return InternalPromise::CreateAndReject(*mCryptoError, __func__);
  }

  AMediaCrypto* crypto =
      AMediaCrypto_new(mUuid, mCryptoSessionId.ptr, mCryptoSessionId.length);
  if (NS_WARN_IF(!crypto)) {
    AMediaDrm_closeSession(mDrm, &mCryptoSessionId);
    mCryptoSessionId = {};
    mCryptoError.emplace(NS_ERROR_DOM_INVALID_STATE_ERR,
                         "AMediaCrypto_new failed"_ns);
    return InternalPromise::CreateAndReject(*mCryptoError, __func__);
  }

  MutexAutoLock lock(mMutex);
  mCrypto = MakeRefPtr<MediaDrmCrypto>(crypto);
  return InternalPromise::CreateAndResolve(true, __func__);
}

RefPtr<MediaDrmRemoteCDMParent::InternalPromise>
MediaDrmRemoteCDMParent::EnsureProvisioned() {
  if (NS_WARN_IF(!mDrm)) {
    return InternalPromise::CreateAndReject(
        MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, "Missing AMediaDrm"_ns),
        __func__);
  }

  if (mProvisionError) {
    return InternalPromise::CreateAndReject(*mProvisionError, __func__);
  }

  // There may already be a provision request outstanding.
  bool outstanding = !mProvisionPromise.IsEmpty();
  RefPtr<InternalPromise> p = mProvisionPromise.Ensure(__func__);
  if (outstanding) {
    return p.forget();
  }

  EME_LOG("[{}] MediaDrmRemoteCDMParent::EnsureProvisioned -- get request",
          fmt::ptr(this));

  // AMediaDrm_getProvisionRequest requires the size to be non-zero. It does not
  // use the value for anything besides verification and overwrites in the
  // success case.
  size_t provisionRequestSize = std::numeric_limits<size_t>::max();
  const uint8_t* provisionRequest = nullptr;
  const char* serverUrl = nullptr;
  media_status_t status = AMediaDrm_getProvisionRequest(
      mDrm, &provisionRequest, &provisionRequestSize, &serverUrl);
  if (NS_WARN_IF(status != AMEDIA_OK)) {
    EME_LOG(
        "[{}] MediaDrmRemoteCDMParent::EnsureProvisioned -- failed drm {} "
        "provisionRequest {} size {} serverUrl {} ({}) status {}",
        fmt::ptr(this), fmt::ptr(mDrm), fmt::ptr(provisionRequest),
        provisionRequestSize, fmt::ptr(serverUrl), serverUrl ? serverUrl : "",
        static_cast<int>(status));
    mProvisionError.emplace(
        NS_ERROR_DOM_INVALID_STATE_ERR,
        RESULT_DETAIL("AMediaDrm_getProvisionRequest failed %d", status));
    mProvisionPromise.Reject(*mProvisionError, __func__);
    return p.forget();
  }

  SendProvision(RemoteCDMProvisionRequestIPDL(
                    NS_ConvertUTF8toUTF16(serverUrl),
                    nsTArray<uint8_t>(provisionRequest, provisionRequestSize)))
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [self = RefPtr{this}](RemoteCDMProvisionResponseIPDL&& aResponse) {
            if (aResponse.type() ==
                RemoteCDMProvisionResponseIPDL::TMediaResult) {
              EME_LOG(
                  "[{}] MediaDrmRemoteCDMParent::EnsureProvisioned -- response "
                  "failed",
                  fmt::ptr(self.get()));
              self->mProvisionError.emplace(
                  std::move(aResponse.get_MediaResult()));
              self->mProvisionPromise.RejectIfExists(*self->mProvisionError,
                                                     __func__);
              return;
            }

            media_status_t status = AMediaDrm_provideProvisionResponse(
                self->mDrm, aResponse.get_ArrayOfuint8_t().Elements(),
                aResponse.get_ArrayOfuint8_t().Length());
            if (NS_WARN_IF(status != AMEDIA_OK)) {
              EME_LOG(
                  "[{}] MediaDrmRemoteCDMParent::EnsureProvisioned -- response "
                  "invalid",
                  fmt::ptr(self.get()));
              self->mProvisionError.emplace(
                  NS_ERROR_DOM_INVALID_STATE_ERR,
                  RESULT_DETAIL("AMediaDrm_provideProvisionResponse failed %d",
                                status));
              self->mProvisionPromise.RejectIfExists(*self->mProvisionError,
                                                     __func__);
              return;
            }

            EME_LOG(
                "[{}] MediaDrmRemoteCDMParent::EnsureProvisioned -- success",
                fmt::ptr(self.get()));
            self->mProvisionPromise.ResolveIfExists(true, __func__);
          },
          [](const mozilla::ipc::ResponseRejectReason& aReason) {});
  return p.forget();
}

mozilla::ipc::IPCResult MediaDrmRemoteCDMParent::RecvCreateSession(
    RemoteCDMCreateSessionRequestIPDL&& aRequest,
    CreateSessionResolver&& aResolver) {
  EME_LOG("[{}] MediaDrmRemoteCDMParent::RecvCreateSession", fmt::ptr(this));

  // If we are still provisioning, then the remote side should have queued the
  // requests.
  if (NS_WARN_IF(!mDrm)) {
    aResolver(
        MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, "Missing AMediaDrm"_ns));
    return IPC_OK();
  }

  if (!HasCrypto()) {
    EnsureHasAMediaCrypto()->Then(
        GetCurrentSerialEventTarget(), __func__,
        [self = RefPtr{this}, request = std::move(aRequest),
         resolver = std::move(aResolver)](
            const InternalPromise::ResolveOrRejectValue& aValue) mutable {
          if (aValue.IsReject()) {
            resolver(aValue.RejectValue());
            return;
          }

          self->RecvCreateSession(std::move(request), std::move(resolver));
        });
    return IPC_OK();
  }

  AMediaDrmSessionId sessionId;
  media_status_t status = AMediaDrm_openSession(mDrm, &sessionId);
  if (status == AMEDIA_DRM_NOT_PROVISIONED) {
    EnsureProvisioned()->Then(
        GetCurrentSerialEventTarget(), __func__,
        [self = RefPtr{this}, request = std::move(aRequest),
         resolver = std::move(aResolver)](
            const InternalPromise::ResolveOrRejectValue& aValue) mutable {
          if (aValue.IsReject()) {
            resolver(aValue.RejectValue());
            return;
          }

          self->RecvCreateSession(std::move(request), std::move(resolver));
        });
    return IPC_OK();
  }

  if (NS_WARN_IF(status != AMEDIA_OK)) {
    aResolver(
        MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR,
                    RESULT_DETAIL("AMediaDrm_openSession failed %d", status)));
    return IPC_OK();
  }

  NS_ConvertUTF16toUTF8 mimeType(aRequest.initDataType());

  const uint8_t* keyRequest = nullptr;
  size_t keyRequestSize = 0;
  status =
      AMediaDrm_getKeyRequest(mDrm, &sessionId, aRequest.initData().Elements(),
                              aRequest.initData().Length(), mimeType.get(),
                              AMediaDrmKeyType::KEY_TYPE_STREAMING, nullptr, 0,
                              &keyRequest, &keyRequestSize);

  if (status == AMEDIA_DRM_NOT_PROVISIONED) {
    AMediaDrm_closeSession(mDrm, &sessionId);
    EnsureProvisioned()->Then(
        GetCurrentSerialEventTarget(), __func__,
        [self = RefPtr{this}, request = std::move(aRequest),
         resolver = std::move(aResolver)](
            const InternalPromise::ResolveOrRejectValue& aValue) mutable {
          if (aValue.IsReject()) {
            resolver(aValue.RejectValue());
            return;
          }

          self->RecvCreateSession(std::move(request), std::move(resolver));
        });
    return IPC_OK();
  }

  if (NS_WARN_IF(status != AMEDIA_OK)) {
    AMediaDrm_closeSession(mDrm, &sessionId);
    aResolver(MediaResult(
        NS_ERROR_DOM_INVALID_STATE_ERR,
        RESULT_DETAIL("AMediaDrm_getKeyRequest failed %d", status)));
    return IPC_OK();
  }

  NS_ConvertUTF8toUTF16 sessionIdStr(
      reinterpret_cast<const char*>(sessionId.ptr), sessionId.length);
  mSessions[sessionIdStr] = {sessionId, std::move(mimeType)};
  aResolver(RemoteCDMKeyMessageIPDL(
      std::move(sessionIdStr), MediaKeyMessageType::License_request,
      nsTArray<uint8_t>(reinterpret_cast<const uint8_t*>(keyRequest),
                        keyRequestSize)));
  return IPC_OK();
}

mozilla::ipc::IPCResult MediaDrmRemoteCDMParent::RecvLoadSession(
    const RemoteCDMLoadSessionRequestIPDL& aRequest,
    LoadSessionResolver&& aResolver) {
  EME_LOG("[{}] MediaDrmRemoteCDMParent::RecvLoadSession", fmt::ptr(this));
  aResolver(MediaResult(NS_ERROR_NOT_IMPLEMENTED));
  return IPC_OK();
}

mozilla::ipc::IPCResult MediaDrmRemoteCDMParent::RecvUpdateSession(
    const RemoteCDMUpdateSessionRequestIPDL& aRequest,
    UpdateSessionResolver&& aResolver) {
  EME_LOG("[{}] MediaDrmRemoteCDMParent::RecvUpdateSession", fmt::ptr(this));

  const auto i = mSessions.find(aRequest.sessionId());
  if (i == mSessions.end()) {
    aResolver(
        MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, "Invalid session id"_ns));
    return IPC_OK();
  }

  MOZ_ASSERT(mDrm);
  MOZ_ASSERT(HasCrypto());

  AMediaDrmKeySetId keySetId{};
  media_status_t status = AMediaDrm_provideKeyResponse(
      mDrm, &i->second.id, aRequest.response().Elements(),
      aRequest.response().Length(), &keySetId);
  if (NS_WARN_IF(status != AMEDIA_OK)) {
    aResolver(MediaResult(
        NS_ERROR_DOM_INVALID_STATE_ERR,
        RESULT_DETAIL("AMediaDrm_provideKeyResponse failed %d", status)));
    return IPC_OK();
  }

  aResolver(MediaResult(NS_OK));
  return IPC_OK();
}

mozilla::ipc::IPCResult MediaDrmRemoteCDMParent::RecvRemoveSession(
    const nsString& aSessionId, RemoveSessionResolver&& aResolver) {
  EME_LOG("[{}] MediaDrmRemoteCDMParent::RecvRemoveSession", fmt::ptr(this));
  aResolver(MediaResult(NS_ERROR_NOT_IMPLEMENTED));
  return IPC_OK();
}

mozilla::ipc::IPCResult MediaDrmRemoteCDMParent::RecvCloseSession(
    const nsString& aSessionId, CloseSessionResolver&& aResolver) {
  const auto i = mSessions.find(aSessionId);
  if (i == mSessions.end()) {
    EME_LOG("[{}] MediaDrmRemoteCDMParent::RecvCloseSession -- invalid session",
            fmt::ptr(this));
    aResolver(
        MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, "Invalid session id"_ns));
    return IPC_OK();
  }

  MOZ_ASSERT(mDrm);
  MOZ_ASSERT(HasCrypto());

  EME_LOG("[{}] MediaDrmRemoteCDMParent::RecvCloseSession -- closeSession",
          fmt::ptr(this));
  media_status_t status = AMediaDrm_closeSession(mDrm, &i->second.id);
  EME_LOG("[{}] MediaDrmRemoteCDMParent::RecvCloseSession -- status {}",
          fmt::ptr(this), static_cast<int>(status));
  if (NS_WARN_IF(status != AMEDIA_OK)) {
    aResolver(
        MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR,
                    RESULT_DETAIL("AMediaDrm_closeSession failed %d", status)));
  } else {
    aResolver(MediaResult(NS_OK));
  }

  mSessions.erase(i);
  return IPC_OK();
}

mozilla::ipc::IPCResult MediaDrmRemoteCDMParent::RecvSetServerCertificate(
    mozilla::Span<uint8_t const> aCertificate,
    SetServerCertificateResolver&& aResolver) {
  if (NS_WARN_IF(!mDrm)) {
    EME_LOG(
        "[{}] MediaDrmRemoteCDMParent::RecvSetServerCertificate -- not init",
        fmt::ptr(this));
    aResolver(
        MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, "Missing AMediaDrm"_ns));
    return IPC_OK();
  }

  EME_LOG(
      "[{}] MediaDrmRemoteCDMParent::RecvSetServerCertificate -- "
      "setPropertyByteArray",
      fmt::ptr(this));
  media_status_t status = AMediaDrm_setPropertyByteArray(
      mDrm, "certificate", aCertificate.Elements(), aCertificate.Length());
  EME_LOG("[{}] MediaDrmRemoteCDMParent::RecvSetServerCertificate -- status {}",
          fmt::ptr(this), static_cast<int>(status));
  if (NS_WARN_IF(status != AMEDIA_OK)) {
    aResolver(MediaResult(
        NS_ERROR_DOM_INVALID_STATE_ERR,
        RESULT_DETAIL("AMediaDrm_setPropertyByteArray certificate failed %d",
                      status)));
    return IPC_OK();
  }

  aResolver(MediaResult(NS_OK));
  return IPC_OK();
}

void MediaDrmRemoteCDMParent::HandleEvent(nsString&& aSessionId,
                                          AMediaDrmEventType aEventType,
                                          int aExtra,
                                          nsTArray<uint8_t>&& aData) {
  const auto i = mSessions.find(aSessionId);
  if (i == mSessions.end()) {
    EME_LOG("[{}] MediaDrmRemoteCDMParent::HandleEvent -- session not found",
            fmt::ptr(this));
    return;
  }

  EME_LOG("[{}] MediaDrmRemoteCDMParent::HandleEvent -- event {}",
          fmt::ptr(this), static_cast<int>(aEventType));
  MOZ_ASSERT(mDrm);

  switch (aEventType) {
    case AMediaDrmEventType::EVENT_PROVISION_REQUIRED:
      EnsureProvisioned();
      break;
    case AMediaDrmEventType::EVENT_KEY_REQUIRED: {
      const uint8_t* keyRequest = nullptr;
      size_t keyRequestSize = 0;
      AMediaDrmKeyRequestType keyRequestType =
          AMediaDrmKeyRequestType::KEY_REQUEST_TYPE_INITIAL;
      media_status_t status;

      if (__builtin_available(android 33, *)) {
        status = AMediaDrm_getKeyRequestWithDefaultUrlAndType(
            mDrm, &i->second.id, aData.Elements(), aData.Length(),
            i->second.mimeType.get(), AMediaDrmKeyType::KEY_TYPE_STREAMING,
            nullptr, 0, &keyRequest, &keyRequestSize, nullptr, &keyRequestType);
      } else {
        status = AMediaDrm_getKeyRequest(
            mDrm, &i->second.id, aData.Elements(), aData.Length(),
            i->second.mimeType.get(), AMediaDrmKeyType::KEY_TYPE_STREAMING,
            nullptr, 0, &keyRequest, &keyRequestSize);
      }

      if (status == AMEDIA_DRM_NOT_PROVISIONED) {
        EnsureProvisioned()->Then(
            GetCurrentSerialEventTarget(), __func__,
            [self = RefPtr{this}, sessionId = std::move(aSessionId),
             data = std::move(aData), aEventType, aExtra](
                const InternalPromise::ResolveOrRejectValue& aValue) mutable {
              if (aValue.IsReject()) {
                return;
              }

              self->HandleEvent(std::move(sessionId), aEventType, aExtra,
                                std::move(data));
            });
        return;
      }

      if (NS_WARN_IF(status != AMEDIA_OK)) {
        return;
      }

      dom::MediaKeyMessageType keyMessageType;
      switch (keyRequestType) {
        case AMediaDrmKeyRequestType::KEY_REQUEST_TYPE_NONE:
          // Already have what we need.
          return;
        case AMediaDrmKeyRequestType::KEY_REQUEST_TYPE_RELEASE:
          keyMessageType = MediaKeyMessageType::License_release;
          break;
        case AMediaDrmKeyRequestType::KEY_REQUEST_TYPE_RENEWAL:
          keyMessageType = MediaKeyMessageType::License_renewal;
          break;
        case AMediaDrmKeyRequestType::KEY_REQUEST_TYPE_UPDATE:
          // Not directly equivalent but needs an additional license request.
          keyMessageType = MediaKeyMessageType::License_request;
          break;
        default:
          MOZ_FALLTHROUGH_ASSERT("Unhandled AMediaDrmKeyRequestType");
        case AMediaDrmKeyRequestType::KEY_REQUEST_TYPE_INITIAL:
          keyMessageType = MediaKeyMessageType::License_request;
          break;
      }

      (void)SendOnSessionKeyMessage(RemoteCDMKeyMessageIPDL(
          std::move(aSessionId), keyMessageType,
          nsTArray<uint8_t>(reinterpret_cast<const uint8_t*>(keyRequest),
                            keyRequestSize)));
    } break;
    case AMediaDrmEventType::EVENT_KEY_EXPIRED:
      break;
    case AMediaDrmEventType::EVENT_VENDOR_DEFINED:
      break;
    case AMediaDrmEventType::EVENT_SESSION_RECLAIMED:
      break;
    default:
      break;
  }
}

void MediaDrmRemoteCDMParent::HandleExpirationUpdate(nsString&& aSessionId,
                                                     int64_t aExpiryTimeInMS) {
  const auto i = mSessions.find(aSessionId);
  if (i == mSessions.end()) {
    EME_LOG(
        "[{}] MediaDrmRemoteCDMParent::HandleExpirationUpdate -- session not "
        "found",
        fmt::ptr(this));
    return;
  }

  EME_LOG("[{}] MediaDrmRemoteCDMParent::HandleExpirationUpdate",
          fmt::ptr(this));
  (void)SendOnSessionKeyExpiration(RemoteCDMKeyExpirationIPDL(
      std::move(aSessionId), static_cast<double>(aExpiryTimeInMS)));
}

void MediaDrmRemoteCDMParent::HandleKeysChange(
    nsString&& aSessionId, bool aHasNewUsableKey,
    nsTArray<CDMKeyInfo>&& aKeyInfo) {
  const auto i = mSessions.find(aSessionId);
  if (i == mSessions.end()) {
    EME_LOG(
        "[{}] MediaDrmRemoteCDMParent::HandleKeysChange -- session not found",
        fmt::ptr(this));
    return;
  }

  EME_LOG("[{}] MediaDrmRemoteCDMParent::HandleKeysChange", fmt::ptr(this));
  (void)SendOnSessionKeyStatus(
      RemoteCDMKeyStatusIPDL(std::move(aSessionId), std::move(aKeyInfo)));
}

already_AddRefed<MediaDrmCryptoInfo> MediaDrmRemoteCDMParent::CreateCryptoInfo(
    MediaRawData* aSample) {
  MOZ_ASSERT(mDrm);

  if (NS_WARN_IF(!aSample)) {
    return nullptr;
  }

  const CryptoSample& cryptoObj = aSample->mCrypto;
  if (!cryptoObj.IsEncrypted()) {
    return nullptr;
  }

  uint32_t numSubSamples = std::min<uint32_t>(
      cryptoObj.mPlainSizes.Length(), cryptoObj.mEncryptedSizes.Length());
  MOZ_ASSERT(numSubSamples <= INT32_MAX);

  // Deep copy the plain and encrypted sizes so we can modify them.
  nsTArray<size_t> plainSizes(cryptoObj.mPlainSizes.Length());
  nsTArray<size_t> encryptedSizes(cryptoObj.mEncryptedSizes.Length());
  if (numSubSamples > 0) {
    uint32_t totalSubSamplesSize = 0;
    for (const auto& size : cryptoObj.mPlainSizes) {
      plainSizes.AppendElement(size);
      totalSubSamplesSize += size;
    }
    for (const auto& size : cryptoObj.mEncryptedSizes) {
      encryptedSizes.AppendElement(size);
      totalSubSamplesSize += size;
    }

    auto codecSpecificDataSize =
        CheckedInt<size_t>(aSample->Size()) - totalSubSamplesSize;
    if (!codecSpecificDataSize.isValid()) {
      MOZ_ASSERT_UNREACHABLE("totalSubSamplesSize greater than sample size");
      return nullptr;
    }

    // Size of codec specific data("CSD") for Android java::sdk::MediaCodec
    // usage should be included in the 1st plain size if it exists.
    if (codecSpecificDataSize.value() && !plainSizes.IsEmpty()) {
      // This shouldn't overflow as the the plain size should be UINT16_MAX at
      // most, and the CSD should never be that large. Checked int acts like a
      // diagnostic assert here to help catch if we ever have insane inputs.
      auto newLeadingPlainSize = codecSpecificDataSize + plainSizes[0];
      if (!newLeadingPlainSize.isValid()) {
        MOZ_ASSERT_UNREACHABLE("newLeadingPlainSize overflowed");
        return nullptr;
      }
      plainSizes[0] = newLeadingPlainSize.value();
    }
  } else {
    // MediaCodec expects us to provide at least one subsample. Whole-block full
    // sample encryption does not carry subsample information, so we need to
    // synthesize it by creating a subsample as big as the sample itself. See
    // bug 1759936.
    numSubSamples = 1;
    plainSizes.AppendElement(0);
    encryptedSizes.AppendElement(aSample->Size());
  }

  const CopyableTArray<uint8_t>* srcIV;
  cryptoinfo_mode_t mode;
  switch (cryptoObj.mCryptoScheme) {
    case CryptoScheme::None:
      mode = AMEDIACODECRYPTOINFO_MODE_CLEAR;
      srcIV = &cryptoObj.mIV;
      break;
    case CryptoScheme::Cenc:
      mode = AMEDIACODECRYPTOINFO_MODE_AES_CTR;
      srcIV = &cryptoObj.mIV;
      break;
    case CryptoScheme::Cbcs:
    case CryptoScheme::Cbcs_1_9:
      mode = AMEDIACODECRYPTOINFO_MODE_AES_CBC;
      srcIV = &cryptoObj.mConstantIV;
      break;
    default:
      MOZ_ASSERT_UNREACHABLE("Unhandled CryptoScheme!");
      return nullptr;
  }

  uint8_t key[16] = {};
  uint8_t iv[16] = {};

  if (NS_WARN_IF(srcIV->Length() > sizeof(iv))) {
    MOZ_ASSERT_UNREACHABLE("IV too big for Android!");
    return nullptr;
  }

  if (NS_WARN_IF(cryptoObj.mKeyId.Length() > sizeof(key))) {
    MOZ_ASSERT_UNREACHABLE("Key too big for Android!");
    return nullptr;
  }

  if (!srcIV->IsEmpty()) {
    memcpy(iv, srcIV->Elements(), srcIV->Length());
  }

  if (!cryptoObj.mKeyId.IsEmpty()) {
    memcpy(key, cryptoObj.mKeyId.Elements(), cryptoObj.mKeyId.Length());
  }

  AMediaCodecCryptoInfo* cryptoInfo = AMediaCodecCryptoInfo_new(
      static_cast<int32_t>(numSubSamples), key, iv, mode, plainSizes.Elements(),
      encryptedSizes.Elements());
  if (NS_WARN_IF(!cryptoInfo)) {
    MOZ_ASSERT_UNREACHABLE("Failed to create AMediaCodecCryptoInfo");
    return nullptr;
  }

  if (mode == AMEDIACODECRYPTOINFO_MODE_AES_CBC) {
    cryptoinfo_pattern_t pattern = {};
    pattern.encryptBlocks = cryptoObj.mCryptByteBlock;
    pattern.skipBlocks = cryptoObj.mSkipByteBlock;
    AMediaCodecCryptoInfo_setPattern(cryptoInfo, &pattern);
  }

  return MakeAndAddRef<MediaDrmCryptoInfo>(cryptoInfo);
}

MediaDrmCrypto::~MediaDrmCrypto() { AMediaCrypto_delete(mCrypto); }

MediaDrmCryptoInfo::~MediaDrmCryptoInfo() {
  AMediaCodecCryptoInfo_delete(mCryptoInfo);
}

/* static */
void MediaDrmRemoteCDMParent::UnprovisionMediaDrmOrigins(
    const nsTArray<nsCString>& aOriginKeys) {
  EME_LOG("UnprovisionMediaDrmOrigins: unprovisioning {} origin(s)",
          aOriginKeys.Length());

  for (const auto& originKey : aOriginKeys) {
    AMediaDrm* drm = AMediaDrm_createByUUID(WIDEVINE_UUID);
    if (!drm) {
      EME_LOG("UnprovisionMediaDrmOrigins: failed to create AMediaDrm");
      continue;
    }

    media_status_t status =
        AMediaDrm_setPropertyString(drm, "origin", originKey.get());
    if (status != AMEDIA_OK) {
      EME_LOG("UnprovisionMediaDrmOrigins: setPropertyString origin failed: {}",
              static_cast<int>(status));
      AMediaDrm_release(drm);
      continue;
    }

    static constexpr char kUnprovision[] = "unprovision";
    status = AMediaDrm_provideProvisionResponse(
        drm, reinterpret_cast<const uint8_t*>(kUnprovision),
        sizeof(kUnprovision) - 1);
    if (status != AMEDIA_OK) {
      EME_LOG(
          "UnprovisionMediaDrmOrigins: provideProvisionResponse failed: {} "
          "(may already be unprovisioned)",
          static_cast<int>(status));
    }

    AMediaDrm_release(drm);
  }
}

}  // namespace mozilla

Messung V0.5 in Prozent
C=89 H=100 G=94

¤ Dauer der Verarbeitung: 0.17 Sekunden  (vorverarbeitet am  2026-08-25) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.