Delete chrome.mediaGalleriesPrivate because the functionality unique to it has since...
[chromium-blink-merge.git] / media / cdm / proxy_decryptor.cc
blob434988b78da2e8316af0e0ad6d248c6998bf8f7e
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 "media/cdm/proxy_decryptor.h"
7 #include <cstring>
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 "media/base/cdm_callback_promise.h"
14 #include "media/base/cdm_factory.h"
15 #include "media/base/cdm_key_information.h"
16 #include "media/base/key_systems.h"
17 #include "media/cdm/json_web_key.h"
18 #include "media/cdm/key_system_names.h"
20 namespace media {
22 // Special system code to signal a closed persistent session in a SessionError()
23 // call. This is needed because there is no SessionClosed() call in the prefixed
24 // EME API.
25 const int kSessionClosedSystemCode = 29127;
27 ProxyDecryptor::ProxyDecryptor(const KeyAddedCB& key_added_cb,
28 const KeyErrorCB& key_error_cb,
29 const KeyMessageCB& key_message_cb)
30 : key_added_cb_(key_added_cb),
31 key_error_cb_(key_error_cb),
32 key_message_cb_(key_message_cb),
33 is_clear_key_(false),
34 weak_ptr_factory_(this) {
35 DCHECK(!key_added_cb_.is_null());
36 DCHECK(!key_error_cb_.is_null());
37 DCHECK(!key_message_cb_.is_null());
40 ProxyDecryptor::~ProxyDecryptor() {
41 // Destroy the decryptor explicitly before destroying the plugin.
42 media_keys_.reset();
45 CdmContext* ProxyDecryptor::GetCdmContext() {
46 return media_keys_ ? media_keys_->GetCdmContext() : nullptr;
49 bool ProxyDecryptor::InitializeCDM(CdmFactory* cdm_factory,
50 const std::string& key_system,
51 const GURL& security_origin) {
52 DVLOG(1) << "InitializeCDM: key_system = " << key_system;
54 DCHECK(!media_keys_);
55 media_keys_ = CreateMediaKeys(cdm_factory, key_system, security_origin);
56 if (!media_keys_)
57 return false;
59 is_clear_key_ =
60 IsClearKey(key_system) || IsExternalClearKey(key_system);
61 return true;
64 // Returns true if |data| is prefixed with |header| and has data after the
65 // |header|.
66 bool HasHeader(const uint8* data, int data_length, const std::string& header) {
67 return static_cast<size_t>(data_length) > header.size() &&
68 std::equal(data, data + header.size(), header.begin());
71 // Removes the first |length| items from |data|.
72 void StripHeader(std::vector<uint8>& data, size_t length) {
73 data.erase(data.begin(), data.begin() + length);
76 bool ProxyDecryptor::GenerateKeyRequest(const std::string& init_data_type,
77 const uint8* init_data,
78 int init_data_length) {
79 DVLOG(1) << "GenerateKeyRequest()";
80 const char kPrefixedApiPersistentSessionHeader[] = "PERSISTENT|";
81 const char kPrefixedApiLoadSessionHeader[] = "LOAD_SESSION|";
83 SessionCreationType session_creation_type = TemporarySession;
84 std::vector<uint8> init_data_vector(init_data, init_data + init_data_length);
85 if (HasHeader(init_data, init_data_length, kPrefixedApiLoadSessionHeader)) {
86 session_creation_type = LoadSession;
87 StripHeader(init_data_vector, strlen(kPrefixedApiLoadSessionHeader));
88 } else if (HasHeader(init_data,
89 init_data_length,
90 kPrefixedApiPersistentSessionHeader)) {
91 session_creation_type = PersistentSession;
92 StripHeader(init_data_vector, strlen(kPrefixedApiPersistentSessionHeader));
95 scoped_ptr<NewSessionCdmPromise> promise(
96 new CdmCallbackPromise<std::string>(
97 base::Bind(&ProxyDecryptor::SetSessionId,
98 weak_ptr_factory_.GetWeakPtr(),
99 session_creation_type),
100 base::Bind(&ProxyDecryptor::OnSessionError,
101 weak_ptr_factory_.GetWeakPtr(),
102 std::string()))); // No session id until created.
103 uint8* init_data_vector_data =
104 (init_data_vector.size() > 0) ? &init_data_vector[0] : nullptr;
106 if (session_creation_type == LoadSession) {
107 media_keys_->LoadSession(
108 MediaKeys::PERSISTENT_LICENSE_SESSION,
109 std::string(reinterpret_cast<const char*>(init_data_vector_data),
110 init_data_vector.size()),
111 promise.Pass());
112 return true;
115 MediaKeys::SessionType session_type =
116 session_creation_type == PersistentSession
117 ? MediaKeys::PERSISTENT_LICENSE_SESSION
118 : MediaKeys::TEMPORARY_SESSION;
120 media_keys_->CreateSessionAndGenerateRequest(
121 session_type, init_data_type, init_data_vector_data,
122 init_data_vector.size(), promise.Pass());
123 return true;
126 void ProxyDecryptor::AddKey(const uint8* key,
127 int key_length,
128 const uint8* init_data,
129 int init_data_length,
130 const std::string& web_session_id) {
131 DVLOG(1) << "AddKey()";
133 // In the prefixed API, the session parameter provided to addKey() is
134 // optional, so use the single existing session if it exists.
135 // TODO(jrummell): remove when the prefixed API is removed.
136 std::string session_id(web_session_id);
137 if (session_id.empty()) {
138 if (active_sessions_.size() == 1) {
139 base::hash_map<std::string, bool>::iterator it = active_sessions_.begin();
140 session_id = it->first;
141 } else {
142 OnSessionError(std::string(),
143 MediaKeys::NOT_SUPPORTED_ERROR,
145 "SessionId not specified.");
146 return;
150 scoped_ptr<SimpleCdmPromise> promise(new CdmCallbackPromise<>(
151 base::Bind(&ProxyDecryptor::GenerateKeyAdded,
152 weak_ptr_factory_.GetWeakPtr(),
153 web_session_id),
154 base::Bind(&ProxyDecryptor::OnSessionError,
155 weak_ptr_factory_.GetWeakPtr(),
156 web_session_id)));
158 // EME WD spec only supports a single array passed to the CDM. For
159 // Clear Key using v0.1b, both arrays are used (|init_data| is key_id).
160 // Since the EME WD spec supports the key as a JSON Web Key,
161 // convert the 2 arrays to a JWK and pass it as the single array.
162 if (is_clear_key_) {
163 // Decryptor doesn't support empty key ID (see http://crbug.com/123265).
164 // So ensure a non-empty value is passed.
165 if (!init_data) {
166 static const uint8 kDummyInitData[1] = {0};
167 init_data = kDummyInitData;
168 init_data_length = arraysize(kDummyInitData);
171 std::string jwk =
172 GenerateJWKSet(key, key_length, init_data, init_data_length);
173 DCHECK(!jwk.empty());
174 media_keys_->UpdateSession(session_id,
175 reinterpret_cast<const uint8*>(jwk.data()),
176 jwk.size(),
177 promise.Pass());
178 return;
181 media_keys_->UpdateSession(session_id, key, key_length, promise.Pass());
184 void ProxyDecryptor::CancelKeyRequest(const std::string& web_session_id) {
185 DVLOG(1) << "CancelKeyRequest()";
187 scoped_ptr<SimpleCdmPromise> promise(new CdmCallbackPromise<>(
188 base::Bind(&ProxyDecryptor::OnSessionClosed,
189 weak_ptr_factory_.GetWeakPtr(),
190 web_session_id),
191 base::Bind(&ProxyDecryptor::OnSessionError,
192 weak_ptr_factory_.GetWeakPtr(),
193 web_session_id)));
194 media_keys_->RemoveSession(web_session_id, promise.Pass());
197 scoped_ptr<MediaKeys> ProxyDecryptor::CreateMediaKeys(
198 CdmFactory* cdm_factory,
199 const std::string& key_system,
200 const GURL& security_origin) {
201 base::WeakPtr<ProxyDecryptor> weak_this = weak_ptr_factory_.GetWeakPtr();
202 return cdm_factory->Create(
203 key_system,
204 security_origin,
205 base::Bind(&ProxyDecryptor::OnSessionMessage, weak_this),
206 base::Bind(&ProxyDecryptor::OnSessionClosed, weak_this),
207 base::Bind(&ProxyDecryptor::OnSessionError, weak_this),
208 base::Bind(&ProxyDecryptor::OnSessionKeysChange, weak_this),
209 base::Bind(&ProxyDecryptor::OnSessionExpirationUpdate, weak_this));
212 void ProxyDecryptor::OnSessionMessage(const std::string& web_session_id,
213 MediaKeys::MessageType message_type,
214 const std::vector<uint8>& message,
215 const GURL& legacy_destination_url) {
216 // Assumes that OnSessionCreated() has been called before this.
218 // For ClearKey, convert the message from JSON into just passing the key
219 // as the message. If unable to extract the key, return the message unchanged.
220 if (is_clear_key_) {
221 std::vector<uint8> key;
222 if (ExtractFirstKeyIdFromLicenseRequest(message, &key)) {
223 key_message_cb_.Run(web_session_id, key, legacy_destination_url);
224 return;
228 key_message_cb_.Run(web_session_id, message, legacy_destination_url);
231 void ProxyDecryptor::OnSessionKeysChange(const std::string& web_session_id,
232 bool has_additional_usable_key,
233 CdmKeysInfo keys_info) {
234 // EME v0.1b doesn't support this event.
237 void ProxyDecryptor::OnSessionExpirationUpdate(
238 const std::string& web_session_id,
239 const base::Time& new_expiry_time) {
240 // EME v0.1b doesn't support this event.
243 void ProxyDecryptor::GenerateKeyAdded(const std::string& web_session_id) {
244 // EME WD doesn't support this event, but it is needed for EME v0.1b.
245 key_added_cb_.Run(web_session_id);
248 void ProxyDecryptor::OnSessionClosed(const std::string& web_session_id) {
249 base::hash_map<std::string, bool>::iterator it =
250 active_sessions_.find(web_session_id);
252 // Latest EME spec separates closing a session ("allows an application to
253 // indicate that it no longer needs the session") and actually closing the
254 // session (done by the CDM at any point "such as in response to a close()
255 // call, when the session is no longer needed, or when system resources are
256 // lost.") Thus the CDM may cause 2 close() events -- one to resolve the
257 // close() promise, and a second to actually close the session. Prefixed EME
258 // only expects 1 close event, so drop the second (and subsequent) events.
259 // However, this means we can't tell if the CDM is generating spurious close()
260 // events.
261 if (it == active_sessions_.end())
262 return;
264 if (it->second) {
265 OnSessionError(web_session_id, MediaKeys::NOT_SUPPORTED_ERROR,
266 kSessionClosedSystemCode,
267 "Do not close persistent sessions.");
269 active_sessions_.erase(it);
272 void ProxyDecryptor::OnSessionError(const std::string& web_session_id,
273 MediaKeys::Exception exception_code,
274 uint32 system_code,
275 const std::string& error_message) {
276 // Convert |error_name| back to MediaKeys::KeyError if possible. Prefixed
277 // EME has different error message, so all the specific error events will
278 // get lost.
279 MediaKeys::KeyError error_code;
280 switch (exception_code) {
281 case MediaKeys::CLIENT_ERROR:
282 error_code = MediaKeys::kClientError;
283 break;
284 case MediaKeys::OUTPUT_ERROR:
285 error_code = MediaKeys::kOutputError;
286 break;
287 default:
288 // This will include all other CDM4 errors and any error generated
289 // by CDM5 or later.
290 error_code = MediaKeys::kUnknownError;
291 break;
293 key_error_cb_.Run(web_session_id, error_code, system_code);
296 void ProxyDecryptor::SetSessionId(SessionCreationType session_type,
297 const std::string& web_session_id) {
298 // Loaded sessions are considered persistent.
299 bool is_persistent =
300 session_type == PersistentSession || session_type == LoadSession;
301 active_sessions_.insert(std::make_pair(web_session_id, is_persistent));
303 // For LoadSession(), generate the KeyAdded event.
304 if (session_type == LoadSession)
305 GenerateKeyAdded(web_session_id);
308 } // namespace media