MediaDrmCDMProxy.cpp (15245B)
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 "mozilla/MediaDrmCDMProxy.h" 8 9 #include "MediaDrmCDMCallbackProxy.h" 10 #include "mozilla/dom/MediaKeySession.h" 11 12 namespace mozilla { 13 14 MediaDrmSessionType ToMediaDrmSessionType( 15 dom::MediaKeySessionType aSessionType) { 16 switch (aSessionType) { 17 case dom::MediaKeySessionType::Temporary: 18 return kKeyStreaming; 19 case dom::MediaKeySessionType::Persistent_license: 20 return kKeyOffline; 21 default: 22 return kKeyStreaming; 23 }; 24 } 25 26 MediaDrmCDMProxy::MediaDrmCDMProxy(dom::MediaKeys* aKeys, 27 const nsAString& aKeySystem, 28 bool aDistinctiveIdentifierRequired, 29 bool aPersistentStateRequired) 30 : CDMProxy(aKeys, aKeySystem, aDistinctiveIdentifierRequired, 31 aPersistentStateRequired), 32 mCDM(nullptr), 33 mShutdownCalled(false) { 34 MOZ_ASSERT(NS_IsMainThread()); 35 MOZ_COUNT_CTOR(MediaDrmCDMProxy); 36 } 37 38 MediaDrmCDMProxy::~MediaDrmCDMProxy() { MOZ_COUNT_DTOR(MediaDrmCDMProxy); } 39 40 void MediaDrmCDMProxy::Init(PromiseId aPromiseId, const nsAString& aOrigin, 41 const nsAString& aTopLevelOrigin, 42 const nsAString& aName) { 43 MOZ_ASSERT(NS_IsMainThread()); 44 NS_ENSURE_TRUE_VOID(!mKeys.IsNull()); 45 46 EME_LOG("MediaDrmCDMProxy::Init (%s, %s) %s", 47 NS_ConvertUTF16toUTF8(aOrigin).get(), 48 NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(), 49 NS_ConvertUTF16toUTF8(aName).get()); 50 51 // Create a thread to work with cdm. 52 if (!mOwnerThread) { 53 nsresult rv = 54 NS_NewNamedThread("MDCDMThread", getter_AddRefs(mOwnerThread)); 55 if (NS_FAILED(rv)) { 56 RejectPromiseWithStateError( 57 aPromiseId, nsLiteralCString( 58 "Couldn't create CDM thread MediaDrmCDMProxy::Init")); 59 return; 60 } 61 } 62 63 mCDM = mozilla::MakeUnique<MediaDrmProxySupport>(mKeySystem); 64 nsCOMPtr<nsIRunnable> task( 65 NewRunnableMethod<uint32_t>("MediaDrmCDMProxy::md_Init", this, 66 &MediaDrmCDMProxy::md_Init, aPromiseId)); 67 mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL); 68 } 69 70 void MediaDrmCDMProxy::CreateSession(uint32_t aCreateSessionToken, 71 dom::MediaKeySessionType aSessionType, 72 PromiseId aPromiseId, 73 const nsAString& aInitDataType, 74 nsTArray<uint8_t>& aInitData) { 75 MOZ_ASSERT(NS_IsMainThread()); 76 MOZ_ASSERT(mOwnerThread); 77 78 UniquePtr<CreateSessionData> data(new CreateSessionData()); 79 data->mSessionType = aSessionType; 80 data->mCreateSessionToken = aCreateSessionToken; 81 data->mPromiseId = aPromiseId; 82 data->mInitDataType = NS_ConvertUTF16toUTF8(aInitDataType); 83 data->mInitData = std::move(aInitData); 84 85 nsCOMPtr<nsIRunnable> task(NewRunnableMethod<UniquePtr<CreateSessionData>&&>( 86 "MediaDrmCDMProxy::md_CreateSession", this, 87 &MediaDrmCDMProxy::md_CreateSession, std::move(data))); 88 mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL); 89 } 90 91 void MediaDrmCDMProxy::LoadSession(PromiseId aPromiseId, 92 dom::MediaKeySessionType aSessionType, 93 const nsAString& aSessionId) { 94 // TODO: Implement LoadSession. 95 RejectPromiseWithStateError( 96 aPromiseId, "Currently Fennec does not support LoadSession"_ns); 97 } 98 99 void MediaDrmCDMProxy::SetServerCertificate(PromiseId aPromiseId, 100 nsTArray<uint8_t>& aCert) { 101 MOZ_ASSERT(NS_IsMainThread()); 102 MOZ_ASSERT(mOwnerThread); 103 104 mOwnerThread->Dispatch(NewRunnableMethod<PromiseId, const nsTArray<uint8_t>>( 105 "MediaDrmCDMProxy::md_SetServerCertificate", this, 106 &MediaDrmCDMProxy::md_SetServerCertificate, 107 aPromiseId, std::move(aCert)), 108 NS_DISPATCH_NORMAL); 109 } 110 111 void MediaDrmCDMProxy::UpdateSession(const nsAString& aSessionId, 112 PromiseId aPromiseId, 113 nsTArray<uint8_t>& aResponse) { 114 MOZ_ASSERT(NS_IsMainThread()); 115 MOZ_ASSERT(mOwnerThread); 116 NS_ENSURE_TRUE_VOID(!mKeys.IsNull()); 117 118 UniquePtr<UpdateSessionData> data(new UpdateSessionData()); 119 data->mPromiseId = aPromiseId; 120 data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId); 121 data->mResponse = std::move(aResponse); 122 123 nsCOMPtr<nsIRunnable> task(NewRunnableMethod<UniquePtr<UpdateSessionData>&&>( 124 "MediaDrmCDMProxy::md_UpdateSession", this, 125 &MediaDrmCDMProxy::md_UpdateSession, std::move(data))); 126 mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL); 127 } 128 129 void MediaDrmCDMProxy::CloseSession(const nsAString& aSessionId, 130 PromiseId aPromiseId) { 131 MOZ_ASSERT(NS_IsMainThread()); 132 MOZ_ASSERT(mOwnerThread); 133 NS_ENSURE_TRUE_VOID(!mKeys.IsNull()); 134 135 UniquePtr<SessionOpData> data(new SessionOpData()); 136 data->mPromiseId = aPromiseId; 137 data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId); 138 139 nsCOMPtr<nsIRunnable> task(NewRunnableMethod<UniquePtr<SessionOpData>&&>( 140 "MediaDrmCDMProxy::md_CloseSession", this, 141 &MediaDrmCDMProxy::md_CloseSession, std::move(data))); 142 mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL); 143 } 144 145 void MediaDrmCDMProxy::RemoveSession(const nsAString& aSessionId, 146 PromiseId aPromiseId) { 147 // TODO: Implement RemoveSession. 148 RejectPromiseWithStateError( 149 aPromiseId, "Currently Fennec does not support RemoveSession"_ns); 150 } 151 152 void MediaDrmCDMProxy::QueryOutputProtectionStatus() { 153 // TODO(bryce): determine if this is needed for Android and implement as 154 // needed. See also `NotifyOutputProtectionStatus`. 155 } 156 157 void MediaDrmCDMProxy::NotifyOutputProtectionStatus( 158 OutputProtectionCheckStatus aCheckStatus, 159 OutputProtectionCaptureStatus aCaptureStatus) { 160 // TODO(bryce): determine if this is needed for Android and implement as 161 // needed. See also `QueryOutputProtectionStatus`. 162 } 163 164 void MediaDrmCDMProxy::Shutdown() { 165 MOZ_ASSERT(NS_IsMainThread()); 166 MOZ_ASSERT(mOwnerThread); 167 nsCOMPtr<nsIRunnable> task(NewRunnableMethod( 168 "MediaDrmCDMProxy::md_Shutdown", this, &MediaDrmCDMProxy::md_Shutdown)); 169 170 mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL); 171 mOwnerThread->Shutdown(); 172 mOwnerThread = nullptr; 173 mKeys.Clear(); 174 } 175 176 void MediaDrmCDMProxy::Terminated() { 177 // TODO: Implement Terminated. 178 // Should find a way to handle the case when remote side MediaDrm crashed. 179 } 180 181 void MediaDrmCDMProxy::OnSetSessionId(uint32_t aCreateSessionToken, 182 const nsAString& aSessionId) { 183 MOZ_ASSERT(NS_IsMainThread()); 184 if (mKeys.IsNull()) { 185 return; 186 } 187 188 RefPtr<dom::MediaKeySession> session( 189 mKeys->GetPendingSession(aCreateSessionToken)); 190 if (session) { 191 session->SetSessionId(aSessionId); 192 } 193 } 194 195 void MediaDrmCDMProxy::OnResolveLoadSessionPromise(uint32_t aPromiseId, 196 bool aSuccess) { 197 MOZ_ASSERT(NS_IsMainThread()); 198 if (mKeys.IsNull()) { 199 return; 200 } 201 mKeys->OnSessionLoaded(aPromiseId, aSuccess); 202 } 203 204 void MediaDrmCDMProxy::OnSessionMessage(const nsAString& aSessionId, 205 dom::MediaKeyMessageType aMessageType, 206 const nsTArray<uint8_t>& aMessage) { 207 MOZ_ASSERT(NS_IsMainThread()); 208 if (mKeys.IsNull()) { 209 return; 210 } 211 RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId)); 212 if (session) { 213 session->DispatchKeyMessage(aMessageType, aMessage); 214 } 215 } 216 217 void MediaDrmCDMProxy::OnExpirationChange(const nsAString& aSessionId, 218 UnixTime aExpiryTime) { 219 MOZ_ASSERT(NS_IsMainThread()); 220 if (mKeys.IsNull()) { 221 return; 222 } 223 RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId)); 224 if (session) { 225 session->SetExpiration(static_cast<double>(aExpiryTime)); 226 } 227 } 228 229 void MediaDrmCDMProxy::OnSessionClosed( 230 const nsAString& aSessionId, dom::MediaKeySessionClosedReason aReason) { 231 MOZ_ASSERT(NS_IsMainThread()); 232 if (mKeys.IsNull()) { 233 return; 234 } 235 RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId)); 236 if (session) { 237 session->OnClosed(aReason); 238 } 239 } 240 241 void MediaDrmCDMProxy::OnSessionError(const nsAString& aSessionId, 242 nsresult aException, uint32_t aSystemCode, 243 const nsAString& aMsg) { 244 MOZ_ASSERT(NS_IsMainThread()); 245 if (mKeys.IsNull()) { 246 return; 247 } 248 RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId)); 249 if (session) { 250 session->DispatchKeyError(aSystemCode); 251 } 252 } 253 254 void MediaDrmCDMProxy::OnRejectPromise(uint32_t aPromiseId, 255 ErrorResult&& aException, 256 const nsCString& aMsg) { 257 MOZ_ASSERT(NS_IsMainThread()); 258 RejectPromise(aPromiseId, std::move(aException), aMsg); 259 } 260 261 RefPtr<DecryptPromise> MediaDrmCDMProxy::Decrypt(MediaRawData* aSample) { 262 MOZ_ASSERT_UNREACHABLE("Fennec could not handle decrypting individually"); 263 return nullptr; 264 } 265 266 void MediaDrmCDMProxy::OnDecrypted(uint32_t aId, DecryptStatus aResult, 267 const nsTArray<uint8_t>& aDecryptedData) { 268 MOZ_ASSERT_UNREACHABLE("Fennec could not handle decrypted event"); 269 } 270 271 void MediaDrmCDMProxy::RejectPromise(PromiseId aId, ErrorResult&& aException, 272 const nsCString& aReason) { 273 if (NS_IsMainThread()) { 274 if (!mKeys.IsNull()) { 275 mKeys->RejectPromise(aId, std::move(aException), aReason); 276 } 277 } else { 278 nsCOMPtr<nsIRunnable> task( 279 new RejectPromiseTask(this, aId, std::move(aException), aReason)); 280 mMainThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL); 281 } 282 } 283 284 void MediaDrmCDMProxy::RejectPromiseWithStateError(PromiseId aId, 285 const nsCString& aReason) { 286 ErrorResult rv; 287 rv.ThrowInvalidStateError(aReason); 288 RejectPromise(aId, std::move(rv), aReason); 289 } 290 291 void MediaDrmCDMProxy::ResolvePromise(PromiseId aId) { 292 if (NS_IsMainThread()) { 293 if (!mKeys.IsNull()) { 294 mKeys->ResolvePromise(aId); 295 } else { 296 NS_WARNING("MediaDrmCDMProxy unable to resolve promise!"); 297 } 298 } else { 299 nsCOMPtr<nsIRunnable> task; 300 task = 301 NewRunnableMethod<PromiseId>("MediaDrmCDMProxy::ResolvePromise", this, 302 &MediaDrmCDMProxy::ResolvePromise, aId); 303 mMainThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL); 304 } 305 } 306 307 template <typename T> 308 void MediaDrmCDMProxy::ResolvePromiseWithResult(PromiseId aId, 309 const T& aResult) { 310 if (NS_IsMainThread()) { 311 if (!mKeys.IsNull()) { 312 mKeys->ResolvePromiseWithResult(aId, aResult); 313 } else { 314 NS_WARNING("MediaDrmCDMProxy unable to resolve promise!"); 315 } 316 return; 317 } 318 319 nsCOMPtr<nsIRunnable> task; 320 task = NewRunnableMethod<PromiseId, T>( 321 "MediaDrmCDMProxy::ResolvePromiseWithResult", this, 322 &MediaDrmCDMProxy::ResolvePromiseWithResult<T>, aId, aResult); 323 mMainThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL); 324 } 325 326 void MediaDrmCDMProxy::OnKeyStatusesChange(const nsAString& aSessionId) { 327 MOZ_ASSERT(NS_IsMainThread()); 328 if (mKeys.IsNull()) { 329 return; 330 } 331 RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId)); 332 if (session) { 333 session->DispatchKeyStatusesChange(); 334 } 335 } 336 337 void MediaDrmCDMProxy::GetStatusForPolicy( 338 PromiseId aPromiseId, const dom::HDCPVersion& aMinHdcpVersion) { 339 // TODO: Implement GetStatusForPolicy. 340 constexpr auto err = 341 "Currently Fennec does not support GetStatusForPolicy"_ns; 342 343 ErrorResult rv; 344 rv.ThrowNotSupportedError(err); 345 RejectPromise(aPromiseId, std::move(rv), err); 346 } 347 348 #ifdef DEBUG 349 bool MediaDrmCDMProxy::IsOnOwnerThread() { 350 return NS_GetCurrentThread() == mOwnerThread; 351 } 352 #endif 353 354 const nsString& MediaDrmCDMProxy::GetMediaDrmStubId() const { 355 MOZ_ASSERT(mCDM); 356 return mCDM->GetMediaDrmStubId(); 357 } 358 359 void MediaDrmCDMProxy::OnCDMCreated(uint32_t aPromiseId) { 360 MOZ_ASSERT(NS_IsMainThread()); 361 if (mKeys.IsNull()) { 362 return; 363 } 364 365 if (mCDM) { 366 mKeys->OnCDMCreated(aPromiseId, 0); 367 return; 368 } 369 370 // No CDM? Just reject the promise. 371 constexpr auto err = "Null CDM in OnCDMCreated()"_ns; 372 ErrorResult rv; 373 rv.ThrowInvalidStateError(err); 374 mKeys->RejectPromise(aPromiseId, std::move(rv), err); 375 } 376 377 void MediaDrmCDMProxy::md_Init(uint32_t aPromiseId) { 378 MOZ_ASSERT(IsOnOwnerThread()); 379 MOZ_ASSERT(mCDM); 380 381 UniquePtr<MediaDrmCDMCallbackProxy> callback( 382 new MediaDrmCDMCallbackProxy(this)); 383 mCDM->Init(std::move(callback)); 384 nsCOMPtr<nsIRunnable> task( 385 NewRunnableMethod<uint32_t>("MediaDrmCDMProxy::OnCDMCreated", this, 386 &MediaDrmCDMProxy::OnCDMCreated, aPromiseId)); 387 mMainThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL); 388 } 389 390 void MediaDrmCDMProxy::md_CreateSession(UniquePtr<CreateSessionData>&& aData) { 391 MOZ_ASSERT(IsOnOwnerThread()); 392 393 if (!mCDM) { 394 RejectPromiseWithStateError(aData->mPromiseId, 395 "Null CDM in md_CreateSession"_ns); 396 return; 397 } 398 399 mCDM->CreateSession(aData->mCreateSessionToken, aData->mPromiseId, 400 aData->mInitDataType, aData->mInitData, 401 ToMediaDrmSessionType(aData->mSessionType)); 402 } 403 404 void MediaDrmCDMProxy::md_SetServerCertificate(PromiseId aPromiseId, 405 const nsTArray<uint8_t>& aCert) { 406 MOZ_ASSERT(IsOnOwnerThread()); 407 408 if (!mCDM) { 409 RejectPromiseWithStateError(aPromiseId, 410 "Null CDM in md_SetServerCertificate"_ns); 411 return; 412 } 413 414 if (mCDM->SetServerCertificate(aCert)) { 415 ResolvePromiseWithResult(aPromiseId, true); 416 } else { 417 RejectPromiseWithStateError( 418 aPromiseId, "MediaDrmCDMProxy unable to set server certificate"_ns); 419 } 420 } 421 422 void MediaDrmCDMProxy::md_UpdateSession(UniquePtr<UpdateSessionData>&& aData) { 423 MOZ_ASSERT(IsOnOwnerThread()); 424 425 if (!mCDM) { 426 RejectPromiseWithStateError(aData->mPromiseId, 427 "Null CDM in md_UpdateSession"_ns); 428 return; 429 } 430 mCDM->UpdateSession(aData->mPromiseId, aData->mSessionId, aData->mResponse); 431 } 432 433 void MediaDrmCDMProxy::md_CloseSession(UniquePtr<SessionOpData>&& aData) { 434 MOZ_ASSERT(IsOnOwnerThread()); 435 436 if (!mCDM) { 437 RejectPromiseWithStateError(aData->mPromiseId, 438 "Null CDM in md_CloseSession"_ns); 439 return; 440 } 441 mCDM->CloseSession(aData->mPromiseId, aData->mSessionId); 442 } 443 444 void MediaDrmCDMProxy::md_Shutdown() { 445 MOZ_ASSERT(IsOnOwnerThread()); 446 MOZ_ASSERT(mCDM); 447 if (mShutdownCalled) { 448 return; 449 } 450 mShutdownCalled = true; 451 mCDM->Shutdown(); 452 mCDM = nullptr; 453 } 454 455 } // namespace mozilla