media_drm_bridge.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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 "jni/MediaDrmBridge_jni.h"
15#include "media/base/android/media_player_manager.h"
16
17using base::android::AttachCurrentThread;
18using base::android::ConvertUTF8ToJavaString;
19using base::android::ConvertJavaStringToUTF8;
20using base::android::JavaByteArrayToByteVector;
21using base::android::ScopedJavaLocalRef;
22
23namespace media {
24
25static uint32 ReadUint32(const uint8_t* data) {
26  uint32 value = 0;
27  for (int i = 0; i < 4; ++i)
28    value = (value << 8) | data[i];
29  return value;
30}
31
32static uint64 ReadUint64(const uint8_t* data) {
33  uint64 value = 0;
34  for (int i = 0; i < 8; ++i)
35    value = (value << 8) | data[i];
36  return value;
37}
38
39// The structure of an ISO CENC Protection System Specific Header (PSSH) box is
40// as follows. (See ISO/IEC FDIS 23001-7:2011(E).)
41// Note: ISO boxes use big-endian values.
42//
43// PSSH {
44//   uint32 Size
45//   uint32 Type
46//   uint64 LargeSize  # Field is only present if value(Size) == 1.
47//   uint32 VersionAndFlags
48//   uint8[16] SystemId
49//   uint32 DataSize
50//   uint8[DataSize] Data
51// }
52static const int kBoxHeaderSize = 8;  // Box's header contains Size and Type.
53static const int kBoxLargeSizeSize = 8;
54static const int kPsshVersionFlagSize = 4;
55static const int kPsshSystemIdSize = 16;
56static const int kPsshDataSizeSize = 4;
57static const uint32 kTencType = 0x74656e63;
58static const uint32 kPsshType = 0x70737368;
59
60// Tries to find a PSSH box whose "SystemId" is |uuid| in |data|, parses the
61// "Data" of the box and put it in |pssh_data|. Returns true if such a box is
62// found and successfully parsed. Returns false otherwise.
63// Notes:
64// 1, If multiple PSSH boxes are found,the "Data" of the first matching PSSH box
65// will be set in |pssh_data|.
66// 2, Only PSSH and TENC boxes are allowed in |data|. TENC boxes are skipped.
67static bool GetPsshData(const uint8* data, int data_size,
68                        const std::vector<uint8>& uuid,
69                        std::vector<uint8>* pssh_data) {
70  const uint8* cur = data;
71  const uint8* data_end = data + data_size;
72  int bytes_left = data_size;
73
74  while (bytes_left > 0) {
75    const uint8* box_head = cur;
76
77    if (bytes_left < kBoxHeaderSize)
78      return false;
79
80    uint64_t box_size = ReadUint32(cur);
81    uint32 type = ReadUint32(cur + 4);
82    cur += kBoxHeaderSize;
83    bytes_left -= kBoxHeaderSize;
84
85    if (box_size == 1) {  // LargeSize is present.
86      if (bytes_left < kBoxLargeSizeSize)
87        return false;
88
89      box_size = ReadUint64(cur);
90      cur += kBoxLargeSizeSize;
91      bytes_left -= kBoxLargeSizeSize;
92    } else if (box_size == 0) {
93      box_size = bytes_left + kBoxHeaderSize;
94    }
95
96    const uint8* box_end = box_head + box_size;
97    if (data_end < box_end)
98      return false;
99
100    if (type == kTencType) {
101      // Skip 'tenc' box.
102      cur = box_end;
103      bytes_left = data_end - cur;
104      continue;
105    } else if (type != kPsshType) {
106      return false;
107    }
108
109    const int kPsshBoxMinimumSize =
110        kPsshVersionFlagSize + kPsshSystemIdSize + kPsshDataSizeSize;
111    if (box_end < cur + kPsshBoxMinimumSize)
112      return false;
113
114    uint32 version_and_flags = ReadUint32(cur);
115    cur += kPsshVersionFlagSize;
116    bytes_left -= kPsshVersionFlagSize;
117    if (version_and_flags != 0)
118      return false;
119
120    DCHECK_GE(bytes_left, kPsshSystemIdSize);
121    if (!std::equal(uuid.begin(), uuid.end(), cur)) {
122      cur = box_end;
123      bytes_left = data_end - cur;
124      continue;
125    }
126
127    cur += kPsshSystemIdSize;
128    bytes_left -= kPsshSystemIdSize;
129
130    uint32 data_size = ReadUint32(cur);
131    cur += kPsshDataSizeSize;
132    bytes_left -= kPsshDataSizeSize;
133
134    if (box_end < cur + data_size)
135      return false;
136
137    pssh_data->assign(cur, cur + data_size);
138    return true;
139  }
140
141  return false;
142}
143
144static MediaDrmBridge::SecurityLevel GetSecurityLevelFromString(
145    const std::string& security_level_str) {
146  if (0 == security_level_str.compare("L1"))
147    return MediaDrmBridge::SECURITY_LEVEL_1;
148  if (0 == security_level_str.compare("L3"))
149    return MediaDrmBridge::SECURITY_LEVEL_3;
150  DCHECK(security_level_str.empty());
151  return MediaDrmBridge::SECURITY_LEVEL_NONE;
152}
153
154// static
155scoped_ptr<MediaDrmBridge> MediaDrmBridge::Create(
156    int media_keys_id,
157    const std::vector<uint8>& scheme_uuid,
158    const std::string& security_level,
159    MediaPlayerManager* manager) {
160  scoped_ptr<MediaDrmBridge> media_drm_bridge;
161
162  if (IsAvailable() && !scheme_uuid.empty()) {
163    // TODO(qinmin): check whether the uuid is valid.
164    media_drm_bridge.reset(
165      new MediaDrmBridge(media_keys_id, scheme_uuid, security_level, manager));
166    if (media_drm_bridge->j_media_drm_.is_null())
167      media_drm_bridge.reset();
168  }
169
170  return media_drm_bridge.Pass();
171}
172
173// static
174bool MediaDrmBridge::IsAvailable() {
175  return base::android::BuildInfo::GetInstance()->sdk_int() >= 18;
176}
177
178// static
179bool MediaDrmBridge::IsSecureDecoderRequired(
180    const std::string& security_level_str) {
181  return IsSecureDecoderRequired(
182      GetSecurityLevelFromString(security_level_str));
183}
184
185bool MediaDrmBridge::IsSecurityLevelSupported(
186    const std::vector<uint8>& scheme_uuid,
187    const std::string& security_level) {
188  // Pass 0 as |media_keys_id| and NULL as |manager| as they are not used in
189  // creation time of MediaDrmBridge.
190  return MediaDrmBridge::Create(0, scheme_uuid, security_level, NULL) != NULL;
191}
192
193bool MediaDrmBridge::IsCryptoSchemeSupported(
194    const std::vector<uint8>& scheme_uuid,
195    const std::string& container_mime_type) {
196  JNIEnv* env = AttachCurrentThread();
197  ScopedJavaLocalRef<jbyteArray> j_scheme_uuid =
198      base::android::ToJavaByteArray(env, &scheme_uuid[0], scheme_uuid.size());
199  ScopedJavaLocalRef<jstring> j_container_mime_type =
200      ConvertUTF8ToJavaString(env, container_mime_type);
201  return Java_MediaDrmBridge_isCryptoSchemeSupported(
202      env, j_scheme_uuid.obj(), j_container_mime_type.obj());
203}
204
205bool MediaDrmBridge::RegisterMediaDrmBridge(JNIEnv* env) {
206  return RegisterNativesImpl(env);
207}
208
209MediaDrmBridge::MediaDrmBridge(int media_keys_id,
210                               const std::vector<uint8>& scheme_uuid,
211                               const std::string& security_level,
212                               MediaPlayerManager* manager)
213    : media_keys_id_(media_keys_id),
214      scheme_uuid_(scheme_uuid),
215      manager_(manager) {
216  JNIEnv* env = AttachCurrentThread();
217  CHECK(env);
218
219  ScopedJavaLocalRef<jbyteArray> j_scheme_uuid =
220      base::android::ToJavaByteArray(env, &scheme_uuid[0], scheme_uuid.size());
221  ScopedJavaLocalRef<jstring> j_security_level =
222      ConvertUTF8ToJavaString(env, security_level);
223  j_media_drm_.Reset(Java_MediaDrmBridge_create(
224      env, j_scheme_uuid.obj(), j_security_level.obj(),
225      reinterpret_cast<intptr_t>(this)));
226}
227
228MediaDrmBridge::~MediaDrmBridge() {
229  JNIEnv* env = AttachCurrentThread();
230  if (!j_media_drm_.is_null())
231    Java_MediaDrmBridge_release(env, j_media_drm_.obj());
232}
233
234bool MediaDrmBridge::GenerateKeyRequest(const std::string& type,
235                                        const uint8* init_data,
236                                        int init_data_length) {
237  std::vector<uint8> pssh_data;
238  if (!GetPsshData(init_data, init_data_length, scheme_uuid_, &pssh_data))
239    return false;
240
241  JNIEnv* env = AttachCurrentThread();
242  ScopedJavaLocalRef<jbyteArray> j_pssh_data =
243      base::android::ToJavaByteArray(env, &pssh_data[0], pssh_data.size());
244  ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, type);
245  Java_MediaDrmBridge_generateKeyRequest(
246      env, j_media_drm_.obj(), j_pssh_data.obj(), j_mime.obj());
247  return true;
248}
249
250void MediaDrmBridge::AddKey(const uint8* key, int key_length,
251                            const uint8* init_data, int init_data_length,
252                            const std::string& session_id) {
253  DVLOG(1) << __FUNCTION__;
254  JNIEnv* env = AttachCurrentThread();
255  ScopedJavaLocalRef<jbyteArray> j_key_data =
256      base::android::ToJavaByteArray(env, key, key_length);
257  ScopedJavaLocalRef<jstring> j_session_id =
258      ConvertUTF8ToJavaString(env, session_id);
259  Java_MediaDrmBridge_addKey(
260      env, j_media_drm_.obj(), j_session_id.obj(), j_key_data.obj());
261}
262
263void MediaDrmBridge::CancelKeyRequest(const std::string& session_id) {
264  JNIEnv* env = AttachCurrentThread();
265  ScopedJavaLocalRef<jstring> j_session_id =
266      ConvertUTF8ToJavaString(env, session_id);
267  Java_MediaDrmBridge_cancelKeyRequest(
268      env, j_media_drm_.obj(), j_session_id.obj());
269}
270
271void MediaDrmBridge::SetMediaCryptoReadyCB(const base::Closure& closure) {
272  if (closure.is_null()) {
273    media_crypto_ready_cb_.Reset();
274    return;
275  }
276
277  DCHECK(media_crypto_ready_cb_.is_null());
278
279  if (!GetMediaCrypto().is_null()) {
280    base::MessageLoopProxy::current()->PostTask(FROM_HERE, closure);
281    return;
282  }
283
284  media_crypto_ready_cb_ = closure;
285}
286
287void MediaDrmBridge::OnMediaCryptoReady(JNIEnv* env, jobject) {
288  DCHECK(!GetMediaCrypto().is_null());
289  if (!media_crypto_ready_cb_.is_null())
290    base::ResetAndReturn(&media_crypto_ready_cb_).Run();
291}
292
293void MediaDrmBridge::OnKeyMessage(JNIEnv* env,
294                                  jobject j_media_drm,
295                                  jstring j_session_id,
296                                  jbyteArray j_message,
297                                  jstring j_destination_url) {
298  std::string session_id = ConvertJavaStringToUTF8(env, j_session_id);
299  std::vector<uint8> message;
300  JavaByteArrayToByteVector(env, j_message, &message);
301  std::string destination_url = ConvertJavaStringToUTF8(env, j_destination_url);
302
303  manager_->OnKeyMessage(media_keys_id_, session_id, message, destination_url);
304}
305
306void MediaDrmBridge::OnKeyAdded(JNIEnv* env, jobject, jstring j_session_id) {
307  std::string session_id = ConvertJavaStringToUTF8(env, j_session_id);
308  manager_->OnKeyAdded(media_keys_id_, session_id);
309}
310
311void MediaDrmBridge::OnKeyError(JNIEnv* env, jobject, jstring j_session_id) {
312  // |j_session_id| can be NULL, in which case we'll return an empty string.
313  std::string session_id = ConvertJavaStringToUTF8(env, j_session_id);
314  manager_->OnKeyError(media_keys_id_, session_id, MediaKeys::kUnknownError, 0);
315}
316
317ScopedJavaLocalRef<jobject> MediaDrmBridge::GetMediaCrypto() {
318  JNIEnv* env = AttachCurrentThread();
319  return Java_MediaDrmBridge_getMediaCrypto(env, j_media_drm_.obj());
320}
321
322// static
323bool MediaDrmBridge::IsSecureDecoderRequired(SecurityLevel security_level) {
324  return MediaDrmBridge::SECURITY_LEVEL_1 == security_level;
325}
326
327MediaDrmBridge::SecurityLevel MediaDrmBridge::GetSecurityLevel() {
328  JNIEnv* env = AttachCurrentThread();
329  ScopedJavaLocalRef<jstring> j_security_level =
330      Java_MediaDrmBridge_getSecurityLevel(env, j_media_drm_.obj());
331  std::string security_level_str =
332      ConvertJavaStringToUTF8(env, j_security_level.obj());
333  return GetSecurityLevelFromString(security_level_str);
334}
335
336bool MediaDrmBridge::IsProtectedSurfaceRequired() {
337  return IsSecureDecoderRequired(GetSecurityLevel());
338}
339
340}  // namespace media
341