Supervised user import: Listen for profile creation/deletion
[chromium-blink-merge.git] / content / browser / media / cdm / browser_cdm_manager.cc
blob8f1e78ae84324ea41406913c2d156bda8fa0774b
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 <string>
9 #include "base/bind.h"
10 #include "base/lazy_instance.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/task_runner.h"
13 #include "content/common/media/cdm_messages.h"
14 #include "content/public/browser/browser_context.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "content/public/browser/content_browser_client.h"
17 #include "content/public/browser/permission_manager.h"
18 #include "content/public/browser/permission_type.h"
19 #include "content/public/browser/render_frame_host.h"
20 #include "content/public/browser/render_process_host.h"
21 #include "content/public/browser/render_view_host.h"
22 #include "content/public/browser/web_contents.h"
23 #include "media/base/browser_cdm.h"
24 #include "media/base/browser_cdm_factory.h"
25 #include "media/base/cdm_promise.h"
26 #include "media/base/limits.h"
28 #if defined(OS_ANDROID)
29 #include "content/public/common/renderer_preferences.h"
30 #endif
32 namespace content {
34 using media::BrowserCdm;
35 using media::MediaKeys;
37 namespace {
39 // The ID used in this class is a concatenation of |render_frame_id| and
40 // |cdm_id|, i.e. (render_frame_id << 32) + cdm_id.
42 uint64 GetId(int render_frame_id, int cdm_id) {
43 return (static_cast<uint64>(render_frame_id) << 32) +
44 static_cast<uint64>(cdm_id);
47 bool IdBelongsToFrame(uint64 id, int render_frame_id) {
48 return (id >> 32) == static_cast<uint64>(render_frame_id);
51 // media::CdmPromiseTemplate implementation backed by a BrowserCdmManager.
52 template <typename... T>
53 class CdmPromiseInternal : public media::CdmPromiseTemplate<T...> {
54 public:
55 CdmPromiseInternal(BrowserCdmManager* manager,
56 int render_frame_id,
57 int cdm_id,
58 uint32_t promise_id)
59 : manager_(manager),
60 render_frame_id_(render_frame_id),
61 cdm_id_(cdm_id),
62 promise_id_(promise_id) {
63 DCHECK(manager_);
66 ~CdmPromiseInternal() final {}
68 // CdmPromiseTemplate<> implementation.
69 void resolve(const T&... result) final;
71 void reject(MediaKeys::Exception exception,
72 uint32_t system_code,
73 const std::string& error_message) final {
74 MarkPromiseSettled();
75 manager_->RejectPromise(render_frame_id_, cdm_id_, promise_id_, exception,
76 system_code, error_message);
79 private:
80 using media::CdmPromiseTemplate<T...>::MarkPromiseSettled;
82 BrowserCdmManager* const manager_;
83 const int render_frame_id_;
84 const int cdm_id_;
85 const uint32_t promise_id_;
88 template <>
89 void CdmPromiseInternal<>::resolve() {
90 MarkPromiseSettled();
91 manager_->ResolvePromise(render_frame_id_, cdm_id_, promise_id_);
94 template <>
95 void CdmPromiseInternal<std::string>::resolve(const std::string& session_id) {
96 MarkPromiseSettled();
97 manager_->ResolvePromiseWithSession(render_frame_id_, cdm_id_, promise_id_,
98 session_id);
101 typedef CdmPromiseInternal<> SimplePromise;
102 typedef CdmPromiseInternal<std::string> NewSessionPromise;
104 } // namespace
106 // Render process ID to BrowserCdmManager map.
107 typedef std::map<int, BrowserCdmManager*> BrowserCdmManagerMap;
108 base::LazyInstance<BrowserCdmManagerMap> g_browser_cdm_manager_map =
109 LAZY_INSTANCE_INITIALIZER;
111 BrowserCdmManager* BrowserCdmManager::FromProcess(int render_process_id) {
112 DCHECK_CURRENTLY_ON(BrowserThread::UI);
114 if (!g_browser_cdm_manager_map.Get().count(render_process_id))
115 return NULL;
117 return g_browser_cdm_manager_map.Get()[render_process_id];
120 BrowserCdmManager::BrowserCdmManager(
121 int render_process_id,
122 const scoped_refptr<base::TaskRunner>& task_runner)
123 : BrowserMessageFilter(CdmMsgStart),
124 render_process_id_(render_process_id),
125 task_runner_(task_runner),
126 weak_ptr_factory_(this) {
127 DVLOG(1) << __FUNCTION__ << ": " << render_process_id_;
128 DCHECK_CURRENTLY_ON(BrowserThread::UI);
130 if (!task_runner_.get()) {
131 task_runner_ =
132 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
135 DCHECK(!g_browser_cdm_manager_map.Get().count(render_process_id_))
136 << render_process_id_;
137 g_browser_cdm_manager_map.Get()[render_process_id] = this;
140 BrowserCdmManager::~BrowserCdmManager() {
141 DVLOG(1) << __FUNCTION__ << ": " << render_process_id_;
142 DCHECK_CURRENTLY_ON(BrowserThread::UI);
143 DCHECK(g_browser_cdm_manager_map.Get().count(render_process_id_));
144 DCHECK_EQ(this, g_browser_cdm_manager_map.Get()[render_process_id_]);
146 g_browser_cdm_manager_map.Get().erase(render_process_id_);
149 // Makes sure BrowserCdmManager is always deleted on the Browser UI thread.
150 void BrowserCdmManager::OnDestruct() const {
151 DVLOG(1) << __FUNCTION__ << ": " << render_process_id_;
152 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
153 delete this;
154 } else {
155 BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, this);
159 base::TaskRunner* BrowserCdmManager::OverrideTaskRunnerForMessage(
160 const IPC::Message& message) {
161 // Only handles CDM messages.
162 if (IPC_MESSAGE_CLASS(message) != CdmMsgStart)
163 return NULL;
165 return task_runner_.get();
168 bool BrowserCdmManager::OnMessageReceived(const IPC::Message& msg) {
169 DCHECK(task_runner_->RunsTasksOnCurrentThread());
170 bool handled = true;
171 IPC_BEGIN_MESSAGE_MAP(BrowserCdmManager, msg)
172 IPC_MESSAGE_HANDLER(CdmHostMsg_InitializeCdm, OnInitializeCdm)
173 IPC_MESSAGE_HANDLER(CdmHostMsg_SetServerCertificate, OnSetServerCertificate)
174 IPC_MESSAGE_HANDLER(CdmHostMsg_CreateSessionAndGenerateRequest,
175 OnCreateSessionAndGenerateRequest)
176 IPC_MESSAGE_HANDLER(CdmHostMsg_UpdateSession, OnUpdateSession)
177 IPC_MESSAGE_HANDLER(CdmHostMsg_CloseSession, OnCloseSession)
178 IPC_MESSAGE_HANDLER(CdmHostMsg_DestroyCdm, OnDestroyCdm)
179 IPC_MESSAGE_UNHANDLED(handled = false)
180 IPC_END_MESSAGE_MAP()
181 return handled;
184 media::BrowserCdm* BrowserCdmManager::GetCdm(int render_frame_id,
185 int cdm_id) const {
186 DCHECK(task_runner_->RunsTasksOnCurrentThread());
187 return cdm_map_.get(GetId(render_frame_id, cdm_id));
190 void BrowserCdmManager::RenderFrameDeleted(int render_frame_id) {
191 if (!task_runner_->RunsTasksOnCurrentThread()) {
192 task_runner_->PostTask(
193 FROM_HERE,
194 base::Bind(&BrowserCdmManager::RemoveAllCdmForFrame,
195 this, render_frame_id));
196 return;
198 RemoveAllCdmForFrame(render_frame_id);
201 void BrowserCdmManager::ResolvePromise(int render_frame_id,
202 int cdm_id,
203 uint32_t promise_id) {
204 Send(new CdmMsg_ResolvePromise(render_frame_id, cdm_id, promise_id));
207 void BrowserCdmManager::ResolvePromiseWithSession(
208 int render_frame_id,
209 int cdm_id,
210 uint32_t promise_id,
211 const std::string& session_id) {
212 if (session_id.length() > media::limits::kMaxSessionIdLength) {
213 RejectPromise(render_frame_id, cdm_id, promise_id,
214 MediaKeys::INVALID_ACCESS_ERROR, 0,
215 "Session ID is too long.");
216 return;
219 Send(new CdmMsg_ResolvePromiseWithSession(render_frame_id, cdm_id, promise_id,
220 session_id));
223 void BrowserCdmManager::RejectPromise(int render_frame_id,
224 int cdm_id,
225 uint32_t promise_id,
226 media::MediaKeys::Exception exception,
227 uint32_t system_code,
228 const std::string& error_message) {
229 Send(new CdmMsg_RejectPromise(render_frame_id, cdm_id, promise_id, exception,
230 system_code, error_message));
233 void BrowserCdmManager::OnSessionMessage(
234 int render_frame_id,
235 int cdm_id,
236 const std::string& session_id,
237 media::MediaKeys::MessageType message_type,
238 const std::vector<uint8>& message,
239 const GURL& legacy_destination_url) {
240 GURL verified_gurl = legacy_destination_url;
241 if (!verified_gurl.is_valid() && !verified_gurl.is_empty()) {
242 DLOG(WARNING) << "SessionMessage legacy_destination_url is invalid : "
243 << legacy_destination_url.possibly_invalid_spec();
244 verified_gurl =
245 GURL::EmptyGURL(); // Replace invalid legacy_destination_url.
248 Send(new CdmMsg_SessionMessage(render_frame_id, cdm_id, session_id,
249 message_type, message, verified_gurl));
252 void BrowserCdmManager::OnSessionClosed(int render_frame_id,
253 int cdm_id,
254 const std::string& session_id) {
255 Send(new CdmMsg_SessionClosed(render_frame_id, cdm_id, session_id));
258 void BrowserCdmManager::OnLegacySessionError(
259 int render_frame_id,
260 int cdm_id,
261 const std::string& session_id,
262 MediaKeys::Exception exception_code,
263 uint32 system_code,
264 const std::string& error_message) {
265 Send(new CdmMsg_LegacySessionError(render_frame_id, cdm_id, session_id,
266 exception_code, system_code,
267 error_message));
270 void BrowserCdmManager::OnSessionKeysChange(int render_frame_id,
271 int cdm_id,
272 const std::string& session_id,
273 bool has_additional_usable_key,
274 media::CdmKeysInfo keys_info) {
275 std::vector<media::CdmKeyInformation> key_info_vector;
276 for (const auto& key_info : keys_info)
277 key_info_vector.push_back(*key_info);
278 Send(new CdmMsg_SessionKeysChange(render_frame_id, cdm_id, session_id,
279 has_additional_usable_key,
280 key_info_vector));
283 void BrowserCdmManager::OnSessionExpirationUpdate(
284 int render_frame_id,
285 int cdm_id,
286 const std::string& session_id,
287 const base::Time& new_expiry_time) {
288 Send(new CdmMsg_SessionExpirationUpdate(render_frame_id, cdm_id, session_id,
289 new_expiry_time));
292 void BrowserCdmManager::OnInitializeCdm(int render_frame_id,
293 int cdm_id,
294 const std::string& key_system,
295 const GURL& security_origin) {
296 if (key_system.size() > media::limits::kMaxKeySystemLength) {
297 // This failure will be discovered and reported by OnCreateSession()
298 // as GetCdm() will return null.
299 NOTREACHED() << "Invalid key system: " << key_system;
300 return;
303 AddCdm(render_frame_id, cdm_id, key_system, security_origin);
306 void BrowserCdmManager::OnSetServerCertificate(
307 int render_frame_id,
308 int cdm_id,
309 uint32_t promise_id,
310 const std::vector<uint8_t>& certificate) {
311 DCHECK(task_runner_->RunsTasksOnCurrentThread());
313 scoped_ptr<SimplePromise> promise(
314 new SimplePromise(this, render_frame_id, cdm_id, promise_id));
316 BrowserCdm* cdm = GetCdm(render_frame_id, cdm_id);
317 if (!cdm) {
318 promise->reject(MediaKeys::INVALID_STATE_ERROR, 0, "CDM not found.");
319 return;
322 if (certificate.empty()) {
323 promise->reject(MediaKeys::INVALID_ACCESS_ERROR, 0, "Empty certificate.");
324 return;
327 cdm->SetServerCertificate(certificate, promise.Pass());
330 void BrowserCdmManager::OnCreateSessionAndGenerateRequest(
331 int render_frame_id,
332 int cdm_id,
333 uint32_t promise_id,
334 CdmHostMsg_CreateSession_InitDataType init_data_type,
335 const std::vector<uint8>& init_data) {
336 DCHECK(task_runner_->RunsTasksOnCurrentThread());
338 scoped_ptr<NewSessionPromise> promise(
339 new NewSessionPromise(this, render_frame_id, cdm_id, promise_id));
341 if (init_data.size() > media::limits::kMaxInitDataLength) {
342 LOG(WARNING) << "InitData for ID: " << cdm_id
343 << " too long: " << init_data.size();
344 promise->reject(MediaKeys::INVALID_ACCESS_ERROR, 0, "Init data too long.");
345 return;
348 media::EmeInitDataType eme_init_data_type;
349 switch (init_data_type) {
350 case INIT_DATA_TYPE_WEBM:
351 eme_init_data_type = media::EmeInitDataType::WEBM;
352 break;
353 #if defined(USE_PROPRIETARY_CODECS)
354 case INIT_DATA_TYPE_CENC:
355 eme_init_data_type = media::EmeInitDataType::CENC;
356 break;
357 #endif
358 default:
359 NOTREACHED();
360 promise->reject(MediaKeys::INVALID_ACCESS_ERROR, 0,
361 "Invalid init data type.");
362 return;
365 BrowserCdm* cdm = GetCdm(render_frame_id, cdm_id);
366 if (!cdm) {
367 DLOG(WARNING) << "No CDM found for: " << render_frame_id << ", " << cdm_id;
368 promise->reject(MediaKeys::INVALID_STATE_ERROR, 0, "CDM not found.");
369 return;
372 CheckPermissionStatus(
373 render_frame_id, cdm_id,
374 base::Bind(&BrowserCdmManager::CreateSessionAndGenerateRequestIfPermitted,
375 this, render_frame_id, cdm_id, eme_init_data_type, init_data,
376 base::Passed(&promise)));
379 void BrowserCdmManager::OnUpdateSession(int render_frame_id,
380 int cdm_id,
381 uint32_t promise_id,
382 const std::string& session_id,
383 const std::vector<uint8>& response) {
384 DCHECK(task_runner_->RunsTasksOnCurrentThread());
386 scoped_ptr<SimplePromise> promise(
387 new SimplePromise(this, render_frame_id, cdm_id, promise_id));
389 BrowserCdm* cdm = GetCdm(render_frame_id, cdm_id);
390 if (!cdm) {
391 promise->reject(MediaKeys::INVALID_STATE_ERROR, 0, "CDM not found.");
392 return;
395 if (response.size() > media::limits::kMaxSessionResponseLength) {
396 LOG(WARNING) << "Response for ID " << cdm_id
397 << " is too long: " << response.size();
398 promise->reject(MediaKeys::INVALID_ACCESS_ERROR, 0, "Response too long.");
399 return;
402 if (response.empty()) {
403 promise->reject(MediaKeys::INVALID_ACCESS_ERROR, 0, "Response is empty.");
404 return;
407 cdm->UpdateSession(session_id, response, promise.Pass());
410 void BrowserCdmManager::OnCloseSession(int render_frame_id,
411 int cdm_id,
412 uint32_t promise_id,
413 const std::string& session_id) {
414 DCHECK(task_runner_->RunsTasksOnCurrentThread());
416 scoped_ptr<SimplePromise> promise(
417 new SimplePromise(this, render_frame_id, cdm_id, promise_id));
419 BrowserCdm* cdm = GetCdm(render_frame_id, cdm_id);
420 if (!cdm) {
421 promise->reject(MediaKeys::INVALID_STATE_ERROR, 0, "CDM not found.");
422 return;
425 cdm->CloseSession(session_id, promise.Pass());
428 void BrowserCdmManager::OnDestroyCdm(int render_frame_id, int cdm_id) {
429 DCHECK(task_runner_->RunsTasksOnCurrentThread());
430 RemoveCdm(GetId(render_frame_id, cdm_id));
433 // Use a weak pointer here instead of |this| to avoid circular references.
434 #define BROWSER_CDM_MANAGER_CB(func) \
435 base::Bind(&BrowserCdmManager::func, weak_ptr_factory_.GetWeakPtr(), \
436 render_frame_id, cdm_id)
438 void BrowserCdmManager::AddCdm(int render_frame_id,
439 int cdm_id,
440 const std::string& key_system,
441 const GURL& security_origin) {
442 DCHECK(task_runner_->RunsTasksOnCurrentThread());
443 DCHECK(!GetCdm(render_frame_id, cdm_id));
445 bool use_secure_surface = false;
447 #if defined(OS_ANDROID)
448 // TODO(sandersd): Pass the security level from key system instead.
449 // http://crbug.com/467779
450 RenderFrameHost* rfh =
451 RenderFrameHost::FromID(render_process_id_, render_frame_id);
452 WebContents* web_contents = WebContents::FromRenderFrameHost(rfh);
453 if (web_contents) {
454 content::RendererPreferences* prefs =
455 web_contents->GetMutableRendererPrefs();
456 use_secure_surface = prefs->use_video_overlay_for_embedded_encrypted_video;
458 #endif
460 scoped_ptr<BrowserCdm> cdm(media::CreateBrowserCdm(
461 key_system, use_secure_surface, BROWSER_CDM_MANAGER_CB(OnSessionMessage),
462 BROWSER_CDM_MANAGER_CB(OnSessionClosed),
463 BROWSER_CDM_MANAGER_CB(OnLegacySessionError),
464 BROWSER_CDM_MANAGER_CB(OnSessionKeysChange),
465 BROWSER_CDM_MANAGER_CB(OnSessionExpirationUpdate)));
467 if (!cdm) {
468 // This failure will be discovered and reported by
469 // OnCreateSessionAndGenerateRequest() as GetCdm() will return null.
470 DVLOG(1) << "failed to create CDM.";
471 return;
474 uint64 id = GetId(render_frame_id, cdm_id);
475 cdm_map_.add(id, cdm.Pass());
476 cdm_security_origin_map_[id] = security_origin;
479 void BrowserCdmManager::RemoveAllCdmForFrame(int render_frame_id) {
480 DCHECK(task_runner_->RunsTasksOnCurrentThread());
482 std::vector<uint64> ids_to_remove;
483 for (CdmMap::iterator it = cdm_map_.begin(); it != cdm_map_.end(); ++it) {
484 if (IdBelongsToFrame(it->first, render_frame_id))
485 ids_to_remove.push_back(it->first);
488 for (size_t i = 0; i < ids_to_remove.size(); ++i)
489 RemoveCdm(ids_to_remove[i]);
492 void BrowserCdmManager::RemoveCdm(uint64 id) {
493 DCHECK(task_runner_->RunsTasksOnCurrentThread());
495 cdm_map_.erase(id);
496 cdm_security_origin_map_.erase(id);
499 void BrowserCdmManager::CheckPermissionStatus(
500 int render_frame_id,
501 int cdm_id,
502 const PermissionStatusCB& permission_status_cb) {
503 // Always called on |task_runner_|, which may not be on the UI thread.
504 DCHECK(task_runner_->RunsTasksOnCurrentThread());
506 GURL security_origin;
507 std::map<uint64, GURL>::const_iterator iter =
508 cdm_security_origin_map_.find(GetId(render_frame_id, cdm_id));
509 DCHECK(iter != cdm_security_origin_map_.end());
510 if (iter != cdm_security_origin_map_.end())
511 security_origin = iter->second;
513 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
514 BrowserThread::PostTask(
515 BrowserThread::UI, FROM_HERE,
516 base::Bind(&BrowserCdmManager::CheckPermissionStatusOnUIThread, this,
517 render_frame_id, security_origin, permission_status_cb));
518 } else {
519 CheckPermissionStatusOnUIThread(render_frame_id, security_origin,
520 permission_status_cb);
524 // Note: This function runs on the UI thread, which may be different from
525 // |task_runner_|. Be careful about thread safety!
526 void BrowserCdmManager::CheckPermissionStatusOnUIThread(
527 int render_frame_id,
528 const GURL& security_origin,
529 const base::Callback<void(bool)>& permission_status_cb) {
530 DCHECK_CURRENTLY_ON(BrowserThread::UI);
532 RenderFrameHost* rfh =
533 RenderFrameHost::FromID(render_process_id_, render_frame_id);
534 WebContents* web_contents = WebContents::FromRenderFrameHost(rfh);
535 PermissionManager* permission_manager =
536 web_contents->GetBrowserContext()->GetPermissionManager();
537 if (!permission_manager) {
538 permission_status_cb.Run(false);
539 return;
542 PermissionStatus permission_status = permission_manager->GetPermissionStatus(
543 content::PermissionType::PROTECTED_MEDIA_IDENTIFIER,
544 security_origin,
545 web_contents->GetLastCommittedURL().GetOrigin());
547 bool allowed = (permission_status == PERMISSION_STATUS_GRANTED);
548 if (!task_runner_->RunsTasksOnCurrentThread()) {
549 task_runner_->PostTask(FROM_HERE,
550 base::Bind(permission_status_cb, allowed));
551 } else {
552 permission_status_cb.Run(allowed);
556 void BrowserCdmManager::CreateSessionAndGenerateRequestIfPermitted(
557 int render_frame_id,
558 int cdm_id,
559 media::EmeInitDataType init_data_type,
560 const std::vector<uint8>& init_data,
561 scoped_ptr<media::NewSessionCdmPromise> promise,
562 bool permission_was_allowed) {
563 DCHECK(task_runner_->RunsTasksOnCurrentThread());
565 if (!permission_was_allowed) {
566 promise->reject(MediaKeys::NOT_SUPPORTED_ERROR, 0, "Permission denied.");
567 return;
570 BrowserCdm* cdm = GetCdm(render_frame_id, cdm_id);
571 if (!cdm) {
572 promise->reject(MediaKeys::INVALID_STATE_ERROR, 0, "CDM not found.");
573 return;
576 // Only the temporary session type is supported in browser CDM path.
577 // TODO(xhwang): Add SessionType support if needed.
578 cdm->CreateSessionAndGenerateRequest(media::MediaKeys::TEMPORARY_SESSION,
579 init_data_type, init_data,
580 promise.Pass());
583 } // namespace content