Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / media / cdm / proxy_decryptor.cc
blob9260e1455b654fe434eb567f20c7630e21a9ef35
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/base/media_permission.h"
18 #include "media/cdm/json_web_key.h"
19 #include "media/cdm/key_system_names.h"
21 namespace media {
23 // Special system code to signal a closed persistent session in a SessionError()
24 // call. This is needed because there is no SessionClosed() call in the prefixed
25 // EME API.
26 const int kSessionClosedSystemCode = 29127;
28 ProxyDecryptor::PendingGenerateKeyRequestData::PendingGenerateKeyRequestData(
29 EmeInitDataType init_data_type,
30 const std::vector<uint8>& init_data)
31 : init_data_type(init_data_type), init_data(init_data) {
34 ProxyDecryptor::PendingGenerateKeyRequestData::
35 ~PendingGenerateKeyRequestData() {
38 ProxyDecryptor::ProxyDecryptor(MediaPermission* media_permission,
39 const KeyAddedCB& key_added_cb,
40 const KeyErrorCB& key_error_cb,
41 const KeyMessageCB& key_message_cb)
42 : is_creating_cdm_(false),
43 media_permission_(media_permission),
44 key_added_cb_(key_added_cb),
45 key_error_cb_(key_error_cb),
46 key_message_cb_(key_message_cb),
47 is_clear_key_(false),
48 weak_ptr_factory_(this) {
49 DCHECK(media_permission);
50 DCHECK(!key_added_cb_.is_null());
51 DCHECK(!key_error_cb_.is_null());
52 DCHECK(!key_message_cb_.is_null());
55 ProxyDecryptor::~ProxyDecryptor() {
56 // Destroy the decryptor explicitly before destroying the plugin.
57 media_keys_.reset();
60 void ProxyDecryptor::CreateCdm(CdmFactory* cdm_factory,
61 const std::string& key_system,
62 const GURL& security_origin,
63 const CdmContextReadyCB& cdm_context_ready_cb) {
64 DVLOG(1) << __FUNCTION__ << ": key_system = " << key_system;
65 DCHECK(!is_creating_cdm_);
66 DCHECK(!media_keys_);
68 // TODO(sandersd): Trigger permissions check here and use it to determine
69 // distinctive identifier support, instead of always requiring the
70 // permission. http://crbug.com/455271
71 bool allow_distinctive_identifier = true;
72 bool allow_persistent_state = true;
74 is_creating_cdm_ = true;
76 base::WeakPtr<ProxyDecryptor> weak_this = weak_ptr_factory_.GetWeakPtr();
77 cdm_factory->Create(
78 key_system, allow_distinctive_identifier, allow_persistent_state,
79 security_origin, base::Bind(&ProxyDecryptor::OnSessionMessage, weak_this),
80 base::Bind(&ProxyDecryptor::OnSessionClosed, weak_this),
81 base::Bind(&ProxyDecryptor::OnLegacySessionError, weak_this),
82 base::Bind(&ProxyDecryptor::OnSessionKeysChange, weak_this),
83 base::Bind(&ProxyDecryptor::OnSessionExpirationUpdate, weak_this),
84 base::Bind(&ProxyDecryptor::OnCdmCreated, weak_this, key_system,
85 security_origin, cdm_context_ready_cb));
88 void ProxyDecryptor::OnCdmCreated(const std::string& key_system,
89 const GURL& security_origin,
90 const CdmContextReadyCB& cdm_context_ready_cb,
91 scoped_ptr<MediaKeys> cdm) {
92 is_creating_cdm_ = false;
94 if (!cdm) {
95 cdm_context_ready_cb.Run(nullptr);
96 return;
99 key_system_ = key_system;
100 security_origin_ = security_origin;
101 is_clear_key_ = IsClearKey(key_system) || IsExternalClearKey(key_system);
102 media_keys_ = cdm.Pass();
104 cdm_context_ready_cb.Run(media_keys_->GetCdmContext());
106 for (const auto& request : pending_requests_)
107 GenerateKeyRequestInternal(request->init_data_type, request->init_data);
109 pending_requests_.clear();
112 void ProxyDecryptor::GenerateKeyRequest(EmeInitDataType init_data_type,
113 const uint8* init_data,
114 int init_data_length) {
115 std::vector<uint8> init_data_vector(init_data, init_data + init_data_length);
117 if (is_creating_cdm_) {
118 pending_requests_.push_back(
119 new PendingGenerateKeyRequestData(init_data_type, init_data_vector));
120 return;
123 GenerateKeyRequestInternal(init_data_type, init_data_vector);
126 // Returns true if |data| is prefixed with |header| and has data after the
127 // |header|.
128 bool HasHeader(const std::vector<uint8>& data, const std::string& header) {
129 return data.size() > header.size() &&
130 std::equal(header.begin(), header.end(), data.begin());
133 // Removes the first |length| items from |data|.
134 void StripHeader(std::vector<uint8>& data, size_t length) {
135 data.erase(data.begin(), data.begin() + length);
138 void ProxyDecryptor::GenerateKeyRequestInternal(
139 EmeInitDataType init_data_type,
140 const std::vector<uint8>& init_data) {
141 DVLOG(1) << __FUNCTION__;
142 DCHECK(!is_creating_cdm_);
144 if (!media_keys_) {
145 OnLegacySessionError(std::string(), MediaKeys::NOT_SUPPORTED_ERROR, 0,
146 "CDM creation failed.");
147 return;
150 const char kPrefixedApiPersistentSessionHeader[] = "PERSISTENT|";
151 const char kPrefixedApiLoadSessionHeader[] = "LOAD_SESSION|";
153 SessionCreationType session_creation_type = TemporarySession;
154 std::vector<uint8> stripped_init_data = init_data;
155 if (HasHeader(init_data, kPrefixedApiLoadSessionHeader)) {
156 session_creation_type = LoadSession;
157 StripHeader(stripped_init_data, strlen(kPrefixedApiLoadSessionHeader));
158 } else if (HasHeader(init_data, kPrefixedApiPersistentSessionHeader)) {
159 session_creation_type = PersistentSession;
160 StripHeader(stripped_init_data,
161 strlen(kPrefixedApiPersistentSessionHeader));
164 scoped_ptr<NewSessionCdmPromise> promise(new CdmCallbackPromise<std::string>(
165 base::Bind(&ProxyDecryptor::SetSessionId, weak_ptr_factory_.GetWeakPtr(),
166 session_creation_type),
167 base::Bind(&ProxyDecryptor::OnLegacySessionError,
168 weak_ptr_factory_.GetWeakPtr(),
169 std::string()))); // No session id until created.
171 if (session_creation_type == LoadSession) {
172 media_keys_->LoadSession(
173 MediaKeys::PERSISTENT_LICENSE_SESSION,
174 std::string(
175 reinterpret_cast<const char*>(vector_as_array(&stripped_init_data)),
176 stripped_init_data.size()),
177 promise.Pass());
178 return;
181 MediaKeys::SessionType session_type =
182 session_creation_type == PersistentSession
183 ? MediaKeys::PERSISTENT_LICENSE_SESSION
184 : MediaKeys::TEMPORARY_SESSION;
186 // No permission required when AesDecryptor is used or when the key system is
187 // external clear key.
188 DCHECK(!key_system_.empty());
189 if (CanUseAesDecryptor(key_system_) || IsExternalClearKey(key_system_)) {
190 OnPermissionStatus(session_type, init_data_type, stripped_init_data,
191 promise.Pass(), true /* granted */);
192 return;
195 #if defined(OS_CHROMEOS) || defined(OS_ANDROID)
196 media_permission_->RequestPermission(
197 MediaPermission::PROTECTED_MEDIA_IDENTIFIER, security_origin_,
198 base::Bind(&ProxyDecryptor::OnPermissionStatus,
199 weak_ptr_factory_.GetWeakPtr(), session_type, init_data_type,
200 stripped_init_data, base::Passed(&promise)));
201 #else
202 OnPermissionStatus(session_type, init_data_type, stripped_init_data,
203 promise.Pass(), true /* granted */);
204 #endif
207 void ProxyDecryptor::OnPermissionStatus(
208 MediaKeys::SessionType session_type,
209 EmeInitDataType init_data_type,
210 const std::vector<uint8>& init_data,
211 scoped_ptr<NewSessionCdmPromise> promise,
212 bool granted) {
213 // ProxyDecryptor is only used by Prefixed EME, where RequestPermission() is
214 // only for triggering the permission UI. Later CheckPermission() will be
215 // called (e.g. in PlatformVerificationFlow on ChromeOS; in BrowserCdmManager
216 // on Android) and the permission status will be evaluated then.
217 DVLOG_IF(1, !granted) << "Permission request rejected.";
219 media_keys_->CreateSessionAndGenerateRequest(
220 session_type, init_data_type, vector_as_array(&init_data),
221 init_data.size(), promise.Pass());
224 void ProxyDecryptor::AddKey(const uint8* key,
225 int key_length,
226 const uint8* init_data,
227 int init_data_length,
228 const std::string& session_id) {
229 DVLOG(1) << "AddKey()";
231 if (!media_keys_) {
232 OnLegacySessionError(std::string(), MediaKeys::INVALID_STATE_ERROR, 0,
233 "CDM is not available.");
234 return;
237 // In the prefixed API, the session parameter provided to addKey() is
238 // optional, so use the single existing session if it exists.
239 std::string new_session_id(session_id);
240 if (new_session_id.empty()) {
241 if (active_sessions_.size() == 1) {
242 base::hash_map<std::string, bool>::iterator it = active_sessions_.begin();
243 new_session_id = it->first;
244 } else {
245 OnLegacySessionError(std::string(), MediaKeys::NOT_SUPPORTED_ERROR, 0,
246 "SessionId not specified.");
247 return;
251 scoped_ptr<SimpleCdmPromise> promise(new CdmCallbackPromise<>(
252 base::Bind(&ProxyDecryptor::GenerateKeyAdded,
253 weak_ptr_factory_.GetWeakPtr(), session_id),
254 base::Bind(&ProxyDecryptor::OnLegacySessionError,
255 weak_ptr_factory_.GetWeakPtr(), session_id)));
257 // EME WD spec only supports a single array passed to the CDM. For
258 // Clear Key using v0.1b, both arrays are used (|init_data| is key_id).
259 // Since the EME WD spec supports the key as a JSON Web Key,
260 // convert the 2 arrays to a JWK and pass it as the single array.
261 if (is_clear_key_) {
262 // Decryptor doesn't support empty key ID (see http://crbug.com/123265).
263 // So ensure a non-empty value is passed.
264 if (!init_data) {
265 static const uint8 kDummyInitData[1] = {0};
266 init_data = kDummyInitData;
267 init_data_length = arraysize(kDummyInitData);
270 std::string jwk =
271 GenerateJWKSet(key, key_length, init_data, init_data_length);
272 DCHECK(!jwk.empty());
273 media_keys_->UpdateSession(new_session_id,
274 reinterpret_cast<const uint8*>(jwk.data()),
275 jwk.size(), promise.Pass());
276 return;
279 media_keys_->UpdateSession(new_session_id, key, key_length, promise.Pass());
282 void ProxyDecryptor::CancelKeyRequest(const std::string& session_id) {
283 DVLOG(1) << "CancelKeyRequest()";
285 if (!media_keys_) {
286 OnLegacySessionError(std::string(), MediaKeys::INVALID_STATE_ERROR, 0,
287 "CDM is not available.");
288 return;
291 scoped_ptr<SimpleCdmPromise> promise(new CdmCallbackPromise<>(
292 base::Bind(&ProxyDecryptor::OnSessionClosed,
293 weak_ptr_factory_.GetWeakPtr(), session_id),
294 base::Bind(&ProxyDecryptor::OnLegacySessionError,
295 weak_ptr_factory_.GetWeakPtr(), session_id)));
296 media_keys_->RemoveSession(session_id, promise.Pass());
299 void ProxyDecryptor::OnSessionMessage(const std::string& session_id,
300 MediaKeys::MessageType message_type,
301 const std::vector<uint8>& message,
302 const GURL& legacy_destination_url) {
303 // Assumes that OnSessionCreated() has been called before this.
305 // For ClearKey, convert the message from JSON into just passing the key
306 // as the message. If unable to extract the key, return the message unchanged.
307 if (is_clear_key_) {
308 std::vector<uint8> key;
309 if (ExtractFirstKeyIdFromLicenseRequest(message, &key)) {
310 key_message_cb_.Run(session_id, key, legacy_destination_url);
311 return;
315 key_message_cb_.Run(session_id, message, legacy_destination_url);
318 void ProxyDecryptor::OnSessionKeysChange(const std::string& session_id,
319 bool has_additional_usable_key,
320 CdmKeysInfo keys_info) {
321 // EME v0.1b doesn't support this event.
324 void ProxyDecryptor::OnSessionExpirationUpdate(
325 const std::string& session_id,
326 const base::Time& new_expiry_time) {
327 // EME v0.1b doesn't support this event.
330 void ProxyDecryptor::GenerateKeyAdded(const std::string& session_id) {
331 // EME WD doesn't support this event, but it is needed for EME v0.1b.
332 key_added_cb_.Run(session_id);
335 void ProxyDecryptor::OnSessionClosed(const std::string& session_id) {
336 base::hash_map<std::string, bool>::iterator it =
337 active_sessions_.find(session_id);
339 // Latest EME spec separates closing a session ("allows an application to
340 // indicate that it no longer needs the session") and actually closing the
341 // session (done by the CDM at any point "such as in response to a close()
342 // call, when the session is no longer needed, or when system resources are
343 // lost.") Thus the CDM may cause 2 close() events -- one to resolve the
344 // close() promise, and a second to actually close the session. Prefixed EME
345 // only expects 1 close event, so drop the second (and subsequent) events.
346 // However, this means we can't tell if the CDM is generating spurious close()
347 // events.
348 if (it == active_sessions_.end())
349 return;
351 if (it->second) {
352 OnLegacySessionError(session_id, MediaKeys::NOT_SUPPORTED_ERROR,
353 kSessionClosedSystemCode,
354 "Do not close persistent sessions.");
356 active_sessions_.erase(it);
359 void ProxyDecryptor::OnLegacySessionError(const std::string& session_id,
360 MediaKeys::Exception exception_code,
361 uint32 system_code,
362 const std::string& error_message) {
363 // Convert |error_name| back to MediaKeys::KeyError if possible. Prefixed
364 // EME has different error message, so all the specific error events will
365 // get lost.
366 MediaKeys::KeyError error_code;
367 switch (exception_code) {
368 case MediaKeys::CLIENT_ERROR:
369 error_code = MediaKeys::kClientError;
370 break;
371 case MediaKeys::OUTPUT_ERROR:
372 error_code = MediaKeys::kOutputError;
373 break;
374 default:
375 // This will include all other CDM4 errors and any error generated
376 // by CDM5 or later.
377 error_code = MediaKeys::kUnknownError;
378 break;
380 key_error_cb_.Run(session_id, error_code, system_code);
383 void ProxyDecryptor::SetSessionId(SessionCreationType session_type,
384 const std::string& session_id) {
385 // Loaded sessions are considered persistent.
386 bool is_persistent =
387 session_type == PersistentSession || session_type == LoadSession;
388 active_sessions_.insert(std::make_pair(session_id, is_persistent));
390 // For LoadSession(), generate the KeyAdded event.
391 if (session_type == LoadSession)
392 GenerateKeyAdded(session_id);
395 } // namespace media