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