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.
5 #include "content/renderer/media/crypto/proxy_decryptor.h"
10 #include "base/callback_helpers.h"
11 #include "base/logging.h"
12 #include "base/strings/string_util.h"
13 #include "content/renderer/media/crypto/content_decryption_module_factory.h"
14 #include "media/cdm/json_web_key.h"
15 #include "media/cdm/key_system_names.h"
17 #if defined(ENABLE_PEPPER_CDMS)
18 #include "content/renderer/media/crypto/pepper_cdm_wrapper.h"
19 #endif // defined(ENABLE_PEPPER_CDMS)
21 #if defined(OS_ANDROID)
22 #include "content/renderer/media/android/renderer_media_player_manager.h"
23 #endif // defined(OS_ANDROID)
27 // Since these reference IDs may conflict with the ones generated in
28 // WebContentDecryptionModuleSessionImpl for the short time both paths are
29 // active, start with 100000 and generate the IDs from there.
30 // TODO(jrummell): Only allow one path http://crbug.com/306680.
31 uint32 ProxyDecryptor::next_session_id_ = 100000;
33 const uint32 kInvalidSessionId = 0;
35 // Special system code to signal a closed persistent session in a SessionError()
36 // call. This is needed because there is no SessionClosed() call in the prefixed
38 const int kSessionClosedSystemCode = 29127;
40 ProxyDecryptor::ProxyDecryptor(
41 #if defined(ENABLE_PEPPER_CDMS)
42 const CreatePepperCdmCB& create_pepper_cdm_cb,
43 #elif defined(OS_ANDROID)
44 RendererMediaPlayerManager* manager,
45 #endif // defined(ENABLE_PEPPER_CDMS)
46 const KeyAddedCB& key_added_cb,
47 const KeyErrorCB& key_error_cb,
48 const KeyMessageCB& key_message_cb)
50 #if defined(ENABLE_PEPPER_CDMS)
51 create_pepper_cdm_cb_(create_pepper_cdm_cb),
52 #elif defined(OS_ANDROID)
54 cdm_id_(RendererMediaPlayerManager::kInvalidCdmId),
55 #endif // defined(ENABLE_PEPPER_CDMS)
56 key_added_cb_(key_added_cb),
57 key_error_cb_(key_error_cb),
58 key_message_cb_(key_message_cb),
60 weak_ptr_factory_(this) {
61 #if defined(ENABLE_PEPPER_CDMS)
62 DCHECK(!create_pepper_cdm_cb_.is_null());
63 #endif // defined(ENABLE_PEPPER_CDMS)
64 DCHECK(!key_added_cb_.is_null());
65 DCHECK(!key_error_cb_.is_null());
66 DCHECK(!key_message_cb_.is_null());
69 ProxyDecryptor::~ProxyDecryptor() {
70 // Destroy the decryptor explicitly before destroying the plugin.
74 media::Decryptor* ProxyDecryptor::GetDecryptor() {
75 return media_keys_ ? media_keys_->GetDecryptor() : NULL;
78 #if defined(OS_ANDROID)
79 int ProxyDecryptor::GetCdmId() {
84 bool ProxyDecryptor::InitializeCDM(const std::string& key_system,
85 const GURL& security_origin) {
86 DVLOG(1) << "InitializeCDM: key_system = " << key_system;
89 media_keys_ = CreateMediaKeys(key_system, security_origin);
94 media::IsClearKey(key_system) || media::IsExternalClearKey(key_system);
98 // Returns true if |data| is prefixed with |header| and has data after the
100 bool HasHeader(const uint8* data, int data_length, const std::string& header) {
101 return static_cast<size_t>(data_length) > header.size() &&
102 std::equal(data, data + header.size(), header.begin());
105 bool ProxyDecryptor::GenerateKeyRequest(const std::string& content_type,
106 const uint8* init_data,
107 int init_data_length) {
108 // Use a unique reference id for this request.
109 uint32 session_id = next_session_id_++;
111 const char kPrefixedApiPersistentSessionHeader[] = "PERSISTENT|";
112 const char kPrefixedApiLoadSessionHeader[] = "LOAD_SESSION|";
114 if (HasHeader(init_data, init_data_length, kPrefixedApiLoadSessionHeader)) {
115 persistent_sessions_.insert(session_id);
116 media_keys_->LoadSession(
118 std::string(reinterpret_cast<const char*>(
119 init_data + strlen(kPrefixedApiLoadSessionHeader)),
120 init_data_length - strlen(kPrefixedApiLoadSessionHeader)));
125 init_data, init_data_length, kPrefixedApiPersistentSessionHeader))
126 persistent_sessions_.insert(session_id);
128 return media_keys_->CreateSession(
129 session_id, content_type, init_data, init_data_length);
132 void ProxyDecryptor::AddKey(const uint8* key,
134 const uint8* init_data,
135 int init_data_length,
136 const std::string& web_session_id) {
137 DVLOG(1) << "AddKey()";
139 // WebMediaPlayerImpl ensures GenerateKeyRequest() has been called.
140 uint32 session_id = LookupSessionId(web_session_id);
141 if (session_id == kInvalidSessionId) {
142 // Session hasn't been referenced before, so it is an error.
143 // Note that the specification says "If sessionId is not null and is
144 // unrecognized, throw an INVALID_ACCESS_ERR." However, for backwards
145 // compatibility the error is not thrown, but rather reported as a
147 key_error_cb_.Run(std::string(), media::MediaKeys::kUnknownError, 0);
151 // EME WD spec only supports a single array passed to the CDM. For
152 // Clear Key using v0.1b, both arrays are used (|init_data| is key_id).
153 // Since the EME WD spec supports the key as a JSON Web Key,
154 // convert the 2 arrays to a JWK and pass it as the single array.
156 // Decryptor doesn't support empty key ID (see http://crbug.com/123265).
157 // So ensure a non-empty value is passed.
159 static const uint8 kDummyInitData[1] = {0};
160 init_data = kDummyInitData;
161 init_data_length = arraysize(kDummyInitData);
165 media::GenerateJWKSet(key, key_length, init_data, init_data_length);
166 DCHECK(!jwk.empty());
167 media_keys_->UpdateSession(
168 session_id, reinterpret_cast<const uint8*>(jwk.data()), jwk.size());
172 media_keys_->UpdateSession(session_id, key, key_length);
175 void ProxyDecryptor::CancelKeyRequest(const std::string& session_id) {
176 DVLOG(1) << "CancelKeyRequest()";
178 // WebMediaPlayerImpl ensures GenerateKeyRequest() has been called.
179 uint32 session_reference_id = LookupSessionId(session_id);
180 if (session_reference_id == kInvalidSessionId) {
181 // Session hasn't been created, so it is an error.
183 std::string(), media::MediaKeys::kUnknownError, 0);
186 media_keys_->ReleaseSession(session_reference_id);
190 scoped_ptr<media::MediaKeys> ProxyDecryptor::CreateMediaKeys(
191 const std::string& key_system,
192 const GURL& security_origin) {
193 return ContentDecryptionModuleFactory::Create(
196 #if defined(ENABLE_PEPPER_CDMS)
197 create_pepper_cdm_cb_,
198 #elif defined(OS_ANDROID)
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()));
214 void ProxyDecryptor::OnSessionCreated(uint32 session_id,
215 const std::string& web_session_id) {
216 // Due to heartbeat messages, OnSessionCreated() can get called multiple
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;
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);
231 void ProxyDecryptor::OnSessionReady(uint32 session_id) {
232 // Assumes that OnSessionCreated() has been called before this.
233 key_added_cb_.Run(LookupWebSessionId(session_id));
236 void ProxyDecryptor::OnSessionClosed(uint32 session_id) {
237 std::set<uint32>::iterator it = persistent_sessions_.find(session_id);
238 if (it != persistent_sessions_.end()) {
239 persistent_sessions_.erase(it);
241 session_id, media::MediaKeys::kUnknownError, kSessionClosedSystemCode);
244 sessions_.erase(session_id);
247 void ProxyDecryptor::OnSessionError(uint32 session_id,
248 media::MediaKeys::KeyError error_code,
249 uint32 system_code) {
250 // Assumes that OnSessionCreated() has been called before this.
251 key_error_cb_.Run(LookupWebSessionId(session_id), error_code, system_code);
254 uint32 ProxyDecryptor::LookupSessionId(const std::string& session_id) const {
255 for (SessionIdMap::const_iterator it = sessions_.begin();
256 it != sessions_.end();
258 if (it->second == session_id)
262 // If |session_id| is null, then use the single reference id.
263 if (session_id.empty() && sessions_.size() == 1)
264 return sessions_.begin()->first;
266 return kInvalidSessionId;
269 const std::string& ProxyDecryptor::LookupWebSessionId(uint32 session_id) const {
270 DCHECK_NE(session_id, kInvalidSessionId);
272 // Session may not exist if error happens during GenerateKeyRequest().
273 SessionIdMap::const_iterator it = sessions_.find(session_id);
274 return (it != sessions_.end()) ? it->second : base::EmptyString();
277 } // namespace content