blob: fb7bce7c5222bdcebf1fb6ac004faab72ad93f9c [file] [log] [blame]
// Copyright 2018 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef STARBOARD_SHARED_WIDEVINE_DRM_SYSTEM_WIDEVINE_H_
#define STARBOARD_SHARED_WIDEVINE_DRM_SYSTEM_WIDEVINE_H_
#include <limits>
#include <map>
#include <string>
#include <vector>
#include "starboard/atomic.h"
#include "starboard/common/optional.h"
#include "starboard/common/scoped_ptr.h"
#include "starboard/mutex.h"
#include "starboard/shared/starboard/drm/drm_system_internal.h"
#include "starboard/shared/starboard/thread_checker.h"
#include "starboard/thread.h"
#include "third_party/ce_cdm/cdm/include/cdm.h"
namespace starboard {
namespace shared {
namespace widevine {
// Adapts Widevine's |Content Decryption Module v 3.5| to Starboard's
// |SbDrmSystem|.
//
// All |SbDrmSystemPrivate| methods except Decrypt() must be called from the
// constructor thread.
class DrmSystemWidevine : public SbDrmSystemPrivate,
private ::widevine::Cdm::IEventListener {
public:
DrmSystemWidevine(
void* context,
SbDrmSessionUpdateRequestFunc update_request_callback,
SbDrmSessionUpdatedFunc session_updated_callback,
SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback
#if SB_API_VERSION >= 10
,
SbDrmServerCertificateUpdatedFunc server_certificate_updated_callback
#endif // SB_API_VERSION >= 10
#if SB_HAS(DRM_SESSION_CLOSED)
,
SbDrmSessionClosedFunc session_closed_callback
#endif // SB_HAS(DRM_SESSION_CLOSED)
,
const std::string& company_name,
const std::string& model_name);
~DrmSystemWidevine() override;
static bool IsKeySystemSupported(const char* key_system);
static bool IsDrmSystemWidevine(SbDrmSystem drm_system);
// From |SbDrmSystemPrivate|.
void GenerateSessionUpdateRequest(int ticket,
const char* type,
const void* initialization_data,
int initialization_data_size) override;
void UpdateSession(int ticket,
const void* key,
int key_size,
const void* sb_drm_session_id,
int sb_drm_session_id_size) override;
void CloseSession(const void* sb_drm_session_id,
int sb_drm_session_id_size) override;
DecryptStatus Decrypt(InputBuffer* buffer) override;
#if SB_API_VERSION >= 10
bool IsServerCertificateUpdatable() override { return true; }
// This function is called by the app to explicitly set the server
// certificate. For an app that supports this feature, it should call this
// function before calling any other functions like
// GenerateSessionUpdateRequest(). So we needn't process pending requests in
// this function. Note that it is benign if this function is called in
// parallel with a server certificate request.
void UpdateServerCertificate(int ticket,
const void* certificate,
int certificate_size) override;
#endif // SB_API_VERSION >= 10
private:
// Stores the data necessary to call GenerateSessionUpdateRequestInternal().
struct GenerateSessionUpdateRequestData {
int ticket;
::widevine::Cdm::InitDataType init_data_type;
std::string initialization_data;
};
// An unique id to identify the first SbDrm session id when server
// certificate is not ready. This is necessary to send the server
// certificate request, as EME requires a session id while wvcdm cannot
// generate a session id before having a server certificate.
// This works with |first_wvcdm_session_id_| to map the first session id
// between wvcdm and SbDrm.
static const char kFirstSbDrmSessionId[];
void GenerateSessionUpdateRequestInternal(
int ticket,
::widevine::Cdm::InitDataType init_data_type,
const std::string& initialization_data,
bool is_first_session);
// From |cdm::IEventListener|.
// A message (license request, renewal, etc.) to be dispatched to the
// application's license server. The response, if successful, should be
// provided back to the CDM via a call to Cdm::update().
void onMessage(const std::string& session_id,
::widevine::Cdm::MessageType message_type,
const std::string& message) override;
// There has been a change in the keys in the session or their status.
void onKeyStatusesChange(const std::string& wvcdm_session_id) override;
// A remove() operation has been completed.
void onRemoveComplete(const std::string& wvcdm_session_id) override;
// Called when a deferred action has completed.
void onDeferredComplete(const std::string& wvcdm_session_id,
::widevine::Cdm::Status result) override;
// Called when the CDM requires a new device certificate.
void onDirectIndividualizationRequest(const std::string& wvcdm_session_id,
const std::string& request) override;
void SetTicket(const std::string& sb_drm_session_id, int ticket);
int GetAndResetTicket(const std::string& sb_drm_session_id);
std::string WvdmSessionIdToSbDrmSessionId(
const std::string& wvcdm_session_id);
bool SbDrmSessionIdToWvdmSessionId(const void* sb_drm_session_id,
int sb_drm_session_id_size,
std::string* wvcdm_session_id);
// Generates a special key message to ask for the server certificate. When
// the license server receives the request, it will send back the server
// certificate.
void SendServerCertificateRequest(int ticket);
// When this function is called, the update contains the server certificate.
// The function parses the special update and pass the server certificate to
// the cdm.
// Note that the app shouldn't persist the server certificate across playback
// or across application instances.
::widevine::Cdm::Status ProcessServerCertificateResponse(
const std::string& response);
// If server certificate has been set, send all pending requests.
void TrySendPendingGenerateSessionUpdateRequests();
void SendSessionUpdateRequest(SbDrmSessionRequestType type,
const std::string& sb_drm_session_id,
const std::string& message);
::starboard::shared::starboard::ThreadChecker thread_checker_;
void* const context_;
const SbDrmSessionUpdateRequestFunc session_update_request_callback_;
const SbDrmSessionUpdatedFunc session_updated_callback_;
const SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback_;
#if SB_API_VERSION >= 10
const SbDrmServerCertificateUpdatedFunc server_certificate_updated_callback_;
#endif // SB_API_VERSION >= 10
#if SB_HAS(DRM_SESSION_CLOSED)
const SbDrmSessionClosedFunc session_closed_callback_;
#endif // SB_HAS(DRM_SESSION_CLOSED)
// Store a map from session id generated by the cdm to its associated ticket
// id. The ticket is a unique id passed to GenerateSessionUpdateRequest() to
// allow the caller of GenerateSessionUpdateRequest() to associate the
// session id with the session related data specified by the ticket, as both
// of them will be passed via session_update_request_callback_ when it is
// called for the first time for this paritcular session id. As this is only
// necessary for the first time the callback is called on the particular
// session, every time an entry is used, it will be removed from the map.
// Note that the first callback is always accessed on the thread specificed
// by |ticket_thread_id_|.
std::map<std::string, int> sb_drm_session_id_to_ticket_map_;
// |ticket_| is only valid on the constructor thread within the duration of
// call to |GenerateKeyRequest| or |AddKey|, but CDM may invoke host's methods
// spontaneously from the timer thread. In that case |GetTicket| need to
// return |kSbDrmTicketInvalid|.
const SbThreadId ticket_thread_id_;
std::vector<GenerateSessionUpdateRequestData>
pending_generate_session_update_requests_;
std::string first_wvcdm_session_id_;
scoped_ptr<::widevine::Cdm> cdm_;
#if SB_API_VERSION >= 10
bool is_server_certificate_set_ = false;
#else // SB_API_VERSION >= 10
bool is_server_certificate_set_ = true;
#endif // SB_API_VERSION >= 10
volatile bool quitting_ = false;
Mutex unblock_key_retry_mutex_;
optional<SbTimeMonotonic> unblock_key_retry_start_time_;
#if !defined(COBALT_BUILD_TYPE_GOLD)
int number_of_session_updates_sent_ = 0;
int maximum_number_of_session_updates_ = std::numeric_limits<int>::max();
#endif // !defined(COBALT_BUILD_TYPE_GOLD)
atomic_bool first_update_session_received_{false};
};
} // namespace widevine
} // namespace shared
} // namespace starboard
#endif // STARBOARD_SHARED_WIDEVINE_DRM_SYSTEM_WIDEVINE_H_