blob: cabe97aa74fe3122c7ad1a6e67824c756307ea1c [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/components/cdm_factory_daemon/chromeos_cdm_factory.h"
#include <utility>
#include "base/bind.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chromeos/components/cdm_factory_daemon/cdm_storage_adapter.h"
#include "chromeos/components/cdm_factory_daemon/content_decryption_module_adapter.h"
#include "chromeos/components/cdm_factory_daemon/mojom/content_decryption_module.mojom.h"
#include "media/base/content_decryption_module.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/generic_pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_associated_remote.h"
namespace chromeos {
namespace {
// This holds the global singleton Mojo connection to the browser process.
mojo::Remote<cdm::mojom::CdmFactoryDaemon>& GetCdmFactoryDaemonRemote() {
static base::NoDestructor<mojo::Remote<cdm::mojom::CdmFactoryDaemon>> remote;
return *remote;
}
// This holds the PendingRemote until we can bind it on the proper thread.
mojo::PendingRemote<cdm::mojom::CdmFactoryDaemon>& GetBrowserProxy() {
static base::NoDestructor<mojo::PendingRemote<cdm::mojom::CdmFactoryDaemon>>
browser_proxy;
return *browser_proxy;
}
} // namespace
ChromeOsCdmFactory::ChromeOsCdmFactory(
media::mojom::FrameInterfaceFactory* frame_interfaces)
: frame_interfaces_(frame_interfaces) {
DCHECK(frame_interfaces_);
DVLOG(1) << "Creating the ChromeOsCdmFactory";
}
ChromeOsCdmFactory::~ChromeOsCdmFactory() = default;
mojo::PendingReceiver<cdm::mojom::CdmFactoryDaemon>
ChromeOsCdmFactory::GetCdmFactoryDaemonReceiver() {
mojo::PendingRemote<chromeos::cdm::mojom::CdmFactoryDaemon> browser_proxy;
auto receiver = browser_proxy.InitWithNewPipeAndPassReceiver();
// This does not get called from the mojo thread we will be using, so hold
// onto it and then bind it when we get the first call.
GetBrowserProxy() = std::move(browser_proxy);
return receiver;
}
void ChromeOsCdmFactory::Create(
const std::string& key_system,
const media::CdmConfig& cdm_config,
const media::SessionMessageCB& session_message_cb,
const media::SessionClosedCB& session_closed_cb,
const media::SessionKeysChangeCB& session_keys_change_cb,
const media::SessionExpirationUpdateCB& session_expiration_update_cb,
media::CdmCreatedCB cdm_created_cb) {
DVLOG(1) << __func__ << " key system=" << key_system;
// Check that the user has Verified Access enabled in their Chrome settings
// and if they do not then block this connection since OEMCrypto utilizes
// remote attestation as part of verification.
if (!platform_verification_) {
frame_interfaces_->BindEmbedderReceiver(mojo::GenericPendingReceiver(
platform_verification_.BindNewPipeAndPassReceiver()));
}
platform_verification_->IsVerifiedAccessEnabled(base::BindOnce(
&ChromeOsCdmFactory::OnVerifiedAccessEnabled, weak_factory_.GetWeakPtr(),
key_system, cdm_config, session_message_cb, session_closed_cb,
session_keys_change_cb, session_expiration_update_cb,
std::move(cdm_created_cb)));
}
void ChromeOsCdmFactory::OnVerifiedAccessEnabled(
const std::string& key_system,
const media::CdmConfig& cdm_config,
const media::SessionMessageCB& session_message_cb,
const media::SessionClosedCB& session_closed_cb,
const media::SessionKeysChangeCB& session_keys_change_cb,
const media::SessionExpirationUpdateCB& session_expiration_update_cb,
media::CdmCreatedCB cdm_created_cb,
bool enabled) {
if (!enabled) {
DVLOG(1)
<< "Not using Chrome OS CDM factory due to Verified Access disabled";
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(cdm_created_cb), nullptr,
"Verified Access is disabled."));
return;
}
// This tests if it is bound already.
if (!GetCdmFactoryDaemonRemote()) {
DCHECK(GetBrowserProxy());
DVLOG(1) << "Binding the CdmFactoryDaemon browser proxy";
GetCdmFactoryDaemonRemote().Bind(std::move(GetBrowserProxy()));
}
// If we haven't retrieved the remote CDM factory, do that first.
if (!remote_factory_.is_bound()) {
// Now invoke the call to create the Mojo interface for the CDM factory.
GetCdmFactoryDaemonRemote()->CreateFactory(
key_system,
base::BindOnce(&ChromeOsCdmFactory::OnCreateFactory,
weak_factory_.GetWeakPtr(), cdm_config,
session_message_cb, session_closed_cb,
session_keys_change_cb, session_expiration_update_cb,
std::move(cdm_created_cb)));
return;
}
// Create the remote CDM in the daemon and then pass that into our adapter
// that converts the media::ContentDecryptionModule/Decryptor calls into
// chromeos::cdm::mojom::ContentDecryptionModule calls.
CreateCdm(cdm_config, session_message_cb, session_closed_cb,
session_keys_change_cb, session_expiration_update_cb,
std::move(cdm_created_cb));
}
void ChromeOsCdmFactory::OnCreateFactory(
const media::CdmConfig& cdm_config,
const media::SessionMessageCB& session_message_cb,
const media::SessionClosedCB& session_closed_cb,
const media::SessionKeysChangeCB& session_keys_change_cb,
const media::SessionExpirationUpdateCB& session_expiration_update_cb,
media::CdmCreatedCB cdm_created_cb,
mojo::PendingRemote<cdm::mojom::CdmFactory> remote_factory) {
DVLOG(1) << __func__;
if (!remote_factory) {
LOG(ERROR) << "Failed creating the remote CDM factory";
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(cdm_created_cb), nullptr,
"Remote factory creation failed."));
return;
}
// Check if this is bound already, which could happen due to asynchronous
// calls.
if (!remote_factory_) {
remote_factory_.Bind(std::move(remote_factory));
remote_factory_.set_disconnect_handler(
base::BindOnce(&ChromeOsCdmFactory::OnMojoConnectionError,
weak_factory_.GetWeakPtr()));
}
// We have the factory bound, create the CDM.
CreateCdm(cdm_config, session_message_cb, session_closed_cb,
session_keys_change_cb, session_expiration_update_cb,
std::move(cdm_created_cb));
}
void ChromeOsCdmFactory::CreateCdm(
const media::CdmConfig& /*cdm_config*/,
const media::SessionMessageCB& session_message_cb,
const media::SessionClosedCB& session_closed_cb,
const media::SessionKeysChangeCB& session_keys_change_cb,
const media::SessionExpirationUpdateCB& session_expiration_update_cb,
media::CdmCreatedCB cdm_created_cb) {
DVLOG(1) << __func__;
// Create the storage implementation we are sending to Chrome OS.
mojo::PendingAssociatedRemote<cdm::mojom::CdmStorage> storage_remote;
std::unique_ptr<CdmStorageAdapter> storage =
std::make_unique<CdmStorageAdapter>(
frame_interfaces_,
storage_remote.InitWithNewEndpointAndPassReceiver());
// Create the remote interface for the CDM in Chrome OS.
mojo::AssociatedRemote<cdm::mojom::ContentDecryptionModule> cros_cdm;
mojo::PendingAssociatedReceiver<cdm::mojom::ContentDecryptionModule>
cros_cdm_pending_receiver = cros_cdm.BindNewEndpointAndPassReceiver();
// Create the adapter that proxies calls between
// media::ContentDecryptionModule and
// chromeos::cdm::mojom::ContentDecryptionModule.
scoped_refptr<ContentDecryptionModuleAdapter> cdm =
base::WrapRefCounted<ContentDecryptionModuleAdapter>(
new ContentDecryptionModuleAdapter(
std::move(storage), std::move(cros_cdm), session_message_cb,
session_closed_cb, session_keys_change_cb,
session_expiration_update_cb));
// Now create the remote CDM instance that links everything up.
remote_factory_->CreateCdm(cdm->GetClientInterface(),
std::move(storage_remote),
std::move(cros_cdm_pending_receiver));
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(cdm_created_cb), std::move(cdm), ""));
}
void ChromeOsCdmFactory::OnMojoConnectionError() {
DVLOG(1) << __func__;
remote_factory_.reset();
}
} // namespace chromeos