OnEncrypted event to use enum for |init_data_type|
[chromium-blink-merge.git] / media / cdm / proxy_decryptor.cc
blob944b61405c415927b8bda4e8d1e30ebc0e5fb844
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::ProxyDecryptor(MediaPermission* media_permission,
29 const KeyAddedCB& key_added_cb,
30 const KeyErrorCB& key_error_cb,
31 const KeyMessageCB& key_message_cb)
32 : media_permission_(media_permission),
33 key_added_cb_(key_added_cb),
34 key_error_cb_(key_error_cb),
35 key_message_cb_(key_message_cb),
36 is_clear_key_(false),
37 weak_ptr_factory_(this) {
38 DCHECK(media_permission);
39 DCHECK(!key_added_cb_.is_null());
40 DCHECK(!key_error_cb_.is_null());
41 DCHECK(!key_message_cb_.is_null());
44 ProxyDecryptor::~ProxyDecryptor() {
45 // Destroy the decryptor explicitly before destroying the plugin.
46 media_keys_.reset();
49 CdmContext* ProxyDecryptor::GetCdmContext() {
50 return media_keys_ ? media_keys_->GetCdmContext() : nullptr;
53 bool ProxyDecryptor::InitializeCDM(CdmFactory* cdm_factory,
54 const std::string& key_system,
55 const GURL& security_origin) {
56 DVLOG(1) << "InitializeCDM: key_system = " << key_system;
58 DCHECK(!media_keys_);
59 media_keys_ = CreateMediaKeys(cdm_factory, key_system, security_origin);
60 if (!media_keys_)
61 return false;
63 key_system_ = key_system;
64 security_origin_ = security_origin;
66 is_clear_key_ =
67 IsClearKey(key_system) || IsExternalClearKey(key_system);
68 return true;
71 // Returns true if |data| is prefixed with |header| and has data after the
72 // |header|.
73 bool HasHeader(const uint8* data, int data_length, const std::string& header) {
74 return static_cast<size_t>(data_length) > header.size() &&
75 std::equal(data, data + header.size(), header.begin());
78 // Removes the first |length| items from |data|.
79 void StripHeader(std::vector<uint8>& data, size_t length) {
80 data.erase(data.begin(), data.begin() + length);
83 bool ProxyDecryptor::GenerateKeyRequest(EmeInitDataType init_data_type,
84 const uint8* init_data,
85 int init_data_length) {
86 DVLOG(1) << "GenerateKeyRequest()";
87 const char kPrefixedApiPersistentSessionHeader[] = "PERSISTENT|";
88 const char kPrefixedApiLoadSessionHeader[] = "LOAD_SESSION|";
90 SessionCreationType session_creation_type = TemporarySession;
91 std::vector<uint8> init_data_vector(init_data, init_data + init_data_length);
92 if (HasHeader(init_data, init_data_length, kPrefixedApiLoadSessionHeader)) {
93 session_creation_type = LoadSession;
94 StripHeader(init_data_vector, strlen(kPrefixedApiLoadSessionHeader));
95 } else if (HasHeader(init_data,
96 init_data_length,
97 kPrefixedApiPersistentSessionHeader)) {
98 session_creation_type = PersistentSession;
99 StripHeader(init_data_vector, strlen(kPrefixedApiPersistentSessionHeader));
102 scoped_ptr<NewSessionCdmPromise> promise(new CdmCallbackPromise<std::string>(
103 base::Bind(&ProxyDecryptor::SetSessionId, weak_ptr_factory_.GetWeakPtr(),
104 session_creation_type),
105 base::Bind(&ProxyDecryptor::OnLegacySessionError,
106 weak_ptr_factory_.GetWeakPtr(),
107 std::string()))); // No session id until created.
109 if (session_creation_type == LoadSession) {
110 media_keys_->LoadSession(
111 MediaKeys::PERSISTENT_LICENSE_SESSION,
112 std::string(
113 reinterpret_cast<const char*>(vector_as_array(&init_data_vector)),
114 init_data_vector.size()),
115 promise.Pass());
116 return true;
119 MediaKeys::SessionType session_type =
120 session_creation_type == PersistentSession
121 ? MediaKeys::PERSISTENT_LICENSE_SESSION
122 : MediaKeys::TEMPORARY_SESSION;
124 // No permission required when AesDecryptor is used or when the key system is
125 // external clear key.
126 DCHECK(!key_system_.empty());
127 if (CanUseAesDecryptor(key_system_) || IsExternalClearKey(key_system_)) {
128 OnPermissionStatus(session_type, init_data_type, init_data_vector,
129 promise.Pass(), true /* granted */);
130 return true;
133 #if defined(OS_CHROMEOS) || defined(OS_ANDROID)
134 media_permission_->RequestPermission(
135 MediaPermission::PROTECTED_MEDIA_IDENTIFIER, security_origin_,
136 base::Bind(&ProxyDecryptor::OnPermissionStatus,
137 weak_ptr_factory_.GetWeakPtr(), session_type, init_data_type,
138 init_data_vector, base::Passed(&promise)));
139 #else
140 OnPermissionStatus(session_type, init_data_type, init_data_vector,
141 promise.Pass(), true /* granted */);
142 #endif
144 return true;
147 void ProxyDecryptor::OnPermissionStatus(
148 MediaKeys::SessionType session_type,
149 EmeInitDataType init_data_type,
150 const std::vector<uint8>& init_data,
151 scoped_ptr<NewSessionCdmPromise> promise,
152 bool granted) {
153 // ProxyDecryptor is only used by Prefixed EME, where RequestPermission() is
154 // only for triggering the permission UI. Later CheckPermission() will be
155 // called (e.g. in PlatformVerificationFlow on ChromeOS; in BrowserCdmManager
156 // on Android) and the permission status will be evaluated then.
157 DVLOG_IF(1, !granted) << "Permission request rejected.";
159 media_keys_->CreateSessionAndGenerateRequest(
160 session_type, init_data_type, vector_as_array(&init_data),
161 init_data.size(), promise.Pass());
164 void ProxyDecryptor::AddKey(const uint8* key,
165 int key_length,
166 const uint8* init_data,
167 int init_data_length,
168 const std::string& session_id) {
169 DVLOG(1) << "AddKey()";
171 // In the prefixed API, the session parameter provided to addKey() is
172 // optional, so use the single existing session if it exists.
173 std::string new_session_id(session_id);
174 if (new_session_id.empty()) {
175 if (active_sessions_.size() == 1) {
176 base::hash_map<std::string, bool>::iterator it = active_sessions_.begin();
177 new_session_id = it->first;
178 } else {
179 OnLegacySessionError(std::string(), MediaKeys::NOT_SUPPORTED_ERROR, 0,
180 "SessionId not specified.");
181 return;
185 scoped_ptr<SimpleCdmPromise> promise(new CdmCallbackPromise<>(
186 base::Bind(&ProxyDecryptor::GenerateKeyAdded,
187 weak_ptr_factory_.GetWeakPtr(), session_id),
188 base::Bind(&ProxyDecryptor::OnLegacySessionError,
189 weak_ptr_factory_.GetWeakPtr(), session_id)));
191 // EME WD spec only supports a single array passed to the CDM. For
192 // Clear Key using v0.1b, both arrays are used (|init_data| is key_id).
193 // Since the EME WD spec supports the key as a JSON Web Key,
194 // convert the 2 arrays to a JWK and pass it as the single array.
195 if (is_clear_key_) {
196 // Decryptor doesn't support empty key ID (see http://crbug.com/123265).
197 // So ensure a non-empty value is passed.
198 if (!init_data) {
199 static const uint8 kDummyInitData[1] = {0};
200 init_data = kDummyInitData;
201 init_data_length = arraysize(kDummyInitData);
204 std::string jwk =
205 GenerateJWKSet(key, key_length, init_data, init_data_length);
206 DCHECK(!jwk.empty());
207 media_keys_->UpdateSession(new_session_id,
208 reinterpret_cast<const uint8*>(jwk.data()),
209 jwk.size(), promise.Pass());
210 return;
213 media_keys_->UpdateSession(new_session_id, key, key_length, promise.Pass());
216 void ProxyDecryptor::CancelKeyRequest(const std::string& session_id) {
217 DVLOG(1) << "CancelKeyRequest()";
219 scoped_ptr<SimpleCdmPromise> promise(new CdmCallbackPromise<>(
220 base::Bind(&ProxyDecryptor::OnSessionClosed,
221 weak_ptr_factory_.GetWeakPtr(), session_id),
222 base::Bind(&ProxyDecryptor::OnLegacySessionError,
223 weak_ptr_factory_.GetWeakPtr(), session_id)));
224 media_keys_->RemoveSession(session_id, promise.Pass());
227 scoped_ptr<MediaKeys> ProxyDecryptor::CreateMediaKeys(
228 CdmFactory* cdm_factory,
229 const std::string& key_system,
230 const GURL& security_origin) {
231 // TODO(sandersd): Trigger permissions check here and use it to determine
232 // distinctive identifier support, instead of always requiring the
233 // permission. http://crbug.com/455271
234 bool allow_distinctive_identifier = true;
235 bool allow_persistent_state = true;
236 base::WeakPtr<ProxyDecryptor> weak_this = weak_ptr_factory_.GetWeakPtr();
237 return cdm_factory->Create(
238 key_system, allow_distinctive_identifier, allow_persistent_state,
239 security_origin, base::Bind(&ProxyDecryptor::OnSessionMessage, weak_this),
240 base::Bind(&ProxyDecryptor::OnSessionClosed, weak_this),
241 base::Bind(&ProxyDecryptor::OnLegacySessionError, weak_this),
242 base::Bind(&ProxyDecryptor::OnSessionKeysChange, weak_this),
243 base::Bind(&ProxyDecryptor::OnSessionExpirationUpdate, weak_this));
246 void ProxyDecryptor::OnSessionMessage(const std::string& session_id,
247 MediaKeys::MessageType message_type,
248 const std::vector<uint8>& message,
249 const GURL& legacy_destination_url) {
250 // Assumes that OnSessionCreated() has been called before this.
252 // For ClearKey, convert the message from JSON into just passing the key
253 // as the message. If unable to extract the key, return the message unchanged.
254 if (is_clear_key_) {
255 std::vector<uint8> key;
256 if (ExtractFirstKeyIdFromLicenseRequest(message, &key)) {
257 key_message_cb_.Run(session_id, key, legacy_destination_url);
258 return;
262 key_message_cb_.Run(session_id, message, legacy_destination_url);
265 void ProxyDecryptor::OnSessionKeysChange(const std::string& session_id,
266 bool has_additional_usable_key,
267 CdmKeysInfo keys_info) {
268 // EME v0.1b doesn't support this event.
271 void ProxyDecryptor::OnSessionExpirationUpdate(
272 const std::string& session_id,
273 const base::Time& new_expiry_time) {
274 // EME v0.1b doesn't support this event.
277 void ProxyDecryptor::GenerateKeyAdded(const std::string& session_id) {
278 // EME WD doesn't support this event, but it is needed for EME v0.1b.
279 key_added_cb_.Run(session_id);
282 void ProxyDecryptor::OnSessionClosed(const std::string& session_id) {
283 base::hash_map<std::string, bool>::iterator it =
284 active_sessions_.find(session_id);
286 // Latest EME spec separates closing a session ("allows an application to
287 // indicate that it no longer needs the session") and actually closing the
288 // session (done by the CDM at any point "such as in response to a close()
289 // call, when the session is no longer needed, or when system resources are
290 // lost.") Thus the CDM may cause 2 close() events -- one to resolve the
291 // close() promise, and a second to actually close the session. Prefixed EME
292 // only expects 1 close event, so drop the second (and subsequent) events.
293 // However, this means we can't tell if the CDM is generating spurious close()
294 // events.
295 if (it == active_sessions_.end())
296 return;
298 if (it->second) {
299 OnLegacySessionError(session_id, MediaKeys::NOT_SUPPORTED_ERROR,
300 kSessionClosedSystemCode,
301 "Do not close persistent sessions.");
303 active_sessions_.erase(it);
306 void ProxyDecryptor::OnLegacySessionError(const std::string& session_id,
307 MediaKeys::Exception exception_code,
308 uint32 system_code,
309 const std::string& error_message) {
310 // Convert |error_name| back to MediaKeys::KeyError if possible. Prefixed
311 // EME has different error message, so all the specific error events will
312 // get lost.
313 MediaKeys::KeyError error_code;
314 switch (exception_code) {
315 case MediaKeys::CLIENT_ERROR:
316 error_code = MediaKeys::kClientError;
317 break;
318 case MediaKeys::OUTPUT_ERROR:
319 error_code = MediaKeys::kOutputError;
320 break;
321 default:
322 // This will include all other CDM4 errors and any error generated
323 // by CDM5 or later.
324 error_code = MediaKeys::kUnknownError;
325 break;
327 key_error_cb_.Run(session_id, error_code, system_code);
330 void ProxyDecryptor::SetSessionId(SessionCreationType session_type,
331 const std::string& session_id) {
332 // Loaded sessions are considered persistent.
333 bool is_persistent =
334 session_type == PersistentSession || session_type == LoadSession;
335 active_sessions_.insert(std::make_pair(session_id, is_persistent));
337 // For LoadSession(), generate the KeyAdded event.
338 if (session_type == LoadSession)
339 GenerateKeyAdded(session_id);
342 } // namespace media