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