OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "content/browser/media/android/browser_cdm_manager.h" | |
6 | |
7 #include "base/command_line.h" | |
8 #include "base/stl_util.h" | |
9 #include "content/common/media/cdm_messages.h" | |
10 #include "content/public/browser/browser_context.h" | |
11 #include "content/public/browser/render_frame_host.h" | |
12 #include "content/public/browser/render_process_host.h" | |
13 #include "content/public/browser/render_view_host.h" | |
14 #include "content/public/browser/web_contents.h" | |
15 #include "content/public/common/content_client.h" | |
16 #include "content/public/common/content_switches.h" | |
17 #include "media/base/browser_cdm.h" | |
18 #include "media/base/browser_cdm_factory.h" | |
19 #include "media/base/media_switches.h" | |
20 | |
21 namespace content { | |
22 | |
23 using media::BrowserCdm; | |
24 using media::MediaKeys; | |
25 | |
26 // Maximum lengths for various EME API parameters. These are checks to | |
27 // prevent unnecessarily large parameters from being passed around, and the | |
28 // lengths are somewhat arbitrary as the EME spec doesn't specify any limits. | |
29 const size_t kMaxInitDataLength = 64 * 1024; // 64 KB | |
30 const size_t kMaxSessionResponseLength = 64 * 1024; // 64 KB | |
31 const size_t kMaxKeySystemLength = 256; | |
32 | |
33 // static | |
34 BrowserCdmManager* BrowserCdmManager::Create(RenderFrameHost* rfh) { | |
35 return new BrowserCdmManager(rfh); | |
36 } | |
37 | |
38 BrowserCdmManager::BrowserCdmManager(RenderFrameHost* render_frame_host) | |
39 : render_frame_host_(render_frame_host), | |
40 web_contents_(WebContents::FromRenderFrameHost(render_frame_host)), | |
41 weak_ptr_factory_(this) { | |
42 } | |
43 | |
44 BrowserCdmManager::~BrowserCdmManager() { | |
45 STLDeleteValues(&cdm_map_); | |
46 } | |
47 | |
48 BrowserCdm* BrowserCdmManager::GetCdm(int cdm_id) { | |
49 CdmMap::const_iterator iter = cdm_map_.find(cdm_id); | |
50 return (iter == cdm_map_.end()) ? NULL : iter->second; | |
51 } | |
52 | |
53 void BrowserCdmManager::OnSessionCreated( | |
54 int cdm_id, | |
55 uint32 session_id, | |
56 const std::string& web_session_id) { | |
57 Send(new CdmMsg_SessionCreated( | |
58 RoutingID(), cdm_id, session_id, web_session_id)); | |
59 } | |
60 | |
61 void BrowserCdmManager::OnSessionMessage( | |
62 int cdm_id, | |
63 uint32 session_id, | |
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. | |
71 } | |
72 | |
73 Send(new CdmMsg_SessionMessage( | |
74 RoutingID(), cdm_id, session_id, message, verified_gurl)); | |
75 } | |
76 | |
77 void BrowserCdmManager::OnSessionReady(int cdm_id, uint32 session_id) { | |
78 Send(new CdmMsg_SessionReady(RoutingID(), cdm_id, session_id)); | |
79 } | |
80 | |
81 void BrowserCdmManager::OnSessionClosed(int cdm_id, uint32 session_id) { | |
82 Send(new CdmMsg_SessionClosed(RoutingID(), cdm_id, session_id)); | |
83 } | |
84 | |
85 void BrowserCdmManager::OnSessionError(int cdm_id, | |
86 uint32 session_id, | |
87 MediaKeys::KeyError error_code, | |
88 uint32 system_code) { | |
89 Send(new CdmMsg_SessionError( | |
90 RoutingID(), cdm_id, session_id, error_code, system_code)); | |
91 } | |
92 | |
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; | |
100 return; | |
101 } | |
102 | |
103 AddCdm(cdm_id, key_system, security_origin); | |
104 } | |
105 | |
106 void BrowserCdmManager::OnCreateSession( | |
107 int cdm_id, | |
108 uint32 session_id, | |
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); | |
115 return; | |
116 } | |
117 | |
118 // Convert the session content type into a MIME type. "audio" and "video" | |
119 // don't matter, so using "video" for the MIME type. | |
120 // Ref: | |
121 // https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypte
d-media.html#dom-createsession | |
122 std::string mime_type; | |
123 switch (content_type) { | |
124 case CREATE_SESSION_TYPE_WEBM: | |
125 mime_type = "video/webm"; | |
126 break; | |
127 case CREATE_SESSION_TYPE_MP4: | |
128 mime_type = "video/mp4"; | |
129 break; | |
130 default: | |
131 NOTREACHED(); | |
132 return; | |
133 } | |
134 | |
135 #if defined(OS_ANDROID) | |
136 if (CommandLine::ForCurrentProcess() | |
137 ->HasSwitch(switches::kDisableInfobarForProtectedMediaIdentifier)) { | |
138 CreateSessionIfPermitted(cdm_id, session_id, mime_type, init_data, true); | |
139 return; | |
140 } | |
141 #endif | |
142 | |
143 BrowserCdm* cdm = GetCdm(cdm_id); | |
144 if (!cdm) { | |
145 DLOG(WARNING) << "No CDM for ID " << cdm_id << " found"; | |
146 OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0); | |
147 return; | |
148 } | |
149 | |
150 BrowserContext* context = | |
151 web_contents_->GetRenderProcessHost()->GetBrowserContext(); | |
152 | |
153 std::map<int, GURL>::const_iterator iter = | |
154 cdm_security_origin_map_.find(cdm_id); | |
155 if (iter == cdm_security_origin_map_.end()) { | |
156 NOTREACHED(); | |
157 OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0); | |
158 return; | |
159 } | |
160 | |
161 context->RequestProtectedMediaIdentifierPermission( | |
162 web_contents_->GetRenderProcessHost()->GetID(), | |
163 web_contents_->GetRenderViewHost()->GetRoutingID(), | |
164 iter->second, | |
165 base::Bind(&BrowserCdmManager::CreateSessionIfPermitted, | |
166 weak_ptr_factory_.GetWeakPtr(), | |
167 cdm_id, | |
168 session_id, | |
169 mime_type, | |
170 init_data)); | |
171 } | |
172 | |
173 void BrowserCdmManager::OnUpdateSession( | |
174 int cdm_id, | |
175 uint32 session_id, | |
176 const std::vector<uint8>& response) { | |
177 BrowserCdm* cdm = GetCdm(cdm_id); | |
178 if (!cdm) { | |
179 DLOG(WARNING) << "No CDM for ID " << cdm_id << " found"; | |
180 OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0); | |
181 return; | |
182 } | |
183 | |
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); | |
188 return; | |
189 } | |
190 | |
191 cdm->UpdateSession(session_id, &response[0], response.size()); | |
192 } | |
193 | |
194 void BrowserCdmManager::OnReleaseSession(int cdm_id, uint32 session_id) { | |
195 BrowserCdm* cdm = GetCdm(cdm_id); | |
196 if (!cdm) { | |
197 DLOG(WARNING) << "No CDM for ID " << cdm_id << " found"; | |
198 OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0); | |
199 return; | |
200 } | |
201 | |
202 cdm->ReleaseSession(session_id); | |
203 } | |
204 | |
205 void BrowserCdmManager::OnDestroyCdm(int cdm_id) { | |
206 BrowserCdm* cdm = GetCdm(cdm_id); | |
207 if (!cdm) | |
208 return; | |
209 | |
210 CancelAllPendingSessionCreations(cdm_id); | |
211 RemoveCdm(cdm_id); | |
212 } | |
213 | |
214 void BrowserCdmManager::CancelAllPendingSessionCreations(int cdm_id) { | |
215 BrowserContext* context = | |
216 web_contents_->GetRenderProcessHost()->GetBrowserContext(); | |
217 std::map<int, GURL>::const_iterator iter = | |
218 cdm_security_origin_map_.find(cdm_id); | |
219 if (iter == cdm_security_origin_map_.end()) | |
220 return; | |
221 context->CancelProtectedMediaIdentifierPermissionRequests( | |
222 web_contents_->GetRenderProcessHost()->GetID(), | |
223 web_contents_->GetRenderViewHost()->GetRoutingID(), | |
224 iter->second); | |
225 } | |
226 | |
227 void BrowserCdmManager::AddCdm(int cdm_id, | |
228 const std::string& key_system, | |
229 const GURL& security_origin) { | |
230 DCHECK(!GetCdm(cdm_id)); | |
231 base::WeakPtr<BrowserCdmManager> weak_this = weak_ptr_factory_.GetWeakPtr(); | |
232 scoped_ptr<BrowserCdm> cdm(media::CreateBrowserCdm( | |
233 key_system, | |
234 base::Bind(&BrowserCdmManager::OnSessionCreated, weak_this, cdm_id), | |
235 base::Bind(&BrowserCdmManager::OnSessionMessage, weak_this, cdm_id), | |
236 base::Bind(&BrowserCdmManager::OnSessionReady, weak_this, cdm_id), | |
237 base::Bind(&BrowserCdmManager::OnSessionClosed, weak_this, cdm_id), | |
238 base::Bind(&BrowserCdmManager::OnSessionError, weak_this, cdm_id))); | |
239 | |
240 if (!cdm) { | |
241 // This failure will be discovered and reported by OnCreateSession() | |
242 // as GetCdm() will return null. | |
243 DVLOG(1) << "failed to create CDM."; | |
244 return; | |
245 } | |
246 | |
247 cdm_map_[cdm_id] = cdm.release(); | |
248 cdm_security_origin_map_[cdm_id] = security_origin; | |
249 } | |
250 | |
251 void BrowserCdmManager::RemoveCdm(int cdm_id) { | |
252 // TODO(xhwang): Detach CDM from the player it's set to. In prefixed | |
253 // EME implementation the current code is fine because we always destroy the | |
254 // player before we destroy the DrmBridge. This will not always be the case | |
255 // in unprefixed EME implementation. | |
256 CdmMap::iterator iter = cdm_map_.find(cdm_id); | |
257 if (iter != cdm_map_.end()) { | |
258 delete iter->second; | |
259 cdm_map_.erase(iter); | |
260 } | |
261 cdm_security_origin_map_.erase(cdm_id); | |
262 } | |
263 | |
264 int BrowserCdmManager::RoutingID() { | |
265 return render_frame_host_->GetRoutingID(); | |
266 } | |
267 | |
268 bool BrowserCdmManager::Send(IPC::Message* msg) { | |
269 return render_frame_host_->Send(msg); | |
270 } | |
271 | |
272 void BrowserCdmManager::CreateSessionIfPermitted( | |
273 int cdm_id, | |
274 uint32 session_id, | |
275 const std::string& content_type, | |
276 const std::vector<uint8>& init_data, | |
277 bool permitted) { | |
278 if (!permitted) { | |
279 OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0); | |
280 return; | |
281 } | |
282 | |
283 BrowserCdm* cdm = GetCdm(cdm_id); | |
284 if (!cdm) { | |
285 DLOG(WARNING) << "No CDM for ID: " << cdm_id << " found"; | |
286 OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0); | |
287 return; | |
288 } | |
289 | |
290 // This could fail, in which case a SessionError will be fired. | |
291 cdm->CreateSession(session_id, content_type, &init_data[0], init_data.size()); | |
292 } | |
293 | |
294 } // namespace content | |
OLD | NEW |