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 "base/android/build_info.h"
8 #include "base/android/jni_array.h"
9 #include "base/android/jni_string.h"
10 #include "base/callback_helpers.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/message_loop/message_loop_proxy.h"
14 #include "base/strings/string_util.h"
15 #include "jni/MediaDrmBridge_jni.h"
16 #include "media/base/android/media_player_manager.h"
18 using base::android::AttachCurrentThread;
19 using base::android::ConvertUTF8ToJavaString;
20 using base::android::ConvertJavaStringToUTF8;
21 using base::android::JavaByteArrayToByteVector;
22 using base::android::ScopedJavaLocalRef;
26 static uint32 ReadUint32(const uint8_t* data) {
28 for (int i = 0; i < 4; ++i)
29 value = (value << 8) | data[i];
33 static uint64 ReadUint64(const uint8_t* data) {
35 for (int i = 0; i < 8; ++i)
36 value = (value << 8) | data[i];
40 // The structure of an ISO CENC Protection System Specific Header (PSSH) box is
41 // as follows. (See ISO/IEC FDIS 23001-7:2011(E).)
42 // Note: ISO boxes use big-endian values.
47 // uint64 LargeSize # Field is only present if value(Size) == 1.
48 // uint32 VersionAndFlags
51 // uint8[DataSize] Data
53 static const int kBoxHeaderSize = 8; // Box's header contains Size and Type.
54 static const int kBoxLargeSizeSize = 8;
55 static const int kPsshVersionFlagSize = 4;
56 static const int kPsshSystemIdSize = 16;
57 static const int kPsshDataSizeSize = 4;
58 static const uint32 kTencType = 0x74656e63;
59 static const uint32 kPsshType = 0x70737368;
61 // Tries to find a PSSH box whose "SystemId" is |uuid| in |data|, parses the
62 // "Data" of the box and put it in |pssh_data|. Returns true if such a box is
63 // found and successfully parsed. Returns false otherwise.
65 // 1, If multiple PSSH boxes are found,the "Data" of the first matching PSSH box
66 // will be set in |pssh_data|.
67 // 2, Only PSSH and TENC boxes are allowed in |data|. TENC boxes are skipped.
68 static bool GetPsshData(const uint8* data, int data_size,
69 const std::vector<uint8>& uuid,
70 std::vector<uint8>* pssh_data) {
71 const uint8* cur = data;
72 const uint8* data_end = data + data_size;
73 int bytes_left = data_size;
75 while (bytes_left > 0) {
76 const uint8* box_head = cur;
78 if (bytes_left < kBoxHeaderSize)
81 uint64_t box_size = ReadUint32(cur);
82 uint32 type = ReadUint32(cur + 4);
83 cur += kBoxHeaderSize;
84 bytes_left -= kBoxHeaderSize;
86 if (box_size == 1) { // LargeSize is present.
87 if (bytes_left < kBoxLargeSizeSize)
90 box_size = ReadUint64(cur);
91 cur += kBoxLargeSizeSize;
92 bytes_left -= kBoxLargeSizeSize;
93 } else if (box_size == 0) {
94 box_size = bytes_left + kBoxHeaderSize;
97 const uint8* box_end = box_head + box_size;
98 if (data_end < box_end)
101 if (type == kTencType) {
104 bytes_left = data_end - cur;
106 } else if (type != kPsshType) {
110 const int kPsshBoxMinimumSize =
111 kPsshVersionFlagSize + kPsshSystemIdSize + kPsshDataSizeSize;
112 if (box_end < cur + kPsshBoxMinimumSize)
115 uint32 version_and_flags = ReadUint32(cur);
116 cur += kPsshVersionFlagSize;
117 bytes_left -= kPsshVersionFlagSize;
118 if (version_and_flags != 0)
121 DCHECK_GE(bytes_left, kPsshSystemIdSize);
122 if (!std::equal(uuid.begin(), uuid.end(), cur)) {
124 bytes_left = data_end - cur;
128 cur += kPsshSystemIdSize;
129 bytes_left -= kPsshSystemIdSize;
131 uint32 data_size = ReadUint32(cur);
132 cur += kPsshDataSizeSize;
133 bytes_left -= kPsshDataSizeSize;
135 if (box_end < cur + data_size)
138 pssh_data->assign(cur, cur + data_size);
145 static MediaDrmBridge::SecurityLevel GetSecurityLevelFromString(
146 const std::string& security_level_str) {
147 if (0 == security_level_str.compare("L1"))
148 return MediaDrmBridge::SECURITY_LEVEL_1;
149 if (0 == security_level_str.compare("L3"))
150 return MediaDrmBridge::SECURITY_LEVEL_3;
151 DCHECK(security_level_str.empty());
152 return MediaDrmBridge::SECURITY_LEVEL_NONE;
155 static std::string GetSecurityLevelString(
156 MediaDrmBridge::SecurityLevel security_level) {
157 switch (security_level) {
158 case MediaDrmBridge::SECURITY_LEVEL_NONE:
160 case MediaDrmBridge::SECURITY_LEVEL_1:
162 case MediaDrmBridge::SECURITY_LEVEL_3:
169 bool MediaDrmBridge::IsAvailable() {
170 return base::android::BuildInfo::GetInstance()->sdk_int() >= 19;
174 bool MediaDrmBridge::IsSecureDecoderRequired(SecurityLevel security_level) {
175 return SECURITY_LEVEL_1 == security_level;
178 bool MediaDrmBridge::IsSecurityLevelSupported(
179 const std::vector<uint8>& scheme_uuid,
180 SecurityLevel security_level) {
181 // Pass 0 as |media_keys_id| and NULL as |manager| as they are not used in
182 // creation time of MediaDrmBridge.
183 scoped_ptr<MediaDrmBridge> media_drm_bridge =
184 MediaDrmBridge::Create(0, scheme_uuid, GURL(), NULL);
185 if (!media_drm_bridge)
188 return media_drm_bridge->SetSecurityLevel(security_level);
191 bool MediaDrmBridge::IsCryptoSchemeSupported(
192 const std::vector<uint8>& scheme_uuid,
193 const std::string& container_mime_type) {
194 JNIEnv* env = AttachCurrentThread();
195 ScopedJavaLocalRef<jbyteArray> j_scheme_uuid =
196 base::android::ToJavaByteArray(env, &scheme_uuid[0], scheme_uuid.size());
197 ScopedJavaLocalRef<jstring> j_container_mime_type =
198 ConvertUTF8ToJavaString(env, container_mime_type);
199 return Java_MediaDrmBridge_isCryptoSchemeSupported(
200 env, j_scheme_uuid.obj(), j_container_mime_type.obj());
203 bool MediaDrmBridge::RegisterMediaDrmBridge(JNIEnv* env) {
204 return RegisterNativesImpl(env);
207 MediaDrmBridge::MediaDrmBridge(int media_keys_id,
208 const std::vector<uint8>& scheme_uuid,
209 const GURL& frame_url,
210 MediaPlayerManager* manager)
211 : media_keys_id_(media_keys_id),
212 scheme_uuid_(scheme_uuid),
213 frame_url_(frame_url),
215 JNIEnv* env = AttachCurrentThread();
218 ScopedJavaLocalRef<jbyteArray> j_scheme_uuid =
219 base::android::ToJavaByteArray(env, &scheme_uuid[0], scheme_uuid.size());
220 j_media_drm_.Reset(Java_MediaDrmBridge_create(
221 env, j_scheme_uuid.obj(), reinterpret_cast<intptr_t>(this)));
224 MediaDrmBridge::~MediaDrmBridge() {
225 JNIEnv* env = AttachCurrentThread();
226 if (!j_media_drm_.is_null())
227 Java_MediaDrmBridge_release(env, j_media_drm_.obj());
231 scoped_ptr<MediaDrmBridge> MediaDrmBridge::Create(
233 const std::vector<uint8>& scheme_uuid,
234 const GURL& frame_url,
235 MediaPlayerManager* manager) {
236 scoped_ptr<MediaDrmBridge> media_drm_bridge;
238 if (IsAvailable() && !scheme_uuid.empty()) {
239 // TODO(qinmin): check whether the uuid is valid.
240 media_drm_bridge.reset(new MediaDrmBridge(
241 media_keys_id, scheme_uuid, frame_url, manager));
242 if (media_drm_bridge->j_media_drm_.is_null())
243 media_drm_bridge.reset();
246 return media_drm_bridge.Pass();
249 bool MediaDrmBridge::SetSecurityLevel(SecurityLevel security_level) {
250 JNIEnv* env = AttachCurrentThread();
252 std::string security_level_str = GetSecurityLevelString(security_level);
253 if (security_level_str.empty())
256 ScopedJavaLocalRef<jstring> j_security_level =
257 ConvertUTF8ToJavaString(env, security_level_str);
258 return Java_MediaDrmBridge_setSecurityLevel(
259 env, j_media_drm_.obj(), j_security_level.obj());
262 bool MediaDrmBridge::CreateSession(uint32 session_id,
263 const std::string& content_type,
264 const uint8* init_data,
265 int init_data_length) {
266 std::vector<uint8> pssh_data;
267 if (!GetPsshData(init_data, init_data_length, scheme_uuid_, &pssh_data))
270 JNIEnv* env = AttachCurrentThread();
271 ScopedJavaLocalRef<jbyteArray> j_pssh_data =
272 base::android::ToJavaByteArray(env, &pssh_data[0], pssh_data.size());
273 ScopedJavaLocalRef<jstring> j_mime =
274 ConvertUTF8ToJavaString(env, content_type);
275 Java_MediaDrmBridge_createSession(
276 env, j_media_drm_.obj(), session_id, j_pssh_data.obj(), j_mime.obj());
280 void MediaDrmBridge::LoadSession(uint32 session_id,
281 const std::string& web_session_id) {
282 // MediaDrmBridge doesn't support loading sessions.
286 void MediaDrmBridge::UpdateSession(uint32 session_id,
287 const uint8* response,
288 int response_length) {
289 DVLOG(1) << __FUNCTION__;
290 JNIEnv* env = AttachCurrentThread();
291 ScopedJavaLocalRef<jbyteArray> j_response =
292 base::android::ToJavaByteArray(env, response, response_length);
293 Java_MediaDrmBridge_updateSession(
294 env, j_media_drm_.obj(), session_id, j_response.obj());
297 void MediaDrmBridge::ReleaseSession(uint32 session_id) {
298 DVLOG(1) << __FUNCTION__;
299 JNIEnv* env = AttachCurrentThread();
300 Java_MediaDrmBridge_releaseSession(env, j_media_drm_.obj(), session_id);
303 void MediaDrmBridge::SetMediaCryptoReadyCB(const base::Closure& closure) {
304 if (closure.is_null()) {
305 media_crypto_ready_cb_.Reset();
309 DCHECK(media_crypto_ready_cb_.is_null());
311 if (!GetMediaCrypto().is_null()) {
312 base::MessageLoopProxy::current()->PostTask(FROM_HERE, closure);
316 media_crypto_ready_cb_ = closure;
319 void MediaDrmBridge::OnMediaCryptoReady(JNIEnv* env, jobject) {
320 DCHECK(!GetMediaCrypto().is_null());
321 if (!media_crypto_ready_cb_.is_null())
322 base::ResetAndReturn(&media_crypto_ready_cb_).Run();
325 void MediaDrmBridge::OnSessionCreated(JNIEnv* env,
328 jstring j_web_session_id) {
329 uint32 session_id = j_session_id;
330 std::string web_session_id = ConvertJavaStringToUTF8(env, j_web_session_id);
331 manager_->OnSessionCreated(media_keys_id_, session_id, web_session_id);
334 void MediaDrmBridge::OnSessionMessage(JNIEnv* env,
337 jbyteArray j_message,
338 jstring j_destination_url) {
339 uint32 session_id = j_session_id;
340 std::vector<uint8> message;
341 JavaByteArrayToByteVector(env, j_message, &message);
342 std::string destination_url = ConvertJavaStringToUTF8(env, j_destination_url);
343 GURL destination_gurl(destination_url);
344 if (!destination_gurl.is_valid() && !destination_gurl.is_empty()) {
345 DLOG(WARNING) << "SessionMessage destination_url is invalid : "
346 << destination_gurl.possibly_invalid_spec();
347 destination_gurl = GURL::EmptyGURL(); // Replace invalid destination_url.
350 manager_->OnSessionMessage(
351 media_keys_id_, session_id, message, destination_gurl);
354 void MediaDrmBridge::OnSessionReady(JNIEnv* env,
357 uint32 session_id = j_session_id;
358 manager_->OnSessionReady(media_keys_id_, session_id);
361 void MediaDrmBridge::OnSessionClosed(JNIEnv* env,
364 uint32 session_id = j_session_id;
365 manager_->OnSessionClosed(media_keys_id_, session_id);
368 void MediaDrmBridge::OnSessionError(JNIEnv* env,
371 uint32 session_id = j_session_id;
372 manager_->OnSessionError(
373 media_keys_id_, session_id, MediaKeys::kUnknownError, 0);
376 ScopedJavaLocalRef<jobject> MediaDrmBridge::GetMediaCrypto() {
377 JNIEnv* env = AttachCurrentThread();
378 return Java_MediaDrmBridge_getMediaCrypto(env, j_media_drm_.obj());
381 MediaDrmBridge::SecurityLevel MediaDrmBridge::GetSecurityLevel() {
382 JNIEnv* env = AttachCurrentThread();
383 ScopedJavaLocalRef<jstring> j_security_level =
384 Java_MediaDrmBridge_getSecurityLevel(env, j_media_drm_.obj());
385 std::string security_level_str =
386 ConvertJavaStringToUTF8(env, j_security_level.obj());
387 return GetSecurityLevelFromString(security_level_str);
390 bool MediaDrmBridge::IsProtectedSurfaceRequired() {
391 return IsSecureDecoderRequired(GetSecurityLevel());
394 void MediaDrmBridge::ResetDeviceCredentials(
395 const ResetCredentialsCB& callback) {
396 DCHECK(reset_credentials_cb_.is_null());
397 reset_credentials_cb_ = callback;
398 JNIEnv* env = AttachCurrentThread();
399 Java_MediaDrmBridge_resetDeviceCredentials(env, j_media_drm_.obj());
402 void MediaDrmBridge::OnResetDeviceCredentialsCompleted(
403 JNIEnv* env, jobject, bool success) {
404 base::ResetAndReturn(&reset_credentials_cb_).Run(success);