media_drm_bridge.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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.
4
5#include "media/base/android/media_drm_bridge.h"
6
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"
17
18using base::android::AttachCurrentThread;
19using base::android::ConvertUTF8ToJavaString;
20using base::android::ConvertJavaStringToUTF8;
21using base::android::JavaByteArrayToByteVector;
22using base::android::ScopedJavaLocalRef;
23
24namespace media {
25
26static uint32 ReadUint32(const uint8_t* data) {
27  uint32 value = 0;
28  for (int i = 0; i < 4; ++i)
29    value = (value << 8) | data[i];
30  return value;
31}
32
33static uint64 ReadUint64(const uint8_t* data) {
34  uint64 value = 0;
35  for (int i = 0; i < 8; ++i)
36    value = (value << 8) | data[i];
37  return value;
38}
39
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.
43//
44// PSSH {
45//   uint32 Size
46//   uint32 Type
47//   uint64 LargeSize  # Field is only present if value(Size) == 1.
48//   uint32 VersionAndFlags
49//   uint8[16] SystemId
50//   uint32 DataSize
51//   uint8[DataSize] Data
52// }
53static const int kBoxHeaderSize = 8;  // Box's header contains Size and Type.
54static const int kBoxLargeSizeSize = 8;
55static const int kPsshVersionFlagSize = 4;
56static const int kPsshSystemIdSize = 16;
57static const int kPsshDataSizeSize = 4;
58static const uint32 kTencType = 0x74656e63;
59static const uint32 kPsshType = 0x70737368;
60
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.
64// Notes:
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.
68static 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;
74
75  while (bytes_left > 0) {
76    const uint8* box_head = cur;
77
78    if (bytes_left < kBoxHeaderSize)
79      return false;
80
81    uint64_t box_size = ReadUint32(cur);
82    uint32 type = ReadUint32(cur + 4);
83    cur += kBoxHeaderSize;
84    bytes_left -= kBoxHeaderSize;
85
86    if (box_size == 1) {  // LargeSize is present.
87      if (bytes_left < kBoxLargeSizeSize)
88        return false;
89
90      box_size = ReadUint64(cur);
91      cur += kBoxLargeSizeSize;
92      bytes_left -= kBoxLargeSizeSize;
93    } else if (box_size == 0) {
94      box_size = bytes_left + kBoxHeaderSize;
95    }
96
97    const uint8* box_end = box_head + box_size;
98    if (data_end < box_end)
99      return false;
100
101    if (type == kTencType) {
102      // Skip 'tenc' box.
103      cur = box_end;
104      bytes_left = data_end - cur;
105      continue;
106    } else if (type != kPsshType) {
107      return false;
108    }
109
110    const int kPsshBoxMinimumSize =
111        kPsshVersionFlagSize + kPsshSystemIdSize + kPsshDataSizeSize;
112    if (box_end < cur + kPsshBoxMinimumSize)
113      return false;
114
115    uint32 version_and_flags = ReadUint32(cur);
116    cur += kPsshVersionFlagSize;
117    bytes_left -= kPsshVersionFlagSize;
118    if (version_and_flags != 0)
119      return false;
120
121    DCHECK_GE(bytes_left, kPsshSystemIdSize);
122    if (!std::equal(uuid.begin(), uuid.end(), cur)) {
123      cur = box_end;
124      bytes_left = data_end - cur;
125      continue;
126    }
127
128    cur += kPsshSystemIdSize;
129    bytes_left -= kPsshSystemIdSize;
130
131    uint32 data_size = ReadUint32(cur);
132    cur += kPsshDataSizeSize;
133    bytes_left -= kPsshDataSizeSize;
134
135    if (box_end < cur + data_size)
136      return false;
137
138    pssh_data->assign(cur, cur + data_size);
139    return true;
140  }
141
142  return false;
143}
144
145static 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;
153}
154
155// static
156scoped_ptr<MediaDrmBridge> MediaDrmBridge::Create(
157    int media_keys_id,
158    const std::vector<uint8>& scheme_uuid,
159    const GURL& frame_url,
160    const std::string& security_level,
161    MediaPlayerManager* manager) {
162  scoped_ptr<MediaDrmBridge> media_drm_bridge;
163
164  if (IsAvailable() && !scheme_uuid.empty()) {
165    // TODO(qinmin): check whether the uuid is valid.
166    media_drm_bridge.reset(new MediaDrmBridge(
167        media_keys_id, scheme_uuid, frame_url, security_level, manager));
168    if (media_drm_bridge->j_media_drm_.is_null())
169      media_drm_bridge.reset();
170  }
171
172  return media_drm_bridge.Pass();
173}
174
175// static
176bool MediaDrmBridge::IsAvailable() {
177  return base::android::BuildInfo::GetInstance()->sdk_int() >= 19;
178}
179
180// static
181bool MediaDrmBridge::IsSecureDecoderRequired(
182    const std::string& security_level_str) {
183  return IsSecureDecoderRequired(
184      GetSecurityLevelFromString(security_level_str));
185}
186
187bool MediaDrmBridge::IsSecurityLevelSupported(
188    const std::vector<uint8>& scheme_uuid,
189    const std::string& security_level) {
190  // Pass 0 as |media_keys_id| and NULL as |manager| as they are not used in
191  // creation time of MediaDrmBridge.
192  return MediaDrmBridge::Create(0, scheme_uuid, GURL(), security_level, NULL) !=
193      NULL;
194}
195
196bool MediaDrmBridge::IsCryptoSchemeSupported(
197    const std::vector<uint8>& scheme_uuid,
198    const std::string& container_mime_type) {
199  JNIEnv* env = AttachCurrentThread();
200  ScopedJavaLocalRef<jbyteArray> j_scheme_uuid =
201      base::android::ToJavaByteArray(env, &scheme_uuid[0], scheme_uuid.size());
202  ScopedJavaLocalRef<jstring> j_container_mime_type =
203      ConvertUTF8ToJavaString(env, container_mime_type);
204  return Java_MediaDrmBridge_isCryptoSchemeSupported(
205      env, j_scheme_uuid.obj(), j_container_mime_type.obj());
206}
207
208bool MediaDrmBridge::RegisterMediaDrmBridge(JNIEnv* env) {
209  return RegisterNativesImpl(env);
210}
211
212MediaDrmBridge::MediaDrmBridge(int media_keys_id,
213                               const std::vector<uint8>& scheme_uuid,
214                               const GURL& frame_url,
215                               const std::string& security_level,
216                               MediaPlayerManager* manager)
217    : media_keys_id_(media_keys_id),
218      scheme_uuid_(scheme_uuid),
219      frame_url_(frame_url),
220      manager_(manager) {
221  JNIEnv* env = AttachCurrentThread();
222  CHECK(env);
223
224  ScopedJavaLocalRef<jbyteArray> j_scheme_uuid =
225      base::android::ToJavaByteArray(env, &scheme_uuid[0], scheme_uuid.size());
226  ScopedJavaLocalRef<jstring> j_security_level =
227      ConvertUTF8ToJavaString(env, security_level);
228  j_media_drm_.Reset(Java_MediaDrmBridge_create(
229      env, j_scheme_uuid.obj(), j_security_level.obj(),
230      reinterpret_cast<intptr_t>(this)));
231}
232
233MediaDrmBridge::~MediaDrmBridge() {
234  JNIEnv* env = AttachCurrentThread();
235  if (!j_media_drm_.is_null())
236    Java_MediaDrmBridge_release(env, j_media_drm_.obj());
237}
238
239bool MediaDrmBridge::CreateSession(uint32 session_id,
240                                   const std::string& type,
241                                   const uint8* init_data,
242                                   int init_data_length) {
243  std::vector<uint8> pssh_data;
244  if (!GetPsshData(init_data, init_data_length, scheme_uuid_, &pssh_data))
245    return false;
246
247  JNIEnv* env = AttachCurrentThread();
248  ScopedJavaLocalRef<jbyteArray> j_pssh_data =
249      base::android::ToJavaByteArray(env, &pssh_data[0], pssh_data.size());
250  ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, type);
251  Java_MediaDrmBridge_createSession(
252      env, j_media_drm_.obj(), session_id, j_pssh_data.obj(), j_mime.obj());
253  return true;
254}
255
256void MediaDrmBridge::UpdateSession(uint32 session_id,
257                                   const uint8* response,
258                                   int response_length) {
259  DVLOG(1) << __FUNCTION__;
260  JNIEnv* env = AttachCurrentThread();
261  ScopedJavaLocalRef<jbyteArray> j_response =
262      base::android::ToJavaByteArray(env, response, response_length);
263  Java_MediaDrmBridge_updateSession(
264      env, j_media_drm_.obj(), session_id, j_response.obj());
265}
266
267void MediaDrmBridge::ReleaseSession(uint32 session_id) {
268  DVLOG(1) << __FUNCTION__;
269  JNIEnv* env = AttachCurrentThread();
270  Java_MediaDrmBridge_releaseSession(env, j_media_drm_.obj(), session_id);
271}
272
273void MediaDrmBridge::SetMediaCryptoReadyCB(const base::Closure& closure) {
274  if (closure.is_null()) {
275    media_crypto_ready_cb_.Reset();
276    return;
277  }
278
279  DCHECK(media_crypto_ready_cb_.is_null());
280
281  if (!GetMediaCrypto().is_null()) {
282    base::MessageLoopProxy::current()->PostTask(FROM_HERE, closure);
283    return;
284  }
285
286  media_crypto_ready_cb_ = closure;
287}
288
289void MediaDrmBridge::OnMediaCryptoReady(JNIEnv* env, jobject) {
290  DCHECK(!GetMediaCrypto().is_null());
291  if (!media_crypto_ready_cb_.is_null())
292    base::ResetAndReturn(&media_crypto_ready_cb_).Run();
293}
294
295void MediaDrmBridge::OnSessionCreated(JNIEnv* env,
296                                      jobject j_media_drm,
297                                      jint j_session_id,
298                                      jstring j_web_session_id) {
299  uint32 session_id = j_session_id;
300  std::string web_session_id = ConvertJavaStringToUTF8(env, j_web_session_id);
301  manager_->OnSessionCreated(media_keys_id_, session_id, web_session_id);
302}
303
304void MediaDrmBridge::OnSessionMessage(JNIEnv* env,
305                                      jobject j_media_drm,
306                                      jint j_session_id,
307                                      jbyteArray j_message,
308                                      jstring j_destination_url) {
309  uint32 session_id = j_session_id;
310  std::vector<uint8> message;
311  JavaByteArrayToByteVector(env, j_message, &message);
312  std::string destination_url = ConvertJavaStringToUTF8(env, j_destination_url);
313  manager_->OnSessionMessage(
314      media_keys_id_, session_id, message, destination_url);
315}
316
317void MediaDrmBridge::OnSessionReady(JNIEnv* env,
318                                    jobject j_media_drm,
319                                    jint j_session_id) {
320  uint32 session_id = j_session_id;
321  manager_->OnSessionReady(media_keys_id_, session_id);
322}
323
324void MediaDrmBridge::OnSessionClosed(JNIEnv* env,
325                                     jobject j_media_drm,
326                                     jint j_session_id) {
327  uint32 session_id = j_session_id;
328  manager_->OnSessionClosed(media_keys_id_, session_id);
329}
330
331void MediaDrmBridge::OnSessionError(JNIEnv* env,
332                                    jobject j_media_drm,
333                                    jint j_session_id) {
334  uint32 session_id = j_session_id;
335  manager_->OnSessionError(
336      media_keys_id_, session_id, MediaKeys::kUnknownError, 0);
337}
338
339ScopedJavaLocalRef<jobject> MediaDrmBridge::GetMediaCrypto() {
340  JNIEnv* env = AttachCurrentThread();
341  return Java_MediaDrmBridge_getMediaCrypto(env, j_media_drm_.obj());
342}
343
344// static
345bool MediaDrmBridge::IsSecureDecoderRequired(SecurityLevel security_level) {
346  return MediaDrmBridge::SECURITY_LEVEL_1 == security_level;
347}
348
349MediaDrmBridge::SecurityLevel MediaDrmBridge::GetSecurityLevel() {
350  JNIEnv* env = AttachCurrentThread();
351  ScopedJavaLocalRef<jstring> j_security_level =
352      Java_MediaDrmBridge_getSecurityLevel(env, j_media_drm_.obj());
353  std::string security_level_str =
354      ConvertJavaStringToUTF8(env, j_security_level.obj());
355  return GetSecurityLevelFromString(security_level_str);
356}
357
358bool MediaDrmBridge::IsProtectedSurfaceRequired() {
359  return IsSecureDecoderRequired(GetSecurityLevel());
360}
361
362void MediaDrmBridge::ResetDeviceCredentials(
363    const ResetCredentialsCB& callback) {
364  DCHECK(reset_credentials_cb_.is_null());
365  reset_credentials_cb_ = callback;
366  JNIEnv* env = AttachCurrentThread();
367  Java_MediaDrmBridge_resetDeviceCredentials(env, j_media_drm_.obj());
368}
369
370void MediaDrmBridge::OnResetDeviceCredentialsCompleted(
371    JNIEnv* env, jobject, bool success) {
372  base::ResetAndReturn(&reset_credentials_cb_).Run(success);
373}
374
375}  // namespace media
376