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