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) 29 void ProxyDecryptor::DestroyHelperPlugin() { 30 ContentDecryptionModuleFactory::DestroyHelperPlugin( 31 web_media_player_client_, web_frame_); 32 } 33 #endif // defined(ENABLE_PEPPER_CDMS) 34 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 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. 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 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 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 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 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 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 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 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 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 236 void ProxyDecryptor::OnSessionClosed(uint32 session_id) { 237 // No closed event in EME v0.1b. 238 } 239 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 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 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