• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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;
23 
24 namespace media {
25 
ReadUint32(const uint8_t * data)26 static 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 
ReadUint64(const uint8_t * data)33 static 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 // }
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;
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.
GetPsshData(const uint8 * data,int data_size,const std::vector<uint8> & uuid,std::vector<uint8> * pssh_data)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;
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 
GetSecurityLevelFromString(const std::string & security_level_str)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;
153 }
154 
155 // static
Create(int media_keys_id,const std::vector<uint8> & scheme_uuid,const GURL & frame_url,const std::string & security_level,MediaPlayerManager * manager)156 scoped_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
IsAvailable()176 bool MediaDrmBridge::IsAvailable() {
177   return base::android::BuildInfo::GetInstance()->sdk_int() >= 19;
178 }
179 
180 // static
IsSecureDecoderRequired(const std::string & security_level_str)181 bool MediaDrmBridge::IsSecureDecoderRequired(
182     const std::string& security_level_str) {
183   return IsSecureDecoderRequired(
184       GetSecurityLevelFromString(security_level_str));
185 }
186 
IsSecurityLevelSupported(const std::vector<uint8> & scheme_uuid,const std::string & security_level)187 bool 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 
IsCryptoSchemeSupported(const std::vector<uint8> & scheme_uuid,const std::string & container_mime_type)196 bool 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 
RegisterMediaDrmBridge(JNIEnv * env)208 bool MediaDrmBridge::RegisterMediaDrmBridge(JNIEnv* env) {
209   return RegisterNativesImpl(env);
210 }
211 
MediaDrmBridge(int media_keys_id,const std::vector<uint8> & scheme_uuid,const GURL & frame_url,const std::string & security_level,MediaPlayerManager * manager)212 MediaDrmBridge::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 
~MediaDrmBridge()233 MediaDrmBridge::~MediaDrmBridge() {
234   JNIEnv* env = AttachCurrentThread();
235   if (!j_media_drm_.is_null())
236     Java_MediaDrmBridge_release(env, j_media_drm_.obj());
237 }
238 
CreateSession(uint32 session_id,const std::string & type,const uint8 * init_data,int init_data_length)239 bool 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 
UpdateSession(uint32 session_id,const uint8 * response,int response_length)256 void 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 
ReleaseSession(uint32 session_id)267 void 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 
SetMediaCryptoReadyCB(const base::Closure & closure)273 void 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 
OnMediaCryptoReady(JNIEnv * env,jobject)289 void 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 
OnSessionCreated(JNIEnv * env,jobject j_media_drm,jint j_session_id,jstring j_web_session_id)295 void 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 
OnSessionMessage(JNIEnv * env,jobject j_media_drm,jint j_session_id,jbyteArray j_message,jstring j_destination_url)304 void 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 
OnSessionReady(JNIEnv * env,jobject j_media_drm,jint j_session_id)317 void 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 
OnSessionClosed(JNIEnv * env,jobject j_media_drm,jint j_session_id)324 void 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 
OnSessionError(JNIEnv * env,jobject j_media_drm,jint j_session_id)331 void 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 
GetMediaCrypto()339 ScopedJavaLocalRef<jobject> MediaDrmBridge::GetMediaCrypto() {
340   JNIEnv* env = AttachCurrentThread();
341   return Java_MediaDrmBridge_getMediaCrypto(env, j_media_drm_.obj());
342 }
343 
344 // static
IsSecureDecoderRequired(SecurityLevel security_level)345 bool MediaDrmBridge::IsSecureDecoderRequired(SecurityLevel security_level) {
346   return MediaDrmBridge::SECURITY_LEVEL_1 == security_level;
347 }
348 
GetSecurityLevel()349 MediaDrmBridge::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 
IsProtectedSurfaceRequired()358 bool MediaDrmBridge::IsProtectedSurfaceRequired() {
359   return IsSecureDecoderRequired(GetSecurityLevel());
360 }
361 
ResetDeviceCredentials(const ResetCredentialsCB & callback)362 void 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 
OnResetDeviceCredentialsCompleted(JNIEnv * env,jobject,bool success)370 void MediaDrmBridge::OnResetDeviceCredentialsCompleted(
371     JNIEnv* env, jobject, bool success) {
372   base::ResetAndReturn(&reset_credentials_cb_).Run(success);
373 }
374 
375 }  // namespace media
376