proxy_decryptor.cc revision f2477e01787aa58f445919b809d89e252beef54f
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
18namespace 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.
24uint32 ProxyDecryptor::next_reference_id_ = 100000;
25
26const uint32 INVALID_REFERENCE_ID = 0;
27
28#if defined(ENABLE_PEPPER_CDMS)
29void ProxyDecryptor::DestroyHelperPlugin() {
30  ContentDecryptionModuleFactory::DestroyHelperPlugin(
31      web_media_player_client_, web_frame_);
32}
33#endif  // defined(ENABLE_PEPPER_CDMS)
34
35ProxyDecryptor::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
63ProxyDecryptor::~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.
76void 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
96bool 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
115bool 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 reference_id = next_reference_id_++;
120  if (!media_keys_->GenerateKeyRequest(
121           reference_id, type, init_data, init_data_length)) {
122    media_keys_.reset();
123    return false;
124  }
125
126  return true;
127}
128
129void ProxyDecryptor::AddKey(const uint8* key,
130                            int key_length,
131                            const uint8* init_data,
132                            int init_data_length,
133                            const std::string& session_id) {
134  DVLOG(1) << "AddKey()";
135
136  // WebMediaPlayerImpl ensures GenerateKeyRequest() has been called.
137  uint32 reference_id = LookupReferenceId(session_id);
138  if (reference_id == INVALID_REFERENCE_ID) {
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_->AddKey(reference_id,
165                        reinterpret_cast<const uint8*>(jwk.data()), jwk.size(),
166                        NULL, 0);
167    return;
168  }
169
170  media_keys_->AddKey(reference_id, key, key_length, NULL, 0);
171}
172
173void ProxyDecryptor::CancelKeyRequest(const std::string& session_id) {
174  DVLOG(1) << "CancelKeyRequest()";
175
176  // WebMediaPlayerImpl ensures GenerateKeyRequest() has been called.
177  uint32 reference_id = LookupReferenceId(session_id);
178  if (reference_id == INVALID_REFERENCE_ID) {
179    // Session hasn't been created, so it is an error.
180    key_error_cb_.Run(
181        std::string(), media::MediaKeys::kUnknownError, 0);
182  }
183  else {
184    media_keys_->CancelKeyRequest(reference_id);
185  }
186}
187
188scoped_ptr<media::MediaKeys> ProxyDecryptor::CreateMediaKeys(
189    const std::string& key_system,
190    const GURL& frame_url) {
191  return ContentDecryptionModuleFactory::Create(
192      key_system,
193#if defined(ENABLE_PEPPER_CDMS)
194      web_media_player_client_,
195      web_frame_,
196      base::Bind(&ProxyDecryptor::DestroyHelperPlugin,
197                 weak_ptr_factory_.GetWeakPtr()),
198#elif defined(OS_ANDROID)
199      manager_,
200      media_keys_id_,
201      frame_url,
202#endif  // defined(ENABLE_PEPPER_CDMS)
203      base::Bind(&ProxyDecryptor::KeyAdded, weak_ptr_factory_.GetWeakPtr()),
204      base::Bind(&ProxyDecryptor::KeyError, weak_ptr_factory_.GetWeakPtr()),
205      base::Bind(&ProxyDecryptor::KeyMessage, weak_ptr_factory_.GetWeakPtr()),
206      base::Bind(&ProxyDecryptor::SetSessionId,
207                 weak_ptr_factory_.GetWeakPtr()));
208}
209
210void ProxyDecryptor::KeyAdded(uint32 reference_id) {
211  // Assumes that SetSessionId() has been called before this.
212  key_added_cb_.Run(LookupSessionId(reference_id));
213}
214
215void ProxyDecryptor::KeyError(uint32 reference_id,
216                              media::MediaKeys::KeyError error_code,
217                              int system_code) {
218  // Assumes that SetSessionId() has been called before this.
219  key_error_cb_.Run(LookupSessionId(reference_id), error_code, system_code);
220}
221
222void ProxyDecryptor::KeyMessage(uint32 reference_id,
223                                const std::vector<uint8>& message,
224                                const std::string& default_url) {
225  // Assumes that SetSessionId() has been called before this.
226  key_message_cb_.Run(LookupSessionId(reference_id), message, default_url);
227}
228
229void ProxyDecryptor::SetSessionId(uint32 reference_id,
230                                  const std::string& session_id) {
231  // Due to heartbeat messages, SetSessionId() can get called multiple times.
232  SessionIdMap::iterator it = sessions_.find(reference_id);
233  DCHECK(it == sessions_.end() || it->second == session_id);
234  if (it == sessions_.end())
235    sessions_[reference_id] = session_id;
236}
237
238uint32 ProxyDecryptor::LookupReferenceId(const std::string& session_id) {
239  for (SessionIdMap::iterator it = sessions_.begin();
240       it != sessions_.end();
241       ++it) {
242    if (it->second == session_id)
243      return it->first;
244  }
245
246  // If |session_id| is null, then use the single reference id.
247  if (session_id.empty() && sessions_.size() == 1)
248    return sessions_.begin()->first;
249
250  return INVALID_REFERENCE_ID;
251}
252
253const std::string& ProxyDecryptor::LookupSessionId(uint32 reference_id) {
254  DCHECK_NE(reference_id, INVALID_REFERENCE_ID);
255
256  // Session may not exist if error happens during GenerateKeyRequest().
257  SessionIdMap::iterator it = sessions_.find(reference_id);
258  return (it != sessions_.end()) ? it->second : EmptyString();
259}
260
261}  // namespace content
262