1 // Copyright 2018 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/mojo/services/cdm_service.h"
10 #include "base/files/file_path.h"
11 #include "base/functional/bind.h"
12 #include "base/memory/raw_ptr.h"
13 #include "base/run_loop.h"
14 #include "base/test/task_environment.h"
15 #include "base/unguessable_token.h"
16 #include "build/build_config.h"
17 #include "media/base/mock_filters.h"
18 #include "media/cdm/clear_key_cdm_common.h"
19 #include "media/cdm/default_cdm_factory.h"
20 #include "media/media_buildflags.h"
21 #include "mojo/public/cpp/bindings/pending_remote.h"
22 #include "mojo/public/cpp/bindings/remote.h"
23 #include "testing/gmock/include/gmock/gmock.h"
24 #include "testing/gtest/include/gtest/gtest.h"
32 using testing::Invoke;
33 using testing::InvokeWithoutArgs;
34 using testing::NiceMock;
35 using testing::Return;
37 MATCHER_P(MatchesResult, success, "") {
38 return arg->success == success;
41 // MockCdmFactory treats any non-empty key system as valid and the empty key
43 const char kInvalidKeySystem[] = "";
45 // Needed since MockCdmServiceClient needs to return unique_ptr of CdmFactory.
46 class CdmFactoryWrapper : public CdmFactory {
48 explicit CdmFactoryWrapper(CdmFactory* cdm_factory)
49 : cdm_factory_(cdm_factory) {}
51 // CdmFactory implementation.
52 void Create(const CdmConfig& cdm_config,
53 const SessionMessageCB& session_message_cb,
54 const SessionClosedCB& session_closed_cb,
55 const SessionKeysChangeCB& session_keys_change_cb,
56 const SessionExpirationUpdateCB& session_expiration_update_cb,
57 CdmCreatedCB cdm_created_cb) override {
58 cdm_factory_->Create(cdm_config, session_message_cb, session_closed_cb,
59 session_keys_change_cb, session_expiration_update_cb,
60 std::move(cdm_created_cb));
64 const raw_ptr<CdmFactory> cdm_factory_;
67 class MockCdmServiceClient : public CdmService::Client {
69 explicit MockCdmServiceClient(CdmFactory* cdm_factory)
70 : cdm_factory_(cdm_factory) {}
71 ~MockCdmServiceClient() override = default;
73 // CdmService::Client implementation.
74 MOCK_METHOD0(EnsureSandboxed, void());
76 std::unique_ptr<CdmFactory> CreateCdmFactory(
77 mojom::FrameInterfaceFactory* frame_interfaces) override {
78 return std::make_unique<CdmFactoryWrapper>(cdm_factory_);
81 #if BUILDFLAG(ENABLE_CDM_HOST_VERIFICATION)
82 void AddCdmHostFilePaths(std::vector<CdmHostFilePath>*) override {}
83 #endif // BUILDFLAG(ENABLE_CDM_HOST_VERIFICATION)
86 const raw_ptr<CdmFactory> cdm_factory_;
89 class CdmServiceTest : public testing::Test {
91 CdmServiceTest() = default;
93 CdmServiceTest(const CdmServiceTest&) = delete;
94 CdmServiceTest& operator=(const CdmServiceTest&) = delete;
96 ~CdmServiceTest() override = default;
98 MOCK_METHOD0(CdmServiceIdle, void());
99 MOCK_METHOD0(CdmFactoryConnectionClosed, void());
100 MOCK_METHOD0(CdmConnectionClosed, void());
103 EXPECT_CALL(*mock_cdm_, GetCdmContext())
104 .WillRepeatedly(Return(&cdm_context_));
106 auto mock_cdm_service_client =
107 std::make_unique<MockCdmServiceClient>(&mock_cdm_factory_);
108 mock_cdm_service_client_ = mock_cdm_service_client.get();
110 service_ = std::make_unique<CdmService>(
111 std::move(mock_cdm_service_client),
112 cdm_service_remote_.BindNewPipeAndPassReceiver());
113 cdm_service_remote_.set_idle_handler(
114 base::TimeDelta(), base::BindRepeating(&CdmServiceTest::CdmServiceIdle,
115 base::Unretained(this)));
117 mojo::PendingRemote<mojom::FrameInterfaceFactory> interfaces;
118 std::ignore = interfaces.InitWithNewPipeAndPassReceiver();
120 ASSERT_FALSE(cdm_factory_remote_);
121 cdm_service_remote_->CreateCdmFactory(
122 cdm_factory_remote_.BindNewPipeAndPassReceiver(),
123 std::move(interfaces));
124 cdm_service_remote_.FlushForTesting();
125 ASSERT_TRUE(cdm_factory_remote_);
126 cdm_factory_remote_.set_disconnect_handler(base::BindOnce(
127 &CdmServiceTest::CdmFactoryConnectionClosed, base::Unretained(this)));
130 void InitializeCdm(const std::string& key_system, bool expected_result) {
131 cdm_factory_remote_->CreateCdm(
132 {key_system, false, false, false},
133 base::BindOnce(&CdmServiceTest::OnCdmCreated, base::Unretained(this),
135 cdm_factory_remote_.FlushForTesting();
138 void DestroyService() { service_.reset(); }
140 MockCdmServiceClient* mock_cdm_service_client() {
141 return mock_cdm_service_client_;
144 CdmService* cdm_service() { return service_.get(); }
146 base::test::TaskEnvironment task_environment_;
147 mojo::Remote<mojom::CdmService> cdm_service_remote_;
148 mojo::Remote<mojom::CdmFactory> cdm_factory_remote_;
149 mojo::Remote<mojom::ContentDecryptionModule> cdm_remote_;
151 // MojoCdmService will always create/use `mock_cdm_factory_` and `mock_cdm_`,
152 // so it's easier to set expectations on them.
153 scoped_refptr<MockCdm> mock_cdm_{new MockCdm()};
154 MockCdmFactory mock_cdm_factory_{mock_cdm_};
155 NiceMock<MockCdmContext> cdm_context_;
158 void OnCdmCreated(bool expected_result,
159 mojo::PendingRemote<mojom::ContentDecryptionModule> remote,
160 mojom::CdmContextPtr cdm_context,
161 const std::string& error_message) {
162 if (!expected_result) {
163 EXPECT_FALSE(remote);
164 EXPECT_FALSE(cdm_context);
165 EXPECT_TRUE(!error_message.empty());
169 EXPECT_TRUE(error_message.empty());
170 cdm_remote_.Bind(std::move(remote));
171 cdm_remote_.set_disconnect_handler(base::BindOnce(
172 &CdmServiceTest::CdmConnectionClosed, base::Unretained(this)));
174 std::unique_ptr<CdmService> service_;
175 raw_ptr<MockCdmServiceClient, AcrossTasksDanglingUntriaged>
176 mock_cdm_service_client_ = nullptr;
181 TEST_F(CdmServiceTest, InitializeCdm_Success) {
183 InitializeCdm(kClearKeyKeySystem, true);
186 TEST_F(CdmServiceTest, InitializeCdm_InvalidKeySystem) {
188 InitializeCdm(kInvalidKeySystem, false);
191 TEST_F(CdmServiceTest, DestroyAndRecreateCdm) {
193 InitializeCdm(kClearKeyKeySystem, true);
195 InitializeCdm(kClearKeyKeySystem, true);
198 // CdmFactory disconnection will cause the service to idle.
199 TEST_F(CdmServiceTest, DestroyCdmFactory) {
201 auto* service = cdm_service();
203 InitializeCdm(kClearKeyKeySystem, true);
204 EXPECT_EQ(service->BoundCdmFactorySizeForTesting(), 1u);
205 EXPECT_EQ(service->UnboundCdmFactorySizeForTesting(), 0u);
207 cdm_factory_remote_.reset();
208 EXPECT_CALL(*this, CdmServiceIdle());
209 base::RunLoop().RunUntilIdle();
210 EXPECT_EQ(service->BoundCdmFactorySizeForTesting(), 0u);
211 EXPECT_EQ(service->UnboundCdmFactorySizeForTesting(), 1u);
214 // Destroy service will destroy the CdmFactory and all CDMs.
215 TEST_F(CdmServiceTest, DestroyCdmService_AfterCdmCreation) {
217 InitializeCdm(kClearKeyKeySystem, true);
219 base::RunLoop run_loop;
220 // Ideally we should not care about order, and should only quit the loop when
221 // both connections are closed.
222 EXPECT_CALL(*this, CdmFactoryConnectionClosed());
223 EXPECT_CALL(*this, CdmConnectionClosed())
224 .WillOnce(Invoke(&run_loop, &base::RunLoop::Quit));
229 // Before the CDM is fully created, CdmService has been destroyed. We should
230 // fail gracefully instead of a crash. See crbug.com/1190319.
231 TEST_F(CdmServiceTest, DestroyCdmService_DuringCdmCreation) {
232 base::RunLoop run_loop;
233 EXPECT_CALL(*this, CdmFactoryConnectionClosed())
234 .WillOnce(Invoke(&run_loop, &base::RunLoop::Quit));
235 mock_cdm_factory_.SetBeforeCreationCB(base::BindRepeating(
236 &CdmServiceTest::DestroyService, base::Unretained(this)));
238 InitializeCdm(kClearKeyKeySystem, true);