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/location.h"
14 #include "base/logging.h"
15 #include "base/message_loop/message_loop_proxy.h"
16 #include "base/strings/string_util.h"
17 #include "jni/MediaDrmBridge_jni.h"
18 #include "media/base/android/media_player_manager.h"
20 #include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR.
22 using base::android::AttachCurrentThread
;
23 using base::android::ConvertUTF8ToJavaString
;
24 using base::android::ConvertJavaStringToUTF8
;
25 using base::android::JavaByteArrayToByteVector
;
26 using base::android::ScopedJavaLocalRef
;
30 static uint32
ReadUint32(const uint8_t* data
) {
32 for (int i
= 0; i
< 4; ++i
)
33 value
= (value
<< 8) | data
[i
];
37 static uint64
ReadUint64(const uint8_t* data
) {
39 for (int i
= 0; i
< 8; ++i
)
40 value
= (value
<< 8) | data
[i
];
44 // The structure of an ISO CENC Protection System Specific Header (PSSH) box is
45 // as follows. (See ISO/IEC FDIS 23001-7:2011(E).)
46 // Note: ISO boxes use big-endian values.
51 // uint64 LargeSize # Field is only present if value(Size) == 1.
52 // uint32 VersionAndFlags
55 // uint8[DataSize] Data
57 const int kBoxHeaderSize
= 8; // Box's header contains Size and Type.
58 const int kBoxLargeSizeSize
= 8;
59 const int kPsshVersionFlagSize
= 4;
60 const int kPsshSystemIdSize
= 16;
61 const int kPsshDataSizeSize
= 4;
62 const uint32 kTencType
= 0x74656e63;
63 const uint32 kPsshType
= 0x70737368;
64 const uint8 kWidevineUuid
[16] = {
65 0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE,
66 0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED };
68 static std::vector
<uint8
> GetUUID(const std::string
& key_system
) {
69 // For security reasons, we only do exact string comparisons here - we don't
70 // try to parse the |key_system| in any way.
71 if (key_system
== kWidevineKeySystem
) {
72 return std::vector
<uint8
>(kWidevineUuid
,
73 kWidevineUuid
+ arraysize(kWidevineUuid
));
75 return std::vector
<uint8
>();
78 // Tries to find a PSSH box whose "SystemId" is |uuid| in |data|, parses the
79 // "Data" of the box and put it in |pssh_data|. Returns true if such a box is
80 // found and successfully parsed. Returns false otherwise.
82 // 1, If multiple PSSH boxes are found,the "Data" of the first matching PSSH box
83 // will be set in |pssh_data|.
84 // 2, Only PSSH and TENC boxes are allowed in |data|. TENC boxes are skipped.
85 static bool GetPsshData(const uint8
* data
, int data_size
,
86 const std::vector
<uint8
>& uuid
,
87 std::vector
<uint8
>* pssh_data
) {
88 const uint8
* cur
= data
;
89 const uint8
* data_end
= data
+ data_size
;
90 int bytes_left
= data_size
;
92 while (bytes_left
> 0) {
93 const uint8
* box_head
= cur
;
95 if (bytes_left
< kBoxHeaderSize
)
98 uint64_t box_size
= ReadUint32(cur
);
99 uint32 type
= ReadUint32(cur
+ 4);
100 cur
+= kBoxHeaderSize
;
101 bytes_left
-= kBoxHeaderSize
;
103 if (box_size
== 1) { // LargeSize is present.
104 if (bytes_left
< kBoxLargeSizeSize
)
107 box_size
= ReadUint64(cur
);
108 cur
+= kBoxLargeSizeSize
;
109 bytes_left
-= kBoxLargeSizeSize
;
110 } else if (box_size
== 0) {
111 box_size
= bytes_left
+ kBoxHeaderSize
;
114 const uint8
* box_end
= box_head
+ box_size
;
115 if (data_end
< box_end
)
118 if (type
== kTencType
) {
121 bytes_left
= data_end
- cur
;
123 } else if (type
!= kPsshType
) {
127 const int kPsshBoxMinimumSize
=
128 kPsshVersionFlagSize
+ kPsshSystemIdSize
+ kPsshDataSizeSize
;
129 if (box_end
< cur
+ kPsshBoxMinimumSize
)
132 uint32 version_and_flags
= ReadUint32(cur
);
133 cur
+= kPsshVersionFlagSize
;
134 bytes_left
-= kPsshVersionFlagSize
;
135 if (version_and_flags
!= 0)
138 DCHECK_GE(bytes_left
, kPsshSystemIdSize
);
139 if (!std::equal(uuid
.begin(), uuid
.end(), cur
)) {
141 bytes_left
= data_end
- cur
;
145 cur
+= kPsshSystemIdSize
;
146 bytes_left
-= kPsshSystemIdSize
;
148 uint32 data_size
= ReadUint32(cur
);
149 cur
+= kPsshDataSizeSize
;
150 bytes_left
-= kPsshDataSizeSize
;
152 if (box_end
< cur
+ data_size
)
155 pssh_data
->assign(cur
, cur
+ data_size
);
162 static MediaDrmBridge::SecurityLevel
GetSecurityLevelFromString(
163 const std::string
& security_level_str
) {
164 if (0 == security_level_str
.compare("L1"))
165 return MediaDrmBridge::SECURITY_LEVEL_1
;
166 if (0 == security_level_str
.compare("L3"))
167 return MediaDrmBridge::SECURITY_LEVEL_3
;
168 DCHECK(security_level_str
.empty());
169 return MediaDrmBridge::SECURITY_LEVEL_NONE
;
172 static std::string
GetSecurityLevelString(
173 MediaDrmBridge::SecurityLevel security_level
) {
174 switch (security_level
) {
175 case MediaDrmBridge::SECURITY_LEVEL_NONE
:
177 case MediaDrmBridge::SECURITY_LEVEL_1
:
179 case MediaDrmBridge::SECURITY_LEVEL_3
:
185 // Checks whether |key_system| is supported with |container_mime_type|. Only
186 // checks |key_system| support if |container_mime_type| is empty.
187 // TODO(xhwang): The |container_mime_type| is not the same as contentType in
188 // the EME spec. Revisit this once the spec issue with initData type is
190 static bool IsKeySystemSupportedWithTypeImpl(
191 const std::string
& key_system
,
192 const std::string
& container_mime_type
) {
193 if (!MediaDrmBridge::IsAvailable())
196 std::vector
<uint8
> scheme_uuid
= GetUUID(key_system
);
197 if (scheme_uuid
.empty())
200 JNIEnv
* env
= AttachCurrentThread();
201 ScopedJavaLocalRef
<jbyteArray
> j_scheme_uuid
=
202 base::android::ToJavaByteArray(env
, &scheme_uuid
[0], scheme_uuid
.size());
203 ScopedJavaLocalRef
<jstring
> j_container_mime_type
=
204 ConvertUTF8ToJavaString(env
, container_mime_type
);
205 return Java_MediaDrmBridge_isCryptoSchemeSupported(
206 env
, j_scheme_uuid
.obj(), j_container_mime_type
.obj());
210 bool MediaDrmBridge::IsAvailable() {
211 return base::android::BuildInfo::GetInstance()->sdk_int() >= 19;
215 bool MediaDrmBridge::IsSecureDecoderRequired(SecurityLevel security_level
) {
216 DCHECK(IsAvailable());
217 return SECURITY_LEVEL_1
== security_level
;
221 bool MediaDrmBridge::IsSecurityLevelSupported(const std::string
& key_system
,
222 SecurityLevel security_level
) {
226 // Pass 0 as |cdm_id| and NULL as |manager| as they are not used in
227 // creation time of MediaDrmBridge.
228 scoped_ptr
<MediaDrmBridge
> media_drm_bridge
=
229 MediaDrmBridge::Create(0, key_system
, GURL(), NULL
);
230 if (!media_drm_bridge
)
233 return media_drm_bridge
->SetSecurityLevel(security_level
);
237 bool MediaDrmBridge::IsKeySystemSupported(const std::string
& key_system
) {
238 DCHECK(!key_system
.empty());
239 return IsKeySystemSupportedWithTypeImpl(key_system
, "");
243 bool MediaDrmBridge::IsKeySystemSupportedWithType(
244 const std::string
& key_system
,
245 const std::string
& container_mime_type
) {
246 DCHECK(!key_system
.empty() && !container_mime_type
.empty());
247 return IsKeySystemSupportedWithTypeImpl(key_system
, container_mime_type
);
250 bool MediaDrmBridge::RegisterMediaDrmBridge(JNIEnv
* env
) {
251 return RegisterNativesImpl(env
);
254 MediaDrmBridge::MediaDrmBridge(int cdm_id
,
255 const std::vector
<uint8
>& scheme_uuid
,
256 const GURL
& security_origin
,
257 MediaPlayerManager
* manager
)
259 scheme_uuid_(scheme_uuid
),
260 security_origin_(security_origin
),
262 JNIEnv
* env
= AttachCurrentThread();
265 ScopedJavaLocalRef
<jbyteArray
> j_scheme_uuid
=
266 base::android::ToJavaByteArray(env
, &scheme_uuid
[0], scheme_uuid
.size());
267 j_media_drm_
.Reset(Java_MediaDrmBridge_create(
268 env
, j_scheme_uuid
.obj(), reinterpret_cast<intptr_t>(this)));
271 MediaDrmBridge::~MediaDrmBridge() {
272 JNIEnv
* env
= AttachCurrentThread();
273 if (!j_media_drm_
.is_null())
274 Java_MediaDrmBridge_release(env
, j_media_drm_
.obj());
278 scoped_ptr
<MediaDrmBridge
> MediaDrmBridge::Create(int cdm_id
,
279 const std::string
& key_system
,
280 const GURL
& security_origin
,
281 MediaPlayerManager
* manager
) {
282 scoped_ptr
<MediaDrmBridge
> media_drm_bridge
;
284 return media_drm_bridge
.Pass();
286 std::vector
<uint8
> scheme_uuid
= GetUUID(key_system
);
287 if (scheme_uuid
.empty())
288 return media_drm_bridge
.Pass();
290 media_drm_bridge
.reset(
291 new MediaDrmBridge(cdm_id
, scheme_uuid
, security_origin
, manager
));
292 if (media_drm_bridge
->j_media_drm_
.is_null())
293 media_drm_bridge
.reset();
295 return media_drm_bridge
.Pass();
298 bool MediaDrmBridge::SetSecurityLevel(SecurityLevel security_level
) {
299 JNIEnv
* env
= AttachCurrentThread();
301 std::string security_level_str
= GetSecurityLevelString(security_level
);
302 if (security_level_str
.empty())
305 ScopedJavaLocalRef
<jstring
> j_security_level
=
306 ConvertUTF8ToJavaString(env
, security_level_str
);
307 return Java_MediaDrmBridge_setSecurityLevel(
308 env
, j_media_drm_
.obj(), j_security_level
.obj());
311 bool MediaDrmBridge::CreateSession(uint32 session_id
,
312 const std::string
& content_type
,
313 const uint8
* init_data
,
314 int init_data_length
) {
315 JNIEnv
* env
= AttachCurrentThread();
316 ScopedJavaLocalRef
<jbyteArray
> j_init_data
;
317 // Caller should always use "video/*" content types.
318 DCHECK_EQ(0u, content_type
.find("video/"));
320 // Widevine MediaDrm plugin only accepts the "data" part of the PSSH box as
321 // the init data when using MP4 container.
322 if (std::equal(scheme_uuid_
.begin(), scheme_uuid_
.end(), kWidevineUuid
) &&
323 content_type
== "video/mp4") {
324 std::vector
<uint8
> pssh_data
;
325 if (!GetPsshData(init_data
, init_data_length
, scheme_uuid_
, &pssh_data
))
328 base::android::ToJavaByteArray(env
, &pssh_data
[0], pssh_data
.size());
331 base::android::ToJavaByteArray(env
, init_data
, init_data_length
);
334 ScopedJavaLocalRef
<jstring
> j_mime
=
335 ConvertUTF8ToJavaString(env
, content_type
);
336 Java_MediaDrmBridge_createSession(
337 env
, j_media_drm_
.obj(), session_id
, j_init_data
.obj(), j_mime
.obj());
341 void MediaDrmBridge::LoadSession(uint32 session_id
,
342 const std::string
& web_session_id
) {
343 // MediaDrmBridge doesn't support loading sessions.
347 void MediaDrmBridge::UpdateSession(uint32 session_id
,
348 const uint8
* response
,
349 int response_length
) {
350 DVLOG(1) << __FUNCTION__
;
351 JNIEnv
* env
= AttachCurrentThread();
352 ScopedJavaLocalRef
<jbyteArray
> j_response
=
353 base::android::ToJavaByteArray(env
, response
, response_length
);
354 Java_MediaDrmBridge_updateSession(
355 env
, j_media_drm_
.obj(), session_id
, j_response
.obj());
358 void MediaDrmBridge::ReleaseSession(uint32 session_id
) {
359 DVLOG(1) << __FUNCTION__
;
360 JNIEnv
* env
= AttachCurrentThread();
361 Java_MediaDrmBridge_releaseSession(env
, j_media_drm_
.obj(), session_id
);
364 void MediaDrmBridge::SetMediaCryptoReadyCB(const base::Closure
& closure
) {
365 if (closure
.is_null()) {
366 media_crypto_ready_cb_
.Reset();
370 DCHECK(media_crypto_ready_cb_
.is_null());
372 if (!GetMediaCrypto().is_null()) {
373 base::MessageLoopProxy::current()->PostTask(FROM_HERE
, closure
);
377 media_crypto_ready_cb_
= closure
;
380 void MediaDrmBridge::OnMediaCryptoReady(JNIEnv
* env
, jobject
) {
381 DCHECK(!GetMediaCrypto().is_null());
382 if (!media_crypto_ready_cb_
.is_null())
383 base::ResetAndReturn(&media_crypto_ready_cb_
).Run();
386 void MediaDrmBridge::OnSessionCreated(JNIEnv
* env
,
389 jstring j_web_session_id
) {
390 uint32 session_id
= j_session_id
;
391 std::string web_session_id
= ConvertJavaStringToUTF8(env
, j_web_session_id
);
392 manager_
->OnSessionCreated(cdm_id_
, session_id
, web_session_id
);
395 void MediaDrmBridge::OnSessionMessage(JNIEnv
* env
,
398 jbyteArray j_message
,
399 jstring j_destination_url
) {
400 uint32 session_id
= j_session_id
;
401 std::vector
<uint8
> message
;
402 JavaByteArrayToByteVector(env
, j_message
, &message
);
403 std::string destination_url
= ConvertJavaStringToUTF8(env
, j_destination_url
);
404 GURL
destination_gurl(destination_url
);
405 if (!destination_gurl
.is_valid() && !destination_gurl
.is_empty()) {
406 DLOG(WARNING
) << "SessionMessage destination_url is invalid : "
407 << destination_gurl
.possibly_invalid_spec();
408 destination_gurl
= GURL::EmptyGURL(); // Replace invalid destination_url.
411 manager_
->OnSessionMessage(cdm_id_
, session_id
, message
, destination_gurl
);
414 void MediaDrmBridge::OnSessionReady(JNIEnv
* env
,
417 uint32 session_id
= j_session_id
;
418 manager_
->OnSessionReady(cdm_id_
, session_id
);
421 void MediaDrmBridge::OnSessionClosed(JNIEnv
* env
,
424 uint32 session_id
= j_session_id
;
425 manager_
->OnSessionClosed(cdm_id_
, session_id
);
428 void MediaDrmBridge::OnSessionError(JNIEnv
* env
,
431 uint32 session_id
= j_session_id
;
432 manager_
->OnSessionError(cdm_id_
, session_id
, MediaKeys::kUnknownError
, 0);
435 ScopedJavaLocalRef
<jobject
> MediaDrmBridge::GetMediaCrypto() {
436 JNIEnv
* env
= AttachCurrentThread();
437 return Java_MediaDrmBridge_getMediaCrypto(env
, j_media_drm_
.obj());
440 MediaDrmBridge::SecurityLevel
MediaDrmBridge::GetSecurityLevel() {
441 JNIEnv
* env
= AttachCurrentThread();
442 ScopedJavaLocalRef
<jstring
> j_security_level
=
443 Java_MediaDrmBridge_getSecurityLevel(env
, j_media_drm_
.obj());
444 std::string security_level_str
=
445 ConvertJavaStringToUTF8(env
, j_security_level
.obj());
446 return GetSecurityLevelFromString(security_level_str
);
449 bool MediaDrmBridge::IsProtectedSurfaceRequired() {
450 return IsSecureDecoderRequired(GetSecurityLevel());
453 void MediaDrmBridge::ResetDeviceCredentials(
454 const ResetCredentialsCB
& callback
) {
455 DCHECK(reset_credentials_cb_
.is_null());
456 reset_credentials_cb_
= callback
;
457 JNIEnv
* env
= AttachCurrentThread();
458 Java_MediaDrmBridge_resetDeviceCredentials(env
, j_media_drm_
.obj());
461 void MediaDrmBridge::OnResetDeviceCredentialsCompleted(
462 JNIEnv
* env
, jobject
, bool success
) {
463 base::ResetAndReturn(&reset_credentials_cb_
).Run(success
);