Home | History | Annotate | Download | only in cdm
      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/cdm/browser_cdm_manager.h"
      6 
      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"
     18 
     19 namespace content {
     20 
     21 using media::BrowserCdm;
     22 using media::MediaKeys;
     23 
     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;
     30 
     31 // static
     32 BrowserCdmManager* BrowserCdmManager::Create(RenderFrameHost* rfh) {
     33   return new BrowserCdmManager(rfh);
     34 }
     35 
     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) {
     40 }
     41 
     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.
     47 }
     48 
     49 BrowserCdm* BrowserCdmManager::GetCdm(int cdm_id) {
     50   return cdm_map_.get(cdm_id);
     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/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";
    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   std::map<int, GURL>::const_iterator iter =
    151       cdm_security_origin_map_.find(cdm_id);
    152   if (iter == cdm_security_origin_map_.end()) {
    153     NOTREACHED();
    154     OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0);
    155     return;
    156   }
    157 
    158   base::Closure cancel_callback;
    159   GetContentClient()->browser()->RequestProtectedMediaIdentifierPermission(
    160       web_contents_,
    161       iter->second,
    162       base::Bind(&BrowserCdmManager::CreateSessionIfPermitted,
    163                  weak_ptr_factory_.GetWeakPtr(),
    164                  cdm_id,
    165                  session_id,
    166                  mime_type,
    167                  init_data),
    168       &cancel_callback);
    169   if (!cancel_callback.is_null())
    170     cdm_cancel_permision_map_[cdm_id] = cancel_callback;
    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   if (cdm_cancel_permision_map_.count(cdm_id)) {
    216     cdm_cancel_permision_map_[cdm_id].Run();
    217     cdm_cancel_permision_map_.erase(cdm_id);
    218   }
    219 }
    220 
    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(
    227       key_system,
    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)));
    233 
    234   if (!cdm) {
    235     // This failure will be discovered and reported by OnCreateSession()
    236     // as GetCdm() will return null.
    237     DVLOG(1) << "failed to create CDM.";
    238     return;
    239   }
    240 
    241   cdm_map_.add(cdm_id, cdm.Pass());
    242   cdm_security_origin_map_[cdm_id] = security_origin;
    243 }
    244 
    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);
    253 }
    254 
    255 int BrowserCdmManager::RoutingID() {
    256   return render_frame_host_->GetRoutingID();
    257 }
    258 
    259 bool BrowserCdmManager::Send(IPC::Message* msg) {
    260   return render_frame_host_->Send(msg);
    261 }
    262 
    263 void BrowserCdmManager::CreateSessionIfPermitted(
    264     int cdm_id,
    265     uint32 session_id,
    266     const std::string& content_type,
    267     const std::vector<uint8>& init_data,
    268     bool permitted) {
    269   cdm_cancel_permision_map_.erase(cdm_id);
    270   if (!permitted) {
    271     OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0);
    272     return;
    273   }
    274 
    275   BrowserCdm* cdm = GetCdm(cdm_id);
    276   if (!cdm) {
    277     DLOG(WARNING) << "No CDM for ID: " << cdm_id << " found";
    278     OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0);
    279     return;
    280   }
    281 
    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());
    284 }
    285 
    286 }  // namespace content
    287