Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / media / base / android / media_drm_bridge.cc
blob17ed784e5cdae8fef6ec46b77c174b3a966ace25
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.
5 #include "media/base/android/media_drm_bridge.h"
7 #include <algorithm>
9 #include "base/android/build_info.h"
10 #include "base/android/jni_array.h"
11 #include "base/android/jni_string.h"
12 #include "base/callback_helpers.h"
13 #include "base/containers/hash_tables.h"
14 #include "base/lazy_instance.h"
15 #include "base/location.h"
16 #include "base/logging.h"
17 #include "base/single_thread_task_runner.h"
18 #include "base/stl_util.h"
19 #include "base/strings/string_util.h"
20 #include "base/sys_byteorder.h"
21 #include "base/sys_info.h"
22 #include "base/thread_task_runner_handle.h"
23 #include "jni/MediaDrmBridge_jni.h"
24 #include "media/base/android/media_client_android.h"
25 #include "media/base/android/media_drm_bridge_delegate.h"
26 #include "media/base/cdm_key_information.h"
28 #include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR.
30 using base::android::AttachCurrentThread;
31 using base::android::ConvertUTF8ToJavaString;
32 using base::android::ConvertJavaStringToUTF8;
33 using base::android::JavaByteArrayToByteVector;
34 using base::android::ScopedJavaLocalRef;
36 namespace media {
38 namespace {
40 // DrmBridge supports session expiration event but doesn't provide detailed
41 // status for each key ID, which is required by the EME spec. Use a dummy key ID
42 // here to report session expiration info.
43 const char kDummyKeyId[] = "Dummy Key Id";
45 // Returns string session ID from jbyteArray (byte[] in Java).
46 std::string GetSessionId(JNIEnv* env, jbyteArray j_session_id) {
47 std::vector<uint8> session_id_vector;
48 JavaByteArrayToByteVector(env, j_session_id, &session_id_vector);
49 return std::string(session_id_vector.begin(), session_id_vector.end());
52 const uint8 kWidevineUuid[16] = {
53 0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE,
54 0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED };
56 // Convert |init_data_type| to a string supported by MediaDRM.
57 // "audio"/"video" does not matter, so use "video".
58 std::string ConvertInitDataType(media::EmeInitDataType init_data_type) {
59 // TODO(jrummell/xhwang): EME init data types like "webm" and "cenc" are
60 // supported in API level >=21 for Widevine key system. Switch to use those
61 // strings when they are officially supported in Android for all key systems.
62 switch (init_data_type) {
63 case media::EmeInitDataType::WEBM:
64 return "video/webm";
65 case media::EmeInitDataType::CENC:
66 return "video/mp4";
67 case media::EmeInitDataType::KEYIDS:
68 return "keyids";
69 default:
70 NOTREACHED();
71 return "unknown";
75 class KeySystemManager {
76 public:
77 KeySystemManager();
78 UUID GetUUID(const std::string& key_system);
79 std::vector<std::string> GetPlatformKeySystemNames();
81 private:
82 using KeySystemUuidMap = MediaClientAndroid::KeySystemUuidMap;
84 KeySystemUuidMap key_system_uuid_map_;
86 DISALLOW_COPY_AND_ASSIGN(KeySystemManager);
89 KeySystemManager::KeySystemManager() {
90 // Widevine is always supported in Android.
91 key_system_uuid_map_[kWidevineKeySystem] =
92 UUID(kWidevineUuid, kWidevineUuid + arraysize(kWidevineUuid));
93 MediaClientAndroid* client = GetMediaClientAndroid();
94 if (client)
95 client->AddKeySystemUUIDMappings(&key_system_uuid_map_);
98 UUID KeySystemManager::GetUUID(const std::string& key_system) {
99 KeySystemUuidMap::iterator it = key_system_uuid_map_.find(key_system);
100 if (it == key_system_uuid_map_.end())
101 return UUID();
102 return it->second;
105 std::vector<std::string> KeySystemManager::GetPlatformKeySystemNames() {
106 std::vector<std::string> key_systems;
107 for (KeySystemUuidMap::iterator it = key_system_uuid_map_.begin();
108 it != key_system_uuid_map_.end(); ++it) {
109 // Rule out the key system handled by Chrome explicitly.
110 if (it->first != kWidevineKeySystem)
111 key_systems.push_back(it->first);
113 return key_systems;
116 base::LazyInstance<KeySystemManager>::Leaky g_key_system_manager =
117 LAZY_INSTANCE_INITIALIZER;
119 // Checks whether |key_system| is supported with |container_mime_type|. Only
120 // checks |key_system| support if |container_mime_type| is empty.
121 // TODO(xhwang): The |container_mime_type| is not the same as contentType in
122 // the EME spec. Revisit this once the spec issue with initData type is
123 // resolved.
124 bool IsKeySystemSupportedWithTypeImpl(const std::string& key_system,
125 const std::string& container_mime_type) {
126 if (!MediaDrmBridge::IsAvailable())
127 return false;
129 UUID scheme_uuid = g_key_system_manager.Get().GetUUID(key_system);
130 if (scheme_uuid.empty())
131 return false;
133 JNIEnv* env = AttachCurrentThread();
134 ScopedJavaLocalRef<jbyteArray> j_scheme_uuid =
135 base::android::ToJavaByteArray(env, &scheme_uuid[0], scheme_uuid.size());
136 ScopedJavaLocalRef<jstring> j_container_mime_type =
137 ConvertUTF8ToJavaString(env, container_mime_type);
138 return Java_MediaDrmBridge_isCryptoSchemeSupported(
139 env, j_scheme_uuid.obj(), j_container_mime_type.obj());
142 MediaDrmBridge::SecurityLevel GetSecurityLevelFromString(
143 const std::string& security_level_str) {
144 if (0 == security_level_str.compare("L1"))
145 return MediaDrmBridge::SECURITY_LEVEL_1;
146 if (0 == security_level_str.compare("L3"))
147 return MediaDrmBridge::SECURITY_LEVEL_3;
148 DCHECK(security_level_str.empty());
149 return MediaDrmBridge::SECURITY_LEVEL_NONE;
152 std::string GetSecurityLevelString(
153 MediaDrmBridge::SecurityLevel security_level) {
154 switch (security_level) {
155 case MediaDrmBridge::SECURITY_LEVEL_NONE:
156 return "";
157 case MediaDrmBridge::SECURITY_LEVEL_1:
158 return "L1";
159 case MediaDrmBridge::SECURITY_LEVEL_3:
160 return "L3";
162 return "";
165 } // namespace
167 // static
168 bool MediaDrmBridge::IsAvailable() {
169 if (base::android::BuildInfo::GetInstance()->sdk_int() < 19)
170 return false;
172 int32 os_major_version = 0;
173 int32 os_minor_version = 0;
174 int32 os_bugfix_version = 0;
175 base::SysInfo::OperatingSystemVersionNumbers(&os_major_version,
176 &os_minor_version,
177 &os_bugfix_version);
178 if (os_major_version == 4 && os_minor_version == 4 && os_bugfix_version == 0)
179 return false;
181 return true;
184 // TODO(ddorwin): This is specific to Widevine. http://crbug.com/459400
185 // static
186 bool MediaDrmBridge::IsSecureDecoderRequired(SecurityLevel security_level) {
187 DCHECK(IsAvailable());
188 return SECURITY_LEVEL_1 == security_level;
191 // static
192 std::vector<std::string> MediaDrmBridge::GetPlatformKeySystemNames() {
193 return g_key_system_manager.Get().GetPlatformKeySystemNames();
196 // static
197 bool MediaDrmBridge::IsKeySystemSupported(const std::string& key_system) {
198 DCHECK(!key_system.empty());
199 return IsKeySystemSupportedWithTypeImpl(key_system, "");
202 // static
203 bool MediaDrmBridge::IsKeySystemSupportedWithType(
204 const std::string& key_system,
205 const std::string& container_mime_type) {
206 DCHECK(!key_system.empty() && !container_mime_type.empty());
207 return IsKeySystemSupportedWithTypeImpl(key_system, container_mime_type);
210 bool MediaDrmBridge::RegisterMediaDrmBridge(JNIEnv* env) {
211 return RegisterNativesImpl(env);
214 MediaDrmBridge::MediaDrmBridge(
215 const std::vector<uint8>& scheme_uuid,
216 const SessionMessageCB& session_message_cb,
217 const SessionClosedCB& session_closed_cb,
218 const LegacySessionErrorCB& legacy_session_error_cb,
219 const SessionKeysChangeCB& session_keys_change_cb)
220 : scheme_uuid_(scheme_uuid),
221 session_message_cb_(session_message_cb),
222 session_closed_cb_(session_closed_cb),
223 legacy_session_error_cb_(legacy_session_error_cb),
224 session_keys_change_cb_(session_keys_change_cb) {
225 JNIEnv* env = AttachCurrentThread();
226 CHECK(env);
228 ScopedJavaLocalRef<jbyteArray> j_scheme_uuid =
229 base::android::ToJavaByteArray(env, &scheme_uuid[0], scheme_uuid.size());
230 j_media_drm_.Reset(Java_MediaDrmBridge_create(
231 env, j_scheme_uuid.obj(), reinterpret_cast<intptr_t>(this)));
234 MediaDrmBridge::~MediaDrmBridge() {
235 JNIEnv* env = AttachCurrentThread();
236 player_tracker_.NotifyCdmUnset();
237 if (!j_media_drm_.is_null())
238 Java_MediaDrmBridge_destroy(env, j_media_drm_.obj());
241 // static
242 // TODO(xhwang): Enable SessionExpirationUpdateCB when it is supported.
243 scoped_ptr<MediaDrmBridge> MediaDrmBridge::Create(
244 const std::string& key_system,
245 const SessionMessageCB& session_message_cb,
246 const SessionClosedCB& session_closed_cb,
247 const LegacySessionErrorCB& legacy_session_error_cb,
248 const SessionKeysChangeCB& session_keys_change_cb,
249 const SessionExpirationUpdateCB& /* session_expiration_update_cb */) {
250 scoped_ptr<MediaDrmBridge> media_drm_bridge;
251 if (!IsAvailable())
252 return media_drm_bridge.Pass();
254 UUID scheme_uuid = g_key_system_manager.Get().GetUUID(key_system);
255 if (scheme_uuid.empty())
256 return media_drm_bridge.Pass();
258 media_drm_bridge.reset(
259 new MediaDrmBridge(scheme_uuid, session_message_cb, session_closed_cb,
260 legacy_session_error_cb, session_keys_change_cb));
262 if (media_drm_bridge->j_media_drm_.is_null())
263 media_drm_bridge.reset();
265 return media_drm_bridge.Pass();
268 // static
269 scoped_ptr<MediaDrmBridge> MediaDrmBridge::CreateWithoutSessionSupport(
270 const std::string& key_system) {
271 return MediaDrmBridge::Create(
272 key_system, SessionMessageCB(), SessionClosedCB(), LegacySessionErrorCB(),
273 SessionKeysChangeCB(), SessionExpirationUpdateCB());
276 bool MediaDrmBridge::SetSecurityLevel(SecurityLevel security_level) {
277 if (security_level != SECURITY_LEVEL_NONE &&
278 !std::equal(scheme_uuid_.begin(), scheme_uuid_.end(), kWidevineUuid)) {
279 NOTREACHED() << "Widevine security level " << security_level
280 << "used with another key system";
281 return false;
284 JNIEnv* env = AttachCurrentThread();
286 std::string security_level_str = GetSecurityLevelString(security_level);
287 if (security_level_str.empty())
288 return false;
290 ScopedJavaLocalRef<jstring> j_security_level =
291 ConvertUTF8ToJavaString(env, security_level_str);
292 return Java_MediaDrmBridge_setSecurityLevel(
293 env, j_media_drm_.obj(), j_security_level.obj());
296 void MediaDrmBridge::SetServerCertificate(
297 const std::vector<uint8_t>& certificate,
298 scoped_ptr<media::SimpleCdmPromise> promise) {
299 DCHECK(!certificate.empty());
301 JNIEnv* env = AttachCurrentThread();
302 ScopedJavaLocalRef<jbyteArray> j_certificate;
303 if (Java_MediaDrmBridge_setServerCertificate(env, j_media_drm_.obj(),
304 j_certificate.obj())) {
305 promise->resolve();
306 } else {
307 promise->reject(INVALID_ACCESS_ERROR, 0, "Set server certificate failed.");
311 void MediaDrmBridge::CreateSessionAndGenerateRequest(
312 SessionType session_type,
313 media::EmeInitDataType init_data_type,
314 const std::vector<uint8_t>& init_data,
315 scoped_ptr<media::NewSessionCdmPromise> promise) {
316 DVLOG(1) << __FUNCTION__;
318 if (session_type != media::MediaKeys::TEMPORARY_SESSION) {
319 promise->reject(NOT_SUPPORTED_ERROR, 0,
320 "Only the temporary session type is supported.");
321 return;
324 JNIEnv* env = AttachCurrentThread();
325 ScopedJavaLocalRef<jbyteArray> j_init_data;
326 ScopedJavaLocalRef<jobjectArray> j_optional_parameters;
328 MediaClientAndroid* client = GetMediaClientAndroid();
329 if (client) {
330 MediaDrmBridgeDelegate* delegate =
331 client->GetMediaDrmBridgeDelegate(scheme_uuid_);
332 if (delegate) {
333 std::vector<uint8> init_data_from_delegate;
334 std::vector<std::string> optional_parameters_from_delegate;
335 if (!delegate->OnCreateSession(init_data_type, init_data,
336 &init_data_from_delegate,
337 &optional_parameters_from_delegate)) {
338 promise->reject(INVALID_ACCESS_ERROR, 0, "Invalid init data.");
340 if (!init_data_from_delegate.empty()) {
341 j_init_data = base::android::ToJavaByteArray(
342 env, vector_as_array(&init_data_from_delegate),
343 init_data_from_delegate.size());
345 if (!optional_parameters_from_delegate.empty()) {
346 j_optional_parameters = base::android::ToJavaArrayOfStrings(
347 env, optional_parameters_from_delegate);
352 if (j_init_data.is_null()) {
353 j_init_data = base::android::ToJavaByteArray(
354 env, vector_as_array(&init_data), init_data.size());
357 ScopedJavaLocalRef<jstring> j_mime =
358 ConvertUTF8ToJavaString(env, ConvertInitDataType(init_data_type));
359 uint32_t promise_id = cdm_promise_adapter_.SavePromise(promise.Pass());
360 Java_MediaDrmBridge_createSessionFromNative(env, j_media_drm_.obj(),
361 j_init_data.obj(), j_mime.obj(),
362 j_optional_parameters.obj(),
363 promise_id);
366 void MediaDrmBridge::LoadSession(
367 SessionType session_type,
368 const std::string& session_id,
369 scoped_ptr<media::NewSessionCdmPromise> promise) {
370 promise->reject(NOT_SUPPORTED_ERROR, 0, "LoadSession() is not supported.");
373 void MediaDrmBridge::UpdateSession(
374 const std::string& session_id,
375 const std::vector<uint8_t>& response,
376 scoped_ptr<media::SimpleCdmPromise> promise) {
377 DVLOG(1) << __FUNCTION__;
379 JNIEnv* env = AttachCurrentThread();
380 ScopedJavaLocalRef<jbyteArray> j_response = base::android::ToJavaByteArray(
381 env, vector_as_array(&response), response.size());
382 ScopedJavaLocalRef<jbyteArray> j_session_id = base::android::ToJavaByteArray(
383 env, reinterpret_cast<const uint8_t*>(session_id.data()),
384 session_id.size());
385 uint32_t promise_id = cdm_promise_adapter_.SavePromise(promise.Pass());
386 Java_MediaDrmBridge_updateSession(env, j_media_drm_.obj(), j_session_id.obj(),
387 j_response.obj(), promise_id);
390 void MediaDrmBridge::CloseSession(const std::string& session_id,
391 scoped_ptr<media::SimpleCdmPromise> promise) {
392 DVLOG(1) << __FUNCTION__;
393 JNIEnv* env = AttachCurrentThread();
394 ScopedJavaLocalRef<jbyteArray> j_session_id = base::android::ToJavaByteArray(
395 env, reinterpret_cast<const uint8_t*>(session_id.data()),
396 session_id.size());
397 uint32_t promise_id = cdm_promise_adapter_.SavePromise(promise.Pass());
398 Java_MediaDrmBridge_closeSession(env, j_media_drm_.obj(), j_session_id.obj(),
399 promise_id);
402 void MediaDrmBridge::RemoveSession(
403 const std::string& session_id,
404 scoped_ptr<media::SimpleCdmPromise> promise) {
405 promise->reject(NOT_SUPPORTED_ERROR, 0, "RemoveSession() is not supported.");
408 CdmContext* MediaDrmBridge::GetCdmContext() {
409 NOTREACHED();
410 return nullptr;
413 int MediaDrmBridge::RegisterPlayer(const base::Closure& new_key_cb,
414 const base::Closure& cdm_unset_cb) {
415 return player_tracker_.RegisterPlayer(new_key_cb, cdm_unset_cb);
418 void MediaDrmBridge::UnregisterPlayer(int registration_id) {
419 player_tracker_.UnregisterPlayer(registration_id);
422 void MediaDrmBridge::SetMediaCryptoReadyCB(const base::Closure& closure) {
423 if (closure.is_null()) {
424 media_crypto_ready_cb_.Reset();
425 return;
428 DCHECK(media_crypto_ready_cb_.is_null());
430 if (!GetMediaCrypto().is_null()) {
431 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, closure);
432 return;
435 media_crypto_ready_cb_ = closure;
438 void MediaDrmBridge::OnMediaCryptoReady(JNIEnv* env, jobject) {
439 DCHECK(!GetMediaCrypto().is_null());
440 if (!media_crypto_ready_cb_.is_null())
441 base::ResetAndReturn(&media_crypto_ready_cb_).Run();
444 void MediaDrmBridge::OnPromiseResolved(JNIEnv* env,
445 jobject j_media_drm,
446 jint j_promise_id) {
447 cdm_promise_adapter_.ResolvePromise(j_promise_id);
450 void MediaDrmBridge::OnPromiseResolvedWithSession(JNIEnv* env,
451 jobject j_media_drm,
452 jint j_promise_id,
453 jbyteArray j_session_id) {
454 cdm_promise_adapter_.ResolvePromise(j_promise_id,
455 GetSessionId(env, j_session_id));
458 void MediaDrmBridge::OnPromiseRejected(JNIEnv* env,
459 jobject j_media_drm,
460 jint j_promise_id,
461 jstring j_error_message) {
462 std::string error_message = ConvertJavaStringToUTF8(env, j_error_message);
463 cdm_promise_adapter_.RejectPromise(j_promise_id, MediaKeys::UNKNOWN_ERROR, 0,
464 error_message);
467 void MediaDrmBridge::OnSessionMessage(JNIEnv* env,
468 jobject j_media_drm,
469 jbyteArray j_session_id,
470 jbyteArray j_message,
471 jstring j_legacy_destination_url) {
472 std::vector<uint8> message;
473 JavaByteArrayToByteVector(env, j_message, &message);
474 GURL legacy_destination_url =
475 GURL(ConvertJavaStringToUTF8(env, j_legacy_destination_url));
476 // Note: Message type is not supported in MediaDrm. Do our best guess here.
477 media::MediaKeys::MessageType message_type =
478 legacy_destination_url.is_empty() ? media::MediaKeys::LICENSE_REQUEST
479 : media::MediaKeys::LICENSE_RENEWAL;
481 session_message_cb_.Run(GetSessionId(env, j_session_id), message_type,
482 message, legacy_destination_url);
485 void MediaDrmBridge::OnSessionClosed(JNIEnv* env,
486 jobject j_media_drm,
487 jbyteArray j_session_id) {
488 session_closed_cb_.Run(GetSessionId(env, j_session_id));
491 void MediaDrmBridge::OnSessionKeysChange(JNIEnv* env,
492 jobject j_media_drm,
493 jbyteArray j_session_id,
494 bool has_additional_usable_key,
495 jint j_key_status) {
496 if (has_additional_usable_key)
497 player_tracker_.NotifyNewKey();
499 scoped_ptr<CdmKeyInformation> cdm_key_information(new CdmKeyInformation());
500 cdm_key_information->key_id.assign(kDummyKeyId,
501 kDummyKeyId + sizeof(kDummyKeyId));
502 cdm_key_information->status =
503 static_cast<CdmKeyInformation::KeyStatus>(j_key_status);
504 CdmKeysInfo cdm_keys_info;
505 cdm_keys_info.push_back(cdm_key_information.release());
507 session_keys_change_cb_.Run(GetSessionId(env, j_session_id),
508 has_additional_usable_key, cdm_keys_info.Pass());
511 void MediaDrmBridge::OnLegacySessionError(JNIEnv* env,
512 jobject j_media_drm,
513 jbyteArray j_session_id,
514 jstring j_error_message) {
515 std::string error_message = ConvertJavaStringToUTF8(env, j_error_message);
516 legacy_session_error_cb_.Run(GetSessionId(env, j_session_id),
517 MediaKeys::UNKNOWN_ERROR, 0, error_message);
520 ScopedJavaLocalRef<jobject> MediaDrmBridge::GetMediaCrypto() {
521 JNIEnv* env = AttachCurrentThread();
522 return Java_MediaDrmBridge_getMediaCrypto(env, j_media_drm_.obj());
525 MediaDrmBridge::SecurityLevel MediaDrmBridge::GetSecurityLevel() {
526 JNIEnv* env = AttachCurrentThread();
527 ScopedJavaLocalRef<jstring> j_security_level =
528 Java_MediaDrmBridge_getSecurityLevel(env, j_media_drm_.obj());
529 std::string security_level_str =
530 ConvertJavaStringToUTF8(env, j_security_level.obj());
531 return GetSecurityLevelFromString(security_level_str);
534 bool MediaDrmBridge::IsProtectedSurfaceRequired() {
535 // For Widevine, this depends on the security level.
536 if (std::equal(scheme_uuid_.begin(), scheme_uuid_.end(), kWidevineUuid))
537 return IsSecureDecoderRequired(GetSecurityLevel());
539 // For other key systems, assume true.
540 return true;
543 void MediaDrmBridge::ResetDeviceCredentials(
544 const ResetCredentialsCB& callback) {
545 DCHECK(reset_credentials_cb_.is_null());
546 reset_credentials_cb_ = callback;
547 JNIEnv* env = AttachCurrentThread();
548 Java_MediaDrmBridge_resetDeviceCredentials(env, j_media_drm_.obj());
551 void MediaDrmBridge::OnResetDeviceCredentialsCompleted(
552 JNIEnv* env, jobject, bool success) {
553 base::ResetAndReturn(&reset_credentials_cb_).Run(success);
556 } // namespace media