media_drm_bridge.cc revision 3551c9c881056c480085172ff9840cab31610854
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/logging.h"
11#include "jni/MediaDrmBridge_jni.h"
12#include "media/base/android/media_player_manager.h"
13
14using base::android::AttachCurrentThread;
15using base::android::ConvertUTF8ToJavaString;
16using base::android::ConvertJavaStringToUTF8;
17using base::android::JavaByteArrayToByteVector;
18using base::android::ScopedJavaLocalRef;
19
20namespace media {
21
22static uint32 ReadUint32(const uint8_t* data) {
23  uint32 value = 0;
24  for (int i = 0; i < 4; ++i)
25    value = (value << 8) | data[i];
26  return value;
27}
28
29static uint64 ReadUint64(const uint8_t* data) {
30  uint64 value = 0;
31  for (int i = 0; i < 8; ++i)
32    value = (value << 8) | data[i];
33  return value;
34}
35
36// The structure of an ISO CENC Protection System Specific Header (PSSH) box is
37// as follows. (See ISO/IEC FDIS 23001-7:2011(E).)
38// Note: ISO boxes use big-endian values.
39//
40// PSSH {
41//   uint32 Size
42//   uint32 Type
43//   uint64 LargeSize  # Field is only present if value(Size) == 1.
44//   uint32 VersionAndFlags
45//   uint8[16] SystemId
46//   uint32 DataSize
47//   uint8[DataSize] Data
48// }
49static const int kBoxHeaderSize = 8;  // Box's header contains Size and Type.
50static const int kBoxLargeSizeSize = 8;
51static const int kPsshVersionFlagSize = 4;
52static const int kPsshSystemIdSize = 16;
53static const int kPsshDataSizeSize = 4;
54static const uint32 kTencType = 0x74656e63;
55static const uint32 kPsshType = 0x70737368;
56
57// Tries to find a PSSH box whose "SystemId" is |uuid| in |data|, parses the
58// "Data" of the box and put it in |pssh_data|. Returns true if such a box is
59// found and successfully parsed. Returns false otherwise.
60// Notes:
61// 1, If multiple PSSH boxes are found,the "Data" of the first matching PSSH box
62// will be set in |pssh_data|.
63// 2, Only PSSH and TENC boxes are allowed in |data|. TENC boxes are skipped.
64static bool GetPsshData(const uint8* data, int data_size,
65                        const std::vector<uint8>& uuid,
66                        std::vector<uint8>* pssh_data) {
67  const uint8* cur = data;
68  const uint8* data_end = data + data_size;
69  int bytes_left = data_size;
70
71  while (bytes_left > 0) {
72    const uint8* box_head = cur;
73
74    if (bytes_left < kBoxHeaderSize)
75      return false;
76
77    uint64_t box_size = ReadUint32(cur);
78    uint32 type = ReadUint32(cur + 4);
79    cur += kBoxHeaderSize;
80    bytes_left -= kBoxHeaderSize;
81
82    if (box_size == 1) {  // LargeSize is present.
83      if (bytes_left < kBoxLargeSizeSize)
84        return false;
85
86      box_size = ReadUint64(cur);
87      cur += kBoxLargeSizeSize;
88      bytes_left -= kBoxLargeSizeSize;
89    } else if (box_size == 0) {
90      box_size = bytes_left + kBoxHeaderSize;
91    }
92
93    const uint8* box_end = box_head + box_size;
94    if (data_end < box_end)
95      return false;
96
97    if (type == kTencType) {
98      // Skip 'tenc' box.
99      cur = box_end;
100      bytes_left = data_end - cur;
101      continue;
102    } else if (type != kPsshType) {
103      return false;
104    }
105
106    const int kPsshBoxMinimumSize =
107        kPsshVersionFlagSize + kPsshSystemIdSize + kPsshDataSizeSize;
108    if (box_end < cur + kPsshBoxMinimumSize)
109      return false;
110
111    uint32 version_and_flags = ReadUint32(cur);
112    cur += kPsshVersionFlagSize;
113    bytes_left -= kPsshVersionFlagSize;
114    if (version_and_flags != 0)
115      return false;
116
117    DCHECK_GE(bytes_left, kPsshSystemIdSize);
118    if (!std::equal(uuid.begin(), uuid.end(), cur)) {
119      cur = box_end;
120      bytes_left = data_end - cur;
121      continue;
122    }
123
124    cur += kPsshSystemIdSize;
125    bytes_left -= kPsshSystemIdSize;
126
127    uint32 data_size = ReadUint32(cur);
128    cur += kPsshDataSizeSize;
129    bytes_left -= kPsshDataSizeSize;
130
131    if (box_end < cur + data_size)
132      return false;
133
134    pssh_data->assign(cur, cur + data_size);
135    return true;
136  }
137
138  return false;
139}
140
141// static
142MediaDrmBridge* MediaDrmBridge::Create(int media_keys_id,
143                                       const std::vector<uint8>& scheme_uuid,
144                                       MediaPlayerManager* manager) {
145  if (!IsAvailable() || scheme_uuid.empty())
146    return NULL;
147
148  // TODO(qinmin): check whether the uuid is valid.
149  return new MediaDrmBridge(media_keys_id, scheme_uuid, manager);
150}
151
152bool MediaDrmBridge::IsAvailable() {
153  return base::android::BuildInfo::GetInstance()->sdk_int() >= 18;
154}
155
156bool MediaDrmBridge::RegisterMediaDrmBridge(JNIEnv* env) {
157  return RegisterNativesImpl(env);
158}
159
160MediaDrmBridge::MediaDrmBridge(int media_keys_id,
161                               const std::vector<uint8>& scheme_uuid,
162                               MediaPlayerManager* manager)
163    : media_keys_id_(media_keys_id),
164      scheme_uuid_(scheme_uuid),
165      manager_(manager) {
166  JNIEnv* env = AttachCurrentThread();
167  CHECK(env);
168
169  ScopedJavaLocalRef<jbyteArray> j_scheme_uuid =
170      base::android::ToJavaByteArray(env, &scheme_uuid[0], scheme_uuid.size());
171  j_media_drm_.Reset(Java_MediaDrmBridge_create(
172      env, j_scheme_uuid.obj(), reinterpret_cast<intptr_t>(this)));
173}
174
175MediaDrmBridge::~MediaDrmBridge() {
176  JNIEnv* env = AttachCurrentThread();
177  Java_MediaDrmBridge_release(env, j_media_drm_.obj());
178}
179
180bool MediaDrmBridge::GenerateKeyRequest(const std::string& type,
181                                        const uint8* init_data,
182                                        int init_data_length) {
183  std::vector<uint8> pssh_data;
184  if (!GetPsshData(init_data, init_data_length, scheme_uuid_, &pssh_data))
185    return false;
186
187  JNIEnv* env = AttachCurrentThread();
188  ScopedJavaLocalRef<jbyteArray> j_pssh_data =
189      base::android::ToJavaByteArray(env, &pssh_data[0], pssh_data.size());
190  ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, type);
191  Java_MediaDrmBridge_generateKeyRequest(
192      env, j_media_drm_.obj(), j_pssh_data.obj(), j_mime.obj());
193  return true;
194}
195
196void MediaDrmBridge::AddKey(const uint8* key, int key_length,
197                            const uint8* init_data, int init_data_length,
198                            const std::string& session_id) {
199  JNIEnv* env = AttachCurrentThread();
200  ScopedJavaLocalRef<jbyteArray> j_key_data =
201      base::android::ToJavaByteArray(env, key, key_length);
202  ScopedJavaLocalRef<jstring> j_session_id =
203      ConvertUTF8ToJavaString(env, session_id);
204  Java_MediaDrmBridge_addKey(
205      env, j_media_drm_.obj(), j_session_id.obj(), j_key_data.obj());
206}
207
208ScopedJavaLocalRef<jobject> MediaDrmBridge::GetMediaCrypto() {
209  JNIEnv* env = AttachCurrentThread();
210  return Java_MediaDrmBridge_getMediaCrypto(env, j_media_drm_.obj());
211}
212
213void MediaDrmBridge::CancelKeyRequest(const std::string& session_id) {
214  JNIEnv* env = AttachCurrentThread();
215  ScopedJavaLocalRef<jstring> j_session_id =
216      ConvertUTF8ToJavaString(env, session_id);
217  Java_MediaDrmBridge_cancelKeyRequest(
218      env, j_media_drm_.obj(), j_session_id.obj());
219}
220
221void MediaDrmBridge::OnKeyMessage(JNIEnv* env,
222                                  jobject j_media_drm,
223                                  jstring j_session_id,
224                                  jbyteArray j_message,
225                                  jstring j_destination_url) {
226  std::string session_id = ConvertJavaStringToUTF8(env, j_session_id);
227  std::vector<uint8> message;
228  JavaByteArrayToByteVector(env, j_message, &message);
229  std::string destination_url = ConvertJavaStringToUTF8(env, j_destination_url);
230
231  manager_->OnKeyMessage(media_keys_id_, session_id, message, destination_url);
232}
233
234void MediaDrmBridge::OnKeyAdded(JNIEnv* env, jobject, jstring j_session_id) {
235  std::string session_id = ConvertJavaStringToUTF8(env, j_session_id);
236  manager_->OnKeyAdded(media_keys_id_, session_id);
237}
238
239void MediaDrmBridge::OnKeyError(JNIEnv* env, jobject, jstring j_session_id) {
240  std::string session_id = ConvertJavaStringToUTF8(env, j_session_id);
241  manager_->OnKeyError(media_keys_id_, session_id, MediaKeys::kUnknownError, 0);
242}
243
244}  // namespace media
245