Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / content / renderer / media / crypto / proxy_decryptor.cc
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 <cstring>
8
9 #include "base/bind.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"
16
17 #if defined(ENABLE_PEPPER_CDMS)
18 #include "content/renderer/media/crypto/pepper_cdm_wrapper.h"
19 #endif  // defined(ENABLE_PEPPER_CDMS)
20
21 #if defined(OS_ANDROID)
22 #include "content/renderer/media/android/renderer_media_player_manager.h"
23 #endif  // defined(OS_ANDROID)
24
25 namespace content {
26
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;
32
33 const uint32 kInvalidSessionId = 0;
34
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
37 // EME API.
38 const int kSessionClosedSystemCode = 29127;
39
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)
49     :
50 #if defined(ENABLE_PEPPER_CDMS)
51       create_pepper_cdm_cb_(create_pepper_cdm_cb),
52 #elif defined(OS_ANDROID)
53       manager_(manager),
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),
59       is_clear_key_(false),
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());
67 }
68
69 ProxyDecryptor::~ProxyDecryptor() {
70   // Destroy the decryptor explicitly before destroying the plugin.
71   media_keys_.reset();
72 }
73
74 media::Decryptor* ProxyDecryptor::GetDecryptor() {
75   return media_keys_ ? media_keys_->GetDecryptor() : NULL;
76 }
77
78 #if defined(OS_ANDROID)
79 int ProxyDecryptor::GetCdmId() {
80   return cdm_id_;
81 }
82 #endif
83
84 bool ProxyDecryptor::InitializeCDM(const std::string& key_system,
85                                    const GURL& security_origin) {
86   DVLOG(1) << "InitializeCDM: key_system = " << key_system;
87
88   DCHECK(!media_keys_);
89   media_keys_ = CreateMediaKeys(key_system, security_origin);
90   if (!media_keys_)
91     return false;
92
93   is_clear_key_ =
94       media::IsClearKey(key_system) || media::IsExternalClearKey(key_system);
95   return true;
96 }
97
98 // Returns true if |data| is prefixed with |header| and has data after the
99 // |header|.
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());
103 }
104
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_++;
110
111   const char kPrefixedApiPersistentSessionHeader[] = "PERSISTENT|";
112   const char kPrefixedApiLoadSessionHeader[] = "LOAD_SESSION|";
113
114   if (HasHeader(init_data, init_data_length, kPrefixedApiLoadSessionHeader)) {
115     persistent_sessions_.insert(session_id);
116     media_keys_->LoadSession(
117         session_id,
118         std::string(reinterpret_cast<const char*>(
119                         init_data + strlen(kPrefixedApiLoadSessionHeader)),
120                     init_data_length - strlen(kPrefixedApiLoadSessionHeader)));
121     return true;
122   }
123
124   if (HasHeader(
125           init_data, init_data_length, kPrefixedApiPersistentSessionHeader))
126     persistent_sessions_.insert(session_id);
127
128   return media_keys_->CreateSession(
129       session_id, content_type, init_data, init_data_length);
130 }
131
132 void ProxyDecryptor::AddKey(const uint8* key,
133                             int key_length,
134                             const uint8* init_data,
135                             int init_data_length,
136                             const std::string& web_session_id) {
137   DVLOG(1) << "AddKey()";
138
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
146     // KeyError.
147     key_error_cb_.Run(std::string(), media::MediaKeys::kUnknownError, 0);
148     return;
149   }
150
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.
155   if (is_clear_key_) {
156     // Decryptor doesn't support empty key ID (see http://crbug.com/123265).
157     // So ensure a non-empty value is passed.
158     if (!init_data) {
159       static const uint8 kDummyInitData[1] = {0};
160       init_data = kDummyInitData;
161       init_data_length = arraysize(kDummyInitData);
162     }
163
164     std::string jwk =
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());
169     return;
170   }
171
172   media_keys_->UpdateSession(session_id, key, key_length);
173 }
174
175 void ProxyDecryptor::CancelKeyRequest(const std::string& session_id) {
176   DVLOG(1) << "CancelKeyRequest()";
177
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.
182     key_error_cb_.Run(
183         std::string(), media::MediaKeys::kUnknownError, 0);
184   }
185   else {
186     media_keys_->ReleaseSession(session_reference_id);
187   }
188 }
189
190 scoped_ptr<media::MediaKeys> ProxyDecryptor::CreateMediaKeys(
191     const std::string& key_system,
192     const GURL& security_origin) {
193   return ContentDecryptionModuleFactory::Create(
194       key_system,
195       security_origin,
196 #if defined(ENABLE_PEPPER_CDMS)
197       create_pepper_cdm_cb_,
198 #elif defined(OS_ANDROID)
199       manager_,
200       &cdm_id_,
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   std::set<uint32>::iterator it = persistent_sessions_.find(session_id);
238   if (it != persistent_sessions_.end()) {
239     persistent_sessions_.erase(it);
240     OnSessionError(
241         session_id, media::MediaKeys::kUnknownError, kSessionClosedSystemCode);
242   }
243
244   sessions_.erase(session_id);
245 }
246
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);
252 }
253
254 uint32 ProxyDecryptor::LookupSessionId(const std::string& session_id) const {
255   for (SessionIdMap::const_iterator it = sessions_.begin();
256        it != sessions_.end();
257        ++it) {
258     if (it->second == session_id)
259       return it->first;
260   }
261
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;
265
266   return kInvalidSessionId;
267 }
268
269 const std::string& ProxyDecryptor::LookupWebSessionId(uint32 session_id) const {
270   DCHECK_NE(session_id, kInvalidSessionId);
271
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();
275 }
276
277 }  // namespace content