tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

MediaDrmRemoteCDMParent.cpp (30948B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "MediaDrmRemoteCDMParent.h"
      8 
      9 #include <limits>
     10 
     11 #include "mozilla/CheckedInt.h"
     12 #include "mozilla/EMEUtils.h"
     13 #include "mozilla/RemoteMediaManagerParent.h"
     14 
     15 namespace mozilla {
     16 
     17 StaticAutoPtr<MediaDrmRemoteCDMParent::DrmCallbackMap>
     18    MediaDrmRemoteCDMParent::sCbMap;
     19 
     20 /* static */
     21 void MediaDrmRemoteCDMParent::InitializeStatics() {
     22  if (sCbMap) {
     23    return;
     24  }
     25 
     26  sCbMap = new DrmCallbackMap();
     27 }
     28 
     29 /* static */
     30 void MediaDrmRemoteCDMParent::HandleEventCb(
     31    AMediaDrm* aDrm, const AMediaDrmSessionId* aSessionId,
     32    AMediaDrmEventType aEventType, int aExtra, const uint8_t* aData,
     33    size_t aDataSize) {
     34  EME_LOG("MediaDrmRemoteCDMParent::HandleEventCb -- drm %p, event %d", aDrm,
     35          aEventType);
     36 
     37  // Called from an internal NDK thread. We need to dispatch to the owning
     38  // thread of the actor with the same AMediaDrm object.
     39  NS_ConvertUTF8toUTF16 sessionIdStr(
     40      reinterpret_cast<const char*>(aSessionId->ptr), aSessionId->length);
     41 
     42  size_t dataSize = aData ? aDataSize : 0;
     43  nsTArray<uint8_t> data(dataSize);
     44  if (dataSize) {
     45    data.AppendElements(aData, dataSize);
     46  }
     47 
     48  RemoteMediaManagerParent::Dispatch(NS_NewRunnableFunction(
     49      __func__,
     50      [aDrm, aEventType, aExtra, sessionIdStr = std::move(sessionIdStr),
     51       data = std::move(data)]() mutable {
     52        auto i = sCbMap->find(aDrm);
     53        if (i == sCbMap->end()) {
     54          return;
     55        }
     56 
     57        i->second->HandleEvent(std::move(sessionIdStr), aEventType, aExtra,
     58                               std::move(data));
     59      }));
     60 }
     61 
     62 /* static */
     63 void MediaDrmRemoteCDMParent::HandleExpirationUpdateCb(
     64    AMediaDrm* aDrm, const AMediaDrmSessionId* aSessionId,
     65    int64_t aExpiryTimeInMS) {
     66  EME_LOG("MediaDrmRemoteCDMParent::HandleExpirationUpdateCb -- drm %p", aDrm);
     67 
     68  // Called from an internal NDK thread. We need to dispatch to the owning
     69  // thread of the actor with the same AMediaDrm object.
     70  NS_ConvertUTF8toUTF16 sessionIdStr(
     71      reinterpret_cast<const char*>(aSessionId->ptr), aSessionId->length);
     72 
     73  RemoteMediaManagerParent::Dispatch(NS_NewRunnableFunction(
     74      __func__, [aDrm, aExpiryTimeInMS,
     75                 sessionIdStr = std::move(sessionIdStr)]() mutable {
     76        auto i = sCbMap->find(aDrm);
     77        if (i == sCbMap->end()) {
     78          return;
     79        }
     80 
     81        i->second->HandleExpirationUpdate(std::move(sessionIdStr),
     82                                          aExpiryTimeInMS);
     83      }));
     84 }
     85 
     86 /* static */
     87 void MediaDrmRemoteCDMParent::HandleKeysChangeCb(
     88    AMediaDrm* aDrm, const AMediaDrmSessionId* aSessionId,
     89    const AMediaDrmKeyStatus* aKeyStatus, size_t aNumKeys,
     90    bool aHasNewUsableKey) {
     91  EME_LOG("MediaDrmRemoteCDMParent::HandleKeysChangeCb -- drm %p", aDrm);
     92 
     93  // Called from an internal NDK thread. We need to dispatch to the owning
     94  // thread of the actor with the same AMediaDrm object.
     95  NS_ConvertUTF8toUTF16 sessionIdStr(
     96      reinterpret_cast<const char*>(aSessionId->ptr), aSessionId->length);
     97 
     98  nsTArray<CDMKeyInfo> keyInfo(aNumKeys);
     99  if (aKeyStatus) {
    100    for (size_t i = 0; i < aNumKeys; ++i) {
    101      nsTArray<uint8_t> keyId(aKeyStatus[i].keyId.ptr,
    102                              aKeyStatus[i].keyId.length);
    103 
    104      dom::Optional<dom::MediaKeyStatus> keyStatus;
    105      switch (aKeyStatus[i].keyType) {
    106        case AMediaKeyStatusType::KEY_STATUS_TYPE_USABLE:
    107          keyStatus.Construct(dom::MediaKeyStatus::Usable);
    108          break;
    109        case AMediaKeyStatusType::KEY_STATUS_TYPE_EXPIRED:
    110          keyStatus.Construct(dom::MediaKeyStatus::Expired);
    111          break;
    112        case AMediaKeyStatusType::KEY_STATUS_TYPE_OUTPUTNOTALLOWED:
    113          keyStatus.Construct(dom::MediaKeyStatus::Output_restricted);
    114          break;
    115        case AMediaKeyStatusType::KEY_STATUS_TYPE_STATUSPENDING:
    116          keyStatus.Construct(dom::MediaKeyStatus::Status_pending);
    117          break;
    118        default:
    119          MOZ_FALLTHROUGH_ASSERT("Unhandled AMediaKeyStatusType!");
    120        case AMediaKeyStatusType::KEY_STATUS_TYPE_INTERNALERROR:
    121          keyStatus.Construct(dom::MediaKeyStatus::Internal_error);
    122          break;
    123      }
    124 
    125      keyInfo.AppendElement(CDMKeyInfo(std::move(keyId), std::move(keyStatus)));
    126    }
    127  }
    128 
    129  RemoteMediaManagerParent::Dispatch(NS_NewRunnableFunction(
    130      __func__, [aDrm, aHasNewUsableKey, sessionIdStr = std::move(sessionIdStr),
    131                 keyInfo = std::move(keyInfo)]() mutable {
    132        auto i = sCbMap->find(aDrm);
    133        if (i == sCbMap->end()) {
    134          return;
    135        }
    136 
    137        i->second->HandleKeysChange(std::move(sessionIdStr), aHasNewUsableKey,
    138                                    std::move(keyInfo));
    139      }));
    140 }
    141 
    142 MediaDrmRemoteCDMParent::MediaDrmRemoteCDMParent(const nsAString& aKeySystem)
    143    : mMutex("MediaDrmRemoteCDMParent::mMutex") {
    144  EME_LOG("[%p] MediaDrmRemoteCDMParent::MediaDrmRemoteCDMParent", this);
    145  if (IsWidevineKeySystem(aKeySystem)) {
    146    mUuid = WIDEVINE_UUID;
    147  } else if (IsClearkeyKeySystem(aKeySystem)) {
    148    mUuid = CLEARKEY_UUID;
    149  }
    150 }
    151 
    152 MediaDrmRemoteCDMParent::~MediaDrmRemoteCDMParent() {
    153  EME_LOG("[%p] MediaDrmRemoteCDMParent::~MediaDrmRemoteCDMParent", this);
    154  Destroy();
    155 }
    156 
    157 void MediaDrmRemoteCDMParent::ActorDestroy(ActorDestroyReason aWhy) {
    158  EME_LOG("[%p] MediaDrmRemoteCDMParent::ActorDestroy", this);
    159  Destroy();
    160 }
    161 
    162 void MediaDrmRemoteCDMParent::Destroy() {
    163  EME_LOG("[%p] MediaDrmRemoteCDMParent::Destroy -- drm %p", this, mDrm);
    164 
    165  mProvisionPromise.RejectIfExists(
    166      MediaResult(NS_ERROR_DOM_ABORT_ERR, "Destroyed"_ns), __func__);
    167 
    168  for (const auto& i : mSessions) {
    169    AMediaDrm_closeSession(mDrm, &i.second.id);
    170  }
    171  mSessions.clear();
    172 
    173  {
    174    MutexAutoLock lock(mMutex);
    175    if (mCrypto) {
    176      mCrypto = nullptr;
    177      mCryptoSessionId = {};
    178    }
    179  }
    180 
    181  if (mDrm) {
    182    auto i = sCbMap->find(mDrm);
    183    if (i != sCbMap->end()) {
    184      sCbMap->erase(i);
    185    } else {
    186      MOZ_ASSERT_UNREACHABLE("Missing MediaDrm in sCbMap");
    187    }
    188    AMediaDrm_release(mDrm);
    189    mDrm = nullptr;
    190  }
    191 }
    192 
    193 mozilla::ipc::IPCResult MediaDrmRemoteCDMParent::RecvInit(
    194    const RemoteCDMInitRequestIPDL& request, InitResolver&& aResolver) {
    195  EME_LOG("[%p] MediaDrmRemoteCDMParent::RecvInit", this);
    196 
    197  if (NS_WARN_IF(!mUuid)) {
    198    aResolver(MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, "Invalid uuid"_ns));
    199    return IPC_OK();
    200  }
    201 
    202  if (NS_WARN_IF(mDrm)) {
    203    aResolver(MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR,
    204                          "AMediaDrm already initialized"_ns));
    205    return IPC_OK();
    206  }
    207 
    208  if (NS_WARN_IF(!AMediaCrypto_isCryptoSchemeSupported(mUuid))) {
    209    aResolver(MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR,
    210                          "AMediaCrypto_isCryptoSchemeSupported failed"_ns));
    211    return IPC_OK();
    212  }
    213 
    214  mDrm = AMediaDrm_createByUUID(mUuid);
    215  if (NS_WARN_IF(!mDrm)) {
    216    aResolver(MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR,
    217                          "AMediaDrm_createByUUID failed"_ns));
    218    return IPC_OK();
    219  }
    220 
    221  media_status_t status =
    222      AMediaDrm_setPropertyString(mDrm, "securityLevel", "L3");
    223  if (NS_WARN_IF(status != AMEDIA_OK)) {
    224    aResolver(MediaResult(
    225        NS_ERROR_DOM_INVALID_STATE_ERR,
    226        RESULT_DETAIL("AMediaDrm_setPropertyString securityLevel failed %d",
    227                      status)));
    228    return IPC_OK();
    229  }
    230 
    231  status = AMediaDrm_setPropertyString(mDrm, "privacyMode", "enable");
    232  if (NS_WARN_IF(status != AMEDIA_OK)) {
    233    aResolver(MediaResult(
    234        NS_ERROR_DOM_INVALID_STATE_ERR,
    235        RESULT_DETAIL("AMediaDrm_setPropertyString privateMode failed %d",
    236                      status)));
    237    return IPC_OK();
    238  }
    239 
    240  status = AMediaDrm_setPropertyString(mDrm, "sessionSharing", "enable");
    241  if (NS_WARN_IF(status != AMEDIA_OK)) {
    242    aResolver(MediaResult(
    243        NS_ERROR_DOM_INVALID_STATE_ERR,
    244        RESULT_DETAIL("AMediaDrm_setPropertyString sessionSharing failed %d",
    245                      status)));
    246    return IPC_OK();
    247  }
    248 
    249  status = AMediaDrm_setOnEventListener(mDrm, HandleEventCb);
    250  if (NS_WARN_IF(status != AMEDIA_OK)) {
    251    aResolver(MediaResult(
    252        NS_ERROR_DOM_INVALID_STATE_ERR,
    253        RESULT_DETAIL("AMediaDrm_setOnEventListener failed %d", status)));
    254    return IPC_OK();
    255  }
    256 
    257  if (__builtin_available(android 29, *)) {
    258    status =
    259        AMediaDrm_setOnExpirationUpdateListener(mDrm, HandleExpirationUpdateCb);
    260    if (NS_WARN_IF(status != AMEDIA_OK)) {
    261      aResolver(MediaResult(
    262          NS_ERROR_DOM_INVALID_STATE_ERR,
    263          RESULT_DETAIL("AMediaDrm_setOnExpirationUpdateListener failed %d",
    264                        status)));
    265      return IPC_OK();
    266    }
    267 
    268    status = AMediaDrm_setOnKeysChangeListener(mDrm, HandleKeysChangeCb);
    269    if (NS_WARN_IF(status != AMEDIA_OK)) {
    270      aResolver(MediaResult(
    271          NS_ERROR_DOM_INVALID_STATE_ERR,
    272          RESULT_DETAIL("AMediaDrm_setOnKeysChangeListener failed %d",
    273                        status)));
    274      return IPC_OK();
    275    }
    276  }
    277 
    278  EME_LOG("[%p] MediaDrmRemoteCDMParent::RecvInit -- drm %p", this, mDrm);
    279  InitializeStatics();
    280  sCbMap->insert(std::pair{mDrm, this});
    281  aResolver(MediaResult(NS_OK));
    282 
    283  EnsureHasAMediaCrypto();
    284  return IPC_OK();
    285 }
    286 
    287 RefPtr<MediaDrmRemoteCDMParent::InternalPromise>
    288 MediaDrmRemoteCDMParent::EnsureHasAMediaCrypto() {
    289  if (NS_WARN_IF(!mDrm)) {
    290    return InternalPromise::CreateAndReject(
    291        MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, "Missing AMediaDrm"_ns),
    292        __func__);
    293  }
    294 
    295  if (HasCrypto()) {
    296    return InternalPromise::CreateAndResolve(true, __func__);
    297  }
    298 
    299  if (mCryptoError) {
    300    return InternalPromise::CreateAndReject(*mCryptoError, __func__);
    301  }
    302 
    303  EME_LOG("[%p] MediaDrmRemoteCDMParent::EnsureHasAMediaCrypto -- open session",
    304          this);
    305 
    306  media_status_t status = AMediaDrm_openSession(mDrm, &mCryptoSessionId);
    307  if (status == AMEDIA_DRM_NOT_PROVISIONED) {
    308    return EnsureProvisioned()->Then(
    309        GetCurrentSerialEventTarget(), __func__,
    310        [self = RefPtr{this}](const InternalPromise::ResolveOrRejectValue&) {
    311          return self->EnsureHasAMediaCrypto();
    312        });
    313  }
    314 
    315  if (NS_WARN_IF(status != AMEDIA_OK)) {
    316    mCryptoError.emplace(
    317        NS_ERROR_DOM_INVALID_STATE_ERR,
    318        RESULT_DETAIL("AMediaDrm_openSession failed for crypto %d", status));
    319    return InternalPromise::CreateAndReject(*mCryptoError, __func__);
    320  }
    321 
    322  AMediaCrypto* crypto =
    323      AMediaCrypto_new(mUuid, mCryptoSessionId.ptr, mCryptoSessionId.length);
    324  if (NS_WARN_IF(!crypto)) {
    325    AMediaDrm_closeSession(mDrm, &mCryptoSessionId);
    326    mCryptoSessionId = {};
    327    mCryptoError.emplace(NS_ERROR_DOM_INVALID_STATE_ERR,
    328                         "AMediaCrypto_new failed"_ns);
    329    return InternalPromise::CreateAndReject(*mCryptoError, __func__);
    330  }
    331 
    332  MutexAutoLock lock(mMutex);
    333  mCrypto = MakeRefPtr<MediaDrmCrypto>(crypto);
    334  return InternalPromise::CreateAndResolve(true, __func__);
    335 }
    336 
    337 RefPtr<MediaDrmRemoteCDMParent::InternalPromise>
    338 MediaDrmRemoteCDMParent::EnsureProvisioned() {
    339  if (NS_WARN_IF(!mDrm)) {
    340    return InternalPromise::CreateAndReject(
    341        MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, "Missing AMediaDrm"_ns),
    342        __func__);
    343  }
    344 
    345  if (mProvisionError) {
    346    return InternalPromise::CreateAndReject(*mProvisionError, __func__);
    347  }
    348 
    349  // There may already be a provision request outstanding.
    350  bool outstanding = !mProvisionPromise.IsEmpty();
    351  RefPtr<InternalPromise> p = mProvisionPromise.Ensure(__func__);
    352  if (outstanding) {
    353    return p.forget();
    354  }
    355 
    356  EME_LOG("[%p] MediaDrmRemoteCDMParent::EnsureProvisioned -- get request",
    357          this);
    358 
    359  // AMediaDrm_getProvisionRequest requires the size to be non-zero. It does not
    360  // use the value for anything besides verification and overwrites in the
    361  // success case.
    362  size_t provisionRequestSize = std::numeric_limits<size_t>::max();
    363  const uint8_t* provisionRequest = nullptr;
    364  const char* serverUrl = nullptr;
    365  media_status_t status = AMediaDrm_getProvisionRequest(
    366      mDrm, &provisionRequest, &provisionRequestSize, &serverUrl);
    367  if (NS_WARN_IF(status != AMEDIA_OK)) {
    368    EME_LOG(
    369        "[%p] MediaDrmRemoteCDMParent::EnsureProvisioned -- failed drm %p "
    370        "provisionRequest %p size %zu serverUrl %p (%s) status %d",
    371        this, mDrm, provisionRequest, provisionRequestSize, serverUrl,
    372        serverUrl ? serverUrl : "", status);
    373    mProvisionError.emplace(
    374        NS_ERROR_DOM_INVALID_STATE_ERR,
    375        RESULT_DETAIL("AMediaDrm_getProvisionRequest failed %d", status));
    376    mProvisionPromise.Reject(*mProvisionError, __func__);
    377    return p.forget();
    378  }
    379 
    380  SendProvision(RemoteCDMProvisionRequestIPDL(
    381                    NS_ConvertUTF8toUTF16(serverUrl),
    382                    nsTArray<uint8_t>(provisionRequest, provisionRequestSize)))
    383      ->Then(
    384          GetCurrentSerialEventTarget(), __func__,
    385          [self = RefPtr{this}](RemoteCDMProvisionResponseIPDL&& aResponse) {
    386            if (aResponse.type() ==
    387                RemoteCDMProvisionResponseIPDL::TMediaResult) {
    388              EME_LOG(
    389                  "[%p] MediaDrmRemoteCDMParent::EnsureProvisioned -- response "
    390                  "failed",
    391                  self.get());
    392              self->mProvisionError.emplace(
    393                  std::move(aResponse.get_MediaResult()));
    394              self->mProvisionPromise.RejectIfExists(*self->mProvisionError,
    395                                                     __func__);
    396              return;
    397            }
    398 
    399            media_status_t status = AMediaDrm_provideProvisionResponse(
    400                self->mDrm, aResponse.get_ArrayOfuint8_t().Elements(),
    401                aResponse.get_ArrayOfuint8_t().Length());
    402            if (NS_WARN_IF(status != AMEDIA_OK)) {
    403              EME_LOG(
    404                  "[%p] MediaDrmRemoteCDMParent::EnsureProvisioned -- response "
    405                  "invalid",
    406                  self.get());
    407              self->mProvisionError.emplace(
    408                  NS_ERROR_DOM_INVALID_STATE_ERR,
    409                  RESULT_DETAIL("AMediaDrm_provideProvisionResponse failed %d",
    410                                status));
    411              self->mProvisionPromise.RejectIfExists(*self->mProvisionError,
    412                                                     __func__);
    413              return;
    414            }
    415 
    416            EME_LOG(
    417                "[%p] MediaDrmRemoteCDMParent::EnsureProvisioned -- success",
    418                self.get());
    419            self->mProvisionPromise.ResolveIfExists(true, __func__);
    420          },
    421          [](const mozilla::ipc::ResponseRejectReason& aReason) {});
    422  return p.forget();
    423 }
    424 
    425 mozilla::ipc::IPCResult MediaDrmRemoteCDMParent::RecvCreateSession(
    426    RemoteCDMCreateSessionRequestIPDL&& aRequest,
    427    CreateSessionResolver&& aResolver) {
    428  EME_LOG("[%p] MediaDrmRemoteCDMParent::RecvCreateSession", this);
    429 
    430  // If we are still provisioning, then the remote side should have queued the
    431  // requests.
    432  if (NS_WARN_IF(!mDrm)) {
    433    aResolver(
    434        MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, "Missing AMediaDrm"_ns));
    435    return IPC_OK();
    436  }
    437 
    438  if (!HasCrypto()) {
    439    EnsureHasAMediaCrypto()->Then(
    440        GetCurrentSerialEventTarget(), __func__,
    441        [self = RefPtr{this}, request = std::move(aRequest),
    442         resolver = std::move(aResolver)](
    443            const InternalPromise::ResolveOrRejectValue& aValue) mutable {
    444          if (aValue.IsReject()) {
    445            resolver(aValue.RejectValue());
    446            return;
    447          }
    448 
    449          self->RecvCreateSession(std::move(request), std::move(resolver));
    450        });
    451    return IPC_OK();
    452  }
    453 
    454  AMediaDrmSessionId sessionId;
    455  media_status_t status = AMediaDrm_openSession(mDrm, &sessionId);
    456  if (status == AMEDIA_DRM_NOT_PROVISIONED) {
    457    EnsureProvisioned()->Then(
    458        GetCurrentSerialEventTarget(), __func__,
    459        [self = RefPtr{this}, request = std::move(aRequest),
    460         resolver = std::move(aResolver)](
    461            const InternalPromise::ResolveOrRejectValue& aValue) mutable {
    462          if (aValue.IsReject()) {
    463            resolver(aValue.RejectValue());
    464            return;
    465          }
    466 
    467          self->RecvCreateSession(std::move(request), std::move(resolver));
    468        });
    469    return IPC_OK();
    470  }
    471 
    472  if (NS_WARN_IF(status != AMEDIA_OK)) {
    473    aResolver(
    474        MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR,
    475                    RESULT_DETAIL("AMediaDrm_openSession failed %d", status)));
    476    return IPC_OK();
    477  }
    478 
    479  NS_ConvertUTF16toUTF8 mimeType(aRequest.initDataType());
    480 
    481  const uint8_t* keyRequest = nullptr;
    482  size_t keyRequestSize = 0;
    483  status =
    484      AMediaDrm_getKeyRequest(mDrm, &sessionId, aRequest.initData().Elements(),
    485                              aRequest.initData().Length(), mimeType.get(),
    486                              AMediaDrmKeyType::KEY_TYPE_STREAMING, nullptr, 0,
    487                              &keyRequest, &keyRequestSize);
    488 
    489  if (status == AMEDIA_DRM_NOT_PROVISIONED) {
    490    AMediaDrm_closeSession(mDrm, &sessionId);
    491    EnsureProvisioned()->Then(
    492        GetCurrentSerialEventTarget(), __func__,
    493        [self = RefPtr{this}, request = std::move(aRequest),
    494         resolver = std::move(aResolver)](
    495            const InternalPromise::ResolveOrRejectValue& aValue) mutable {
    496          if (aValue.IsReject()) {
    497            resolver(aValue.RejectValue());
    498            return;
    499          }
    500 
    501          self->RecvCreateSession(std::move(request), std::move(resolver));
    502        });
    503    return IPC_OK();
    504  }
    505 
    506  if (NS_WARN_IF(status != AMEDIA_OK)) {
    507    AMediaDrm_closeSession(mDrm, &sessionId);
    508    aResolver(MediaResult(
    509        NS_ERROR_DOM_INVALID_STATE_ERR,
    510        RESULT_DETAIL("AMediaDrm_getKeyRequest failed %d", status)));
    511    return IPC_OK();
    512  }
    513 
    514  NS_ConvertUTF8toUTF16 sessionIdStr(
    515      reinterpret_cast<const char*>(sessionId.ptr), sessionId.length);
    516  mSessions[sessionIdStr] = {sessionId, std::move(mimeType)};
    517  aResolver(RemoteCDMKeyMessageIPDL(
    518      std::move(sessionIdStr), MediaKeyMessageType::License_request,
    519      nsTArray<uint8_t>(reinterpret_cast<const uint8_t*>(keyRequest),
    520                        keyRequestSize)));
    521  return IPC_OK();
    522 }
    523 
    524 mozilla::ipc::IPCResult MediaDrmRemoteCDMParent::RecvLoadSession(
    525    const RemoteCDMLoadSessionRequestIPDL& aRequest,
    526    LoadSessionResolver&& aResolver) {
    527  EME_LOG("[%p] MediaDrmRemoteCDMParent::RecvLoadSession", this);
    528  aResolver(MediaResult(NS_ERROR_NOT_IMPLEMENTED));
    529  return IPC_OK();
    530 }
    531 
    532 mozilla::ipc::IPCResult MediaDrmRemoteCDMParent::RecvUpdateSession(
    533    const RemoteCDMUpdateSessionRequestIPDL& aRequest,
    534    UpdateSessionResolver&& aResolver) {
    535  EME_LOG("[%p] MediaDrmRemoteCDMParent::RecvUpdateSession", this);
    536 
    537  const auto i = mSessions.find(aRequest.sessionId());
    538  if (i == mSessions.end()) {
    539    aResolver(
    540        MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, "Invalid session id"_ns));
    541    return IPC_OK();
    542  }
    543 
    544  MOZ_ASSERT(mDrm);
    545  MOZ_ASSERT(HasCrypto());
    546 
    547  AMediaDrmKeySetId keySetId{};
    548  media_status_t status = AMediaDrm_provideKeyResponse(
    549      mDrm, &i->second.id, aRequest.response().Elements(),
    550      aRequest.response().Length(), &keySetId);
    551  if (NS_WARN_IF(status != AMEDIA_OK)) {
    552    aResolver(MediaResult(
    553        NS_ERROR_DOM_INVALID_STATE_ERR,
    554        RESULT_DETAIL("AMediaDrm_provideKeyResponse failed %d", status)));
    555    return IPC_OK();
    556  }
    557 
    558  aResolver(MediaResult(NS_OK));
    559  return IPC_OK();
    560 }
    561 
    562 mozilla::ipc::IPCResult MediaDrmRemoteCDMParent::RecvRemoveSession(
    563    const nsString& aSessionId, RemoveSessionResolver&& aResolver) {
    564  EME_LOG("[%p] MediaDrmRemoteCDMParent::RecvRemoveSession", this);
    565  aResolver(MediaResult(NS_ERROR_NOT_IMPLEMENTED));
    566  return IPC_OK();
    567 }
    568 
    569 mozilla::ipc::IPCResult MediaDrmRemoteCDMParent::RecvCloseSession(
    570    const nsString& aSessionId, CloseSessionResolver&& aResolver) {
    571  const auto i = mSessions.find(aSessionId);
    572  if (i == mSessions.end()) {
    573    EME_LOG("[%p] MediaDrmRemoteCDMParent::RecvCloseSession -- invalid session",
    574            this);
    575    aResolver(
    576        MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, "Invalid session id"_ns));
    577    return IPC_OK();
    578  }
    579 
    580  MOZ_ASSERT(mDrm);
    581  MOZ_ASSERT(HasCrypto());
    582 
    583  EME_LOG("[%p] MediaDrmRemoteCDMParent::RecvCloseSession -- closeSession",
    584          this);
    585  media_status_t status = AMediaDrm_closeSession(mDrm, &i->second.id);
    586  EME_LOG("[%p] MediaDrmRemoteCDMParent::RecvCloseSession -- status %d", this,
    587          status);
    588  if (NS_WARN_IF(status != AMEDIA_OK)) {
    589    aResolver(
    590        MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR,
    591                    RESULT_DETAIL("AMediaDrm_closeSession failed %d", status)));
    592  } else {
    593    aResolver(MediaResult(NS_OK));
    594  }
    595 
    596  mSessions.erase(i);
    597  return IPC_OK();
    598 }
    599 
    600 mozilla::ipc::IPCResult MediaDrmRemoteCDMParent::RecvSetServerCertificate(
    601    mozilla::Span<uint8_t const> aCertificate,
    602    SetServerCertificateResolver&& aResolver) {
    603  if (NS_WARN_IF(!mDrm)) {
    604    EME_LOG(
    605        "[%p] MediaDrmRemoteCDMParent::RecvSetServerCertificate -- not init",
    606        this);
    607    aResolver(
    608        MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, "Missing AMediaDrm"_ns));
    609    return IPC_OK();
    610  }
    611 
    612  EME_LOG(
    613      "[%p] MediaDrmRemoteCDMParent::RecvSetServerCertificate -- "
    614      "setPropertyByteArray",
    615      this);
    616  media_status_t status = AMediaDrm_setPropertyByteArray(
    617      mDrm, "certificate", aCertificate.Elements(), aCertificate.Length());
    618  EME_LOG("[%p] MediaDrmRemoteCDMParent::RecvSetServerCertificate -- status %d",
    619          this, status);
    620  if (NS_WARN_IF(status != AMEDIA_OK)) {
    621    aResolver(MediaResult(
    622        NS_ERROR_DOM_INVALID_STATE_ERR,
    623        RESULT_DETAIL("AMediaDrm_setPropertyByteArray certificate failed %d",
    624                      status)));
    625    return IPC_OK();
    626  }
    627 
    628  aResolver(MediaResult(NS_OK));
    629  return IPC_OK();
    630 }
    631 
    632 void MediaDrmRemoteCDMParent::HandleEvent(nsString&& aSessionId,
    633                                          AMediaDrmEventType aEventType,
    634                                          int aExtra,
    635                                          nsTArray<uint8_t>&& aData) {
    636  const auto i = mSessions.find(aSessionId);
    637  if (i == mSessions.end()) {
    638    EME_LOG("[%p] MediaDrmRemoteCDMParent::HandleEvent -- session not found",
    639            this);
    640    return;
    641  }
    642 
    643  EME_LOG("[%p] MediaDrmRemoteCDMParent::HandleEvent -- event %d", this,
    644          aEventType);
    645  MOZ_ASSERT(mDrm);
    646 
    647  switch (aEventType) {
    648    case AMediaDrmEventType::EVENT_PROVISION_REQUIRED:
    649      EnsureProvisioned();
    650      break;
    651    case AMediaDrmEventType::EVENT_KEY_REQUIRED: {
    652      const uint8_t* keyRequest = nullptr;
    653      size_t keyRequestSize = 0;
    654      AMediaDrmKeyRequestType keyRequestType =
    655          AMediaDrmKeyRequestType::KEY_REQUEST_TYPE_INITIAL;
    656      media_status_t status;
    657 
    658      if (__builtin_available(android 33, *)) {
    659        status = AMediaDrm_getKeyRequestWithDefaultUrlAndType(
    660            mDrm, &i->second.id, aData.Elements(), aData.Length(),
    661            i->second.mimeType.get(), AMediaDrmKeyType::KEY_TYPE_STREAMING,
    662            nullptr, 0, &keyRequest, &keyRequestSize, nullptr, &keyRequestType);
    663      } else {
    664        status = AMediaDrm_getKeyRequest(
    665            mDrm, &i->second.id, aData.Elements(), aData.Length(),
    666            i->second.mimeType.get(), AMediaDrmKeyType::KEY_TYPE_STREAMING,
    667            nullptr, 0, &keyRequest, &keyRequestSize);
    668      }
    669 
    670      if (status == AMEDIA_DRM_NOT_PROVISIONED) {
    671        EnsureProvisioned()->Then(
    672            GetCurrentSerialEventTarget(), __func__,
    673            [self = RefPtr{this}, sessionId = std::move(aSessionId),
    674             data = std::move(aData), aEventType, aExtra](
    675                const InternalPromise::ResolveOrRejectValue& aValue) mutable {
    676              if (aValue.IsReject()) {
    677                return;
    678              }
    679 
    680              self->HandleEvent(std::move(sessionId), aEventType, aExtra,
    681                                std::move(data));
    682            });
    683        return;
    684      }
    685 
    686      if (NS_WARN_IF(status != AMEDIA_OK)) {
    687        return;
    688      }
    689 
    690      dom::MediaKeyMessageType keyMessageType;
    691      switch (keyRequestType) {
    692        case AMediaDrmKeyRequestType::KEY_REQUEST_TYPE_NONE:
    693          // Already have what we need.
    694          return;
    695        case AMediaDrmKeyRequestType::KEY_REQUEST_TYPE_RELEASE:
    696          keyMessageType = MediaKeyMessageType::License_release;
    697          break;
    698        case AMediaDrmKeyRequestType::KEY_REQUEST_TYPE_RENEWAL:
    699          keyMessageType = MediaKeyMessageType::License_renewal;
    700          break;
    701        case AMediaDrmKeyRequestType::KEY_REQUEST_TYPE_UPDATE:
    702          // Not directly equivalent but needs an additional license request.
    703          keyMessageType = MediaKeyMessageType::License_request;
    704          break;
    705        default:
    706          MOZ_FALLTHROUGH_ASSERT("Unhandled AMediaDrmKeyRequestType");
    707        case AMediaDrmKeyRequestType::KEY_REQUEST_TYPE_INITIAL:
    708          keyMessageType = MediaKeyMessageType::License_request;
    709          break;
    710      }
    711 
    712      (void)SendOnSessionKeyMessage(RemoteCDMKeyMessageIPDL(
    713          std::move(aSessionId), keyMessageType,
    714          nsTArray<uint8_t>(reinterpret_cast<const uint8_t*>(keyRequest),
    715                            keyRequestSize)));
    716    } break;
    717    case AMediaDrmEventType::EVENT_KEY_EXPIRED:
    718      break;
    719    case AMediaDrmEventType::EVENT_VENDOR_DEFINED:
    720      break;
    721    case AMediaDrmEventType::EVENT_SESSION_RECLAIMED:
    722      break;
    723    default:
    724      break;
    725  }
    726 }
    727 
    728 void MediaDrmRemoteCDMParent::HandleExpirationUpdate(nsString&& aSessionId,
    729                                                     int64_t aExpiryTimeInMS) {
    730  const auto i = mSessions.find(aSessionId);
    731  if (i == mSessions.end()) {
    732    EME_LOG(
    733        "[%p] MediaDrmRemoteCDMParent::HandleExpirationUpdate -- session not "
    734        "found",
    735        this);
    736    return;
    737  }
    738 
    739  EME_LOG("[%p] MediaDrmRemoteCDMParent::HandleExpirationUpdate", this);
    740  (void)SendOnSessionKeyExpiration(RemoteCDMKeyExpirationIPDL(
    741      std::move(aSessionId), static_cast<double>(aExpiryTimeInMS)));
    742 }
    743 
    744 void MediaDrmRemoteCDMParent::HandleKeysChange(
    745    nsString&& aSessionId, bool aHasNewUsableKey,
    746    nsTArray<CDMKeyInfo>&& aKeyInfo) {
    747  const auto i = mSessions.find(aSessionId);
    748  if (i == mSessions.end()) {
    749    EME_LOG(
    750        "[%p] MediaDrmRemoteCDMParent::HandleKeysChange -- session not found",
    751        this);
    752    return;
    753  }
    754 
    755  EME_LOG("[%p] MediaDrmRemoteCDMParent::HandleKeysChange", this);
    756  (void)SendOnSessionKeyStatus(
    757      RemoteCDMKeyStatusIPDL(std::move(aSessionId), std::move(aKeyInfo)));
    758 }
    759 
    760 already_AddRefed<MediaDrmCryptoInfo> MediaDrmRemoteCDMParent::CreateCryptoInfo(
    761    MediaRawData* aSample) {
    762  MOZ_ASSERT(mDrm);
    763 
    764  if (NS_WARN_IF(!aSample)) {
    765    return nullptr;
    766  }
    767 
    768  const CryptoSample& cryptoObj = aSample->mCrypto;
    769  if (!cryptoObj.IsEncrypted()) {
    770    return nullptr;
    771  }
    772 
    773  uint32_t numSubSamples = std::min<uint32_t>(
    774      cryptoObj.mPlainSizes.Length(), cryptoObj.mEncryptedSizes.Length());
    775  MOZ_ASSERT(numSubSamples <= INT32_MAX);
    776 
    777  // Deep copy the plain and encrypted sizes so we can modify them.
    778  nsTArray<size_t> plainSizes(cryptoObj.mPlainSizes.Length());
    779  nsTArray<size_t> encryptedSizes(cryptoObj.mEncryptedSizes.Length());
    780  if (numSubSamples > 0) {
    781    uint32_t totalSubSamplesSize = 0;
    782    for (const auto& size : cryptoObj.mPlainSizes) {
    783      plainSizes.AppendElement(size);
    784      totalSubSamplesSize += size;
    785    }
    786    for (const auto& size : cryptoObj.mEncryptedSizes) {
    787      encryptedSizes.AppendElement(size);
    788      totalSubSamplesSize += size;
    789    }
    790 
    791    auto codecSpecificDataSize =
    792        CheckedInt<size_t>(aSample->Size()) - totalSubSamplesSize;
    793    if (!codecSpecificDataSize.isValid()) {
    794      MOZ_ASSERT_UNREACHABLE("totalSubSamplesSize greater than sample size");
    795      return nullptr;
    796    }
    797 
    798    // Size of codec specific data("CSD") for Android java::sdk::MediaCodec
    799    // usage should be included in the 1st plain size if it exists.
    800    if (codecSpecificDataSize.value() && !plainSizes.IsEmpty()) {
    801      // This shouldn't overflow as the the plain size should be UINT16_MAX at
    802      // most, and the CSD should never be that large. Checked int acts like a
    803      // diagnostic assert here to help catch if we ever have insane inputs.
    804      auto newLeadingPlainSize = codecSpecificDataSize + plainSizes[0];
    805      if (!newLeadingPlainSize.isValid()) {
    806        MOZ_ASSERT_UNREACHABLE("newLeadingPlainSize overflowed");
    807        return nullptr;
    808      }
    809      plainSizes[0] = newLeadingPlainSize.value();
    810    }
    811  } else {
    812    // MediaCodec expects us to provide at least one subsample. Whole-block full
    813    // sample encryption does not carry subsample information, so we need to
    814    // synthesize it by creating a subsample as big as the sample itself. See
    815    // bug 1759936.
    816    numSubSamples = 1;
    817    plainSizes.AppendElement(0);
    818    encryptedSizes.AppendElement(aSample->Size());
    819  }
    820 
    821  const CopyableTArray<uint8_t>* srcIV;
    822  cryptoinfo_mode_t mode;
    823  switch (cryptoObj.mCryptoScheme) {
    824    case CryptoScheme::None:
    825      mode = AMEDIACODECRYPTOINFO_MODE_CLEAR;
    826      srcIV = &cryptoObj.mIV;
    827      break;
    828    case CryptoScheme::Cenc:
    829      mode = AMEDIACODECRYPTOINFO_MODE_AES_CTR;
    830      srcIV = &cryptoObj.mIV;
    831      break;
    832    case CryptoScheme::Cbcs:
    833    case CryptoScheme::Cbcs_1_9:
    834      mode = AMEDIACODECRYPTOINFO_MODE_AES_CBC;
    835      srcIV = &cryptoObj.mConstantIV;
    836      break;
    837    default:
    838      MOZ_ASSERT_UNREACHABLE("Unhandled CryptoScheme!");
    839      return nullptr;
    840  }
    841 
    842  uint8_t key[16] = {};
    843  uint8_t iv[16] = {};
    844 
    845  if (NS_WARN_IF(srcIV->Length() > sizeof(iv))) {
    846    MOZ_ASSERT_UNREACHABLE("IV too big for Android!");
    847    return nullptr;
    848  }
    849 
    850  if (NS_WARN_IF(cryptoObj.mKeyId.Length() > sizeof(key))) {
    851    MOZ_ASSERT_UNREACHABLE("Key too big for Android!");
    852    return nullptr;
    853  }
    854 
    855  if (!srcIV->IsEmpty()) {
    856    memcpy(iv, srcIV->Elements(), srcIV->Length());
    857  }
    858 
    859  if (!cryptoObj.mKeyId.IsEmpty()) {
    860    memcpy(key, cryptoObj.mKeyId.Elements(), cryptoObj.mKeyId.Length());
    861  }
    862 
    863  AMediaCodecCryptoInfo* cryptoInfo = AMediaCodecCryptoInfo_new(
    864      static_cast<int32_t>(numSubSamples), key, iv, mode, plainSizes.Elements(),
    865      encryptedSizes.Elements());
    866  if (NS_WARN_IF(!cryptoInfo)) {
    867    MOZ_ASSERT_UNREACHABLE("Failed to create AMediaCodecCryptoInfo");
    868    return nullptr;
    869  }
    870 
    871  if (mode == AMEDIACODECRYPTOINFO_MODE_AES_CBC) {
    872    cryptoinfo_pattern_t pattern = {};
    873    pattern.encryptBlocks = cryptoObj.mCryptByteBlock;
    874    pattern.skipBlocks = cryptoObj.mSkipByteBlock;
    875    AMediaCodecCryptoInfo_setPattern(cryptoInfo, &pattern);
    876  }
    877 
    878  return MakeAndAddRef<MediaDrmCryptoInfo>(cryptoInfo);
    879 }
    880 
    881 MediaDrmCrypto::~MediaDrmCrypto() { AMediaCrypto_delete(mCrypto); }
    882 
    883 MediaDrmCryptoInfo::~MediaDrmCryptoInfo() {
    884  AMediaCodecCryptoInfo_delete(mCryptoInfo);
    885 }
    886 
    887 }  // namespace mozilla