media_drm_bridge.cc revision 424c4d7b64af9d0d8fd9624f381f469654d5e3d2
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
144// static
145MediaDrmBridge* MediaDrmBridge::Create(int media_keys_id,
146                                       const std::vector<uint8>& scheme_uuid,
147                                       MediaPlayerManager* manager) {
148  if (!IsAvailable() || scheme_uuid.empty())
149    return NULL;
150
151  // TODO(qinmin): check whether the uuid is valid.
152  return new MediaDrmBridge(media_keys_id, scheme_uuid, manager);
153}
154
155bool MediaDrmBridge::IsAvailable() {
156  return base::android::BuildInfo::GetInstance()->sdk_int() >= 18;
157}
158
159bool MediaDrmBridge::RegisterMediaDrmBridge(JNIEnv* env) {
160  return RegisterNativesImpl(env);
161}
162
163MediaDrmBridge::MediaDrmBridge(int media_keys_id,
164                               const std::vector<uint8>& scheme_uuid,
165                               MediaPlayerManager* manager)
166    : media_keys_id_(media_keys_id),
167      scheme_uuid_(scheme_uuid),
168      manager_(manager) {
169  JNIEnv* env = AttachCurrentThread();
170  CHECK(env);
171
172  ScopedJavaLocalRef<jbyteArray> j_scheme_uuid =
173      base::android::ToJavaByteArray(env, &scheme_uuid[0], scheme_uuid.size());
174  j_media_drm_.Reset(Java_MediaDrmBridge_create(
175      env, j_scheme_uuid.obj(), reinterpret_cast<intptr_t>(this)));
176}
177
178MediaDrmBridge::~MediaDrmBridge() {
179  JNIEnv* env = AttachCurrentThread();
180  Java_MediaDrmBridge_release(env, j_media_drm_.obj());
181}
182
183bool MediaDrmBridge::GenerateKeyRequest(const std::string& type,
184                                        const uint8* init_data,
185                                        int init_data_length) {
186  std::vector<uint8> pssh_data;
187  if (!GetPsshData(init_data, init_data_length, scheme_uuid_, &pssh_data))
188    return false;
189
190  JNIEnv* env = AttachCurrentThread();
191  ScopedJavaLocalRef<jbyteArray> j_pssh_data =
192      base::android::ToJavaByteArray(env, &pssh_data[0], pssh_data.size());
193  ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, type);
194  Java_MediaDrmBridge_generateKeyRequest(
195      env, j_media_drm_.obj(), j_pssh_data.obj(), j_mime.obj());
196  return true;
197}
198
199void MediaDrmBridge::AddKey(const uint8* key, int key_length,
200                            const uint8* init_data, int init_data_length,
201                            const std::string& session_id) {
202  JNIEnv* env = AttachCurrentThread();
203  ScopedJavaLocalRef<jbyteArray> j_key_data =
204      base::android::ToJavaByteArray(env, key, key_length);
205  ScopedJavaLocalRef<jstring> j_session_id =
206      ConvertUTF8ToJavaString(env, session_id);
207  Java_MediaDrmBridge_addKey(
208      env, j_media_drm_.obj(), j_session_id.obj(), j_key_data.obj());
209}
210
211void MediaDrmBridge::CancelKeyRequest(const std::string& session_id) {
212  JNIEnv* env = AttachCurrentThread();
213  ScopedJavaLocalRef<jstring> j_session_id =
214      ConvertUTF8ToJavaString(env, session_id);
215  Java_MediaDrmBridge_cancelKeyRequest(
216      env, j_media_drm_.obj(), j_session_id.obj());
217}
218
219void MediaDrmBridge::SetMediaCryptoReadyCB(const base::Closure& closure) {
220  if (closure.is_null()) {
221    media_crypto_ready_cb_.Reset();
222    return;
223  }
224
225  DCHECK(media_crypto_ready_cb_.is_null());
226
227  if (!GetMediaCrypto().is_null()) {
228    base::MessageLoopProxy::current()->PostTask(FROM_HERE, closure);
229    return;
230  }
231
232  media_crypto_ready_cb_ = closure;
233}
234
235void MediaDrmBridge::OnMediaCryptoReady(JNIEnv* env, jobject) {
236  DCHECK(!GetMediaCrypto().is_null());
237  if (!media_crypto_ready_cb_.is_null())
238    base::ResetAndReturn(&media_crypto_ready_cb_).Run();
239}
240
241void MediaDrmBridge::OnKeyMessage(JNIEnv* env,
242                                  jobject j_media_drm,
243                                  jstring j_session_id,
244                                  jbyteArray j_message,
245                                  jstring j_destination_url) {
246  std::string session_id = ConvertJavaStringToUTF8(env, j_session_id);
247  std::vector<uint8> message;
248  JavaByteArrayToByteVector(env, j_message, &message);
249  std::string destination_url = ConvertJavaStringToUTF8(env, j_destination_url);
250
251  manager_->OnKeyMessage(media_keys_id_, session_id, message, destination_url);
252}
253
254void MediaDrmBridge::OnKeyAdded(JNIEnv* env, jobject, jstring j_session_id) {
255  std::string session_id = ConvertJavaStringToUTF8(env, j_session_id);
256  manager_->OnKeyAdded(media_keys_id_, session_id);
257}
258
259void MediaDrmBridge::OnKeyError(JNIEnv* env, jobject, jstring j_session_id) {
260  // |j_session_id| can be NULL, in which case we'll return an empty string.
261  std::string session_id = ConvertJavaStringToUTF8(env, j_session_id);
262  manager_->OnKeyError(media_keys_id_, session_id, MediaKeys::kUnknownError, 0);
263}
264
265ScopedJavaLocalRef<jobject> MediaDrmBridge::GetMediaCrypto() {
266  JNIEnv* env = AttachCurrentThread();
267  return Java_MediaDrmBridge_getMediaCrypto(env, j_media_drm_.obj());
268}
269
270MediaDrmBridge::SecurityLevel MediaDrmBridge::GetSecurityLevel() {
271  JNIEnv* env = AttachCurrentThread();
272  ScopedJavaLocalRef<jstring> j_security_level =
273      Java_MediaDrmBridge_getSecurityLevel(env, j_media_drm_.obj());
274  std::string security_level =
275      ConvertJavaStringToUTF8(env, j_security_level.obj());
276  if (0 == security_level.compare("L1"))
277    return SECURITY_LEVEL_1;
278  if (0 == security_level.compare("L3"))
279    return SECURITY_LEVEL_3;
280  DCHECK(security_level.empty());
281  return SECURITY_LEVEL_NONE;
282}
283
284bool MediaDrmBridge::IsProtectedSurfaceRequired() {
285  return MediaDrmBridge::SECURITY_LEVEL_1 == GetSecurityLevel();
286}
287
288}  // namespace media
289