RemoteCDMChild.cpp (19865B)
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 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "RemoteCDMChild.h" 8 9 #include "mozilla/RemoteDecodeUtils.h" 10 #include "mozilla/dom/MediaKeySession.h" 11 12 #ifdef MOZ_WIDGET_ANDROID 13 # include "mozilla/MediaDrmProvisioningHelper.h" 14 #endif 15 16 namespace mozilla { 17 18 #define LOGD(msg, ...) \ 19 MOZ_LOG_FMT(gRemoteDecodeLog, LogLevel::Debug, msg, ##__VA_ARGS__) 20 21 RemoteCDMChild::RemoteCDMChild( 22 nsCOMPtr<nsISerialEventTarget>&& aThread, 23 RefPtr<GenericNonExclusivePromise>&& aIPDLPromise, RemoteMediaIn aLocation, 24 dom::MediaKeys* aKeys, const nsAString& aKeySystem, 25 bool aDistinctiveIdentifierRequired, bool aPersistentStateRequired) 26 : CDMProxy(aKeys, aKeySystem, aDistinctiveIdentifierRequired, 27 aPersistentStateRequired), 28 mThread(std::move(aThread)), 29 mIPDLPromise(std::move(aIPDLPromise)), 30 mLocation(aLocation) {} 31 32 RemoteCDMChild::~RemoteCDMChild() = default; 33 34 void RemoteCDMChild::ActorDestroy(ActorDestroyReason aWhy) { 35 LOGD("[{}] RemoteCDMChild::ActorDestroy", fmt::ptr(this)); 36 mNeedsShutdown = false; 37 } 38 39 mozilla::ipc::IPCResult RemoteCDMChild::RecvProvision( 40 const RemoteCDMProvisionRequestIPDL& aRequest, 41 ProvisionResolver&& aResolver) { 42 LOGD("[{}] RemoteCDMChild::RecvProvision", fmt::ptr(this)); 43 #ifdef MOZ_WIDGET_ANDROID 44 auto helper = 45 MakeRefPtr<MediaDrmProvisioningHelper>(aRequest, std::move(aResolver)); 46 helper->Provision(); 47 #else 48 aResolver(MediaResult(NS_ERROR_DOM_MEDIA_NOT_SUPPORTED_ERR)); 49 #endif 50 return IPC_OK(); 51 } 52 53 mozilla::ipc::IPCResult RemoteCDMChild::RecvOnSessionKeyStatus( 54 const RemoteCDMKeyStatusIPDL& aMsg) { 55 LOGD("[{}] RemoteCDMChild::RecvOnSessionKeyStatus", fmt::ptr(this)); 56 bool changed = false; 57 { 58 auto caps = mCapabilites.Lock(); 59 for (const auto& keyInfo : aMsg.keyInfo()) { 60 changed |= 61 caps->SetKeyStatus(keyInfo.mKeyId, aMsg.sessionId(), keyInfo.mStatus); 62 } 63 } 64 65 if (!changed) { 66 return IPC_OK(); 67 } 68 69 NS_DispatchToMainThread(NS_NewRunnableFunction( 70 __func__, [self = RefPtr{this}, sessionId = aMsg.sessionId()]() { 71 if (self->mKeys.IsNull()) { 72 return; 73 } 74 if (RefPtr<dom::MediaKeySession> session = 75 self->mKeys->GetSession(sessionId)) { 76 session->DispatchKeyStatusesChange(); 77 } 78 })); 79 return IPC_OK(); 80 } 81 82 mozilla::ipc::IPCResult RemoteCDMChild::RecvOnSessionKeyExpiration( 83 RemoteCDMKeyExpirationIPDL&& aMsg) { 84 LOGD("[{}] RemoteCDMChild::RecvOnSessionKeyExpiration", fmt::ptr(this)); 85 NS_DispatchToMainThread(NS_NewRunnableFunction( 86 __func__, [self = RefPtr{this}, msg = std::move(aMsg)]() { 87 if (self->mKeys.IsNull()) { 88 return; 89 } 90 if (RefPtr<dom::MediaKeySession> session = 91 self->mKeys->GetSession(msg.sessionId())) { 92 session->SetExpiration(msg.expiredTimeMilliSecondsSinceEpoch()); 93 } 94 })); 95 return IPC_OK(); 96 } 97 98 mozilla::ipc::IPCResult RemoteCDMChild::RecvOnSessionKeyMessage( 99 RemoteCDMKeyMessageIPDL&& aMsg) { 100 LOGD("[{}] RemoteCDMChild::RecvOnSessionKeyMessage", fmt::ptr(this)); 101 NS_DispatchToMainThread(NS_NewRunnableFunction( 102 __func__, [self = RefPtr{this}, msg = std::move(aMsg)]() { 103 if (self->mKeys.IsNull()) { 104 return; 105 } 106 if (RefPtr<dom::MediaKeySession> session = 107 self->mKeys->GetSession(msg.sessionId())) { 108 session->DispatchKeyMessage(msg.type(), msg.message()); 109 } 110 })); 111 return IPC_OK(); 112 } 113 114 void RemoteCDMChild::Init(PromiseId aPromiseId, const nsAString& aOrigin, 115 const nsAString& aTopLevelOrigin, 116 const nsAString& aName) { 117 MOZ_ASSERT(NS_IsMainThread()); 118 119 if (mKeys.IsNull()) { 120 return; 121 } 122 123 LOGD("[{}] RemoteCDMChild::Init -- promise {}", fmt::ptr(this), aPromiseId); 124 if (!mIPDLPromise) { 125 RejectPromise(aPromiseId, 126 MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, 127 "PRemoteCDMChild already initialized"_ns)); 128 return; 129 } 130 131 mIPDLPromise->Then( 132 mThread, __func__, 133 [self = RefPtr{this}, aPromiseId]( 134 const GenericNonExclusivePromise::ResolveOrRejectValue& aValue) { 135 LOGD("[{}] RemoteCDMChild::Init -- promise {} resolved {}", 136 fmt::ptr(self.get()), aPromiseId, aValue.IsResolve()); 137 138 if (aValue.IsReject()) { 139 self->RejectPromise( 140 aPromiseId, 141 MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, 142 "PRemoteCDMChild ensure process fail"_ns)); 143 return; 144 } 145 146 self->InitInternal(aPromiseId); 147 }); 148 mIPDLPromise = nullptr; 149 } 150 151 void RemoteCDMChild::InitInternal(PromiseId aPromiseId) { 152 LOGD("[{}] RemoteCDMChild::InitInternal -- promise {}", fmt::ptr(this), 153 aPromiseId); 154 RefPtr<RemoteMediaManagerChild> manager = 155 RemoteMediaManagerChild::GetSingleton(mLocation); 156 if (!manager) { 157 RejectPromise(aPromiseId, 158 MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, 159 "PRemoteCDMChild manager is not available"_ns)); 160 return; 161 } 162 163 LOGD("[{}] RemoteCDMChild::InitInternal -- send constructor", fmt::ptr(this)); 164 if (!manager->SendPRemoteCDMConstructor(this, mKeySystem)) { 165 RejectPromise(aPromiseId, 166 MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, 167 "PRemoteCDMChild manager is unable to send"_ns)); 168 return; 169 } 170 171 LOGD("[{}] RemoteCDMChild::InitInternal -- send init", fmt::ptr(this)); 172 SendInit(RemoteCDMInitRequestIPDL(mDistinctiveIdentifierRequired, 173 mPersistentStateRequired)) 174 ->Then( 175 GetMainThreadSerialEventTarget(), __func__, 176 [self = RefPtr{this}, 177 aPromiseId](const InitPromise::ResolveOrRejectValue& aValue) { 178 LOGD("[{}] RemoteCDMChild::InitInternal -- promise {} resolved {}", 179 fmt::ptr(self.get()), aPromiseId, aValue.IsResolve()); 180 181 if (self->mKeys.IsNull()) { 182 return; 183 } 184 185 if (aValue.IsReject()) { 186 self->RejectPromise( 187 aPromiseId, 188 MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, 189 "PRemoteCDMChild::SendInit IPC fail"_ns)); 190 return; 191 } 192 193 self->mKeys->OnCDMCreated(aPromiseId, 0); 194 }); 195 } 196 197 void RemoteCDMChild::CreateSession(uint32_t aCreateSessionToken, 198 MediaKeySessionType aSessionType, 199 PromiseId aPromiseId, 200 const nsAString& aInitDataType, 201 nsTArray<uint8_t>& aInitData) { 202 MOZ_ALWAYS_SUCCEEDS(mThread->Dispatch(NS_NewRunnableFunction( 203 __func__, [self = RefPtr{this}, aCreateSessionToken, aSessionType, 204 aPromiseId, initDataType = nsString(aInitDataType), 205 initData = std::move(aInitData)]() mutable { 206 LOGD("[{}] RemoteCDMChild::CreateSession -- promise {}", 207 fmt::ptr(self.get()), aPromiseId); 208 self 209 ->SendCreateSession(RemoteCDMCreateSessionRequestIPDL( 210 aSessionType, std::move(initDataType), std::move(initData))) 211 ->Then( 212 GetMainThreadSerialEventTarget(), __func__, 213 [self, aCreateSessionToken, aPromiseId]( 214 const CreateSessionPromise::ResolveOrRejectValue& aValue) { 215 if (self->mKeys.IsNull()) { 216 return; 217 } 218 219 if (aValue.IsReject()) { 220 self->RejectPromise( 221 aPromiseId, 222 MediaResult( 223 NS_ERROR_DOM_INVALID_STATE_ERR, 224 "PRemoteCDMChild::SendCreateSession IPC fail"_ns)); 225 return; 226 } 227 228 const auto& response = aValue.ResolveValue(); 229 if (response.type() == 230 RemoteCDMSessionResponseIPDL::TMediaResult) { 231 self->RejectPromise(aPromiseId, response.get_MediaResult()); 232 return; 233 } 234 235 const auto& msg = response.get_RemoteCDMKeyMessageIPDL(); 236 const auto& sessionId = msg.sessionId(); 237 if (RefPtr<dom::MediaKeySession> session = 238 self->mKeys->GetPendingSession(aCreateSessionToken)) { 239 session->SetSessionId(sessionId); 240 session->DispatchKeyMessage(msg.type(), msg.message()); 241 } 242 243 self->ResolvePromise(aPromiseId); 244 }); 245 }))); 246 } 247 248 void RemoteCDMChild::LoadSession(PromiseId aPromiseId, 249 dom::MediaKeySessionType aSessionType, 250 const nsAString& aSessionId) { 251 MOZ_ALWAYS_SUCCEEDS(mThread->Dispatch(NS_NewRunnableFunction( 252 __func__, [self = RefPtr{this}, aPromiseId, aSessionType, 253 sessionId = nsString(aSessionId)]() mutable { 254 LOGD("[{}] RemoteCDMChild::LoadSession -- promise {}", 255 fmt::ptr(self.get()), aPromiseId); 256 self->SendLoadSession(RemoteCDMLoadSessionRequestIPDL( 257 aSessionType, std::move(sessionId))) 258 ->Then(GetMainThreadSerialEventTarget(), __func__, 259 [self, aPromiseId]( 260 const LoadSessionPromise::ResolveOrRejectValue& aValue) { 261 if (self->mKeys.IsNull()) { 262 return; 263 } 264 265 self->mKeys->OnSessionLoaded( 266 aPromiseId, aValue.IsResolve() && 267 NS_SUCCEEDED(aValue.ResolveValue())); 268 }); 269 }))); 270 } 271 272 void RemoteCDMChild::SetServerCertificate(PromiseId aPromiseId, 273 nsTArray<uint8_t>& aCert) { 274 MOZ_ALWAYS_SUCCEEDS(mThread->Dispatch(NS_NewRunnableFunction( 275 __func__, 276 [self = RefPtr{this}, aPromiseId, cert = std::move(aCert)]() mutable { 277 LOGD("[{}] RemoteCDMChild::SetServerCertificate -- promise {}", 278 fmt::ptr(self.get()), aPromiseId); 279 self->SendSetServerCertificate(std::move(cert)) 280 ->Then( 281 GetMainThreadSerialEventTarget(), __func__, 282 [self, aPromiseId]( 283 const SetServerCertificatePromise::ResolveOrRejectValue& 284 aValue) { 285 if (self->mKeys.IsNull()) { 286 return; 287 } 288 289 if (aValue.IsReject()) { 290 self->RejectPromise( 291 aPromiseId, 292 MediaResult( 293 NS_ERROR_DOM_INVALID_STATE_ERR, 294 "PRemoteCDMChild::SendSetServerCertificate IPC fail"_ns)); 295 return; 296 } 297 298 self->ResolveOrRejectPromise(aPromiseId, 299 aValue.ResolveValue()); 300 }); 301 }))); 302 } 303 304 void RemoteCDMChild::UpdateSession(const nsAString& aSessionId, 305 PromiseId aPromiseId, 306 nsTArray<uint8_t>& aResponse) { 307 MOZ_ALWAYS_SUCCEEDS(mThread->Dispatch(NS_NewRunnableFunction( 308 __func__, [self = RefPtr{this}, sessionId = nsString(aSessionId), 309 aPromiseId, response = std::move(aResponse)]() mutable { 310 LOGD("[{}] RemoteCDMChild::UpdateSession -- promise {}", 311 fmt::ptr(self.get()), aPromiseId); 312 self->SendUpdateSession(RemoteCDMUpdateSessionRequestIPDL( 313 std::move(sessionId), std::move(response))) 314 ->Then( 315 GetMainThreadSerialEventTarget(), __func__, 316 [self, aPromiseId]( 317 const UpdateSessionPromise::ResolveOrRejectValue& aValue) { 318 if (self->mKeys.IsNull()) { 319 return; 320 } 321 322 if (aValue.IsReject()) { 323 self->RejectPromise( 324 aPromiseId, 325 MediaResult( 326 NS_ERROR_DOM_INVALID_STATE_ERR, 327 "PRemoteCDMChild::SendUpdateSession IPC fail"_ns)); 328 return; 329 } 330 331 self->ResolveOrRejectPromise(aPromiseId, 332 aValue.ResolveValue()); 333 }); 334 }))); 335 } 336 337 void RemoteCDMChild::CloseSession(const nsAString& aSessionId, 338 PromiseId aPromiseId) { 339 MOZ_ALWAYS_SUCCEEDS(mThread->Dispatch(NS_NewRunnableFunction( 340 __func__, [self = RefPtr{this}, sessionId = nsString(aSessionId), 341 aPromiseId]() mutable { 342 LOGD("[{}] RemoteCDMChild::CloseSession -- promise {}", 343 fmt::ptr(self.get()), aPromiseId); 344 self->SendCloseSession(std::move(sessionId)) 345 ->Then( 346 GetMainThreadSerialEventTarget(), __func__, 347 [self, aPromiseId]( 348 const CloseSessionPromise::ResolveOrRejectValue& aValue) { 349 if (self->mKeys.IsNull()) { 350 return; 351 } 352 353 if (aValue.IsReject()) { 354 self->RejectPromise( 355 aPromiseId, 356 MediaResult( 357 NS_ERROR_DOM_INVALID_STATE_ERR, 358 "PRemoteCDMChild::SendCloseSession IPC fail"_ns)); 359 return; 360 } 361 362 self->ResolveOrRejectPromise(aPromiseId, 363 aValue.ResolveValue()); 364 }); 365 }))); 366 } 367 368 void RemoteCDMChild::RemoveSession(const nsAString& aSessionId, 369 PromiseId aPromiseId) { 370 MOZ_ALWAYS_SUCCEEDS(mThread->Dispatch(NS_NewRunnableFunction( 371 __func__, [self = RefPtr{this}, sessionId = nsString(aSessionId), 372 aPromiseId]() mutable { 373 LOGD("[{}] RemoteCDMChild::RemoveSession -- promise {}", 374 fmt::ptr(self.get()), aPromiseId); 375 self->SendRemoveSession(std::move(sessionId)) 376 ->Then( 377 GetMainThreadSerialEventTarget(), __func__, 378 [self, aPromiseId]( 379 const RemoveSessionPromise::ResolveOrRejectValue& aValue) { 380 if (self->mKeys.IsNull()) { 381 return; 382 } 383 384 if (aValue.IsReject()) { 385 self->RejectPromise( 386 aPromiseId, 387 MediaResult( 388 NS_ERROR_DOM_INVALID_STATE_ERR, 389 "PRemoteCDMChild::SendRemoveSession IPC fail"_ns)); 390 return; 391 } 392 393 self->ResolveOrRejectPromise(aPromiseId, 394 aValue.ResolveValue()); 395 }); 396 }))); 397 } 398 399 void RemoteCDMChild::QueryOutputProtectionStatus() {} 400 401 void RemoteCDMChild::NotifyOutputProtectionStatus( 402 OutputProtectionCheckStatus aCheckStatus, 403 OutputProtectionCaptureStatus aCaptureStatus) {} 404 405 void RemoteCDMChild::Shutdown() { 406 LOGD("[{}] RemoteCDMChild::Shutdown", fmt::ptr(this)); 407 // If this is the last reference, and we still have an actor, then we know 408 // that the last reference is solely due to the IPDL reference. Dispatch to 409 // the owning thread to delete that so that we can clean up. 410 // 411 // It is an atomic because ActorDestroy will be called on the IPDL / manager 412 // thread, and Shutdown is likely called on the main thread. 413 if (mNeedsShutdown.exchange(false)) { 414 mThread->Dispatch(NS_NewRunnableFunction(__func__, [self = RefPtr{this}]() { 415 if (self->CanSend()) { 416 self->Send__delete__(self); 417 } 418 })); 419 } 420 } 421 422 void RemoteCDMChild::Terminated() { 423 MOZ_ASSERT_UNREACHABLE("Unexpected to be called!"); 424 } 425 426 void RemoteCDMChild::OnSetSessionId(uint32_t aCreateSessionToken, 427 const nsAString& aSessionId) { 428 MOZ_ASSERT_UNREACHABLE("Unexpected to be called!"); 429 } 430 431 void RemoteCDMChild::OnResolveLoadSessionPromise(uint32_t aPromiseId, 432 bool aSuccess) { 433 MOZ_ASSERT_UNREACHABLE("Unexpected to be called!"); 434 } 435 436 void RemoteCDMChild::OnSessionMessage(const nsAString& aSessionId, 437 dom::MediaKeyMessageType aMessageType, 438 const nsTArray<uint8_t>& aMessage) { 439 MOZ_ASSERT_UNREACHABLE("Unexpected to be called!"); 440 } 441 442 void RemoteCDMChild::OnExpirationChange(const nsAString& aSessionId, 443 UnixTime aExpiryTime) { 444 MOZ_ASSERT_UNREACHABLE("Unexpected to be called!"); 445 } 446 447 void RemoteCDMChild::OnSessionClosed(const nsAString& aSessionId, 448 dom::MediaKeySessionClosedReason aReason) { 449 MOZ_ASSERT_UNREACHABLE("Unexpected to be called!"); 450 } 451 452 void RemoteCDMChild::OnSessionError(const nsAString& aSessionId, 453 nsresult aException, uint32_t aSystemCode, 454 const nsAString& aMsg) { 455 MOZ_ASSERT_UNREACHABLE("Unexpected to be called!"); 456 } 457 458 void RemoteCDMChild::OnRejectPromise(uint32_t aPromiseId, 459 ErrorResult&& aException, 460 const nsCString& aMsg) { 461 MOZ_ASSERT_UNREACHABLE("Unexpected to be called!"); 462 } 463 464 RefPtr<DecryptPromise> RemoteCDMChild::Decrypt(MediaRawData* aSample) { 465 MOZ_ASSERT_UNREACHABLE("Unexpected to be called!"); 466 return nullptr; 467 } 468 469 void RemoteCDMChild::OnDecrypted(uint32_t aId, DecryptStatus aResult, 470 const nsTArray<uint8_t>& aDecryptedData) { 471 MOZ_ASSERT_UNREACHABLE("Unexpected to be called!"); 472 } 473 474 void RemoteCDMChild::RejectPromise(PromiseId aId, ErrorResult&& aException, 475 const nsCString& aReason) { 476 LOGD("[{}] RemoteCDMChild::RejectPromise -- {}", fmt::ptr(this), aId); 477 MOZ_ASSERT(NS_IsMainThread()); 478 MOZ_ASSERT(!mKeys.IsNull()); 479 mKeys->RejectPromise(aId, std::move(aException), aReason); 480 } 481 482 void RemoteCDMChild::ResolvePromise(PromiseId aId) { 483 LOGD("[{}] RemoteCDMChild::ResolvePromise -- {}", fmt::ptr(this), aId); 484 MOZ_ASSERT(NS_IsMainThread()); 485 MOZ_ASSERT(!mKeys.IsNull()); 486 mKeys->ResolvePromise(aId); 487 } 488 489 void RemoteCDMChild::RejectPromise(PromiseId aId, const MediaResult& aResult) { 490 MOZ_ASSERT(NS_FAILED(aResult.Code())); 491 492 ErrorResult rv; 493 aResult.ThrowTo(rv); 494 RejectPromise(aId, std::move(rv), aResult.Message()); 495 } 496 497 void RemoteCDMChild::ResolveOrRejectPromise(PromiseId aId, 498 const MediaResult& aResult) { 499 if (aResult.Code() == NS_OK) { 500 ResolvePromise(aId); 501 return; 502 } 503 504 RejectPromise(aId, aResult); 505 } 506 507 void RemoteCDMChild::OnKeyStatusesChange(const nsAString& aSessionId) {} 508 509 void RemoteCDMChild::GetStatusForPolicy( 510 PromiseId aPromiseId, const dom::HDCPVersion& aMinHdcpVersion) { 511 RejectPromise( 512 aPromiseId, 513 MediaResult(NS_ERROR_DOM_MEDIA_NOT_SUPPORTED_ERR, 514 "Currently Fennec does not support GetStatusForPolicy")); 515 } 516 517 #ifdef DEBUG 518 bool RemoteCDMChild::IsOnOwnerThread() { return mThread->IsOnCurrentThread(); } 519 #endif 520 521 #undef LOGD 522 523 } // namespace mozilla