blob: bd41763da2bd6b89f5ec5bb9ded607093fad54d9 [file] [log] [blame]
// Copyright 2017 Google Inc. 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.
#include "cobalt/media/base/drm_system.h"
#include "base/bind.h"
#include "base/logging.h"
namespace cobalt {
namespace media {
DrmSystem::Session::Session(DrmSystem* drm_system)
: drm_system_(drm_system), closed_(false) {}
DrmSystem::Session::~Session() {
if (id_ && !closed_) {
// Auto-closing semantics is derived from EME spec.
//
// If a MediaKeySession object is not closed when it becomes inaccessible
// to the page, the CDM shall close the key session associated with
// the object.
// https://www.w3.org/TR/encrypted-media/#mediakeysession-interface
Close();
}
}
void DrmSystem::Session::GenerateUpdateRequest(
const std::string& type, const uint8* init_data, int init_data_length,
const SessionUpdateRequestGeneratedCallback&
session_update_request_generated_callback,
const SessionUpdateRequestDidNotGenerateCallback&
session_update_request_did_not_generate_callback) {
update_request_generated_callback_ =
session_update_request_generated_callback;
drm_system_->GenerateSessionUpdateRequest(
this, type, init_data, init_data_length,
session_update_request_generated_callback,
session_update_request_did_not_generate_callback);
}
void DrmSystem::Session::Update(
const uint8* key, int key_length,
const SessionUpdatedCallback& session_updated_callback,
const SessionDidNotUpdateCallback& session_did_not_update_callback) {
drm_system_->UpdateSession(*id_, key, key_length, session_updated_callback,
session_did_not_update_callback);
}
void DrmSystem::Session::Close() {
drm_system_->CloseSession(*id_);
closed_ = true;
}
DrmSystem::DrmSystem(const char* key_system)
: wrapped_drm_system_(SbDrmCreateSystem(key_system, this,
OnSessionUpdateRequestGeneratedFunc,
OnSessionUpdatedFunc)),
message_loop_(MessageLoop::current()),
ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
weak_this_(weak_ptr_factory_.GetWeakPtr()),
next_session_update_request_ticket_(0),
next_session_update_ticket_(0) {
DCHECK_NE(kSbDrmSystemInvalid, wrapped_drm_system_);
}
DrmSystem::~DrmSystem() { SbDrmDestroySystem(wrapped_drm_system_); }
scoped_ptr<DrmSystem::Session> DrmSystem::CreateSession() {
return make_scoped_ptr(new Session(this));
}
void DrmSystem::GenerateSessionUpdateRequest(
Session* session, const std::string& type, const uint8_t* init_data,
int init_data_length, const SessionUpdateRequestGeneratedCallback&
session_update_request_generated_callback,
const SessionUpdateRequestDidNotGenerateCallback&
session_update_request_did_not_generate_callback) {
// Store the context of the call.
SessionUpdateRequest session_update_request;
session_update_request.session = session;
session_update_request.generated_callback =
session_update_request_generated_callback;
session_update_request.did_not_generate_callback =
session_update_request_did_not_generate_callback;
int ticket = next_session_update_request_ticket_++;
ticket_to_session_update_request_map_.insert(
std::make_pair(ticket, session_update_request));
SbDrmGenerateSessionUpdateRequest(wrapped_drm_system_, ticket, type.c_str(),
init_data, init_data_length);
}
void DrmSystem::UpdateSession(
const std::string& session_id, const uint8_t* key, int key_length,
const SessionUpdatedCallback& session_updated_callback,
const SessionDidNotUpdateCallback& session_did_not_update_callback) {
// Store the context of the call.
SessionUpdate session_update;
session_update.updated_callback = session_updated_callback;
session_update.did_not_update_callback = session_did_not_update_callback;
int ticket = next_session_update_ticket_++;
ticket_to_session_update_map_.insert(std::make_pair(ticket, session_update));
SbDrmUpdateSession(wrapped_drm_system_, ticket, key, key_length,
session_id.c_str(), session_id.size());
}
void DrmSystem::CloseSession(const std::string& session_id) {
id_to_session_map_.erase(session_id);
SbDrmCloseSession(wrapped_drm_system_, session_id.c_str(), session_id.size());
}
void DrmSystem::OnSessionUpdateRequestGenerated(
int ticket, const base::optional<std::string>& session_id,
scoped_array<uint8> message, int message_size) {
if (SbDrmTicketIsValid(ticket)) {
// Called back as a result of |SbDrmGenerateSessionUpdateRequest|.
// Restore the context of |GenerateSessionUpdateRequest|.
TicketToSessionUpdateRequestMap::iterator session_update_request_iterator =
ticket_to_session_update_request_map_.find(ticket);
if (session_update_request_iterator ==
ticket_to_session_update_request_map_.end()) {
LOG(ERROR) << "Unknown session update request ticket: " << ticket << ".";
return;
}
const SessionUpdateRequest& session_update_request =
session_update_request_iterator->second;
// Interpret the result.
if (session_id) {
// Successful request generation.
// Enable session lookup by id which is used by spontaneous callbacks.
session_update_request.session->set_id(*session_id);
id_to_session_map_.insert(
std::make_pair(*session_id, session_update_request.session));
session_update_request.generated_callback.Run(message.Pass(),
message_size);
} else {
// Failure during request generation.
session_update_request.did_not_generate_callback.Run();
}
// Sweep the context of |GenerateSessionUpdateRequest|.
ticket_to_session_update_request_map_.erase(
session_update_request_iterator);
} else {
// Called back spontaneously by the underlying DRM system.
// Spontaneous calls must refer to a valid session.
if (!session_id) {
DLOG(FATAL) << "SbDrmSessionUpdateRequestFunc() should not be called "
"with both invalid ticket and null session id.";
return;
}
// Find the session by ID.
IdToSessionMap::iterator session_iterator =
id_to_session_map_.find(*session_id);
if (session_iterator == id_to_session_map_.end()) {
LOG(ERROR) << "Unknown session id: " << *session_id << ".";
return;
}
Session* session = session_iterator->second;
session->update_request_generated_callback().Run(message.Pass(),
message_size);
}
}
void DrmSystem::OnSessionUpdated(int ticket, bool succeeded) {
// Restore the context of |UpdateSession|.
TicketToSessionUpdateMap::iterator session_update_iterator =
ticket_to_session_update_map_.find(ticket);
if (session_update_iterator == ticket_to_session_update_map_.end()) {
LOG(ERROR) << "Unknown session update ticket: " << ticket << ".";
return;
}
const SessionUpdate& session_update = session_update_iterator->second;
// Interpret the result.
if (succeeded) {
session_update.updated_callback.Run();
} else {
session_update.did_not_update_callback.Run();
}
// Sweep the context of |UpdateSession|.
ticket_to_session_update_map_.erase(session_update_iterator);
}
// static
void DrmSystem::OnSessionUpdateRequestGeneratedFunc(
SbDrmSystem wrapped_drm_system, void* context, int ticket,
const void* session_id, int session_id_size, const void* content,
int content_size, const char* url) {
DCHECK(context);
DrmSystem* drm_system = static_cast<DrmSystem*>(context);
DCHECK_EQ(wrapped_drm_system, drm_system->wrapped_drm_system_);
base::optional<std::string> session_id_copy;
scoped_array<uint8> content_copy;
if (session_id) {
session_id_copy =
std::string(static_cast<const char*>(session_id),
static_cast<const char*>(session_id) + session_id_size);
content_copy.reset(new uint8[content_size]);
SbMemoryCopy(content_copy.get(), content, content_size);
}
drm_system->message_loop_->PostTask(
FROM_HERE, base::Bind(&DrmSystem::OnSessionUpdateRequestGenerated,
drm_system->weak_this_, ticket, session_id_copy,
base::Passed(&content_copy), content_size));
}
// static
void DrmSystem::OnSessionUpdatedFunc(SbDrmSystem wrapped_drm_system,
void* context, int ticket,
const void* /*session_id*/,
int /*session_id_size*/, bool succeeded) {
DCHECK(context);
DrmSystem* drm_system = static_cast<DrmSystem*>(context);
DCHECK_EQ(wrapped_drm_system, drm_system->wrapped_drm_system_);
drm_system->message_loop_->PostTask(
FROM_HERE, base::Bind(&DrmSystem::OnSessionUpdated,
drm_system->weak_this_, ticket, succeeded));
}
} // namespace media
} // namespace cobalt