1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/media/cdm/browser_cdm_manager.h"
7 #include "base/command_line.h"
8 #include "content/common/media/cdm_messages.h"
9 #include "content/public/browser/content_browser_client.h"
10 #include "content/public/browser/render_frame_host.h"
11 #include "content/public/browser/render_process_host.h"
12 #include "content/public/browser/render_view_host.h"
13 #include "content/public/browser/web_contents.h"
14 #include "content/public/common/content_switches.h"
15 #include "media/base/browser_cdm.h"
16 #include "media/base/browser_cdm_factory.h"
17 #include "media/base/media_switches.h"
21 using media::BrowserCdm
;
22 using media::MediaKeys
;
24 // Maximum lengths for various EME API parameters. These are checks to
25 // prevent unnecessarily large parameters from being passed around, and the
26 // lengths are somewhat arbitrary as the EME spec doesn't specify any limits.
27 const size_t kMaxInitDataLength
= 64 * 1024; // 64 KB
28 const size_t kMaxSessionResponseLength
= 64 * 1024; // 64 KB
29 const size_t kMaxKeySystemLength
= 256;
32 BrowserCdmManager
* BrowserCdmManager::Create(RenderFrameHost
* rfh
) {
33 return new BrowserCdmManager(rfh
);
36 BrowserCdmManager::BrowserCdmManager(RenderFrameHost
* render_frame_host
)
37 : render_frame_host_(render_frame_host
),
38 web_contents_(WebContents::FromRenderFrameHost(render_frame_host
)),
39 weak_ptr_factory_(this) {
42 BrowserCdmManager::~BrowserCdmManager() {
43 // During the tear down process, OnDestroyCdm() may or may not be called
44 // (e.g. WebContents may be destroyed before the render process is killed). So
45 // we cannot DCHECK(cdm_map_.empty()) here. Instead, all CDMs in |cdm_map_|
46 // will be destroyed here because they are owned by BrowserCdmManager.
49 BrowserCdm
* BrowserCdmManager::GetCdm(int cdm_id
) {
50 return cdm_map_
.get(cdm_id
);
53 void BrowserCdmManager::OnSessionCreated(
56 const std::string
& web_session_id
) {
57 Send(new CdmMsg_SessionCreated(
58 RoutingID(), cdm_id
, session_id
, web_session_id
));
61 void BrowserCdmManager::OnSessionMessage(
64 const std::vector
<uint8
>& message
,
65 const GURL
& destination_url
) {
66 GURL verified_gurl
= destination_url
;
67 if (!verified_gurl
.is_valid() && !verified_gurl
.is_empty()) {
68 DLOG(WARNING
) << "SessionMessage destination_url is invalid : "
69 << destination_url
.possibly_invalid_spec();
70 verified_gurl
= GURL::EmptyGURL(); // Replace invalid destination_url.
73 Send(new CdmMsg_SessionMessage(
74 RoutingID(), cdm_id
, session_id
, message
, verified_gurl
));
77 void BrowserCdmManager::OnSessionReady(int cdm_id
, uint32 session_id
) {
78 Send(new CdmMsg_SessionReady(RoutingID(), cdm_id
, session_id
));
81 void BrowserCdmManager::OnSessionClosed(int cdm_id
, uint32 session_id
) {
82 Send(new CdmMsg_SessionClosed(RoutingID(), cdm_id
, session_id
));
85 void BrowserCdmManager::OnSessionError(int cdm_id
,
87 MediaKeys::KeyError error_code
,
89 Send(new CdmMsg_SessionError(
90 RoutingID(), cdm_id
, session_id
, error_code
, system_code
));
93 void BrowserCdmManager::OnInitializeCdm(int cdm_id
,
94 const std::string
& key_system
,
95 const GURL
& security_origin
) {
96 if (key_system
.size() > kMaxKeySystemLength
) {
97 // This failure will be discovered and reported by OnCreateSession()
98 // as GetCdm() will return null.
99 NOTREACHED() << "Invalid key system: " << key_system
;
103 AddCdm(cdm_id
, key_system
, security_origin
);
106 void BrowserCdmManager::OnCreateSession(
109 CdmHostMsg_CreateSession_ContentType content_type
,
110 const std::vector
<uint8
>& init_data
) {
111 if (init_data
.size() > kMaxInitDataLength
) {
112 LOG(WARNING
) << "InitData for ID: " << cdm_id
113 << " too long: " << init_data
.size();
114 OnSessionError(cdm_id
, session_id
, MediaKeys::kUnknownError
, 0);
118 // Convert the session content type into a MIME type. "audio" and "video"
119 // don't matter, so using "video" for the MIME type.
121 // https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html#dom-createsession
122 std::string mime_type
;
123 switch (content_type
) {
124 case CREATE_SESSION_TYPE_WEBM
:
125 mime_type
= "video/webm";
127 case CREATE_SESSION_TYPE_MP4
:
128 mime_type
= "video/mp4";
135 #if defined(OS_ANDROID)
136 if (CommandLine::ForCurrentProcess()
137 ->HasSwitch(switches::kDisableInfobarForProtectedMediaIdentifier
)) {
138 CreateSessionIfPermitted(cdm_id
, session_id
, mime_type
, init_data
, true);
143 BrowserCdm
* cdm
= GetCdm(cdm_id
);
145 DLOG(WARNING
) << "No CDM for ID " << cdm_id
<< " found";
146 OnSessionError(cdm_id
, session_id
, MediaKeys::kUnknownError
, 0);
150 std::map
<int, GURL
>::const_iterator iter
=
151 cdm_security_origin_map_
.find(cdm_id
);
152 if (iter
== cdm_security_origin_map_
.end()) {
154 OnSessionError(cdm_id
, session_id
, MediaKeys::kUnknownError
, 0);
158 base::Closure cancel_callback
;
159 GetContentClient()->browser()->RequestProtectedMediaIdentifierPermission(
162 base::Bind(&BrowserCdmManager::CreateSessionIfPermitted
,
163 weak_ptr_factory_
.GetWeakPtr(),
169 if (!cancel_callback
.is_null())
170 cdm_cancel_permision_map_
[cdm_id
] = cancel_callback
;
173 void BrowserCdmManager::OnUpdateSession(
176 const std::vector
<uint8
>& response
) {
177 BrowserCdm
* cdm
= GetCdm(cdm_id
);
179 DLOG(WARNING
) << "No CDM for ID " << cdm_id
<< " found";
180 OnSessionError(cdm_id
, session_id
, MediaKeys::kUnknownError
, 0);
184 if (response
.size() > kMaxSessionResponseLength
) {
185 LOG(WARNING
) << "Response for ID " << cdm_id
186 << " is too long: " << response
.size();
187 OnSessionError(cdm_id
, session_id
, MediaKeys::kUnknownError
, 0);
191 cdm
->UpdateSession(session_id
, &response
[0], response
.size());
194 void BrowserCdmManager::OnReleaseSession(int cdm_id
, uint32 session_id
) {
195 BrowserCdm
* cdm
= GetCdm(cdm_id
);
197 DLOG(WARNING
) << "No CDM for ID " << cdm_id
<< " found";
198 OnSessionError(cdm_id
, session_id
, MediaKeys::kUnknownError
, 0);
202 cdm
->ReleaseSession(session_id
);
205 void BrowserCdmManager::OnDestroyCdm(int cdm_id
) {
206 BrowserCdm
* cdm
= GetCdm(cdm_id
);
210 CancelAllPendingSessionCreations(cdm_id
);
214 void BrowserCdmManager::CancelAllPendingSessionCreations(int cdm_id
) {
215 if (cdm_cancel_permision_map_
.count(cdm_id
)) {
216 cdm_cancel_permision_map_
[cdm_id
].Run();
217 cdm_cancel_permision_map_
.erase(cdm_id
);
221 void BrowserCdmManager::AddCdm(int cdm_id
,
222 const std::string
& key_system
,
223 const GURL
& security_origin
) {
224 DCHECK(!GetCdm(cdm_id
));
225 base::WeakPtr
<BrowserCdmManager
> weak_this
= weak_ptr_factory_
.GetWeakPtr();
226 scoped_ptr
<BrowserCdm
> cdm(media::CreateBrowserCdm(
228 base::Bind(&BrowserCdmManager::OnSessionCreated
, weak_this
, cdm_id
),
229 base::Bind(&BrowserCdmManager::OnSessionMessage
, weak_this
, cdm_id
),
230 base::Bind(&BrowserCdmManager::OnSessionReady
, weak_this
, cdm_id
),
231 base::Bind(&BrowserCdmManager::OnSessionClosed
, weak_this
, cdm_id
),
232 base::Bind(&BrowserCdmManager::OnSessionError
, weak_this
, cdm_id
)));
235 // This failure will be discovered and reported by OnCreateSession()
236 // as GetCdm() will return null.
237 DVLOG(1) << "failed to create CDM.";
241 cdm_map_
.add(cdm_id
, cdm
.Pass());
242 cdm_security_origin_map_
[cdm_id
] = security_origin
;
245 void BrowserCdmManager::RemoveCdm(int cdm_id
) {
246 // TODO(xhwang): Detach CDM from the player it's set to. In prefixed
247 // EME implementation the current code is fine because we always destroy the
248 // player before we destroy the DrmBridge. This will not always be the case
249 // in unprefixed EME implementation.
250 cdm_map_
.erase(cdm_id
);
251 cdm_security_origin_map_
.erase(cdm_id
);
252 cdm_cancel_permision_map_
.erase(cdm_id
);
255 int BrowserCdmManager::RoutingID() {
256 return render_frame_host_
->GetRoutingID();
259 bool BrowserCdmManager::Send(IPC::Message
* msg
) {
260 return render_frame_host_
->Send(msg
);
263 void BrowserCdmManager::CreateSessionIfPermitted(
266 const std::string
& content_type
,
267 const std::vector
<uint8
>& init_data
,
269 cdm_cancel_permision_map_
.erase(cdm_id
);
271 OnSessionError(cdm_id
, session_id
, MediaKeys::kUnknownError
, 0);
275 BrowserCdm
* cdm
= GetCdm(cdm_id
);
277 DLOG(WARNING
) << "No CDM for ID: " << cdm_id
<< " found";
278 OnSessionError(cdm_id
, session_id
, MediaKeys::kUnknownError
, 0);
282 // This could fail, in which case a SessionError will be fired.
283 cdm
->CreateSession(session_id
, content_type
, &init_data
[0], init_data
.size());
286 } // namespace content