• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 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/renderer/media/crypto/proxy_decryptor.h"
6 
7 #include "base/bind.h"
8 #include "base/callback_helpers.h"
9 #include "base/logging.h"
10 #include "base/strings/string_util.h"
11 #include "content/renderer/media/crypto/content_decryption_module_factory.h"
12 #if defined(OS_ANDROID)
13 #include "content/renderer/media/android/renderer_media_player_manager.h"
14 #endif  // defined(OS_ANDROID)
15 #include "media/cdm/json_web_key.h"
16 #include "media/cdm/key_system_names.h"
17 
18 namespace content {
19 
20 // Since these reference IDs may conflict with the ones generated in
21 // WebContentDecryptionModuleSessionImpl for the short time both paths are
22 // active, start with 100000 and generate the IDs from there.
23 // TODO(jrummell): Only allow one path http://crbug.com/306680.
24 uint32 ProxyDecryptor::next_session_id_ = 100000;
25 
26 const uint32 kInvalidSessionId = 0;
27 
28 #if defined(ENABLE_PEPPER_CDMS)
DestroyHelperPlugin()29 void ProxyDecryptor::DestroyHelperPlugin() {
30   ContentDecryptionModuleFactory::DestroyHelperPlugin(
31       web_media_player_client_, web_frame_);
32 }
33 #endif  // defined(ENABLE_PEPPER_CDMS)
34 
ProxyDecryptor(blink::WebMediaPlayerClient * web_media_player_client,blink::WebFrame * web_frame,const KeyAddedCB & key_added_cb,const KeyErrorCB & key_error_cb,const KeyMessageCB & key_message_cb)35 ProxyDecryptor::ProxyDecryptor(
36 #if defined(ENABLE_PEPPER_CDMS)
37     blink::WebMediaPlayerClient* web_media_player_client,
38     blink::WebFrame* web_frame,
39 #elif defined(OS_ANDROID)
40     RendererMediaPlayerManager* manager,
41     int media_keys_id,
42 #endif  // defined(ENABLE_PEPPER_CDMS)
43     const KeyAddedCB& key_added_cb,
44     const KeyErrorCB& key_error_cb,
45     const KeyMessageCB& key_message_cb)
46     : weak_ptr_factory_(this),
47 #if defined(ENABLE_PEPPER_CDMS)
48       web_media_player_client_(web_media_player_client),
49       web_frame_(web_frame),
50 #elif defined(OS_ANDROID)
51       manager_(manager),
52       media_keys_id_(media_keys_id),
53 #endif  // defined(ENABLE_PEPPER_CDMS)
54       key_added_cb_(key_added_cb),
55       key_error_cb_(key_error_cb),
56       key_message_cb_(key_message_cb),
57       is_clear_key_(false) {
58   DCHECK(!key_added_cb_.is_null());
59   DCHECK(!key_error_cb_.is_null());
60   DCHECK(!key_message_cb_.is_null());
61 }
62 
~ProxyDecryptor()63 ProxyDecryptor::~ProxyDecryptor() {
64   // Destroy the decryptor explicitly before destroying the plugin.
65   {
66     base::AutoLock auto_lock(lock_);
67     media_keys_.reset();
68   }
69 }
70 
71 // TODO(xhwang): Support multiple decryptor notification request (e.g. from
72 // video and audio decoders). The current implementation is okay for the current
73 // media pipeline since we initialize audio and video decoders in sequence.
74 // But ProxyDecryptor should not depend on media pipeline's implementation
75 // detail.
SetDecryptorReadyCB(const media::DecryptorReadyCB & decryptor_ready_cb)76 void ProxyDecryptor::SetDecryptorReadyCB(
77      const media::DecryptorReadyCB& decryptor_ready_cb) {
78   base::AutoLock auto_lock(lock_);
79 
80   // Cancels the previous decryptor request.
81   if (decryptor_ready_cb.is_null()) {
82     if (!decryptor_ready_cb_.is_null())
83       base::ResetAndReturn(&decryptor_ready_cb_).Run(NULL);
84     return;
85   }
86 
87   // Normal decryptor request.
88   DCHECK(decryptor_ready_cb_.is_null());
89   if (media_keys_) {
90     decryptor_ready_cb.Run(media_keys_->GetDecryptor());
91     return;
92   }
93   decryptor_ready_cb_ = decryptor_ready_cb;
94 }
95 
InitializeCDM(const std::string & key_system,const GURL & frame_url)96 bool ProxyDecryptor::InitializeCDM(const std::string& key_system,
97                                    const GURL& frame_url) {
98   DVLOG(1) << "InitializeCDM: key_system = " << key_system;
99 
100   base::AutoLock auto_lock(lock_);
101 
102   DCHECK(!media_keys_);
103   media_keys_ = CreateMediaKeys(key_system, frame_url);
104   if (!media_keys_)
105     return false;
106 
107   if (!decryptor_ready_cb_.is_null())
108     base::ResetAndReturn(&decryptor_ready_cb_).Run(media_keys_->GetDecryptor());
109 
110   is_clear_key_ =
111       media::IsClearKey(key_system) || media::IsExternalClearKey(key_system);
112   return true;
113 }
114 
GenerateKeyRequest(const std::string & type,const uint8 * init_data,int init_data_length)115 bool ProxyDecryptor::GenerateKeyRequest(const std::string& type,
116                                         const uint8* init_data,
117                                         int init_data_length) {
118   // Use a unique reference id for this request.
119   uint32 session_id = next_session_id_++;
120   if (!media_keys_->CreateSession(
121            session_id, type, init_data, init_data_length)) {
122     media_keys_.reset();
123     return false;
124   }
125 
126   return true;
127 }
128 
AddKey(const uint8 * key,int key_length,const uint8 * init_data,int init_data_length,const std::string & web_session_id)129 void ProxyDecryptor::AddKey(const uint8* key,
130                             int key_length,
131                             const uint8* init_data,
132                             int init_data_length,
133                             const std::string& web_session_id) {
134   DVLOG(1) << "AddKey()";
135 
136   // WebMediaPlayerImpl ensures GenerateKeyRequest() has been called.
137   uint32 session_id = LookupSessionId(web_session_id);
138   if (session_id == kInvalidSessionId) {
139     // Session hasn't been referenced before, so it is an error.
140     // Note that the specification says "If sessionId is not null and is
141     // unrecognized, throw an INVALID_ACCESS_ERR." However, for backwards
142     // compatibility the error is not thrown, but rather reported as a
143     // KeyError.
144     key_error_cb_.Run(std::string(), media::MediaKeys::kUnknownError, 0);
145     return;
146   }
147 
148   // EME WD spec only supports a single array passed to the CDM. For
149   // Clear Key using v0.1b, both arrays are used (|init_data| is key_id).
150   // Since the EME WD spec supports the key as a JSON Web Key,
151   // convert the 2 arrays to a JWK and pass it as the single array.
152   if (is_clear_key_) {
153     // Decryptor doesn't support empty key ID (see http://crbug.com/123265).
154     // So ensure a non-empty value is passed.
155     if (!init_data) {
156       static const uint8 kDummyInitData[1] = {0};
157       init_data = kDummyInitData;
158       init_data_length = arraysize(kDummyInitData);
159     }
160 
161     std::string jwk =
162         media::GenerateJWKSet(key, key_length, init_data, init_data_length);
163     DCHECK(!jwk.empty());
164     media_keys_->UpdateSession(
165         session_id, reinterpret_cast<const uint8*>(jwk.data()), jwk.size());
166     return;
167   }
168 
169   media_keys_->UpdateSession(session_id, key, key_length);
170 }
171 
CancelKeyRequest(const std::string & session_id)172 void ProxyDecryptor::CancelKeyRequest(const std::string& session_id) {
173   DVLOG(1) << "CancelKeyRequest()";
174 
175   // WebMediaPlayerImpl ensures GenerateKeyRequest() has been called.
176   uint32 session_reference_id = LookupSessionId(session_id);
177   if (session_reference_id == kInvalidSessionId) {
178     // Session hasn't been created, so it is an error.
179     key_error_cb_.Run(
180         std::string(), media::MediaKeys::kUnknownError, 0);
181   }
182   else {
183     media_keys_->ReleaseSession(session_reference_id);
184   }
185 }
186 
CreateMediaKeys(const std::string & key_system,const GURL & frame_url)187 scoped_ptr<media::MediaKeys> ProxyDecryptor::CreateMediaKeys(
188     const std::string& key_system,
189     const GURL& frame_url) {
190   return ContentDecryptionModuleFactory::Create(
191       key_system,
192 #if defined(ENABLE_PEPPER_CDMS)
193       web_media_player_client_,
194       web_frame_,
195       base::Bind(&ProxyDecryptor::DestroyHelperPlugin,
196                  weak_ptr_factory_.GetWeakPtr()),
197 #elif defined(OS_ANDROID)
198       manager_,
199       media_keys_id_,
200       frame_url,
201 #endif  // defined(ENABLE_PEPPER_CDMS)
202       base::Bind(&ProxyDecryptor::OnSessionCreated,
203                  weak_ptr_factory_.GetWeakPtr()),
204       base::Bind(&ProxyDecryptor::OnSessionMessage,
205                  weak_ptr_factory_.GetWeakPtr()),
206       base::Bind(&ProxyDecryptor::OnSessionReady,
207                  weak_ptr_factory_.GetWeakPtr()),
208       base::Bind(&ProxyDecryptor::OnSessionClosed,
209                  weak_ptr_factory_.GetWeakPtr()),
210       base::Bind(&ProxyDecryptor::OnSessionError,
211                  weak_ptr_factory_.GetWeakPtr()));
212 }
213 
OnSessionCreated(uint32 session_id,const std::string & web_session_id)214 void ProxyDecryptor::OnSessionCreated(uint32 session_id,
215                                       const std::string& web_session_id) {
216   // Due to heartbeat messages, OnSessionCreated() can get called multiple
217   // times.
218   SessionIdMap::iterator it = sessions_.find(session_id);
219   DCHECK(it == sessions_.end() || it->second == web_session_id);
220   if (it == sessions_.end())
221     sessions_[session_id] = web_session_id;
222 }
223 
OnSessionMessage(uint32 session_id,const std::vector<uint8> & message,const std::string & destination_url)224 void ProxyDecryptor::OnSessionMessage(uint32 session_id,
225                                       const std::vector<uint8>& message,
226                                       const std::string& destination_url) {
227   // Assumes that OnSessionCreated() has been called before this.
228   key_message_cb_.Run(LookupWebSessionId(session_id), message, destination_url);
229 }
230 
OnSessionReady(uint32 session_id)231 void ProxyDecryptor::OnSessionReady(uint32 session_id) {
232   // Assumes that OnSessionCreated() has been called before this.
233   key_added_cb_.Run(LookupWebSessionId(session_id));
234 }
235 
OnSessionClosed(uint32 session_id)236 void ProxyDecryptor::OnSessionClosed(uint32 session_id) {
237   // No closed event in EME v0.1b.
238 }
239 
OnSessionError(uint32 session_id,media::MediaKeys::KeyError error_code,int system_code)240 void ProxyDecryptor::OnSessionError(uint32 session_id,
241                                     media::MediaKeys::KeyError error_code,
242                                     int system_code) {
243   // Assumes that OnSessionCreated() has been called before this.
244   key_error_cb_.Run(LookupWebSessionId(session_id), error_code, system_code);
245 }
246 
LookupSessionId(const std::string & session_id)247 uint32 ProxyDecryptor::LookupSessionId(const std::string& session_id) {
248   for (SessionIdMap::iterator it = sessions_.begin();
249        it != sessions_.end();
250        ++it) {
251     if (it->second == session_id)
252       return it->first;
253   }
254 
255   // If |session_id| is null, then use the single reference id.
256   if (session_id.empty() && sessions_.size() == 1)
257     return sessions_.begin()->first;
258 
259   return kInvalidSessionId;
260 }
261 
LookupWebSessionId(uint32 session_id)262 const std::string& ProxyDecryptor::LookupWebSessionId(uint32 session_id) {
263   DCHECK_NE(session_id, kInvalidSessionId);
264 
265   // Session may not exist if error happens during GenerateKeyRequest().
266   SessionIdMap::iterator it = sessions_.find(session_id);
267   return (it != sessions_.end()) ? it->second : base::EmptyString();
268 }
269 
270 }  // namespace content
271