Home | History | Annotate | Download | only in android
      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 
     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 
     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.
     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 
    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
    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
    176 bool MediaDrmBridge::IsAvailable() {
    177   return base::android::BuildInfo::GetInstance()->sdk_int() >= 19;
    178 }
    179 
    180 // static
    181 bool MediaDrmBridge::IsSecureDecoderRequired(
    182     const std::string& security_level_str) {
    183   return IsSecureDecoderRequired(
    184       GetSecurityLevelFromString(security_level_str));
    185 }
    186 
    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 
    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 
    208 bool MediaDrmBridge::RegisterMediaDrmBridge(JNIEnv* env) {
    209   return RegisterNativesImpl(env);
    210 }
    211 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    339 ScopedJavaLocalRef<jobject> MediaDrmBridge::GetMediaCrypto() {
    340   JNIEnv* env = AttachCurrentThread();
    341   return Java_MediaDrmBridge_getMediaCrypto(env, j_media_drm_.obj());
    342 }
    343 
    344 // static
    345 bool MediaDrmBridge::IsSecureDecoderRequired(SecurityLevel security_level) {
    346   return MediaDrmBridge::SECURITY_LEVEL_1 == security_level;
    347 }
    348 
    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 
    358 bool MediaDrmBridge::IsProtectedSurfaceRequired() {
    359   return IsSecureDecoderRequired(GetSecurityLevel());
    360 }
    361 
    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 
    370 void MediaDrmBridge::OnResetDeviceCredentialsCompleted(
    371     JNIEnv* env, jobject, bool success) {
    372   base::ResetAndReturn(&reset_credentials_cb_).Run(success);
    373 }
    374 
    375 }  // namespace media
    376