MFCDMChild.cpp (19846B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "MFCDMChild.h" 6 7 #include "RemoteMediaManagerChild.h" 8 #include "mozilla/EMEUtils.h" 9 #include "mozilla/KeySystemConfig.h" 10 #include "mozilla/RefPtr.h" 11 #include "mozilla/StaticString.h" 12 #include "mozilla/WMFCDMProxyCallback.h" 13 #include "nsString.h" 14 15 namespace mozilla { 16 17 #define LOG(msg, ...) \ 18 EME_LOG("MFCDMChild[%p]@%s: " msg, this, __func__, ##__VA_ARGS__) 19 #define SLOG(msg, ...) EME_LOG("MFCDMChild@%s: " msg, __func__, ##__VA_ARGS__) 20 21 #define HANDLE_PENDING_PROMISE(method, callsite, promise, promiseId) \ 22 do { \ 23 promise->Then( \ 24 self->mManagerThread, callsite, \ 25 [self, promiseId, callsite]( \ 26 PMFCDMChild::method##Promise::ResolveOrRejectValue&& result) { \ 27 MutexAutoLock lock(self->mMutex); \ 28 auto iter = self->mPendingGenericPromises.find(promiseId); \ 29 if (iter == self->mPendingGenericPromises.end()) { \ 30 return; \ 31 } \ 32 auto& promiseHolder = iter->second; \ 33 if (result.IsResolve()) { \ 34 if (NS_SUCCEEDED(result.ResolveValue())) { \ 35 promiseHolder.ResolveIfExists(true, callsite); \ 36 } else { \ 37 promiseHolder.RejectIfExists(result.ResolveValue(), callsite); \ 38 } \ 39 } else { \ 40 /* IPC die */ \ 41 promiseHolder.RejectIfExists(NS_ERROR_FAILURE, callsite); \ 42 } \ 43 self->mPendingGenericPromises.erase(iter); \ 44 }); \ 45 } while (0) 46 47 #define INVOKE_ASYNC(method, promiseId, param1) \ 48 do { \ 49 StaticString callsite = __func__; \ 50 using ParamType = std::remove_reference<decltype(param1)>::type; \ 51 mManagerThread->Dispatch(NS_NewRunnableFunction( \ 52 callsite, [self = RefPtr{this}, callsite, promiseId, \ 53 param_1 = std::forward<ParamType>(param1)] { \ 54 auto p = self->Send##method(param_1); \ 55 HANDLE_PENDING_PROMISE(method, callsite, p, promiseId); \ 56 })); \ 57 } while (0) 58 59 #define INVOKE_ASYNC2(method, promiseId, param1, param2) \ 60 do { \ 61 StaticString callsite = __func__; \ 62 using ParamType1 = std::remove_reference<decltype(param1)>::type; \ 63 using ParamType2 = std::remove_reference<decltype(param2)>::type; \ 64 mManagerThread->Dispatch(NS_NewRunnableFunction( \ 65 callsite, [self = RefPtr{this}, callsite, promiseId, \ 66 param_1 = std::forward<ParamType1>(param1), \ 67 param_2 = std::forward<ParamType2>(param2)] { \ 68 auto p = self->Send##method(param_1, param_2); \ 69 HANDLE_PENDING_PROMISE(method, callsite, p, promiseId); \ 70 })); \ 71 } while (0) 72 73 MFCDMChild::MFCDMChild(const nsAString& aKeySystem) 74 : mKeySystem(aKeySystem), 75 mManagerThread(RemoteMediaManagerChild::GetManagerThread()), 76 mState(NS_ERROR_NOT_INITIALIZED), 77 mShutdown(false) {} 78 79 MFCDMChild::~MFCDMChild() {} 80 81 void MFCDMChild::EnsureRemote() { 82 if (mRemotePromise) { 83 LOG("already created remote promise"); 84 return; 85 } 86 87 if (!mManagerThread) { 88 LOG("no manager thread"); 89 mState = NS_ERROR_NOT_AVAILABLE; 90 mRemotePromise = RemotePromise::CreateAndReject(mState, __func__); 91 return; 92 } 93 94 mRemotePromise = mRemotePromiseHolder.Ensure(__func__); 95 96 RefPtr<MFCDMChild> self = this; 97 RemoteMediaManagerChild::LaunchUtilityProcessIfNeeded( 98 RemoteMediaIn::UtilityProcess_MFMediaEngineCDM) 99 ->Then( 100 mManagerThread, __func__, 101 [self, this](bool) { 102 mRemoteRequest.Complete(); 103 RefPtr<RemoteMediaManagerChild> manager = 104 RemoteMediaManagerChild::GetSingleton( 105 RemoteMediaIn::UtilityProcess_MFMediaEngineCDM); 106 if (!manager || !manager->CanSend()) { 107 LOG("manager not exists or can't send"); 108 mState = NS_ERROR_NOT_AVAILABLE; 109 mRemotePromiseHolder.RejectIfExists(mState, __func__); 110 return; 111 } 112 113 mIPDLSelfRef = this; 114 MOZ_ALWAYS_TRUE(manager->SendPMFCDMConstructor(this, mKeySystem)); 115 mState = NS_OK; 116 mRemotePromiseHolder.ResolveIfExists(true, __func__); 117 }, 118 [self, this](nsresult rv) { 119 mRemoteRequest.Complete(); 120 LOG("fail to launch MFCDM process"); 121 mState = rv; 122 mRemotePromiseHolder.RejectIfExists(rv, __func__); 123 }) 124 ->Track(mRemoteRequest); 125 } 126 127 void MFCDMChild::Shutdown() { 128 MOZ_ASSERT(!mShutdown); 129 mShutdown = true; 130 if (mState == NS_OK) { 131 mManagerThread->Dispatch( 132 NS_NewRunnableFunction(__func__, [self = RefPtr{this}, this]() { 133 mRemoteRequest.DisconnectIfExists(); 134 mInitRequest.DisconnectIfExists(); 135 mProxyCallback = nullptr; 136 137 { 138 MutexAutoLock lock(mMutex); 139 for (auto& promise : mPendingSessionPromises) { 140 promise.second.RejectIfExists(NS_ERROR_ABORT, __func__); 141 } 142 mPendingSessionPromises.clear(); 143 144 for (auto& promise : mPendingGenericPromises) { 145 promise.second.RejectIfExists(NS_ERROR_ABORT, __func__); 146 } 147 mPendingGenericPromises.clear(); 148 } 149 150 mRemotePromiseHolder.RejectIfExists(NS_ERROR_ABORT, __func__); 151 mCapabilitiesPromiseHolder.RejectIfExists(NS_ERROR_ABORT, __func__); 152 153 Send__delete__(this); 154 })); 155 } 156 } 157 158 RefPtr<MFCDMChild::CapabilitiesPromise> MFCDMChild::GetCapabilities( 159 MFCDMCapabilitiesRequest&& aRequest) { 160 MOZ_ASSERT(mManagerThread); 161 162 if (mShutdown) { 163 return CapabilitiesPromise::CreateAndReject(NS_ERROR_ABORT, __func__); 164 } 165 166 if (mState != NS_OK && mState != NS_ERROR_NOT_INITIALIZED) { 167 LOG("error=%x", uint32_t(nsresult(mState))); 168 return CapabilitiesPromise::CreateAndReject(mState, __func__); 169 } 170 171 auto doSend = [self = RefPtr{this}, request = std::move(aRequest), this]() { 172 SendGetCapabilities(request)->Then( 173 mManagerThread, __func__, 174 [self, this](MFCDMCapabilitiesResult&& aResult) { 175 if (aResult.type() == MFCDMCapabilitiesResult::Tnsresult) { 176 mCapabilitiesPromiseHolder.RejectIfExists(aResult.get_nsresult(), 177 __func__); 178 return; 179 } 180 mCapabilitiesPromiseHolder.ResolveIfExists( 181 std::move(aResult.get_MFCDMCapabilitiesIPDL()), __func__); 182 }, 183 [self, this](const mozilla::ipc::ResponseRejectReason& aReason) { 184 mCapabilitiesPromiseHolder.RejectIfExists(NS_ERROR_FAILURE, __func__); 185 }); 186 }; 187 188 return InvokeAsync(doSend, __func__, mCapabilitiesPromiseHolder); 189 } 190 191 // Neither error nor shutdown. 192 void MFCDMChild::AssertSendable() { 193 MOZ_ASSERT((mState == NS_ERROR_NOT_INITIALIZED || mState == NS_OK) && 194 mShutdown == false); 195 } 196 197 template <typename PromiseType> 198 already_AddRefed<PromiseType> MFCDMChild::InvokeAsync( 199 std::function<void()>&& aCall, StaticString aCallerName, 200 MozPromiseHolder<PromiseType>& aPromise) { 201 AssertSendable(); 202 203 if (mState == NS_OK) { 204 // mRemotePromise is resolved, send on manager thread. 205 mManagerThread->Dispatch( 206 NS_NewRunnableFunction(aCallerName, std::move(aCall))); 207 } else if (mState == NS_ERROR_NOT_INITIALIZED) { 208 // mRemotePromise is pending, chain to it. 209 mRemotePromise->Then( 210 mManagerThread, __func__, std::move(aCall), 211 [self = RefPtr{this}, this, &aPromise, aCallerName](nsresult rv) { 212 LOG("error=%x", uint32_t(rv)); 213 mState = rv; 214 aPromise.RejectIfExists(rv, aCallerName); 215 }); 216 } 217 218 return aPromise.Ensure(aCallerName); 219 } 220 221 RefPtr<MFCDMChild::InitPromise> MFCDMChild::Init( 222 const nsAString& aOrigin, const CopyableTArray<nsString>& aInitDataTypes, 223 const KeySystemConfig::Requirement aPersistentState, 224 const KeySystemConfig::Requirement aDistinctiveID, 225 const CopyableTArray<MFCDMMediaCapability>& aAudioCapabilities, 226 const CopyableTArray<MFCDMMediaCapability>& aVideoCapabilities, 227 WMFCDMProxyCallback* aProxyCallback) { 228 MOZ_ASSERT(mManagerThread); 229 230 if (mShutdown) { 231 return InitPromise::CreateAndReject(NS_ERROR_ABORT, __func__); 232 } 233 234 if (mState != NS_OK && mState != NS_ERROR_NOT_INITIALIZED) { 235 LOG("error=%x", uint32_t(nsresult(mState))); 236 return InitPromise::CreateAndReject(mState, __func__); 237 } 238 239 RefPtr<WMFCDMProxyCallback> callback = aProxyCallback; 240 MFCDMInitParamsIPDL params{nsString(aOrigin), aInitDataTypes, 241 aDistinctiveID, aPersistentState, 242 aAudioCapabilities, aVideoCapabilities}; 243 auto doSend = [self = RefPtr{this}, this, params, callback]() { 244 SendInit(params) 245 ->Then( 246 mManagerThread, __func__, 247 [self, callback, this](MFCDMInitResult&& aResult) { 248 mInitRequest.Complete(); 249 if (aResult.type() == MFCDMInitResult::Tnsresult) { 250 nsresult rv = aResult.get_nsresult(); 251 mInitPromiseHolder.RejectIfExists(rv, __func__); 252 return; 253 } 254 mProxyCallback = callback; 255 mId = aResult.get_MFCDMInitIPDL().id(); 256 mInitPromiseHolder.ResolveIfExists(aResult.get_MFCDMInitIPDL(), 257 __func__); 258 }, 259 [self, this](const mozilla::ipc::ResponseRejectReason& aReason) { 260 mInitRequest.Complete(); 261 mInitPromiseHolder.RejectIfExists(NS_ERROR_FAILURE, __func__); 262 }) 263 ->Track(mInitRequest); 264 }; 265 266 return InvokeAsync(std::move(doSend), __func__, mInitPromiseHolder); 267 } 268 269 RefPtr<MFCDMChild::SessionPromise> MFCDMChild::CreateSessionAndGenerateRequest( 270 uint32_t aPromiseId, KeySystemConfig::SessionType aSessionType, 271 const nsAString& aInitDataType, const nsTArray<uint8_t>& aInitData) { 272 MOZ_ASSERT(mManagerThread); 273 MOZ_ASSERT(mId > 0, "Should call Init() first and wait for it"); 274 275 if (mShutdown) { 276 return MFCDMChild::SessionPromise::CreateAndReject(NS_ERROR_ABORT, 277 __func__); 278 } 279 280 MutexAutoLock lock(mMutex); 281 MOZ_ASSERT(mPendingSessionPromises.find(aPromiseId) == 282 mPendingSessionPromises.end()); 283 mPendingSessionPromises.emplace(aPromiseId, 284 MozPromiseHolder<SessionPromise>{}); 285 mManagerThread->Dispatch(NS_NewRunnableFunction( 286 __func__, [self = RefPtr{this}, this, 287 params = 288 MFCDMCreateSessionParamsIPDL{ 289 aSessionType, nsString{aInitDataType}, aInitData}, 290 aPromiseId] { 291 SendCreateSessionAndGenerateRequest(params)->Then( 292 mManagerThread, __func__, 293 [self, aPromiseId, this](const MFCDMSessionResult& result) { 294 MutexAutoLock lock(mMutex); 295 auto iter = mPendingSessionPromises.find(aPromiseId); 296 if (iter == mPendingSessionPromises.end()) { 297 return; 298 } 299 auto& promiseHolder = iter->second; 300 if (result.type() == MFCDMSessionResult::Tnsresult) { 301 promiseHolder.RejectIfExists(result.get_nsresult(), __func__); 302 } else { 303 LOG("session ID=[%zu]%s", result.get_nsString().Length(), 304 NS_ConvertUTF16toUTF8(result.get_nsString()).get()); 305 promiseHolder.ResolveIfExists(result.get_nsString(), __func__); 306 } 307 mPendingSessionPromises.erase(iter); 308 }, 309 [self, aPromiseId, 310 this](const mozilla::ipc::ResponseRejectReason& aReason) { 311 MutexAutoLock lock(mMutex); 312 auto iter = mPendingSessionPromises.find(aPromiseId); 313 if (iter == mPendingSessionPromises.end()) { 314 return; 315 } 316 auto& promiseHolder = iter->second; 317 promiseHolder.RejectIfExists(NS_ERROR_FAILURE, __func__); 318 mPendingSessionPromises.erase(iter); 319 }); 320 })); 321 return mPendingSessionPromises[aPromiseId].Ensure(__func__); 322 } 323 324 RefPtr<GenericPromise> MFCDMChild::LoadSession( 325 uint32_t aPromiseId, const KeySystemConfig::SessionType aSessionType, 326 const nsAString& aSessionId) { 327 MOZ_ASSERT(mManagerThread); 328 MOZ_ASSERT(mId > 0, "Should call Init() first and wait for it"); 329 330 if (mShutdown) { 331 return GenericPromise::CreateAndReject(NS_ERROR_ABORT, __func__); 332 } 333 334 MutexAutoLock lock(mMutex); 335 MOZ_ASSERT(mPendingGenericPromises.find(aPromiseId) == 336 mPendingGenericPromises.end()); 337 mPendingGenericPromises.emplace(aPromiseId, 338 MozPromiseHolder<GenericPromise>{}); 339 INVOKE_ASYNC2(LoadSession, aPromiseId, aSessionType, nsString{aSessionId}); 340 return mPendingGenericPromises[aPromiseId].Ensure(__func__); 341 } 342 343 RefPtr<GenericPromise> MFCDMChild::UpdateSession(uint32_t aPromiseId, 344 const nsAString& aSessionId, 345 nsTArray<uint8_t>& aResponse) { 346 MOZ_ASSERT(mManagerThread); 347 MOZ_ASSERT(mId > 0, "Should call Init() first and wait for it"); 348 349 if (mShutdown) { 350 return GenericPromise::CreateAndReject(NS_ERROR_ABORT, __func__); 351 } 352 353 MutexAutoLock lock(mMutex); 354 MOZ_ASSERT(mPendingGenericPromises.find(aPromiseId) == 355 mPendingGenericPromises.end()); 356 mPendingGenericPromises.emplace(aPromiseId, 357 MozPromiseHolder<GenericPromise>{}); 358 INVOKE_ASYNC2(UpdateSession, aPromiseId, nsString{aSessionId}, 359 std::move(aResponse)); 360 return mPendingGenericPromises[aPromiseId].Ensure(__func__); 361 } 362 363 RefPtr<GenericPromise> MFCDMChild::CloseSession(uint32_t aPromiseId, 364 const nsAString& aSessionId) { 365 MOZ_ASSERT(mManagerThread); 366 MOZ_ASSERT(mId > 0, "Should call Init() first and wait for it"); 367 368 if (mShutdown) { 369 return GenericPromise::CreateAndReject(NS_ERROR_ABORT, __func__); 370 } 371 372 MutexAutoLock lock(mMutex); 373 MOZ_ASSERT(mPendingGenericPromises.find(aPromiseId) == 374 mPendingGenericPromises.end()); 375 mPendingGenericPromises.emplace(aPromiseId, 376 MozPromiseHolder<GenericPromise>{}); 377 INVOKE_ASYNC(CloseSession, aPromiseId, nsString{aSessionId}); 378 return mPendingGenericPromises[aPromiseId].Ensure(__func__); 379 } 380 381 RefPtr<GenericPromise> MFCDMChild::RemoveSession(uint32_t aPromiseId, 382 const nsAString& aSessionId) { 383 MOZ_ASSERT(mManagerThread); 384 MOZ_ASSERT(mId > 0, "Should call Init() first and wait for it"); 385 386 if (mShutdown) { 387 return GenericPromise::CreateAndReject(NS_ERROR_ABORT, __func__); 388 } 389 390 MutexAutoLock lock(mMutex); 391 MOZ_ASSERT(mPendingGenericPromises.find(aPromiseId) == 392 mPendingGenericPromises.end()); 393 mPendingGenericPromises.emplace(aPromiseId, 394 MozPromiseHolder<GenericPromise>{}); 395 INVOKE_ASYNC(RemoveSession, aPromiseId, nsString{aSessionId}); 396 return mPendingGenericPromises[aPromiseId].Ensure(__func__); 397 } 398 399 RefPtr<GenericPromise> MFCDMChild::SetServerCertificate( 400 uint32_t aPromiseId, nsTArray<uint8_t>& aCert) { 401 MOZ_ASSERT(mManagerThread); 402 MOZ_ASSERT(mId > 0, "Should call Init() first and wait for it"); 403 404 if (mShutdown) { 405 return GenericPromise::CreateAndReject(NS_ERROR_ABORT, __func__); 406 } 407 408 MutexAutoLock lock(mMutex); 409 MOZ_ASSERT(mPendingGenericPromises.find(aPromiseId) == 410 mPendingGenericPromises.end()); 411 mPendingGenericPromises.emplace(aPromiseId, 412 MozPromiseHolder<GenericPromise>{}); 413 INVOKE_ASYNC(SetServerCertificate, aPromiseId, std::move(aCert)); 414 return mPendingGenericPromises[aPromiseId].Ensure(__func__); 415 } 416 417 RefPtr<GenericPromise> MFCDMChild::GetStatusForPolicy( 418 uint32_t aPromiseId, const dom::HDCPVersion& aMinHdcpVersion) { 419 MOZ_ASSERT(mManagerThread); 420 MOZ_ASSERT(mId > 0, "Should call Init() first and wait for it"); 421 422 if (mShutdown) { 423 return GenericPromise::CreateAndReject(NS_ERROR_ABORT, __func__); 424 } 425 426 MutexAutoLock lock(mMutex); 427 MOZ_ASSERT(mPendingGenericPromises.find(aPromiseId) == 428 mPendingGenericPromises.end()); 429 mPendingGenericPromises.emplace(aPromiseId, 430 MozPromiseHolder<GenericPromise>{}); 431 INVOKE_ASYNC(GetStatusForPolicy, aPromiseId, aMinHdcpVersion); 432 return mPendingGenericPromises[aPromiseId].Ensure(__func__); 433 } 434 435 mozilla::ipc::IPCResult MFCDMChild::RecvOnSessionKeyMessage( 436 const MFCDMKeyMessage& aMessage) { 437 LOG("RecvOnSessionKeyMessage, sessionId=%s", 438 NS_ConvertUTF16toUTF8(aMessage.sessionId()).get()); 439 MOZ_ASSERT(mManagerThread); 440 MOZ_ASSERT(mProxyCallback); 441 mProxyCallback->OnSessionMessage(aMessage); 442 return IPC_OK(); 443 } 444 445 mozilla::ipc::IPCResult MFCDMChild::RecvOnSessionKeyStatusesChanged( 446 const MFCDMKeyStatusChange& aKeyStatuses) { 447 LOG("RecvOnSessionKeyStatusesChanged, sessionId=%s", 448 NS_ConvertUTF16toUTF8(aKeyStatuses.sessionId()).get()); 449 MOZ_ASSERT(mManagerThread); 450 MOZ_ASSERT(mProxyCallback); 451 mProxyCallback->OnSessionKeyStatusesChange(aKeyStatuses); 452 return IPC_OK(); 453 } 454 455 mozilla::ipc::IPCResult MFCDMChild::RecvOnSessionKeyExpiration( 456 const MFCDMKeyExpiration& aExpiration) { 457 LOG("RecvOnSessionKeyExpiration, sessionId=%s", 458 NS_ConvertUTF16toUTF8(aExpiration.sessionId()).get()); 459 MOZ_ASSERT(mManagerThread); 460 MOZ_ASSERT(mProxyCallback); 461 mProxyCallback->OnSessionKeyExpiration(aExpiration); 462 return IPC_OK(); 463 } 464 465 mozilla::ipc::IPCResult MFCDMChild::RecvOnSessionClosed( 466 const MFCDMSessionClosedResult& aResult) { 467 LOG("RecvOnSessionClosed, sessionId=%s", 468 NS_ConvertUTF16toUTF8(aResult.sessionId()).get()); 469 MOZ_ASSERT(mManagerThread); 470 MOZ_ASSERT(mProxyCallback); 471 mProxyCallback->OnSessionClosed(aResult); 472 return IPC_OK(); 473 } 474 475 void MFCDMChild::IPDLActorDestroyed() { 476 AssertOnManagerThread(); 477 mIPDLSelfRef = nullptr; 478 if (!mShutdown) { 479 LOG("IPDLActorDestroyed, remote process crashed!"); 480 mState = NS_ERROR_NOT_AVAILABLE; 481 if (mProxyCallback) { 482 mProxyCallback->OnRemoteProcessCrashed(); 483 } 484 } 485 } 486 487 #undef SLOG 488 #undef LOG 489 490 } // namespace mozilla