gecko-dev/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp

261 lines
7.7 KiB
C++

/*
* Copyright 2015, Mozilla Foundation and contributors
*
* 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 "ClearKeyPersistence.h"
#include "ClearKeyUtils.h"
#include "ClearKeyStorage.h"
#include "ClearKeySessionManager.h"
#include "RefCounted.h"
#include <stdint.h>
#include <string.h>
#include <set>
#include <vector>
#include <sstream>
#include <assert.h>
using namespace std;
// Whether we've loaded the persistent session ids from GMPStorage yet.
enum PersistentKeyState {
UNINITIALIZED,
LOADING,
LOADED
};
static PersistentKeyState sPersistentKeyState = UNINITIALIZED;
// Set of session Ids of the persistent sessions created or residing in
// storage.
static set<uint32_t> sPersistentSessionIds;
static vector<GMPTask*> sTasksBlockedOnSessionIdLoad;
static void
ReadAllRecordsFromIterator(GMPRecordIterator* aRecordIterator,
void* aUserArg,
GMPErr aStatus)
{
assert(sPersistentKeyState == LOADING);
if (GMP_SUCCEEDED(aStatus)) {
// Extract the record names which are valid uint32_t's; they're
// the persistent session ids.
const char* name = nullptr;
uint32_t len = 0;
while (GMP_SUCCEEDED(aRecordIterator->GetName(&name, &len))) {
if (ClearKeyUtils::IsValidSessionId(name, len)) {
assert(name[len] == 0);
sPersistentSessionIds.insert(atoi(name));
}
aRecordIterator->NextRecord();
}
}
sPersistentKeyState = LOADED;
aRecordIterator->Close();
for (size_t i = 0; i < sTasksBlockedOnSessionIdLoad.size(); i++) {
sTasksBlockedOnSessionIdLoad[i]->Run();
sTasksBlockedOnSessionIdLoad[i]->Destroy();
}
sTasksBlockedOnSessionIdLoad.clear();
}
/* static */ void
ClearKeyPersistence::EnsureInitialized()
{
if (sPersistentKeyState == UNINITIALIZED) {
sPersistentKeyState = LOADING;
if (GMP_FAILED(EnumRecordNames(&ReadAllRecordsFromIterator))) {
sPersistentKeyState = LOADED;
}
}
}
/* static */ string
ClearKeyPersistence::GetNewSessionId(GMPSessionType aSessionType)
{
static uint32_t sNextSessionId = 1;
// Ensure we don't re-use a session id that was persisted.
while (Contains(sPersistentSessionIds, sNextSessionId)) {
sNextSessionId++;
}
string sessionId;
stringstream ss;
ss << sNextSessionId;
ss >> sessionId;
if (aSessionType == kGMPPersistentSession) {
sPersistentSessionIds.insert(sNextSessionId);
}
sNextSessionId++;
return sessionId;
}
class CreateSessionTask : public GMPTask {
public:
CreateSessionTask(ClearKeySessionManager* aTarget,
uint32_t aCreateSessionToken,
uint32_t aPromiseId,
const string& aInitDataType,
const uint8_t* aInitData,
uint32_t aInitDataSize,
GMPSessionType aSessionType)
: mTarget(aTarget)
, mCreateSessionToken(aCreateSessionToken)
, mPromiseId(aPromiseId)
, mInitDataType(aInitDataType)
, mSessionType(aSessionType)
{
mInitData.insert(mInitData.end(),
aInitData,
aInitData + aInitDataSize);
}
virtual void Run() override {
mTarget->CreateSession(mCreateSessionToken,
mPromiseId,
mInitDataType.c_str(),
mInitDataType.size(),
&mInitData.front(),
mInitData.size(),
mSessionType);
}
virtual void Destroy() override {
delete this;
}
private:
RefPtr<ClearKeySessionManager> mTarget;
uint32_t mCreateSessionToken;
uint32_t mPromiseId;
const string mInitDataType;
vector<uint8_t> mInitData;
GMPSessionType mSessionType;
};
/* static */ bool
ClearKeyPersistence::DeferCreateSessionIfNotReady(ClearKeySessionManager* aInstance,
uint32_t aCreateSessionToken,
uint32_t aPromiseId,
const string& aInitDataType,
const uint8_t* aInitData,
uint32_t aInitDataSize,
GMPSessionType aSessionType)
{
if (sPersistentKeyState >= LOADED) {
return false;
}
GMPTask* t = new CreateSessionTask(aInstance,
aCreateSessionToken,
aPromiseId,
aInitDataType,
aInitData,
aInitDataSize,
aSessionType);
sTasksBlockedOnSessionIdLoad.push_back(t);
return true;
}
class LoadSessionTask : public GMPTask {
public:
LoadSessionTask(ClearKeySessionManager* aTarget,
uint32_t aPromiseId,
const char* aSessionId,
uint32_t aSessionIdLength)
: mTarget(aTarget)
, mPromiseId(aPromiseId)
, mSessionId(aSessionId, aSessionId + aSessionIdLength)
{
}
virtual void Run() override {
mTarget->LoadSession(mPromiseId,
mSessionId.c_str(),
mSessionId.size());
}
virtual void Destroy() override {
delete this;
}
private:
RefPtr<ClearKeySessionManager> mTarget;
uint32_t mPromiseId;
string mSessionId;
};
/* static */ bool
ClearKeyPersistence::DeferLoadSessionIfNotReady(ClearKeySessionManager* aInstance,
uint32_t aPromiseId,
const char* aSessionId,
uint32_t aSessionIdLength)
{
if (sPersistentKeyState >= LOADED) {
return false;
}
GMPTask* t = new LoadSessionTask(aInstance,
aPromiseId,
aSessionId,
aSessionIdLength);
sTasksBlockedOnSessionIdLoad.push_back(t);
return true;
}
/* static */ bool
ClearKeyPersistence::IsPersistentSessionId(const string& aSessionId)
{
return Contains(sPersistentSessionIds, atoi(aSessionId.c_str()));
}
class LoadSessionFromKeysTask : public ReadContinuation {
public:
LoadSessionFromKeysTask(ClearKeySessionManager* aTarget,
const string& aSessionId,
uint32_t aPromiseId)
: mTarget(aTarget)
, mSessionId(aSessionId)
, mPromiseId(aPromiseId)
{
}
virtual void ReadComplete(GMPErr aStatus,
const uint8_t* aData,
uint32_t aLength) override
{
mTarget->PersistentSessionDataLoaded(aStatus, mPromiseId, mSessionId, aData, aLength);
}
private:
RefPtr<ClearKeySessionManager> mTarget;
string mSessionId;
uint32_t mPromiseId;
};
/* static */ void
ClearKeyPersistence::LoadSessionData(ClearKeySessionManager* aInstance,
const string& aSid,
uint32_t aPromiseId)
{
LoadSessionFromKeysTask* loadTask =
new LoadSessionFromKeysTask(aInstance, aSid, aPromiseId);
ReadData(aSid, loadTask);
}
/* static */ void
ClearKeyPersistence::PersistentSessionRemoved(const string& aSessionId)
{
sPersistentSessionIds.erase(atoi(aSessionId.c_str()));
}