blob: abbbf7a6d614a0165e69703c4cc2cd942148fff0 [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MEDIA_BASE_ANDROID_MEDIA_DRM_BRIDGE_H_
#define MEDIA_BASE_ANDROID_MEDIA_DRM_BRIDGE_H_
#include <jni.h>
#include <stdint.h>
#include <memory>
#include <string>
#include <vector>
#include "base/android/scoped_java_ref.h"
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/sequenced_task_runner_helpers.h"
#include "media/base/android/android_util.h"
#include "media/base/android/media_crypto_context.h"
#include "media/base/android/media_crypto_context_impl.h"
#include "media/base/android/media_drm_storage_bridge.h"
#include "media/base/callback_registry.h"
#include "media/base/cdm_context.h"
#include "media/base/cdm_promise.h"
#include "media/base/cdm_promise_adapter.h"
#include "media/base/content_decryption_module.h"
#include "media/base/media_drm_storage.h"
#include "media/base/media_export.h"
#include "media/base/provision_fetcher.h"
#include "url/origin.h"
namespace base {
class SingleThreadTaskRunner;
}
namespace media {
// Implements a CDM using Android MediaDrm API.
//
// Thread Safety:
//
// This class lives on the thread where it is created. All methods must be
// called on the `task_runner_` except for the `RegisterEventCB()` and
// `SetMediaCryptoReadyCB()`, which can be called on any thread.
class MEDIA_EXPORT MediaDrmBridge : public ContentDecryptionModule,
public CdmContext {
public:
// TODO(ddorwin): These are specific to Widevine. http://crbug.com/459400
enum SecurityLevel {
SECURITY_LEVEL_DEFAULT = 0,
SECURITY_LEVEL_1 = 1,
SECURITY_LEVEL_3 = 3,
};
using MediaCryptoReadyCB = MediaCryptoContext::MediaCryptoReadyCB;
// Checks whether MediaDRM is available and usable, including for decoding.
// All other static methods check IsAvailable() or equivalent internally.
// There is no need to check IsAvailable() explicitly before calling them.
static bool IsAvailable();
// Checks whether |key_system| is supported.
static bool IsKeySystemSupported(const std::string& key_system);
// Checks whether |key_system| is supported with |container_mime_type|.
// |container_mime_type| must not be empty.
static bool IsKeySystemSupportedWithType(
const std::string& key_system,
const std::string& container_mime_type);
// Whether per-origin provisioning (setting "origin" property on MediaDrm) is
// supported or not. If false, per-device provisioning is used.
static bool IsPerOriginProvisioningSupported();
// Returns true if this device supports per-application provisioning, false
// otherwise.
static bool IsPerApplicationProvisioningSupported();
static bool IsPersistentLicenseTypeSupported(const std::string& key_system);
// Returns the list of the platform-supported key system names that
// are not handled by Chrome explicitly.
static std::vector<std::string> GetPlatformKeySystemNames();
// Returns the scheme UUID for |key_system|.
static std::vector<uint8_t> GetUUID(const std::string& key_system);
// Same as Create() except that no session callbacks are provided. This is
// used when we need to use MediaDrmBridge without creating any sessions.
//
// |create_fetcher_cb| can be empty when we don't want origin provision
// to happen, e.g. when unprovision the origin.
static scoped_refptr<MediaDrmBridge> CreateWithoutSessionSupport(
const std::string& key_system,
const std::string& origin_id,
SecurityLevel security_level,
CreateFetcherCB create_fetcher_cb);
// ContentDecryptionModule implementation.
void SetServerCertificate(
const std::vector<uint8_t>& certificate,
std::unique_ptr<media::SimpleCdmPromise> promise) override;
void CreateSessionAndGenerateRequest(
CdmSessionType session_type,
media::EmeInitDataType init_data_type,
const std::vector<uint8_t>& init_data,
std::unique_ptr<media::NewSessionCdmPromise> promise) override;
void LoadSession(
CdmSessionType session_type,
const std::string& session_id,
std::unique_ptr<media::NewSessionCdmPromise> promise) override;
void UpdateSession(const std::string& session_id,
const std::vector<uint8_t>& response,
std::unique_ptr<media::SimpleCdmPromise> promise) override;
void CloseSession(const std::string& session_id,
std::unique_ptr<media::SimpleCdmPromise> promise) override;
void RemoveSession(const std::string& session_id,
std::unique_ptr<media::SimpleCdmPromise> promise) override;
CdmContext* GetCdmContext() override;
void DeleteOnCorrectThread() const override;
// CdmContext implementation.
std::unique_ptr<CallbackRegistration> RegisterEventCB(
EventCB event_cb) override;
MediaCryptoContext* GetMediaCryptoContext() override;
// Provision the origin bound with |this|. |provisioning_complete_cb| will be
// called asynchronously to indicate whether this was successful or not.
// MediaDrmBridge must be created with a valid origin ID.
void Provision(base::OnceCallback<void(bool)> provisioning_complete_cb);
// Unprovision the origin bound with |this|. This will remove the cert for
// current origin and leave the offline licenses in invalid state (offline
// licenses can't be used anymore).
//
// MediaDrmBridge must be created with a valid origin ID without session
// support. This function won't touch persistent storage.
void Unprovision();
// Helper function to determine whether a secure decoder is required for the
// video playback.
bool IsSecureCodecRequired();
// Helper functions to resolve promises.
void ResolvePromise(uint32_t promise_id);
void ResolvePromiseWithSession(uint32_t promise_id,
const std::string& session_id);
void RejectPromise(uint32_t promise_id, const std::string& error_message);
// Registers a callback which will be called when MediaCrypto is ready.
// Can be called on any thread. Only one callback should be registered.
// The registered callbacks will be fired on |task_runner_|. The caller
// should make sure that the callbacks are posted to the correct thread.
void SetMediaCryptoReadyCB(MediaCryptoReadyCB media_crypto_ready_cb);
// All the OnXxx functions below are called from Java. The implementation must
// only do minimal work and then post tasks to avoid reentrancy issues.
// Called by Java after a MediaCrypto object is created.
void OnMediaCryptoReady(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& j_media_drm,
const base::android::JavaParamRef<jobject>& j_media_crypto);
// Called by Java when we need to send a provisioning request,
void OnProvisionRequest(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& j_media_drm,
const base::android::JavaParamRef<jstring>& j_default_url,
const base::android::JavaParamRef<jbyteArray>& j_request_data);
// Called by Java when provisioning is complete. This is only in response to a
// provision() request.
void OnProvisioningComplete(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& j_media_drm,
bool success);
// Callbacks to resolve the promise for |promise_id|.
void OnPromiseResolved(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& j_media_drm,
jint j_promise_id);
void OnPromiseResolvedWithSession(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& j_media_drm,
jint j_promise_id,
const base::android::JavaParamRef<jbyteArray>& j_session_id);
// Callback to reject the promise for |promise_id| with |error_message|.
// Note: No |system_error| is available from MediaDrm.
// TODO(xhwang): Implement Exception code.
void OnPromiseRejected(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& j_media_drm,
jint j_promise_id,
const base::android::JavaParamRef<jstring>& j_error_message);
// Session event callbacks.
void OnSessionMessage(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& j_media_drm,
const base::android::JavaParamRef<jbyteArray>& j_session_id,
jint j_message_type,
const base::android::JavaParamRef<jbyteArray>& j_message);
void OnSessionClosed(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& j_media_drm,
const base::android::JavaParamRef<jbyteArray>& j_session_id);
// Called when key statuses of session are changed. |is_key_release| is set to
// true when releasing keys. Some of the MediaDrm key status codes should be
// mapped to CDM key status differently (e.g. EXPIRE -> RELEASED).
void OnSessionKeysChange(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& j_media_drm,
const base::android::JavaParamRef<jbyteArray>& j_session_id,
// List<KeyStatus>
const base::android::JavaParamRef<jobjectArray>& j_keys_info,
bool has_additional_usable_key,
bool is_key_release);
// |expiry_time_ms| is the new expiration time for the keys in the session.
// The time is in milliseconds, relative to the Unix epoch. A time of 0
// indicates that the keys never expire.
void OnSessionExpirationUpdate(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& j_media_drm,
const base::android::JavaParamRef<jbyteArray>& j_session_id,
jlong expiry_time_ms);
private:
friend class MediaDrmBridgeFactory;
// For DeleteSoon() in DeleteOnCorrectThread().
friend class base::DeleteHelper<MediaDrmBridge>;
static scoped_refptr<MediaDrmBridge> CreateInternal(
const std::vector<uint8_t>& scheme_uuid,
const std::string& origin_id,
SecurityLevel security_level,
bool requires_media_crypto,
std::unique_ptr<MediaDrmStorageBridge> storage,
CreateFetcherCB create_fetcher_cb,
const SessionMessageCB& session_message_cb,
const SessionClosedCB& session_closed_cb,
const SessionKeysChangeCB& session_keys_change_cb,
const SessionExpirationUpdateCB& session_expiration_update_cb);
// Constructs a MediaDrmBridge for |scheme_uuid| and |security_level|. The
// default security level will be used if |security_level| is
// SECURITY_LEVEL_DEFAULT.
//
// |origin_id| is a random string that can identify an origin.
//
// If |requires_media_crypto| is true, MediaCrypto is expected to be created
// and notified via MediaCryptoReadyCB set in SetMediaCryptoReadyCB(). This
// may trigger the provisioning process. Before MediaCrypto is notified, no
// other methods should be called.
// TODO(xhwang): It's odd to rely on MediaCryptoReadyCB. Maybe we should add a
// dedicated Initialize() method.
//
// If |requires_media_crypto| is false, MediaCrypto will not be created. This
// object cannot be used for playback, but can be used to unprovision the
// device/origin via Unprovision(). Sessions are not created in this mode.
MediaDrmBridge(const std::vector<uint8_t>& scheme_uuid,
const std::string& origin_id,
SecurityLevel security_level,
bool requires_media_crypto,
std::unique_ptr<MediaDrmStorageBridge> storage,
const CreateFetcherCB& create_fetcher_cb,
const SessionMessageCB& session_message_cb,
const SessionClosedCB& session_closed_cb,
const SessionKeysChangeCB& session_keys_change_cb,
const SessionExpirationUpdateCB& session_expiration_update_cb);
~MediaDrmBridge() override;
// Get the security level of the media.
SecurityLevel GetSecurityLevel();
// A helper method that is called when MediaCrypto is ready.
void NotifyMediaCryptoReady(JavaObjectPtr j_media_crypto);
// Sends HTTP provisioning request to a provisioning server.
void SendProvisioningRequest(const GURL& default_url,
const std::string& request_data);
// Process the data received by provisioning server.
void ProcessProvisionResponse(bool success, const std::string& response);
// Called on the |task_runner_| when there is additional usable key.
void OnHasAdditionalUsableKey();
// UUID of the key system.
std::vector<uint8_t> scheme_uuid_;
// Persistent storage for session ID map.
std::unique_ptr<MediaDrmStorageBridge> storage_;
// Java MediaDrm instance.
base::android::ScopedJavaGlobalRef<jobject> j_media_drm_;
// Java MediaCrypto instance. Possible values are:
// !j_media_crypto_:
// MediaCrypto creation has not been notified via NotifyMediaCryptoReady().
// !j_media_crypto_->is_null():
// MediaCrypto creation succeeded and it has been notified.
// j_media_crypto_->is_null():
// MediaCrypto creation failed and it has been notified.
JavaObjectPtr j_media_crypto_;
// The callback to create a ProvisionFetcher.
CreateFetcherCB create_fetcher_cb_;
// The ProvisionFetcher that requests and receives provisioning data.
// Non-null iff when a provision request is pending.
std::unique_ptr<ProvisionFetcher> provision_fetcher_;
// The callback to be called when provisioning is complete.
base::OnceCallback<void(bool)> provisioning_complete_cb_;
// Callbacks for firing session events.
SessionMessageCB session_message_cb_;
SessionClosedCB session_closed_cb_;
SessionKeysChangeCB session_keys_change_cb_;
SessionExpirationUpdateCB session_expiration_update_cb_;
MediaCryptoReadyCB media_crypto_ready_cb_;
CallbackRegistry<EventCB::RunType> event_callbacks_;
CdmPromiseAdapter cdm_promise_adapter_;
// Default task runner.
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
MediaCryptoContextImpl media_crypto_context_;
// NOTE: Weak pointers must be invalidated before all other member variables.
base::WeakPtrFactory<MediaDrmBridge> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(MediaDrmBridge);
};
} // namespace media
#endif // MEDIA_BASE_ANDROID_MEDIA_DRM_BRIDGE_H_