1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238

media / base / content_decryption_module.h [blame]

// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MEDIA_BASE_CONTENT_DECRYPTION_MODULE_H_
#define MEDIA_BASE_CONTENT_DECRYPTION_MODULE_H_

#include <stdint.h>

#include <memory>
#include <optional>
#include <string>
#include <vector>

#include "base/functional/callback_forward.h"
#include "base/memory/ref_counted.h"
#include "media/base/cdm_key_information.h"
#include "media/base/eme_constants.h"
#include "media/base/media_export.h"
#include "url/gurl.h"

namespace base {
class Time;
}

namespace media {

class CdmContext;
struct ContentDecryptionModuleTraits;

template <typename... T>
class CdmPromiseTemplate;

typedef CdmPromiseTemplate<std::string> NewSessionCdmPromise;
typedef CdmPromiseTemplate<> SimpleCdmPromise;
typedef CdmPromiseTemplate<CdmKeyInformation::KeyStatus> KeyStatusCdmPromise;

typedef std::vector<std::unique_ptr<CdmKeyInformation>> CdmKeysInfo;

// Type of license required when creating/loading a session.
// Must be consistent with the values specified in the spec:
// https://w3c.github.io/encrypted-media/#idl-def-MediaKeySessionType
enum class CdmSessionType {
  kTemporary,
  kPersistentLicense,
  kMaxValue = kPersistentLicense
};

// Type of message being sent to the application.
// Must be consistent with the values specified in the spec:
// https://w3c.github.io/encrypted-media/#idl-def-MediaKeyMessageType
enum class CdmMessageType {
  LICENSE_REQUEST,
  LICENSE_RENEWAL,
  LICENSE_RELEASE,
  INDIVIDUALIZATION_REQUEST,
  MESSAGE_TYPE_MAX = INDIVIDUALIZATION_REQUEST
};

// This enum is reported to UKM. Existing values should NEVER be changed.
enum class HdcpVersion {
  kHdcpVersionNone = 0,
  kHdcpVersion1_0 = 1,
  kHdcpVersion1_1 = 2,
  kHdcpVersion1_2 = 3,
  kHdcpVersion1_3 = 4,
  kHdcpVersion1_4 = 5,
  kHdcpVersion2_0 = 6,
  kHdcpVersion2_1 = 7,
  kHdcpVersion2_2 = 8,
  kHdcpVersion2_3 = 9,
  kMaxValue = kHdcpVersion2_3
};

// Reasons for CDM session closed.
enum class CdmSessionClosedReason {
  kInternalError,  // An unrecoverable error happened in the CDM., e.g. crash.
  kClose,          // Reaction to MediaKeySession close().
  kReleaseAcknowledged,   // The CDM received a "record-of-license-destruction"
                          // acknowledgement.
  kHardwareContextReset,  // As a result of hardware context reset.
  kResourceEvicted,  // The CDM resource was evicted, e.g. by newer sessions.
  kMaxValue = kResourceEvicted
};

// An interface that represents the Content Decryption Module (CDM) in the
// Encrypted Media Extensions (EME) spec in Chromium.
// See http://w3c.github.io/encrypted-media/#cdm
//
// * Ownership
//
// This class is ref-counted. However, a ref-count should only be held by:
// - The owner of the CDM. This is usually some class in the EME stack, e.g.
//   CdmSessionAdapter in the render process, or MojoCdmService in a non-render
//   process.
// - The media player that uses the CDM, to prevent the CDM from being
//   destructed while still being used by the media player.
//
// When binding class methods into callbacks, prefer WeakPtr to using |this|
// directly to avoid having a ref-count held by the callback.
//
// * Thread Safety
//
// Most CDM operations happen on one thread. However, it is not uncommon that
// the media player lives on a different thread and may call into the CDM from
// that thread. For example, if the CDM supports a Decryptor interface, the
// Decryptor methods could be called on a different thread. The CDM
// implementation should make sure it's thread safe for these situations.
class MEDIA_EXPORT ContentDecryptionModule
    : public base::RefCountedThreadSafe<ContentDecryptionModule,
                                        ContentDecryptionModuleTraits> {
 public:
  ContentDecryptionModule(const ContentDecryptionModule&) = delete;
  ContentDecryptionModule& operator=(const ContentDecryptionModule&) = delete;

  // Provides a server certificate to be used to encrypt messages to the
  // license server.
  virtual void SetServerCertificate(
      const std::vector<uint8_t>& certificate,
      std::unique_ptr<SimpleCdmPromise> promise) = 0;

  // Gets the key status if there's a hypothetical key that requires the
  // |min_hdcp_version|. Resolve the |promise| with the key status after the
  // operation completes. Reject the |promise| if this operation is not
  // supported or unexpected error happened.
  virtual void GetStatusForPolicy(HdcpVersion min_hdcp_version,
                                  std::unique_ptr<KeyStatusCdmPromise> promise);

  // Creates a session with |session_type|. Then generates a request with the
  // |init_data_type| and |init_data|.
  // Note:
  // 1. The session ID will be provided when the |promise| is resolved.
  // 2. The generated request should be returned through a SessionMessageCB,
  //    which must be AFTER the |promise| is resolved. Otherwise, the session ID
  //    in the callback will not be recognized.
  // 3. UpdateSession(), CloseSession() and RemoveSession() should only be
  //    called after the |promise| is resolved.
  virtual void CreateSessionAndGenerateRequest(
      CdmSessionType session_type,
      EmeInitDataType init_data_type,
      const std::vector<uint8_t>& init_data,
      std::unique_ptr<NewSessionCdmPromise> promise) = 0;

  // Loads a session with the |session_id| provided. Resolves the |promise| with
  // |session_id| if the session is successfully loaded. Resolves the |promise|
  // with an empty session ID if the session cannot be found. Rejects the
  // |promise| if session loading is not supported, or other unexpected failure
  // happened.
  // Note: UpdateSession(), CloseSession() and RemoveSession() should only be
  //       called after the |promise| is resolved.
  virtual void LoadSession(CdmSessionType session_type,
                           const std::string& session_id,
                           std::unique_ptr<NewSessionCdmPromise> promise) = 0;

  // Updates a session specified by |session_id| with |response|.
  virtual void UpdateSession(const std::string& session_id,
                             const std::vector<uint8_t>& response,
                             std::unique_ptr<SimpleCdmPromise> promise) = 0;

  // Closes the session specified by |session_id|. The CDM should resolve or
  // reject the |promise| when the call has been processed. This may be before
  // the session is closed. Once the session is closed, a SessionClosedCB must
  // also be called.
  // Note that the EME spec executes the close() action asynchronously, so
  // CloseSession() may be called multiple times on the same session.
  virtual void CloseSession(const std::string& session_id,
                            std::unique_ptr<SimpleCdmPromise> promise) = 0;

  // Removes stored session data associated with the session specified by
  // |session_id|.
  virtual void RemoveSession(const std::string& session_id,
                             std::unique_ptr<SimpleCdmPromise> promise) = 0;

  // Returns the CdmContext associated with |this|. The returned CdmContext is
  // owned by |this| and the caller needs to make sure it is not used after
  // |this| is destructed. This method should never return null.
  virtual CdmContext* GetCdmContext() = 0;

  // Deletes |this| on the correct thread. By default |this| is deleted
  // immediately. Override this method if |this| needs to be deleted on a
  // specific thread.
  virtual void DeleteOnCorrectThread() const;

 protected:
  friend class base::RefCountedThreadSafe<ContentDecryptionModule,
                                          ContentDecryptionModuleTraits>;

  ContentDecryptionModule();
  virtual ~ContentDecryptionModule();
};

struct MEDIA_EXPORT ContentDecryptionModuleTraits {
  // Destroys |cdm| on the correct thread.
  static void Destruct(const ContentDecryptionModule* cdm);
};

// Try to convert `hdcp_version_string` to `HdcpVersion`. Returns std::nullopt
// on failure.
MEDIA_EXPORT std::optional<media::HdcpVersion> MaybeHdcpVersionFromString(
    const std::string& hdcp_version_string);

// CDM session event callbacks.

// Called when the CDM needs to queue a message event to the session object.
// See http://w3c.github.io/encrypted-media/#dom-evt-message
using SessionMessageCB =
    base::RepeatingCallback<void(const std::string& session_id,
                                 CdmMessageType message_type,
                                 const std::vector<uint8_t>& message)>;

// Called when the session specified by `session_id` is closed. Note that the
// CDM may close a session at any point, such as in response to a CloseSession()
// call, when the session is no longer needed, or when system resources are
// lost, as specified by `reason`.
// See http://w3c.github.io/encrypted-media/#session-closed
using SessionClosedCB =
    base::RepeatingCallback<void(const std::string& session_id,
                                 CdmSessionClosedReason reason)>;

// Called when there has been a change in the keys in the session or their
// status. See http://w3c.github.io/encrypted-media/#dom-evt-keystatuseschange
using SessionKeysChangeCB =
    base::RepeatingCallback<void(const std::string& session_id,
                                 bool has_additional_usable_key,
                                 CdmKeysInfo keys_info)>;

// Called when the CDM changes the expiration time of a session.
// See http://w3c.github.io/encrypted-media/#update-expiration
// A null base::Time() will be translated to NaN in Javascript, which means "no
// such time exists or if the license explicitly never expires, as determined
// by the CDM", according to the EME spec.
using SessionExpirationUpdateCB =
    base::RepeatingCallback<void(const std::string& session_id,
                                 base::Time new_expiry_time)>;

}  // namespace media

#endif  // MEDIA_BASE_CONTENT_DECRYPTION_MODULE_H_