Commit 8dfa5a92 authored by RJ Ascani's avatar RJ Ascani Committed by Commit Bot

[Fuchsia] Switch to drm::KeySystem APIs

In order to support EME origin isolation requirements, the
fuchsia::media::drm protocols were updated to give the client control
of the storage used for CDM user data. In particular, the client can
"add" data stores to the KeySystem to use for writing persistent CDM
user data to. The API changes also inverted the provisioning flow and
puts the platform KeySystem service to control when a provisioning
request is initiated.

This CL adopts the use of the new APIs in the FuchsiaCdmManager. For
now, it adds a single shared drm::DataStore to each KeySystem. It also
provides a server implementation for the
fuchsia::media::drm::ProvisioningFetcher protocol, which is used by the
KeySystem for fetching provisioning certificates.

Bug: 991723
Change-Id: Ie799076be5be88abfda9519a0a9207a9667ad19d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2318514
Commit-Queue: RJ Ascani <rjascani@google.com>
Reviewed-by: default avatarXiaohan Wang <xhwang@chromium.org>
Reviewed-by: default avatarWez <wez@chromium.org>
Cr-Commit-Position: refs/heads/master@{#803927}
parent 8dcfda48
......@@ -108,66 +108,43 @@ void MediaResourceProviderImpl::CreateAudioCapturer(
factory->CreateAudioCapturer(std::move(request), /*loopback=*/false);
}
class WidevineHandler : public media::FuchsiaCdmManager::KeySystemHandler {
public:
WidevineHandler() = default;
~WidevineHandler() override = default;
void CreateCdm(
fidl::InterfaceRequest<fuchsia::media::drm::ContentDecryptionModule>
request) override {
auto widevine = base::ComponentContextForProcess()
->svc()
->Connect<fuchsia::media::drm::Widevine>();
widevine->CreateContentDecryptionModule(std::move(request));
}
fuchsia::media::drm::ProvisionerPtr CreateProvisioner() override {
fuchsia::media::drm::ProvisionerPtr provisioner;
auto widevine = base::ComponentContextForProcess()
->svc()
->Connect<fuchsia::media::drm::Widevine>();
widevine->CreateProvisioner(provisioner.NewRequest());
return provisioner;
}
};
class PlayreadyHandler : public media::FuchsiaCdmManager::KeySystemHandler {
public:
PlayreadyHandler() = default;
~PlayreadyHandler() override = default;
void CreateCdm(
fidl::InterfaceRequest<fuchsia::media::drm::ContentDecryptionModule>
request) override {
auto playready = base::ComponentContextForProcess()
->svc()
->Connect<fuchsia::media::drm::PlayReady>();
playready->CreateContentDecryptionModule(std::move(request));
}
fuchsia::media::drm::ProvisionerPtr CreateProvisioner() override {
// Provisioning is not required for PlayReady.
return fuchsia::media::drm::ProvisionerPtr();
}
};
template <typename KeySystemInterface>
fidl::InterfaceHandle<fuchsia::media::drm::KeySystem> ConnectToKeySystem() {
static_assert(
(std::is_same<KeySystemInterface, fuchsia::media::drm::Widevine>::value ||
std::is_same<KeySystemInterface, fuchsia::media::drm::PlayReady>::value),
"KeySystemInterface must be either fuchsia::media::drm::Widevine or "
"fuchsia::media::drm::PlayReady");
// TODO(fxbug.dev/13674): Once the key system specific protocols are turned
// into services, we should not need to manually force the key system specific
// interface into the KeySystem interface.
fidl::InterfaceHandle<fuchsia::media::drm::KeySystem> key_system;
base::ComponentContextForProcess()->svc()->Connect(key_system.NewRequest(),
KeySystemInterface::Name_);
return key_system;
}
std::unique_ptr<media::FuchsiaCdmManager> CreateCdmManager() {
media::FuchsiaCdmManager::KeySystemHandlerMap handlers;
media::FuchsiaCdmManager::CreateKeySystemCallbackMap
create_key_system_callbacks;
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableWidevine)) {
handlers.emplace(kWidevineKeySystem, std::make_unique<WidevineHandler>());
create_key_system_callbacks.emplace(
kWidevineKeySystem,
base::BindRepeating(
&ConnectToKeySystem<fuchsia::media::drm::Widevine>));
}
std::string playready_key_system =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kPlayreadyKeySystem);
if (!playready_key_system.empty()) {
handlers.emplace(playready_key_system,
std::make_unique<PlayreadyHandler>());
create_key_system_callbacks.emplace(
playready_key_system,
base::BindRepeating(
&ConnectToKeySystem<fuchsia::media::drm::PlayReady>));
}
std::string cdm_data_directory =
......@@ -175,7 +152,8 @@ std::unique_ptr<media::FuchsiaCdmManager> CreateCdmManager() {
switches::kCdmDataDirectory);
return std::make_unique<media::FuchsiaCdmManager>(
std::move(handlers), base::FilePath(cdm_data_directory));
std::move(create_key_system_callbacks),
base::FilePath(cdm_data_directory));
}
} // namespace
......
......@@ -183,7 +183,10 @@ test("media_unittests") {
}
if (is_fuchsia) {
deps += [ "//media/fuchsia/audio:unittests" ]
deps += [
"//media/fuchsia/audio:unittests",
"//media/fuchsia/cdm/service:unittests",
]
manifest = "//media/fuchsia/media_unittests.cmx"
}
......
......@@ -8,14 +8,40 @@ source_set("service") {
sources = [
"fuchsia_cdm_manager.cc",
"fuchsia_cdm_manager.h",
"provisioning_fetcher_impl.cc",
"provisioning_fetcher_impl.h",
]
public_deps = [ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.media.drm" ]
public_deps = [
"//media",
"//third_party/fuchsia-sdk/sdk/fidl/fuchsia.media.drm",
]
deps = [
"//fuchsia/base",
"//media",
"//media/fuchsia/mojom",
"//url",
]
}
source_set("unittests") {
testonly = true
deps = [
":service",
"//base",
"//base/test:test_support",
"//fuchsia/base",
"//media",
"//testing/gmock",
"//testing/gtest",
"//url",
]
sources = [
"fuchsia_cdm_manager_test.cc",
"mock_provision_fetcher.cc",
"mock_provision_fetcher.h",
"provisioning_fetcher_impl_test.cc",
]
}
......@@ -8,6 +8,7 @@
#include <fuchsia/media/drm/cpp/fidl.h>
#include <string>
#include "base/callback_forward.h"
#include "base/containers/flat_map.h"
#include "base/files/file_path.h"
#include "base/macros.h"
......@@ -20,38 +21,26 @@ class Origin;
namespace media {
// Create and connect to Fuchsia CDM service. It will provision the origin if
// needed. When provision is needed by multiple web pages for the same origin,
// it will chain all the concurrent provision requests and make sure we
// only handle one provision request for the origin at a time. This is mainly
// because the latest provision response will invalidate old provisioned cert,
// as well as the license sessions. We want to make sure once the channel to
// CDM service is established, nothing from Chromium breaks it.
// Create and connect to Fuchsia CDM services. Additionally manages the storage
// for CDM user data.
class FuchsiaCdmManager {
public:
// Handler for key system specific logic.
class KeySystemHandler {
public:
virtual ~KeySystemHandler() = default;
// Create CDM for license management and decryption.
virtual void CreateCdm(
fidl::InterfaceRequest<fuchsia::media::drm::ContentDecryptionModule>
request) = 0;
// Create Provisioner for origin provision. Impl may return nullptr if
// Provisioner is not supported, in which case the call should assume the
// origin is already provisioned.
virtual fuchsia::media::drm::ProvisionerPtr CreateProvisioner() = 0;
};
// A map from key system to its KeySystemHandler.
using KeySystemHandlerMap =
base::flat_map<std::string, std::unique_ptr<KeySystemHandler>>;
FuchsiaCdmManager(KeySystemHandlerMap handlers, base::FilePath cdm_data_path);
using CreateKeySystemCallback = base::RepeatingCallback<
fidl::InterfaceHandle<fuchsia::media::drm::KeySystem>()>;
// A map from key system name to its CreateKeySystemCallback.
using CreateKeySystemCallbackMap =
base::flat_map<std::string, CreateKeySystemCallback>;
FuchsiaCdmManager(
CreateKeySystemCallbackMap create_key_system_callbacks_by_name,
base::FilePath cdm_data_path);
~FuchsiaCdmManager();
FuchsiaCdmManager(FuchsiaCdmManager&&) = delete;
FuchsiaCdmManager& operator=(FuchsiaCdmManager&&) = delete;
void CreateAndProvision(
const std::string& key_system,
const url::Origin& origin,
......@@ -59,29 +48,36 @@ class FuchsiaCdmManager {
fidl::InterfaceRequest<fuchsia::media::drm::ContentDecryptionModule>
request);
private:
class OriginProvisioner;
OriginProvisioner* GetProvisioner(const std::string& key_system,
const url::Origin& origin,
KeySystemHandler* handler);
// Used by tests to monitor for key system disconnection events. The key
// system name is passed as a parameter to the callback.
void set_on_key_system_disconnect_for_test_callback(
base::RepeatingCallback<void(const std::string&)> disconnect_callback);
void OnProvisionResult(
KeySystemHandler* handler,
fidl::InterfaceRequest<fuchsia::media::drm::ContentDecryptionModule>
request,
bool success);
const KeySystemHandlerMap handlers_;
private:
class KeySystemClient;
using KeySystemClientMap =
base::flat_map<std::string, std::unique_ptr<KeySystemClient>>;
KeySystemClient* GetOrCreateKeySystemClient(
const std::string& key_system_name);
KeySystemClient* CreateKeySystemClient(const std::string& key_system_name);
base::FilePath GetStoragePath(const std::string& key_system_name);
void OnKeySystemClientError(const std::string& key_system_name);
// A map of callbacks to create KeySystem channels indexed by their EME name.
const CreateKeySystemCallbackMap create_key_system_callbacks_by_name_;
const base::FilePath cdm_data_path_;
// key system -> OriginProvisioner
base::flat_map<std::string, std::unique_ptr<OriginProvisioner>>
origin_provisioners_;
// A map of the active KeySystem clients indexed by their EME name. Entries
// in this map will be added on the first CreateAndProvision call for that
// particular KeySystem. They will only be removed if the KeySystem channel
// receives an error.
KeySystemClientMap active_key_system_clients_by_name_;
THREAD_CHECKER(thread_checker_);
base::RepeatingCallback<void(const std::string&)>
on_key_system_disconnect_for_test_callback_;
DISALLOW_COPY_AND_ASSIGN(FuchsiaCdmManager);
THREAD_CHECKER(thread_checker_);
};
} // namespace media
......
// 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 "media/fuchsia/cdm/service/fuchsia_cdm_manager.h"
#include <fuchsia/media/drm/cpp/fidl.h>
#include <fuchsia/media/drm/cpp/fidl_test_base.h>
#include <lib/fidl/cpp/binding_set.h>
#include <lib/fidl/cpp/interface_request.h>
#include <map>
#include "base/bind.h"
#include "base/callback.h"
#include "base/files/scoped_temp_dir.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/test/task_environment.h"
#include "media/fuchsia/cdm/service/mock_provision_fetcher.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/origin.h"
namespace media {
namespace {
namespace drm = ::fuchsia::media::drm;
using ::testing::_;
using ::testing::Eq;
using ::testing::Invoke;
using ::testing::SaveArg;
using ::testing::WithArgs;
using MockProvisionFetcher = ::media::testing::MockProvisionFetcher;
std::unique_ptr<ProvisionFetcher> CreateMockProvisionFetcher() {
auto mock_provision_fetcher = std::make_unique<MockProvisionFetcher>();
ON_CALL(*mock_provision_fetcher, Retrieve(_, _, _))
.WillByDefault(WithArgs<2>(
Invoke([](ProvisionFetcher::ResponseCB response_callback) {
std::move(response_callback).Run(true, "response");
})));
return mock_provision_fetcher;
}
class MockKeySystem : public drm::testing::KeySystem_TestBase {
public:
MockKeySystem() = default;
~MockKeySystem() override = default;
drm::KeySystemHandle AddBinding() { return bindings_.AddBinding(this); }
fidl::BindingSet<drm::KeySystem>& bindings() { return bindings_; }
void NotImplemented_(const std::string& name) override { FAIL() << name; }
MOCK_METHOD(void,
AddDataStore,
(uint32_t data_store_id,
drm::DataStoreParams params,
AddDataStoreCallback callback),
(override));
MOCK_METHOD(
void,
CreateContentDecryptionModule2,
(uint32_t data_store_id,
fidl::InterfaceRequest<drm::ContentDecryptionModule> cdm_request),
(override));
private:
fidl::BindingSet<drm::KeySystem> bindings_;
};
class FuchsiaCdmManagerTest : public ::testing::Test {
public:
FuchsiaCdmManagerTest() { EXPECT_TRUE(temp_dir_.CreateUniqueTempDir()); }
std::unique_ptr<FuchsiaCdmManager> CreateFuchsiaCdmManager(
std::vector<base::StringPiece> key_systems) {
FuchsiaCdmManager::CreateKeySystemCallbackMap create_key_system_callbacks;
for (const base::StringPiece& name : key_systems) {
MockKeySystem& key_system = mock_key_systems_[name];
create_key_system_callbacks.emplace(
name, base::BindRepeating(&MockKeySystem::AddBinding,
base::Unretained(&key_system)));
}
return std::make_unique<FuchsiaCdmManager>(
std::move(create_key_system_callbacks), temp_dir_.GetPath());
}
protected:
using MockKeySystemMap = std::map<base::StringPiece, MockKeySystem>;
MockKeySystem& mock_key_system(const base::StringPiece& key_system_name) {
return mock_key_systems_[key_system_name];
}
base::test::SingleThreadTaskEnvironment task_environment_{
base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
MockKeySystemMap mock_key_systems_;
base::ScopedTempDir temp_dir_;
};
TEST_F(FuchsiaCdmManagerTest, NoKeySystems) {
std::unique_ptr<FuchsiaCdmManager> cdm_manager = CreateFuchsiaCdmManager({});
base::RunLoop run_loop;
drm::ContentDecryptionModulePtr cdm_ptr;
cdm_ptr.set_error_handler([&](zx_status_t status) {
EXPECT_EQ(status, ZX_ERR_PEER_CLOSED);
run_loop.Quit();
});
cdm_manager->CreateAndProvision(
"com.key_system", url::Origin(),
base::BindRepeating(&CreateMockProvisionFetcher), cdm_ptr.NewRequest());
run_loop.Run();
}
TEST_F(FuchsiaCdmManagerTest, CreateAndProvision) {
constexpr char kKeySystem[] = "com.key_system.a";
std::unique_ptr<FuchsiaCdmManager> cdm_manager =
CreateFuchsiaCdmManager({kKeySystem});
base::RunLoop run_loop;
drm::ContentDecryptionModulePtr cdm_ptr;
cdm_ptr.set_error_handler([&](zx_status_t status) { run_loop.Quit(); });
uint32_t added_data_store_id = 0;
uint32_t cdm_data_store_id = 0;
EXPECT_CALL(mock_key_system(kKeySystem), AddDataStore(_, _, _))
.WillOnce(WithArgs<0, 2>(
Invoke([&](uint32_t data_store_id,
drm::KeySystem::AddDataStoreCallback callback) {
added_data_store_id = data_store_id;
callback(fit::ok());
})));
EXPECT_CALL(mock_key_system(kKeySystem), CreateContentDecryptionModule2(_, _))
.WillOnce(SaveArg<0>(&cdm_data_store_id));
cdm_manager->CreateAndProvision(
kKeySystem, url::Origin(),
base::BindRepeating(&CreateMockProvisionFetcher), cdm_ptr.NewRequest());
run_loop.Run();
EXPECT_NE(added_data_store_id, 0u);
EXPECT_EQ(added_data_store_id, cdm_data_store_id);
}
TEST_F(FuchsiaCdmManagerTest, RecreateAfterDisconnect) {
constexpr char kKeySystem[] = "com.key_system.a";
std::unique_ptr<FuchsiaCdmManager> cdm_manager =
CreateFuchsiaCdmManager({kKeySystem});
uint32_t added_data_store_id = 0;
EXPECT_CALL(mock_key_system(kKeySystem), AddDataStore(_, _, _))
.WillOnce(WithArgs<0, 2>(
Invoke([&](uint32_t data_store_id,
drm::KeySystem::AddDataStoreCallback callback) {
added_data_store_id = data_store_id;
callback(fit::ok());
})));
// Create a CDM to force a KeySystem binding
base::RunLoop create_run_loop;
drm::ContentDecryptionModulePtr cdm_ptr;
cdm_ptr.set_error_handler(
[&](zx_status_t status) { create_run_loop.Quit(); });
cdm_manager->CreateAndProvision(
kKeySystem, url::Origin(),
base::BindRepeating(&CreateMockProvisionFetcher), cdm_ptr.NewRequest());
create_run_loop.Run();
ASSERT_EQ(mock_key_system(kKeySystem).bindings().size(), 1u);
// Close the KeySystem's bindings and wait until empty
base::RunLoop disconnect_run_loop;
cdm_manager->set_on_key_system_disconnect_for_test_callback(
base::BindLambdaForTesting([&](const std::string& key_system_name) {
if (key_system_name == kKeySystem) {
disconnect_run_loop.Quit();
}
}));
mock_key_system(kKeySystem).bindings().CloseAll();
disconnect_run_loop.Run();
ASSERT_EQ(mock_key_system(kKeySystem).bindings().size(), 0u);
EXPECT_CALL(mock_key_system(kKeySystem),
AddDataStore(Eq(added_data_store_id), _, _))
.WillOnce(
WithArgs<2>(Invoke([](drm::KeySystem::AddDataStoreCallback callback) {
callback(fit::ok());
})));
base::RunLoop recreate_run_loop;
cdm_ptr.set_error_handler(
[&](zx_status_t status) { recreate_run_loop.Quit(); });
cdm_manager->CreateAndProvision(
kKeySystem, url::Origin(),
base::BindRepeating(&CreateMockProvisionFetcher), cdm_ptr.NewRequest());
recreate_run_loop.Run();
EXPECT_EQ(mock_key_system(kKeySystem).bindings().size(), 1u);
}
} // namespace
} // namespace media
// 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 "media/fuchsia/cdm/service/mock_provision_fetcher.h"
#include "media/base/provision_fetcher.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace testing {
MockProvisionFetcher::MockProvisionFetcher() = default;
MockProvisionFetcher::~MockProvisionFetcher() = default;
} // namespace testing
} // namespace media
// 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.
#ifndef MEDIA_FUCHSIA_CDM_SERVICE_MOCK_PROVISION_FETCHER_H_
#define MEDIA_FUCHSIA_CDM_SERVICE_MOCK_PROVISION_FETCHER_H_
#include <memory>
#include <string>
#include "media/base/provision_fetcher.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace media {
namespace testing {
// This is a mock for the Chromium media::ProvisionFetcher (and not Fuchsia's
// similarly named ProvisioningFetcher protocol).
class MockProvisionFetcher : public ProvisionFetcher {
public:
MockProvisionFetcher();
~MockProvisionFetcher();
// Disallow copy and assign
MockProvisionFetcher(const MockProvisionFetcher&) = delete;
MockProvisionFetcher(MockProvisionFetcher&&) = delete;
MockProvisionFetcher& operator=(const MockProvisionFetcher&) = delete;
MockProvisionFetcher& operator=(MockProvisionFetcher&&) = delete;
MOCK_METHOD(void,
Retrieve,
(const std::string& default_url,
const std::string& request_data,
ResponseCB response_cb),
(override));
};
} // namespace testing
} // namespace media
#endif // MEDIA_FUCHSIA_CDM_SERVICE_MOCK_PROVISION_FETCHER_H_
// 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 "media/fuchsia/cdm/service/provisioning_fetcher_impl.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/logging.h"
#include "fuchsia/base/mem_buffer_util.h"
namespace media {
ProvisioningFetcherImpl::ProvisioningFetcherImpl(
CreateFetcherCB create_fetcher_callback)
: create_fetcher_callback_(std::move(create_fetcher_callback)),
binding_(this) {
DCHECK(create_fetcher_callback_);
}
ProvisioningFetcherImpl::~ProvisioningFetcherImpl() = default;
fidl::InterfaceHandle<fuchsia::media::drm::ProvisioningFetcher>
ProvisioningFetcherImpl::Bind(base::OnceClosure error_callback) {
error_callback_ = std::move(error_callback);
binding_.set_error_handler(
[&error_callback = error_callback_](zx_status_t status) {
ZX_DLOG_IF(ERROR, (status != ZX_OK) && (status != ZX_ERR_PEER_CLOSED),
status)
<< "ProvisioningFetcher closed with an unexpected status";
std::move(error_callback).Run();
});
return binding_.NewBinding();
}
void ProvisioningFetcherImpl::Fetch(
fuchsia::media::drm::ProvisioningRequest request,
FetchCallback callback) {
if (retrieve_in_progress_) {
DLOG(WARNING)
<< "ProvisioningFetcherImpl received too many ProvisioningRequests";
OnError(ZX_ERR_BAD_STATE);
return;
}
std::string request_str;
if (!cr_fuchsia::StringFromMemBuffer(request.message, &request_str)) {
DLOG(WARNING) << "Failed to read ProvisioningRequest.";
OnError(ZX_ERR_INVALID_ARGS);
return;
}
if (!request.default_provisioning_server_url) {
DLOG(WARNING) << "Missing default provisioning server URL.";
OnError(ZX_ERR_INVALID_ARGS);
return;
}
if (!fetcher_) {
fetcher_ = create_fetcher_callback_.Run();
}
retrieve_in_progress_ = true;
fetcher_->Retrieve(
request.default_provisioning_server_url.value(), request_str,
base::BindRepeating(&ProvisioningFetcherImpl::OnRetrieveComplete,
base::Unretained(this),
base::Passed(std::move(callback))));
}
void ProvisioningFetcherImpl::OnRetrieveComplete(FetchCallback callback,
bool success,
const std::string& response) {
retrieve_in_progress_ = false;
// Regardless of success or failure, send the response back to acknowledge
// it has been completed.
DLOG_IF(WARNING, !success) << "Failed to fetch provision response.";
fuchsia::media::drm::ProvisioningResponse provision_response;
provision_response.message =
cr_fuchsia::MemBufferFromString(response, "cr-drm-provision-response");
callback(std::move(provision_response));
}
void ProvisioningFetcherImpl::OnError(zx_status_t status) {
DCHECK(error_callback_);
binding_.Close(status);
std::move(error_callback_).Run();
}
} // namespace media
// 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.
#ifndef MEDIA_FUCHSIA_CDM_SERVICE_PROVISIONING_FETCHER_IMPL_H_
#define MEDIA_FUCHSIA_CDM_SERVICE_PROVISIONING_FETCHER_IMPL_H_
#include <fuchsia/media/drm/cpp/fidl.h>
#include <lib/fidl/cpp/binding.h>
#include <lib/fidl/cpp/interface_handle.h>
#include <memory>
#include "base/callback_forward.h"
#include "media/base/provision_fetcher.h"
namespace media {
// Server end implementation of the fuchsia.media.drm.ProvisioningFetcher
// protocol. The client end is provided to the fuchsia.media.drm.KeySystem so
// that the KeySystem can invoke provisioning retrieval when necessary.
class ProvisioningFetcherImpl
: public fuchsia::media::drm::ProvisioningFetcher {
public:
explicit ProvisioningFetcherImpl(CreateFetcherCB create_fetcher_callback);
~ProvisioningFetcherImpl() override;
// Disallow copy and move
ProvisioningFetcherImpl(const ProvisioningFetcherImpl&) = delete;
ProvisioningFetcherImpl(ProvisioningFetcherImpl&&) = delete;
ProvisioningFetcherImpl& operator=(const ProvisioningFetcherImpl&) = delete;
ProvisioningFetcherImpl& operator=(ProvisioningFetcherImpl&&) = delete;
fidl::InterfaceHandle<fuchsia::media::drm::ProvisioningFetcher> Bind(
base::OnceClosure error_callback);
// fuchsia::media::drm::ProvisioningFetcher implementation.
void Fetch(fuchsia::media::drm::ProvisioningRequest request,
FetchCallback callback) override;
protected:
void OnRetrieveComplete(FetchCallback callback,
bool success,
const std::string& response);
void OnError(zx_status_t status);
private:
CreateFetcherCB create_fetcher_callback_;
fidl::Binding<fuchsia::media::drm::ProvisioningFetcher> binding_;
base::OnceClosure error_callback_;
std::unique_ptr<media::ProvisionFetcher> fetcher_;
bool retrieve_in_progress_ = false;
};
} // namespace media
#endif // MEDIA_FUCHSIA_CDM_SERVICE_PROVISIONING_FETCHER_IMPL_H_
// 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 "media/fuchsia/cdm/service/provisioning_fetcher_impl.h"
#include <fuchsia/media/drm/cpp/fidl.h>
#include <memory>
#include "base/bind.h"
#include "base/location.h"
#include "base/test/bind_test_util.h"
#include "base/test/task_environment.h"
#include "fuchsia/base/mem_buffer_util.h"
#include "media/fuchsia/cdm/service/mock_provision_fetcher.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace {
namespace drm = ::fuchsia::media::drm;
using ::testing::_;
using ::testing::Eq;
using ::testing::Invoke;
using ::testing::WithArgs;
using MockProvisionFetcher = ::media::testing::MockProvisionFetcher;
constexpr char kTestDefaultUrl[] = "http://test_default_url.com";
constexpr char kTestRequest[] = "test_request_message";
constexpr char kTestResponse[] = "test_response_message";
drm::ProvisioningRequest CreateProvisioningRequest(
fidl::StringPtr default_provisioning_server_url,
const std::string& message) {
drm::ProvisioningRequest request;
request.default_provisioning_server_url =
std::move(default_provisioning_server_url);
request.message =
cr_fuchsia::MemBufferFromString(message, "provisioning_request");
return request;
}
class ProvisioningFetcherImplTest : public ::testing::Test {
public:
ProvisioningFetcherImplTest() = default;
protected:
base::test::SingleThreadTaskEnvironment task_environment_{
base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
};
TEST_F(ProvisioningFetcherImplTest, Fetch) {
ProvisioningFetcherImpl fetcher(base::BindLambdaForTesting([]() {
auto mock_provision_fetcher = std::make_unique<MockProvisionFetcher>();
EXPECT_CALL(*mock_provision_fetcher,
Retrieve(Eq(kTestDefaultUrl), Eq(kTestRequest), _))
.WillOnce(WithArgs<2>(
Invoke([](ProvisionFetcher::ResponseCB response_callback) {
std::move(response_callback).Run(true, kTestResponse);
})));
return std::unique_ptr<ProvisionFetcher>(std::move(mock_provision_fetcher));
}));
fetcher.Bind(base::MakeExpectedNotRunClosure(FROM_HERE));
std::string response_message;
fetcher.Fetch(CreateProvisioningRequest(kTestDefaultUrl, kTestRequest),
[&](drm::ProvisioningResponse response) {
ASSERT_TRUE(cr_fuchsia::StringFromMemBuffer(
response.message, &response_message));
});
EXPECT_EQ(response_message, kTestResponse);
}
TEST_F(ProvisioningFetcherImplTest, RetrieveFails) {
ProvisioningFetcherImpl fetcher(base::BindLambdaForTesting([]() {
auto mock_provision_fetcher = std::make_unique<MockProvisionFetcher>();
EXPECT_CALL(*mock_provision_fetcher, Retrieve(_, _, _))
.WillOnce(WithArgs<2>(
Invoke([](ProvisionFetcher::ResponseCB response_callback) {
std::move(response_callback).Run(false, "");
})));
return std::unique_ptr<ProvisionFetcher>(std::move(mock_provision_fetcher));
}));
fetcher.Bind(base::MakeExpectedNotRunClosure(FROM_HERE));
std::string response_message;
fetcher.Fetch(CreateProvisioningRequest(kTestDefaultUrl, kTestRequest),
[&](drm::ProvisioningResponse response) {
ASSERT_TRUE(cr_fuchsia::StringFromMemBuffer(
response.message, &response_message));
});
EXPECT_TRUE(response_message.empty());
}
TEST_F(ProvisioningFetcherImplTest, NoDefaultProvisioningUrl) {
ProvisioningFetcherImpl fetcher(base::BindLambdaForTesting([]() {
auto mock_provision_fetcher = std::make_unique<MockProvisionFetcher>();
EXPECT_CALL(*mock_provision_fetcher, Retrieve(_, _, _))
.WillOnce(WithArgs<2>(
Invoke([](ProvisionFetcher::ResponseCB response_callback) {
std::move(response_callback).Run(true, kTestResponse);
})));
return std::unique_ptr<ProvisionFetcher>(std::move(mock_provision_fetcher));
}));
fetcher.Bind(base::MakeExpectedRunClosure(FROM_HERE));
fetcher.Fetch(CreateProvisioningRequest(fit::nullopt, kTestRequest),
[](drm::ProvisioningResponse response) { FAIL(); });
}
TEST_F(ProvisioningFetcherImplTest, MultipleRequestsFails) {
ProvisioningFetcherImpl fetcher(base::BindLambdaForTesting([]() {
auto mock_provision_fetcher = std::make_unique<MockProvisionFetcher>();
EXPECT_CALL(*mock_provision_fetcher, Retrieve(_, _, _)).Times(1);
return std::unique_ptr<ProvisionFetcher>(std::move(mock_provision_fetcher));
}));
fetcher.Bind(base::MakeExpectedRunClosure(FROM_HERE));
fetcher.Fetch(CreateProvisioningRequest(kTestDefaultUrl, kTestRequest),
[](drm::ProvisioningResponse) { FAIL(); });
fetcher.Fetch(CreateProvisioningRequest(kTestDefaultUrl, kTestRequest),
[](drm::ProvisioningResponse) { FAIL(); });
}
} // namespace
} // namespace media
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment