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"
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/message_loop/message_loop_proxy.h"
18 #include "base/strings/string_util.h"
19 #include "base/sys_byteorder.h"
20 #include "jni/MediaDrmBridge_jni.h"
22 #include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR.
24 using base::android::AttachCurrentThread
;
25 using base::android::ConvertUTF8ToJavaString
;
26 using base::android::ConvertJavaStringToUTF8
;
27 using base::android::JavaByteArrayToByteVector
;
28 using base::android::ScopedJavaLocalRef
;
32 static uint32
ReadUint32(const uint8_t* data
) {
34 for (int i
= 0; i
< 4; ++i
)
35 value
= (value
<< 8) | data
[i
];
39 static uint64
ReadUint64(const uint8_t* data
) {
41 for (int i
= 0; i
< 8; ++i
)
42 value
= (value
<< 8) | data
[i
];
46 // The structure of an ISO CENC Protection System Specific Header (PSSH) box is
47 // as follows. (See ISO/IEC FDIS 23001-7:2011(E).)
48 // Note: ISO boxes use big-endian values.
53 // uint64 LargeSize # Field is only present if value(Size) == 1.
54 // uint32 VersionAndFlags
57 // uint8[DataSize] Data
59 const int kBoxHeaderSize
= 8; // Box's header contains Size and Type.
60 const int kBoxLargeSizeSize
= 8;
61 const int kPsshVersionFlagSize
= 4;
62 const int kPsshSystemIdSize
= 16;
63 const int kPsshDataSizeSize
= 4;
64 const uint32 kTencType
= 0x74656e63;
65 const uint32 kPsshType
= 0x70737368;
66 const uint8 kWidevineUuid
[16] = {
67 0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE,
68 0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED };
70 typedef std::vector
<uint8
> UUID
;
72 class KeySystemUuidManager
{
74 KeySystemUuidManager();
75 UUID
GetUUID(const std::string
& key_system
);
76 void AddMapping(const std::string
& key_system
, const UUID
& uuid
);
77 std::vector
<std::string
> GetPlatformKeySystemNames();
80 typedef base::hash_map
<std::string
, UUID
> KeySystemUuidMap
;
82 KeySystemUuidMap key_system_uuid_map_
;
84 DISALLOW_COPY_AND_ASSIGN(KeySystemUuidManager
);
87 KeySystemUuidManager::KeySystemUuidManager() {
88 // Widevine is always supported in Android.
89 key_system_uuid_map_
[kWidevineKeySystem
] =
90 UUID(kWidevineUuid
, kWidevineUuid
+ arraysize(kWidevineUuid
));
93 UUID
KeySystemUuidManager::GetUUID(const std::string
& key_system
) {
94 KeySystemUuidMap::iterator it
= key_system_uuid_map_
.find(key_system
);
95 if (it
== key_system_uuid_map_
.end())
100 void KeySystemUuidManager::AddMapping(const std::string
& key_system
,
102 KeySystemUuidMap::iterator it
= key_system_uuid_map_
.find(key_system
);
103 DCHECK(it
== key_system_uuid_map_
.end())
104 << "Shouldn't overwrite an existing key system.";
105 if (it
!= key_system_uuid_map_
.end())
107 key_system_uuid_map_
[key_system
] = uuid
;
110 std::vector
<std::string
> KeySystemUuidManager::GetPlatformKeySystemNames() {
111 std::vector
<std::string
> key_systems
;
112 for (KeySystemUuidMap::iterator it
= key_system_uuid_map_
.begin();
113 it
!= key_system_uuid_map_
.end(); ++it
) {
114 // Rule out the key system handled by Chrome explicitly.
115 if (it
->first
!= kWidevineKeySystem
)
116 key_systems
.push_back(it
->first
);
121 base::LazyInstance
<KeySystemUuidManager
>::Leaky g_key_system_uuid_manager
=
122 LAZY_INSTANCE_INITIALIZER
;
124 // Tries to find a PSSH box whose "SystemId" is |uuid| in |data|, parses the
125 // "Data" of the box and put it in |pssh_data|. Returns true if such a box is
126 // found and successfully parsed. Returns false otherwise.
128 // 1, If multiple PSSH boxes are found,the "Data" of the first matching PSSH box
129 // will be set in |pssh_data|.
130 // 2, Only PSSH and TENC boxes are allowed in |data|. TENC boxes are skipped.
131 static bool GetPsshData(const uint8
* data
, int data_size
,
133 std::vector
<uint8
>* pssh_data
) {
134 const uint8
* cur
= data
;
135 const uint8
* data_end
= data
+ data_size
;
136 int bytes_left
= data_size
;
138 while (bytes_left
> 0) {
139 const uint8
* box_head
= cur
;
141 if (bytes_left
< kBoxHeaderSize
)
144 uint64_t box_size
= ReadUint32(cur
);
145 uint32 type
= ReadUint32(cur
+ 4);
146 cur
+= kBoxHeaderSize
;
147 bytes_left
-= kBoxHeaderSize
;
149 if (box_size
== 1) { // LargeSize is present.
150 if (bytes_left
< kBoxLargeSizeSize
)
153 box_size
= ReadUint64(cur
);
154 cur
+= kBoxLargeSizeSize
;
155 bytes_left
-= kBoxLargeSizeSize
;
156 } else if (box_size
== 0) {
157 box_size
= bytes_left
+ kBoxHeaderSize
;
160 const uint8
* box_end
= box_head
+ box_size
;
161 if (data_end
< box_end
)
164 if (type
== kTencType
) {
167 bytes_left
= data_end
- cur
;
169 } else if (type
!= kPsshType
) {
173 const int kPsshBoxMinimumSize
=
174 kPsshVersionFlagSize
+ kPsshSystemIdSize
+ kPsshDataSizeSize
;
175 if (box_end
< cur
+ kPsshBoxMinimumSize
)
178 uint32 version_and_flags
= ReadUint32(cur
);
179 cur
+= kPsshVersionFlagSize
;
180 bytes_left
-= kPsshVersionFlagSize
;
181 if (version_and_flags
!= 0)
184 DCHECK_GE(bytes_left
, kPsshSystemIdSize
);
185 if (!std::equal(uuid
.begin(), uuid
.end(), cur
)) {
187 bytes_left
= data_end
- cur
;
191 cur
+= kPsshSystemIdSize
;
192 bytes_left
-= kPsshSystemIdSize
;
194 uint32 data_size
= ReadUint32(cur
);
195 cur
+= kPsshDataSizeSize
;
196 bytes_left
-= kPsshDataSizeSize
;
198 if (box_end
< cur
+ data_size
)
201 pssh_data
->assign(cur
, cur
+ data_size
);
208 static MediaDrmBridge::SecurityLevel
GetSecurityLevelFromString(
209 const std::string
& security_level_str
) {
210 if (0 == security_level_str
.compare("L1"))
211 return MediaDrmBridge::SECURITY_LEVEL_1
;
212 if (0 == security_level_str
.compare("L3"))
213 return MediaDrmBridge::SECURITY_LEVEL_3
;
214 DCHECK(security_level_str
.empty());
215 return MediaDrmBridge::SECURITY_LEVEL_NONE
;
218 static std::string
GetSecurityLevelString(
219 MediaDrmBridge::SecurityLevel security_level
) {
220 switch (security_level
) {
221 case MediaDrmBridge::SECURITY_LEVEL_NONE
:
223 case MediaDrmBridge::SECURITY_LEVEL_1
:
225 case MediaDrmBridge::SECURITY_LEVEL_3
:
231 // Checks whether |key_system| is supported with |container_mime_type|. Only
232 // checks |key_system| support if |container_mime_type| is empty.
233 // TODO(xhwang): The |container_mime_type| is not the same as contentType in
234 // the EME spec. Revisit this once the spec issue with initData type is
236 static bool IsKeySystemSupportedWithTypeImpl(
237 const std::string
& key_system
,
238 const std::string
& container_mime_type
) {
239 if (!MediaDrmBridge::IsAvailable())
242 UUID scheme_uuid
= g_key_system_uuid_manager
.Get().GetUUID(key_system
);
243 if (scheme_uuid
.empty())
246 JNIEnv
* env
= AttachCurrentThread();
247 ScopedJavaLocalRef
<jbyteArray
> j_scheme_uuid
=
248 base::android::ToJavaByteArray(env
, &scheme_uuid
[0], scheme_uuid
.size());
249 ScopedJavaLocalRef
<jstring
> j_container_mime_type
=
250 ConvertUTF8ToJavaString(env
, container_mime_type
);
251 return Java_MediaDrmBridge_isCryptoSchemeSupported(
252 env
, j_scheme_uuid
.obj(), j_container_mime_type
.obj());
256 bool MediaDrmBridge::IsAvailable() {
257 return base::android::BuildInfo::GetInstance()->sdk_int() >= 19;
261 bool MediaDrmBridge::IsSecureDecoderRequired(SecurityLevel security_level
) {
262 DCHECK(IsAvailable());
263 return SECURITY_LEVEL_1
== security_level
;
267 bool MediaDrmBridge::IsSecurityLevelSupported(const std::string
& key_system
,
268 SecurityLevel security_level
) {
272 scoped_ptr
<MediaDrmBridge
> media_drm_bridge
=
273 MediaDrmBridge::CreateSessionless(key_system
);
274 if (!media_drm_bridge
)
277 return media_drm_bridge
->SetSecurityLevel(security_level
);
280 static void AddKeySystemUuidMapping(JNIEnv
* env
, jclass clazz
,
281 jstring j_key_system
,
283 std::string key_system
= ConvertJavaStringToUTF8(env
, j_key_system
);
284 uint8
* buffer
= static_cast<uint8
*>(env
->GetDirectBufferAddress(j_buffer
));
285 UUID
uuid(buffer
, buffer
+ 16);
286 g_key_system_uuid_manager
.Get().AddMapping(key_system
, uuid
);
290 std::vector
<std::string
> MediaDrmBridge::GetPlatformKeySystemNames() {
291 return g_key_system_uuid_manager
.Get().GetPlatformKeySystemNames();
295 bool MediaDrmBridge::IsKeySystemSupported(const std::string
& key_system
) {
296 DCHECK(!key_system
.empty());
297 return IsKeySystemSupportedWithTypeImpl(key_system
, "");
301 bool MediaDrmBridge::IsKeySystemSupportedWithType(
302 const std::string
& key_system
,
303 const std::string
& container_mime_type
) {
304 DCHECK(!key_system
.empty() && !container_mime_type
.empty());
305 return IsKeySystemSupportedWithTypeImpl(key_system
, container_mime_type
);
308 bool MediaDrmBridge::RegisterMediaDrmBridge(JNIEnv
* env
) {
309 return RegisterNativesImpl(env
);
312 MediaDrmBridge::MediaDrmBridge(const std::vector
<uint8
>& scheme_uuid
,
313 const SessionCreatedCB
& session_created_cb
,
314 const SessionMessageCB
& session_message_cb
,
315 const SessionReadyCB
& session_ready_cb
,
316 const SessionClosedCB
& session_closed_cb
,
317 const SessionErrorCB
& session_error_cb
)
318 : scheme_uuid_(scheme_uuid
),
319 session_created_cb_(session_created_cb
),
320 session_message_cb_(session_message_cb
),
321 session_ready_cb_(session_ready_cb
),
322 session_closed_cb_(session_closed_cb
),
323 session_error_cb_(session_error_cb
) {
324 JNIEnv
* env
= AttachCurrentThread();
327 ScopedJavaLocalRef
<jbyteArray
> j_scheme_uuid
=
328 base::android::ToJavaByteArray(env
, &scheme_uuid
[0], scheme_uuid
.size());
329 j_media_drm_
.Reset(Java_MediaDrmBridge_create(
330 env
, j_scheme_uuid
.obj(), reinterpret_cast<intptr_t>(this)));
333 MediaDrmBridge::~MediaDrmBridge() {
334 JNIEnv
* env
= AttachCurrentThread();
335 player_tracker_
.NotifyCdmUnset();
336 if (!j_media_drm_
.is_null())
337 Java_MediaDrmBridge_release(env
, j_media_drm_
.obj());
341 scoped_ptr
<MediaDrmBridge
> MediaDrmBridge::Create(
342 const std::string
& key_system
,
343 const SessionCreatedCB
& session_created_cb
,
344 const SessionMessageCB
& session_message_cb
,
345 const SessionReadyCB
& session_ready_cb
,
346 const SessionClosedCB
& session_closed_cb
,
347 const SessionErrorCB
& session_error_cb
) {
348 scoped_ptr
<MediaDrmBridge
> media_drm_bridge
;
350 return media_drm_bridge
.Pass();
352 UUID scheme_uuid
= g_key_system_uuid_manager
.Get().GetUUID(key_system
);
353 if (scheme_uuid
.empty())
354 return media_drm_bridge
.Pass();
356 media_drm_bridge
.reset(new MediaDrmBridge(scheme_uuid
,
363 if (media_drm_bridge
->j_media_drm_
.is_null())
364 media_drm_bridge
.reset();
366 return media_drm_bridge
.Pass();
370 scoped_ptr
<MediaDrmBridge
> MediaDrmBridge::CreateSessionless(
371 const std::string
& key_system
) {
372 return MediaDrmBridge::Create(key_system
,
380 bool MediaDrmBridge::SetSecurityLevel(SecurityLevel security_level
) {
381 JNIEnv
* env
= AttachCurrentThread();
383 std::string security_level_str
= GetSecurityLevelString(security_level
);
384 if (security_level_str
.empty())
387 ScopedJavaLocalRef
<jstring
> j_security_level
=
388 ConvertUTF8ToJavaString(env
, security_level_str
);
389 return Java_MediaDrmBridge_setSecurityLevel(
390 env
, j_media_drm_
.obj(), j_security_level
.obj());
393 bool MediaDrmBridge::CreateSession(uint32 session_id
,
394 const std::string
& content_type
,
395 const uint8
* init_data
,
396 int init_data_length
) {
397 DVLOG(1) << __FUNCTION__
;
399 DCHECK(!session_created_cb_
.is_null())
400 << "CreateSession called on a sessionless MediaDrmBridge object.";
402 JNIEnv
* env
= AttachCurrentThread();
403 ScopedJavaLocalRef
<jbyteArray
> j_init_data
;
404 // Caller should always use "video/*" content types.
405 DCHECK_EQ(0u, content_type
.find("video/"));
407 // Widevine MediaDrm plugin only accepts the "data" part of the PSSH box as
408 // the init data when using MP4 container.
409 if (std::equal(scheme_uuid_
.begin(), scheme_uuid_
.end(), kWidevineUuid
) &&
410 content_type
== "video/mp4") {
411 std::vector
<uint8
> pssh_data
;
412 if (!GetPsshData(init_data
, init_data_length
, scheme_uuid_
, &pssh_data
))
415 base::android::ToJavaByteArray(env
, &pssh_data
[0], pssh_data
.size());
418 base::android::ToJavaByteArray(env
, init_data
, init_data_length
);
421 ScopedJavaLocalRef
<jstring
> j_mime
=
422 ConvertUTF8ToJavaString(env
, content_type
);
423 Java_MediaDrmBridge_createSession(
424 env
, j_media_drm_
.obj(), session_id
, j_init_data
.obj(), j_mime
.obj());
428 void MediaDrmBridge::LoadSession(uint32 session_id
,
429 const std::string
& web_session_id
) {
430 // MediaDrmBridge doesn't support loading sessions.
434 void MediaDrmBridge::UpdateSession(uint32 session_id
,
435 const uint8
* response
,
436 int response_length
) {
437 DVLOG(1) << __FUNCTION__
;
439 DCHECK(!session_ready_cb_
.is_null())
440 << __FUNCTION__
<< " called on a sessionless MediaDrmBridge object.";
442 JNIEnv
* env
= AttachCurrentThread();
443 ScopedJavaLocalRef
<jbyteArray
> j_response
=
444 base::android::ToJavaByteArray(env
, response
, response_length
);
445 Java_MediaDrmBridge_updateSession(
446 env
, j_media_drm_
.obj(), session_id
, j_response
.obj());
449 void MediaDrmBridge::ReleaseSession(uint32 session_id
) {
450 DVLOG(1) << __FUNCTION__
;
452 DCHECK(!session_closed_cb_
.is_null())
453 << __FUNCTION__
<< " called on a sessionless MediaDrmBridge object.";
455 JNIEnv
* env
= AttachCurrentThread();
456 Java_MediaDrmBridge_releaseSession(env
, j_media_drm_
.obj(), session_id
);
459 int MediaDrmBridge::RegisterPlayer(const base::Closure
& new_key_cb
,
460 const base::Closure
& cdm_unset_cb
) {
461 return player_tracker_
.RegisterPlayer(new_key_cb
, cdm_unset_cb
);
464 void MediaDrmBridge::UnregisterPlayer(int registration_id
) {
465 player_tracker_
.UnregisterPlayer(registration_id
);
468 void MediaDrmBridge::SetMediaCryptoReadyCB(const base::Closure
& closure
) {
469 if (closure
.is_null()) {
470 media_crypto_ready_cb_
.Reset();
474 DCHECK(media_crypto_ready_cb_
.is_null());
476 if (!GetMediaCrypto().is_null()) {
477 base::MessageLoopProxy::current()->PostTask(FROM_HERE
, closure
);
481 media_crypto_ready_cb_
= closure
;
484 void MediaDrmBridge::OnMediaCryptoReady(JNIEnv
* env
, jobject
) {
485 DCHECK(!GetMediaCrypto().is_null());
486 if (!media_crypto_ready_cb_
.is_null())
487 base::ResetAndReturn(&media_crypto_ready_cb_
).Run();
490 void MediaDrmBridge::OnSessionCreated(JNIEnv
* env
,
493 jstring j_web_session_id
) {
494 uint32 session_id
= j_session_id
;
495 std::string web_session_id
= ConvertJavaStringToUTF8(env
, j_web_session_id
);
496 session_created_cb_
.Run(session_id
, web_session_id
);
499 void MediaDrmBridge::OnSessionMessage(JNIEnv
* env
,
502 jbyteArray j_message
,
503 jstring j_destination_url
) {
504 uint32 session_id
= j_session_id
;
505 std::vector
<uint8
> message
;
506 JavaByteArrayToByteVector(env
, j_message
, &message
);
507 GURL destination_gurl
= GURL(ConvertJavaStringToUTF8(env
, j_destination_url
));
508 if (!destination_gurl
.is_valid() && !destination_gurl
.is_empty()) {
509 DLOG(WARNING
) << "SessionMessage destination_url is invalid : "
510 << destination_gurl
.possibly_invalid_spec();
511 destination_gurl
= GURL::EmptyGURL(); // Replace invalid destination_url.
513 session_message_cb_
.Run(session_id
, message
, destination_gurl
);
516 void MediaDrmBridge::OnSessionReady(JNIEnv
* env
,
519 uint32 session_id
= j_session_id
;
520 session_ready_cb_
.Run(session_id
);
521 // TODO(xhwang/jrummell): Move this when usableKeyIds/keyschange are
523 player_tracker_
.NotifyNewKey();
526 void MediaDrmBridge::OnSessionClosed(JNIEnv
* env
,
529 uint32 session_id
= j_session_id
;
530 session_closed_cb_
.Run(session_id
);
533 void MediaDrmBridge::OnSessionError(JNIEnv
* env
,
536 uint32 session_id
= j_session_id
;
537 session_error_cb_
.Run(session_id
, MediaKeys::kUnknownError
, 0);
540 ScopedJavaLocalRef
<jobject
> MediaDrmBridge::GetMediaCrypto() {
541 JNIEnv
* env
= AttachCurrentThread();
542 return Java_MediaDrmBridge_getMediaCrypto(env
, j_media_drm_
.obj());
545 MediaDrmBridge::SecurityLevel
MediaDrmBridge::GetSecurityLevel() {
546 JNIEnv
* env
= AttachCurrentThread();
547 ScopedJavaLocalRef
<jstring
> j_security_level
=
548 Java_MediaDrmBridge_getSecurityLevel(env
, j_media_drm_
.obj());
549 std::string security_level_str
=
550 ConvertJavaStringToUTF8(env
, j_security_level
.obj());
551 return GetSecurityLevelFromString(security_level_str
);
554 bool MediaDrmBridge::IsProtectedSurfaceRequired() {
555 return IsSecureDecoderRequired(GetSecurityLevel());
558 void MediaDrmBridge::ResetDeviceCredentials(
559 const ResetCredentialsCB
& callback
) {
560 DCHECK(reset_credentials_cb_
.is_null());
561 reset_credentials_cb_
= callback
;
562 JNIEnv
* env
= AttachCurrentThread();
563 Java_MediaDrmBridge_resetDeviceCredentials(env
, j_media_drm_
.obj());
566 void MediaDrmBridge::OnResetDeviceCredentialsCompleted(
567 JNIEnv
* env
, jobject
, bool success
) {
568 base::ResetAndReturn(&reset_credentials_cb_
).Run(success
);