Roll src/third_party/WebKit d10c917:a1123a1 (svn 198729:198730)
[chromium-blink-merge.git] / media / base / android / media_drm_bridge.cc
blob4200635ef5e3d44cb30050bdbce63942b40842ec
1 // Copyright (c) 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/base/android/media_drm_bridge.h"
7 #include <algorithm>
9 #include "base/android/build_info.h"
10 #include "base/android/jni_array.h"
11 #include "base/android/jni_string.h"
12 #include "base/callback_helpers.h"
13 #include "base/containers/hash_tables.h"
14 #include "base/lazy_instance.h"
15 #include "base/location.h"
16 #include "base/logging.h"
17 #include "base/single_thread_task_runner.h"
18 #include "base/stl_util.h"
19 #include "base/strings/string_util.h"
20 #include "base/sys_byteorder.h"
21 #include "base/sys_info.h"
22 #include "base/thread_task_runner_handle.h"
23 #include "jni/MediaDrmBridge_jni.h"
24 #include "media/base/android/media_client_android.h"
25 #include "media/base/android/media_drm_bridge_delegate.h"
26 #include "media/base/cdm_key_information.h"
28 #include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR.
30 using base::android::AttachCurrentThread;
31 using base::android::ConvertUTF8ToJavaString;
32 using base::android::ConvertJavaStringToUTF8;
33 using base::android::JavaByteArrayToByteVector;
34 using base::android::ScopedJavaLocalRef;
36 namespace media {
38 namespace {
40 // DrmBridge supports session expiration event but doesn't provide detailed
41 // status for each key ID, which is required by the EME spec. Use a dummy key ID
42 // here to report session expiration info.
43 const char kDummyKeyId[] = "Dummy Key Id";
45 // Returns string session ID from jbyteArray (byte[] in Java).
46 std::string GetSessionId(JNIEnv* env, jbyteArray j_session_id) {
47 std::vector<uint8> session_id_vector;
48 JavaByteArrayToByteVector(env, j_session_id, &session_id_vector);
49 return std::string(session_id_vector.begin(), session_id_vector.end());
52 const uint8 kWidevineUuid[16] = {
53 0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE,
54 0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED };
56 // Convert |init_data_type| to a string supported by MediaDRM.
57 // "audio"/"video" does not matter, so use "video".
58 std::string ConvertInitDataType(media::EmeInitDataType init_data_type) {
59 // TODO(jrummell/xhwang): EME init data types like "webm" and "cenc" are
60 // supported in API level >=21 for Widevine key system. Switch to use those
61 // strings when they are officially supported in Android for all key systems.
62 switch (init_data_type) {
63 case media::EmeInitDataType::WEBM:
64 return "video/webm";
65 case media::EmeInitDataType::CENC:
66 return "video/mp4";
67 case media::EmeInitDataType::KEYIDS:
68 return "keyids";
69 default:
70 NOTREACHED();
71 return "unknown";
75 class KeySystemManager {
76 public:
77 KeySystemManager();
78 UUID GetUUID(const std::string& key_system);
79 std::vector<std::string> GetPlatformKeySystemNames();
81 private:
82 using KeySystemUuidMap = MediaClientAndroid::KeySystemUuidMap;
84 KeySystemUuidMap key_system_uuid_map_;
86 DISALLOW_COPY_AND_ASSIGN(KeySystemManager);
89 KeySystemManager::KeySystemManager() {
90 // Widevine is always supported in Android.
91 key_system_uuid_map_[kWidevineKeySystem] =
92 UUID(kWidevineUuid, kWidevineUuid + arraysize(kWidevineUuid));
93 MediaClientAndroid* client = GetMediaClientAndroid();
94 if (client)
95 client->AddKeySystemUUIDMappings(&key_system_uuid_map_);
98 UUID KeySystemManager::GetUUID(const std::string& key_system) {
99 KeySystemUuidMap::iterator it = key_system_uuid_map_.find(key_system);
100 if (it == key_system_uuid_map_.end())
101 return UUID();
102 return it->second;
105 std::vector<std::string> KeySystemManager::GetPlatformKeySystemNames() {
106 std::vector<std::string> key_systems;
107 for (KeySystemUuidMap::iterator it = key_system_uuid_map_.begin();
108 it != key_system_uuid_map_.end(); ++it) {
109 // Rule out the key system handled by Chrome explicitly.
110 if (it->first != kWidevineKeySystem)
111 key_systems.push_back(it->first);
113 return key_systems;
116 base::LazyInstance<KeySystemManager>::Leaky g_key_system_manager =
117 LAZY_INSTANCE_INITIALIZER;
119 // Checks whether |key_system| is supported with |container_mime_type|. Only
120 // checks |key_system| support if |container_mime_type| is empty.
121 // TODO(xhwang): The |container_mime_type| is not the same as contentType in
122 // the EME spec. Revisit this once the spec issue with initData type is
123 // resolved.
124 bool IsKeySystemSupportedWithTypeImpl(const std::string& key_system,
125 const std::string& container_mime_type) {
126 if (!MediaDrmBridge::IsAvailable())
127 return false;
129 UUID scheme_uuid = g_key_system_manager.Get().GetUUID(key_system);
130 if (scheme_uuid.empty())
131 return false;
133 JNIEnv* env = AttachCurrentThread();
134 ScopedJavaLocalRef<jbyteArray> j_scheme_uuid =
135 base::android::ToJavaByteArray(env, &scheme_uuid[0], scheme_uuid.size());
136 ScopedJavaLocalRef<jstring> j_container_mime_type =
137 ConvertUTF8ToJavaString(env, container_mime_type);
138 return Java_MediaDrmBridge_isCryptoSchemeSupported(
139 env, j_scheme_uuid.obj(), j_container_mime_type.obj());
142 MediaDrmBridge::SecurityLevel GetSecurityLevelFromString(
143 const std::string& security_level_str) {
144 if (0 == security_level_str.compare("L1"))
145 return MediaDrmBridge::SECURITY_LEVEL_1;
146 if (0 == security_level_str.compare("L3"))
147 return MediaDrmBridge::SECURITY_LEVEL_3;
148 DCHECK(security_level_str.empty());
149 return MediaDrmBridge::SECURITY_LEVEL_NONE;
152 std::string GetSecurityLevelString(
153 MediaDrmBridge::SecurityLevel security_level) {
154 switch (security_level) {
155 case MediaDrmBridge::SECURITY_LEVEL_NONE:
156 return "";
157 case MediaDrmBridge::SECURITY_LEVEL_1:
158 return "L1";
159 case MediaDrmBridge::SECURITY_LEVEL_3:
160 return "L3";
162 return "";
165 } // namespace
167 // static
168 bool MediaDrmBridge::IsAvailable() {
169 if (base::android::BuildInfo::GetInstance()->sdk_int() < 19)
170 return false;
172 int32 os_major_version = 0;
173 int32 os_minor_version = 0;
174 int32 os_bugfix_version = 0;
175 base::SysInfo::OperatingSystemVersionNumbers(&os_major_version,
176 &os_minor_version,
177 &os_bugfix_version);
178 if (os_major_version == 4 && os_minor_version == 4 && os_bugfix_version == 0)
179 return false;
181 return true;
184 // TODO(ddorwin): This is specific to Widevine. http://crbug.com/459400
185 // static
186 bool MediaDrmBridge::IsSecureDecoderRequired(SecurityLevel security_level) {
187 DCHECK(IsAvailable());
188 return SECURITY_LEVEL_1 == security_level;
191 // static
192 std::vector<std::string> MediaDrmBridge::GetPlatformKeySystemNames() {
193 return g_key_system_manager.Get().GetPlatformKeySystemNames();
196 // static
197 bool MediaDrmBridge::IsKeySystemSupported(const std::string& key_system) {
198 DCHECK(!key_system.empty());
199 return IsKeySystemSupportedWithTypeImpl(key_system, "");
202 // static
203 bool MediaDrmBridge::IsKeySystemSupportedWithType(
204 const std::string& key_system,
205 const std::string& container_mime_type) {
206 DCHECK(!key_system.empty() && !container_mime_type.empty());
207 return IsKeySystemSupportedWithTypeImpl(key_system, container_mime_type);
210 bool MediaDrmBridge::RegisterMediaDrmBridge(JNIEnv* env) {
211 return RegisterNativesImpl(env);
214 MediaDrmBridge::MediaDrmBridge(
215 const std::vector<uint8>& scheme_uuid,
216 const SessionMessageCB& session_message_cb,
217 const SessionClosedCB& session_closed_cb,
218 const LegacySessionErrorCB& legacy_session_error_cb,
219 const SessionKeysChangeCB& session_keys_change_cb)
220 : scheme_uuid_(scheme_uuid),
221 session_message_cb_(session_message_cb),
222 session_closed_cb_(session_closed_cb),
223 legacy_session_error_cb_(legacy_session_error_cb),
224 session_keys_change_cb_(session_keys_change_cb) {
225 JNIEnv* env = AttachCurrentThread();
226 CHECK(env);
228 ScopedJavaLocalRef<jbyteArray> j_scheme_uuid =
229 base::android::ToJavaByteArray(env, &scheme_uuid[0], scheme_uuid.size());
230 j_media_drm_.Reset(Java_MediaDrmBridge_create(
231 env, j_scheme_uuid.obj(), reinterpret_cast<intptr_t>(this)));
234 MediaDrmBridge::~MediaDrmBridge() {
235 JNIEnv* env = AttachCurrentThread();
236 player_tracker_.NotifyCdmUnset();
237 if (!j_media_drm_.is_null())
238 Java_MediaDrmBridge_destroy(env, j_media_drm_.obj());
241 // static
242 // TODO(xhwang): Enable SessionExpirationUpdateCB when it is supported.
243 scoped_ptr<MediaDrmBridge> MediaDrmBridge::Create(
244 const std::string& key_system,
245 const SessionMessageCB& session_message_cb,
246 const SessionClosedCB& session_closed_cb,
247 const LegacySessionErrorCB& legacy_session_error_cb,
248 const SessionKeysChangeCB& session_keys_change_cb,
249 const SessionExpirationUpdateCB& /* session_expiration_update_cb */) {
250 scoped_ptr<MediaDrmBridge> media_drm_bridge;
251 if (!IsAvailable())
252 return media_drm_bridge.Pass();
254 UUID scheme_uuid = g_key_system_manager.Get().GetUUID(key_system);
255 if (scheme_uuid.empty())
256 return media_drm_bridge.Pass();
258 media_drm_bridge.reset(
259 new MediaDrmBridge(scheme_uuid, session_message_cb, session_closed_cb,
260 legacy_session_error_cb, session_keys_change_cb));
262 if (media_drm_bridge->j_media_drm_.is_null())
263 media_drm_bridge.reset();
265 return media_drm_bridge.Pass();
268 // static
269 scoped_ptr<MediaDrmBridge> MediaDrmBridge::CreateWithoutSessionSupport(
270 const std::string& key_system) {
271 return MediaDrmBridge::Create(
272 key_system, SessionMessageCB(), SessionClosedCB(), LegacySessionErrorCB(),
273 SessionKeysChangeCB(), SessionExpirationUpdateCB());
276 bool MediaDrmBridge::SetSecurityLevel(SecurityLevel security_level) {
277 if (security_level != SECURITY_LEVEL_NONE &&
278 !std::equal(scheme_uuid_.begin(), scheme_uuid_.end(), kWidevineUuid)) {
279 NOTREACHED() << "Widevine security level " << security_level
280 << "used with another key system";
281 return false;
284 JNIEnv* env = AttachCurrentThread();
286 std::string security_level_str = GetSecurityLevelString(security_level);
287 if (security_level_str.empty())
288 return false;
290 ScopedJavaLocalRef<jstring> j_security_level =
291 ConvertUTF8ToJavaString(env, security_level_str);
292 return Java_MediaDrmBridge_setSecurityLevel(
293 env, j_media_drm_.obj(), j_security_level.obj());
296 void MediaDrmBridge::SetServerCertificate(
297 const std::vector<uint8_t>& certificate,
298 scoped_ptr<media::SimpleCdmPromise> promise) {
299 DCHECK(!certificate.empty());
301 JNIEnv* env = AttachCurrentThread();
302 ScopedJavaLocalRef<jbyteArray> j_certificate;
303 if (Java_MediaDrmBridge_setServerCertificate(env, j_media_drm_.obj(),
304 j_certificate.obj())) {
305 promise->resolve();
306 } else {
307 promise->reject(INVALID_ACCESS_ERROR, 0, "Set server certificate failed.");
311 void MediaDrmBridge::CreateSessionAndGenerateRequest(
312 SessionType session_type,
313 media::EmeInitDataType init_data_type,
314 const std::vector<uint8_t>& init_data,
315 scoped_ptr<media::NewSessionCdmPromise> promise) {
316 DVLOG(1) << __FUNCTION__;
318 if (session_type != media::MediaKeys::TEMPORARY_SESSION) {
319 NOTIMPLEMENTED() << "EME persistent sessions not yet supported on Android.";
320 promise->reject(NOT_SUPPORTED_ERROR, 0,
321 "Only the temporary session type is supported.");
322 return;
325 JNIEnv* env = AttachCurrentThread();
326 ScopedJavaLocalRef<jbyteArray> j_init_data;
327 ScopedJavaLocalRef<jobjectArray> j_optional_parameters;
329 MediaClientAndroid* client = GetMediaClientAndroid();
330 if (client) {
331 MediaDrmBridgeDelegate* delegate =
332 client->GetMediaDrmBridgeDelegate(scheme_uuid_);
333 if (delegate) {
334 std::vector<uint8> init_data_from_delegate;
335 std::vector<std::string> optional_parameters_from_delegate;
336 if (!delegate->OnCreateSession(init_data_type, init_data,
337 &init_data_from_delegate,
338 &optional_parameters_from_delegate)) {
339 promise->reject(INVALID_ACCESS_ERROR, 0, "Invalid init data.");
341 if (!init_data_from_delegate.empty()) {
342 j_init_data = base::android::ToJavaByteArray(
343 env, vector_as_array(&init_data_from_delegate),
344 init_data_from_delegate.size());
346 if (!optional_parameters_from_delegate.empty()) {
347 j_optional_parameters = base::android::ToJavaArrayOfStrings(
348 env, optional_parameters_from_delegate);
353 if (j_init_data.is_null()) {
354 j_init_data = base::android::ToJavaByteArray(
355 env, vector_as_array(&init_data), init_data.size());
358 ScopedJavaLocalRef<jstring> j_mime =
359 ConvertUTF8ToJavaString(env, ConvertInitDataType(init_data_type));
360 uint32_t promise_id = cdm_promise_adapter_.SavePromise(promise.Pass());
361 Java_MediaDrmBridge_createSessionFromNative(env, j_media_drm_.obj(),
362 j_init_data.obj(), j_mime.obj(),
363 j_optional_parameters.obj(),
364 promise_id);
367 void MediaDrmBridge::LoadSession(
368 SessionType session_type,
369 const std::string& session_id,
370 scoped_ptr<media::NewSessionCdmPromise> promise) {
371 NOTIMPLEMENTED() << "EME persistent sessions not yet supported on Android.";
372 promise->reject(NOT_SUPPORTED_ERROR, 0, "LoadSession() is not supported.");
375 void MediaDrmBridge::UpdateSession(
376 const std::string& session_id,
377 const std::vector<uint8_t>& response,
378 scoped_ptr<media::SimpleCdmPromise> promise) {
379 DVLOG(1) << __FUNCTION__;
381 JNIEnv* env = AttachCurrentThread();
382 ScopedJavaLocalRef<jbyteArray> j_response = base::android::ToJavaByteArray(
383 env, vector_as_array(&response), response.size());
384 ScopedJavaLocalRef<jbyteArray> j_session_id = base::android::ToJavaByteArray(
385 env, reinterpret_cast<const uint8_t*>(session_id.data()),
386 session_id.size());
387 uint32_t promise_id = cdm_promise_adapter_.SavePromise(promise.Pass());
388 Java_MediaDrmBridge_updateSession(env, j_media_drm_.obj(), j_session_id.obj(),
389 j_response.obj(), promise_id);
392 void MediaDrmBridge::CloseSession(const std::string& session_id,
393 scoped_ptr<media::SimpleCdmPromise> promise) {
394 DVLOG(1) << __FUNCTION__;
395 JNIEnv* env = AttachCurrentThread();
396 ScopedJavaLocalRef<jbyteArray> j_session_id = base::android::ToJavaByteArray(
397 env, reinterpret_cast<const uint8_t*>(session_id.data()),
398 session_id.size());
399 uint32_t promise_id = cdm_promise_adapter_.SavePromise(promise.Pass());
400 Java_MediaDrmBridge_closeSession(env, j_media_drm_.obj(), j_session_id.obj(),
401 promise_id);
404 void MediaDrmBridge::RemoveSession(
405 const std::string& session_id,
406 scoped_ptr<media::SimpleCdmPromise> promise) {
407 NOTIMPLEMENTED() << "EME persistent sessions not yet supported on Android.";
408 promise->reject(NOT_SUPPORTED_ERROR, 0, "RemoveSession() is not supported.");
411 CdmContext* MediaDrmBridge::GetCdmContext() {
412 NOTREACHED();
413 return nullptr;
416 int MediaDrmBridge::RegisterPlayer(const base::Closure& new_key_cb,
417 const base::Closure& cdm_unset_cb) {
418 return player_tracker_.RegisterPlayer(new_key_cb, cdm_unset_cb);
421 void MediaDrmBridge::UnregisterPlayer(int registration_id) {
422 player_tracker_.UnregisterPlayer(registration_id);
425 void MediaDrmBridge::SetMediaCryptoReadyCB(const base::Closure& closure) {
426 if (closure.is_null()) {
427 media_crypto_ready_cb_.Reset();
428 return;
431 DCHECK(media_crypto_ready_cb_.is_null());
433 if (!GetMediaCrypto().is_null()) {
434 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, closure);
435 return;
438 media_crypto_ready_cb_ = closure;
441 void MediaDrmBridge::OnMediaCryptoReady(JNIEnv* env, jobject) {
442 DCHECK(!GetMediaCrypto().is_null());
443 if (!media_crypto_ready_cb_.is_null())
444 base::ResetAndReturn(&media_crypto_ready_cb_).Run();
447 void MediaDrmBridge::OnPromiseResolved(JNIEnv* env,
448 jobject j_media_drm,
449 jint j_promise_id) {
450 cdm_promise_adapter_.ResolvePromise(j_promise_id);
453 void MediaDrmBridge::OnPromiseResolvedWithSession(JNIEnv* env,
454 jobject j_media_drm,
455 jint j_promise_id,
456 jbyteArray j_session_id) {
457 cdm_promise_adapter_.ResolvePromise(j_promise_id,
458 GetSessionId(env, j_session_id));
461 void MediaDrmBridge::OnPromiseRejected(JNIEnv* env,
462 jobject j_media_drm,
463 jint j_promise_id,
464 jstring j_error_message) {
465 std::string error_message = ConvertJavaStringToUTF8(env, j_error_message);
466 cdm_promise_adapter_.RejectPromise(j_promise_id, MediaKeys::UNKNOWN_ERROR, 0,
467 error_message);
470 void MediaDrmBridge::OnSessionMessage(JNIEnv* env,
471 jobject j_media_drm,
472 jbyteArray j_session_id,
473 jbyteArray j_message,
474 jstring j_legacy_destination_url) {
475 std::vector<uint8> message;
476 JavaByteArrayToByteVector(env, j_message, &message);
477 GURL legacy_destination_url =
478 GURL(ConvertJavaStringToUTF8(env, j_legacy_destination_url));
479 // Note: Message type is not supported in MediaDrm. Do our best guess here.
480 media::MediaKeys::MessageType message_type =
481 legacy_destination_url.is_empty() ? media::MediaKeys::LICENSE_REQUEST
482 : media::MediaKeys::LICENSE_RENEWAL;
484 session_message_cb_.Run(GetSessionId(env, j_session_id), message_type,
485 message, legacy_destination_url);
488 void MediaDrmBridge::OnSessionClosed(JNIEnv* env,
489 jobject j_media_drm,
490 jbyteArray j_session_id) {
491 session_closed_cb_.Run(GetSessionId(env, j_session_id));
494 void MediaDrmBridge::OnSessionKeysChange(JNIEnv* env,
495 jobject j_media_drm,
496 jbyteArray j_session_id,
497 bool has_additional_usable_key,
498 jint j_key_status) {
499 if (has_additional_usable_key)
500 player_tracker_.NotifyNewKey();
502 scoped_ptr<CdmKeyInformation> cdm_key_information(new CdmKeyInformation());
503 cdm_key_information->key_id.assign(kDummyKeyId,
504 kDummyKeyId + sizeof(kDummyKeyId));
505 cdm_key_information->status =
506 static_cast<CdmKeyInformation::KeyStatus>(j_key_status);
507 CdmKeysInfo cdm_keys_info;
508 cdm_keys_info.push_back(cdm_key_information.release());
510 session_keys_change_cb_.Run(GetSessionId(env, j_session_id),
511 has_additional_usable_key, cdm_keys_info.Pass());
514 void MediaDrmBridge::OnLegacySessionError(JNIEnv* env,
515 jobject j_media_drm,
516 jbyteArray j_session_id,
517 jstring j_error_message) {
518 std::string error_message = ConvertJavaStringToUTF8(env, j_error_message);
519 legacy_session_error_cb_.Run(GetSessionId(env, j_session_id),
520 MediaKeys::UNKNOWN_ERROR, 0, error_message);
523 ScopedJavaLocalRef<jobject> MediaDrmBridge::GetMediaCrypto() {
524 JNIEnv* env = AttachCurrentThread();
525 return Java_MediaDrmBridge_getMediaCrypto(env, j_media_drm_.obj());
528 MediaDrmBridge::SecurityLevel MediaDrmBridge::GetSecurityLevel() {
529 JNIEnv* env = AttachCurrentThread();
530 ScopedJavaLocalRef<jstring> j_security_level =
531 Java_MediaDrmBridge_getSecurityLevel(env, j_media_drm_.obj());
532 std::string security_level_str =
533 ConvertJavaStringToUTF8(env, j_security_level.obj());
534 return GetSecurityLevelFromString(security_level_str);
537 bool MediaDrmBridge::IsProtectedSurfaceRequired() {
538 // For Widevine, this depends on the security level.
539 if (std::equal(scheme_uuid_.begin(), scheme_uuid_.end(), kWidevineUuid))
540 return IsSecureDecoderRequired(GetSecurityLevel());
542 // For other key systems, assume true.
543 return true;
546 void MediaDrmBridge::ResetDeviceCredentials(
547 const ResetCredentialsCB& callback) {
548 DCHECK(reset_credentials_cb_.is_null());
549 reset_credentials_cb_ = callback;
550 JNIEnv* env = AttachCurrentThread();
551 Java_MediaDrmBridge_resetDeviceCredentials(env, j_media_drm_.obj());
554 void MediaDrmBridge::OnResetDeviceCredentialsCompleted(
555 JNIEnv* env, jobject, bool success) {
556 base::ResetAndReturn(&reset_credentials_cb_).Run(success);
559 } // namespace media