diff options
Diffstat (limited to 'media/gmp-clearkey/0.1/ClearKeySessionManager.cpp')
-rw-r--r-- | media/gmp-clearkey/0.1/ClearKeySessionManager.cpp | 713 |
1 files changed, 713 insertions, 0 deletions
diff --git a/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp b/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp new file mode 100644 index 0000000000..54a15f4fcf --- /dev/null +++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp @@ -0,0 +1,713 @@ +/* + * 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 "ClearKeySessionManager.h" + +#include <assert.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include "content_decryption_module.h" + +#include "ClearKeyDecryptionManager.h" +#include "ClearKeyPersistence.h" +#include "ClearKeyStorage.h" +#include "ClearKeyUtils.h" +#include "psshparser/PsshParser.h" + +using namespace cdm; + +using std::function; +using std::string; +using std::vector; + +ClearKeySessionManager::ClearKeySessionManager(Host_10* aHost) + : mDecryptionManager(ClearKeyDecryptionManager::Get()) { + CK_LOGD("ClearKeySessionManager ctor %p", this); + AddRef(); + + mHost = aHost; + mPersistence = new ClearKeyPersistence(mHost); +} + +ClearKeySessionManager::~ClearKeySessionManager() { + CK_LOGD("ClearKeySessionManager dtor %p", this); +} + +void ClearKeySessionManager::Init(bool aDistinctiveIdentifierAllowed, + bool aPersistentStateAllowed) { + CK_LOGD("ClearKeySessionManager::Init"); + + RefPtr<ClearKeySessionManager> self(this); + function<void()> onPersistentStateLoaded = [self]() { + while (!self->mDeferredInitialize.empty()) { + function<void()> func = self->mDeferredInitialize.front(); + self->mDeferredInitialize.pop(); + + func(); + } + if (self->mHost) { + // The session manager should be the last thing the ClearKey CDM is + // waiting on to be initialized. + self->mHost->OnInitialized(true); + } + }; + + mPersistence->EnsureInitialized(aPersistentStateAllowed, + std::move(onPersistentStateLoaded)); +} + +void ClearKeySessionManager::CreateSession(uint32_t aPromiseId, + InitDataType aInitDataType, + const uint8_t* aInitData, + uint32_t aInitDataSize, + SessionType aSessionType) { + CK_LOGD("ClearKeySessionManager::CreateSession type:%u", aInitDataType); + + // Copy the init data so it is correctly captured by the lambda + vector<uint8_t> initData(aInitData, aInitData + aInitDataSize); + + RefPtr<ClearKeySessionManager> self(this); + function<void()> deferrer = [self, aPromiseId, aInitDataType, initData, + aSessionType]() { + self->CreateSession(aPromiseId, aInitDataType, initData.data(), + initData.size(), aSessionType); + }; + + // If we haven't loaded, don't do this yet + if (MaybeDeferTillInitialized(std::move(deferrer))) { + CK_LOGD("Deferring CreateSession"); + return; + } + + CK_LOGARRAY("ClearKeySessionManager::CreateSession initdata: ", aInitData, + aInitDataSize); + + // If 'DecryptingComplete' has been called mHost will be null so we can't + // won't be able to resolve our promise + if (!mHost) { + CK_LOGD("ClearKeySessionManager::CreateSession: mHost is nullptr"); + return; + } + + // initDataType must be "cenc", "keyids", or "webm". + if (aInitDataType != InitDataType::kCenc && + aInitDataType != InitDataType::kKeyIds && + aInitDataType != InitDataType::kWebM) { + string message = "initDataType is not supported by ClearKey"; + mHost->OnRejectPromise(aPromiseId, Exception::kExceptionNotSupportedError, + 0, message.c_str(), message.size()); + + return; + } + + string sessionId = mPersistence->GetNewSessionId(aSessionType); + assert(mSessions.find(sessionId) == mSessions.end()); + + ClearKeySession* session = new ClearKeySession(sessionId, aSessionType); + + if (!session->Init(aInitDataType, aInitData, aInitDataSize)) { + CK_LOGD("Failed to initialize session: %s", sessionId.c_str()); + + const static char* message = "Failed to initialize session"; + mHost->OnRejectPromise(aPromiseId, Exception::kExceptionInvalidStateError, + 0, message, strlen(message)); + delete session; + + return; + } + + mSessions[sessionId] = session; + mLastSessionId = sessionId; + + const vector<KeyId>& sessionKeys = session->GetKeyIds(); + vector<KeyId> neededKeys; + + for (auto it = sessionKeys.begin(); it != sessionKeys.end(); it++) { + // Need to request this key ID from the client. We always send a key + // request, whether or not another session has sent a request with the same + // key ID. Otherwise a script can end up waiting for another script to + // respond to the request (which may not necessarily happen). + neededKeys.push_back(*it); + mDecryptionManager->ExpectKeyId(*it); + } + + if (neededKeys.empty()) { + CK_LOGD("No keys needed from client."); + return; + } + + // Send a request for needed key data. + string request; + ClearKeyUtils::MakeKeyRequest(neededKeys, request, aSessionType); + + // Resolve the promise with the new session information. + mHost->OnResolveNewSessionPromise(aPromiseId, sessionId.c_str(), + sessionId.size()); + + mHost->OnSessionMessage(sessionId.c_str(), sessionId.size(), + MessageType::kLicenseRequest, request.c_str(), + request.size()); +} + +void ClearKeySessionManager::LoadSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength) { + CK_LOGD("ClearKeySessionManager::LoadSession"); + + // Copy the sessionId into a string so the lambda captures it properly. + string sessionId(aSessionId, aSessionId + aSessionIdLength); + + // Hold a reference to the SessionManager so that it isn't released before + // we try to use it. + RefPtr<ClearKeySessionManager> self(this); + function<void()> deferrer = [self, aPromiseId, sessionId]() { + self->LoadSession(aPromiseId, sessionId.data(), sessionId.size()); + }; + + if (MaybeDeferTillInitialized(std::move(deferrer))) { + CK_LOGD("Deferring LoadSession"); + return; + } + + // If the SessionManager has been shutdown mHost will be null and we won't + // be able to resolve the promise. + if (!mHost) { + return; + } + + if (!ClearKeyUtils::IsValidSessionId(aSessionId, aSessionIdLength)) { + mHost->OnResolveNewSessionPromise(aPromiseId, nullptr, 0); + return; + } + + if (!mPersistence->IsPersistentSessionId(sessionId)) { + mHost->OnResolveNewSessionPromise(aPromiseId, nullptr, 0); + return; + } + + function<void(const uint8_t*, uint32_t)> success = + [self, sessionId, aPromiseId](const uint8_t* data, uint32_t size) { + self->PersistentSessionDataLoaded(aPromiseId, sessionId, data, size); + }; + + function<void()> failure = [self, aPromiseId] { + if (!self->mHost) { + return; + } + // As per the API described in ContentDecryptionModule_8 + self->mHost->OnResolveNewSessionPromise(aPromiseId, nullptr, 0); + }; + + ReadData(mHost, sessionId, std::move(success), std::move(failure)); +} + +void ClearKeySessionManager::PersistentSessionDataLoaded( + uint32_t aPromiseId, const string& aSessionId, const uint8_t* aKeyData, + uint32_t aKeyDataSize) { + CK_LOGD("ClearKeySessionManager::PersistentSessionDataLoaded"); + + // Check that the SessionManager has not been shut down before we try and + // resolve any promises. + if (!mHost) { + return; + } + + if (Contains(mSessions, aSessionId) || + (aKeyDataSize % (2 * CENC_KEY_LEN)) != 0) { + // As per the instructions in ContentDecryptionModule_8 + mHost->OnResolveNewSessionPromise(aPromiseId, nullptr, 0); + return; + } + + ClearKeySession* session = + new ClearKeySession(aSessionId, SessionType::kPersistentLicense); + + mSessions[aSessionId] = session; + mLastSessionId = aSessionId; + + uint32_t numKeys = aKeyDataSize / (2 * CENC_KEY_LEN); + + vector<KeyInformation> keyInfos; + vector<KeyIdPair> keyPairs; + for (uint32_t i = 0; i < numKeys; i++) { + const uint8_t* base = aKeyData + 2 * CENC_KEY_LEN * i; + + KeyIdPair keyPair; + + keyPair.mKeyId = KeyId(base, base + CENC_KEY_LEN); + assert(keyPair.mKeyId.size() == CENC_KEY_LEN); + + keyPair.mKey = Key(base + CENC_KEY_LEN, base + 2 * CENC_KEY_LEN); + assert(keyPair.mKey.size() == CENC_KEY_LEN); + + session->AddKeyId(keyPair.mKeyId); + + mDecryptionManager->ExpectKeyId(keyPair.mKeyId); + mDecryptionManager->InitKey(keyPair.mKeyId, keyPair.mKey); + mKeyIds.insert(keyPair.mKey); + keyPairs.push_back(keyPair); + + KeyInformation keyInfo = {}; + keyInfo.key_id = &keyPairs.back().mKeyId[0]; + keyInfo.key_id_size = keyPair.mKeyId.size(); + keyInfo.status = KeyStatus::kUsable; + + keyInfos.push_back(keyInfo); + } + + mHost->OnSessionKeysChange(&aSessionId[0], aSessionId.size(), true, + keyInfos.data(), keyInfos.size()); + + mHost->OnResolveNewSessionPromise(aPromiseId, aSessionId.c_str(), + aSessionId.size()); +} + +void ClearKeySessionManager::UpdateSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength, + const uint8_t* aResponse, + uint32_t aResponseSize) { + CK_LOGD("ClearKeySessionManager::UpdateSession"); + + // Copy the method arguments so we can capture them in the lambda + string sessionId(aSessionId, aSessionId + aSessionIdLength); + vector<uint8_t> response(aResponse, aResponse + aResponseSize); + + // Hold a reference to the SessionManager so it isn't released before we + // callback. + RefPtr<ClearKeySessionManager> self(this); + function<void()> deferrer = [self, aPromiseId, sessionId, response]() { + self->UpdateSession(aPromiseId, sessionId.data(), sessionId.size(), + response.data(), response.size()); + }; + + // If we haven't fully loaded, defer calling this method + if (MaybeDeferTillInitialized(std::move(deferrer))) { + CK_LOGD("Deferring LoadSession"); + return; + } + + // Make sure the SessionManager has not been shutdown before we try and + // resolve any promises. + if (!mHost) { + return; + } + + CK_LOGD("Updating session: %s", sessionId.c_str()); + + auto itr = mSessions.find(sessionId); + if (itr == mSessions.end() || !(itr->second)) { + CK_LOGW("ClearKey CDM couldn't resolve session ID in UpdateSession."); + CK_LOGD("Unable to find session: %s", sessionId.c_str()); + mHost->OnRejectPromise(aPromiseId, Exception::kExceptionTypeError, 0, + nullptr, 0); + + return; + } + ClearKeySession* session = itr->second; + + // Verify the size of session response. + if (aResponseSize >= kMaxSessionResponseLength) { + CK_LOGW("Session response size is not within a reasonable size."); + CK_LOGD("Failed to parse response for session %s", sessionId.c_str()); + + mHost->OnRejectPromise(aPromiseId, Exception::kExceptionTypeError, 0, + nullptr, 0); + + return; + } + + // Parse the response for any (key ID, key) pairs. + vector<KeyIdPair> keyPairs; + if (!ClearKeyUtils::ParseJWK(aResponse, aResponseSize, keyPairs, + session->Type())) { + CK_LOGW("ClearKey CDM failed to parse JSON Web Key."); + + mHost->OnRejectPromise(aPromiseId, Exception::kExceptionTypeError, 0, + nullptr, 0); + + return; + } + + vector<KeyInformation> keyInfos; + for (size_t i = 0; i < keyPairs.size(); i++) { + KeyIdPair& keyPair = keyPairs[i]; + mDecryptionManager->InitKey(keyPair.mKeyId, keyPair.mKey); + mKeyIds.insert(keyPair.mKeyId); + + KeyInformation keyInfo = {}; + keyInfo.key_id = &keyPair.mKeyId[0]; + keyInfo.key_id_size = keyPair.mKeyId.size(); + keyInfo.status = KeyStatus::kUsable; + + keyInfos.push_back(keyInfo); + } + + mHost->OnSessionKeysChange(aSessionId, aSessionIdLength, true, + keyInfos.data(), keyInfos.size()); + + if (session->Type() != SessionType::kPersistentLicense) { + mHost->OnResolvePromise(aPromiseId); + return; + } + + // Store the keys on disk. We store a record whose name is the sessionId, + // and simply append each keyId followed by its key. + vector<uint8_t> keydata; + Serialize(session, keydata); + + function<void()> resolve = [self, aPromiseId]() { + if (!self->mHost) { + return; + } + self->mHost->OnResolvePromise(aPromiseId); + }; + + function<void()> reject = [self, aPromiseId]() { + if (!self->mHost) { + return; + } + + static const char* message = "Couldn't store cenc key init data"; + self->mHost->OnRejectPromise(aPromiseId, + Exception::kExceptionInvalidStateError, 0, + message, strlen(message)); + }; + + WriteData(mHost, sessionId, keydata, std::move(resolve), std::move(reject)); +} + +void ClearKeySessionManager::Serialize(const ClearKeySession* aSession, + std::vector<uint8_t>& aOutKeyData) { + const std::vector<KeyId>& keyIds = aSession->GetKeyIds(); + for (size_t i = 0; i < keyIds.size(); i++) { + const KeyId& keyId = keyIds[i]; + if (!mDecryptionManager->HasKeyForKeyId(keyId)) { + continue; + } + assert(keyId.size() == CENC_KEY_LEN); + aOutKeyData.insert(aOutKeyData.end(), keyId.begin(), keyId.end()); + const Key& key = mDecryptionManager->GetDecryptionKey(keyId); + assert(key.size() == CENC_KEY_LEN); + aOutKeyData.insert(aOutKeyData.end(), key.begin(), key.end()); + } +} + +void ClearKeySessionManager::CloseSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength) { + CK_LOGD("ClearKeySessionManager::CloseSession"); + + // Copy the sessionId into a string so we capture it properly. + string sessionId(aSessionId, aSessionId + aSessionIdLength); + // Hold a reference to the session manager, so it doesn't get deleted + // before we need to use it. + RefPtr<ClearKeySessionManager> self(this); + function<void()> deferrer = [self, aPromiseId, sessionId]() { + self->CloseSession(aPromiseId, sessionId.data(), sessionId.size()); + }; + + // If we haven't loaded, call this method later. + if (MaybeDeferTillInitialized(std::move(deferrer))) { + CK_LOGD("Deferring CloseSession"); + return; + } + + // If DecryptingComplete has been called mHost will be null and we won't + // be able to resolve our promise. + if (!mHost) { + return; + } + + auto itr = mSessions.find(sessionId); + if (itr == mSessions.end()) { + CK_LOGW("ClearKey CDM couldn't close non-existent session."); + mHost->OnRejectPromise(aPromiseId, Exception::kExceptionTypeError, 0, + nullptr, 0); + + return; + } + + ClearKeySession* session = itr->second; + assert(session); + + ClearInMemorySessionData(session); + + mHost->OnSessionClosed(aSessionId, aSessionIdLength); + mHost->OnResolvePromise(aPromiseId); +} + +void ClearKeySessionManager::ClearInMemorySessionData( + ClearKeySession* aSession) { + mSessions.erase(aSession->Id()); + delete aSession; +} + +void ClearKeySessionManager::RemoveSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength) { + CK_LOGD("ClearKeySessionManager::RemoveSession"); + + // Copy the sessionId into a string so it can be captured for the lambda. + string sessionId(aSessionId, aSessionId + aSessionIdLength); + + // Hold a reference to the SessionManager, so it isn't released before we + // try and use it. + RefPtr<ClearKeySessionManager> self(this); + function<void()> deferrer = [self, aPromiseId, sessionId]() { + self->RemoveSession(aPromiseId, sessionId.data(), sessionId.size()); + }; + + // If we haven't fully loaded, defer calling this method. + if (MaybeDeferTillInitialized(std::move(deferrer))) { + CK_LOGD("Deferring RemoveSession"); + return; + } + + // Check that the SessionManager has not been shutdown before we try and + // resolve any promises. + if (!mHost) { + return; + } + + auto itr = mSessions.find(sessionId); + if (itr == mSessions.end()) { + CK_LOGW("ClearKey CDM couldn't remove non-existent session."); + + mHost->OnRejectPromise(aPromiseId, Exception::kExceptionTypeError, 0, + nullptr, 0); + + return; + } + + ClearKeySession* session = itr->second; + assert(session); + string sid = session->Id(); + bool isPersistent = session->Type() == SessionType::kPersistentLicense; + ClearInMemorySessionData(session); + + if (!isPersistent) { + mHost->OnResolvePromise(aPromiseId); + return; + } + + mPersistence->PersistentSessionRemoved(sid); + + vector<uint8_t> emptyKeydata; + + function<void()> resolve = [self, aPromiseId]() { + if (!self->mHost) { + return; + } + self->mHost->OnResolvePromise(aPromiseId); + }; + + function<void()> reject = [self, aPromiseId]() { + if (!self->mHost) { + return; + } + static const char* message = "Could not remove session"; + self->mHost->OnRejectPromise(aPromiseId, Exception::kExceptionTypeError, 0, + message, strlen(message)); + }; + + WriteData(mHost, sessionId, emptyKeydata, std::move(resolve), + std::move(reject)); +} + +void ClearKeySessionManager::SetServerCertificate(uint32_t aPromiseId, + const uint8_t* aServerCert, + uint32_t aServerCertSize) { + // ClearKey CDM doesn't support this method by spec. + CK_LOGD("ClearKeySessionManager::SetServerCertificate"); + mHost->OnRejectPromise(aPromiseId, Exception::kExceptionNotSupportedError, 0, + nullptr /* message */, 0 /* messageLen */); +} + +Status ClearKeySessionManager::Decrypt(const InputBuffer_2& aBuffer, + DecryptedBlock* aDecryptedBlock) { + CK_LOGD("ClearKeySessionManager::Decrypt"); + + CK_LOGARRAY("Key: ", aBuffer.key_id, aBuffer.key_id_size); + + Buffer* buffer = mHost->Allocate(aBuffer.data_size); + assert(buffer != nullptr); + assert(buffer->Data() != nullptr); + assert(buffer->Capacity() >= aBuffer.data_size); + + memcpy(buffer->Data(), aBuffer.data, aBuffer.data_size); + + Status status = Status::kSuccess; + // According to the comment `If |iv_size| = 0, the data is unencrypted.` + // Use iv_size to determine if the sample is encrypted. + if (aBuffer.iv_size != 0) { + status = mDecryptionManager->Decrypt(buffer->Data(), buffer->Size(), + CryptoMetaData(&aBuffer)); + } + + aDecryptedBlock->SetDecryptedBuffer(buffer); + aDecryptedBlock->SetTimestamp(aBuffer.timestamp); + + return status; +} + +void ClearKeySessionManager::DecryptingComplete() { + CK_LOGD("ClearKeySessionManager::DecryptingComplete %p", this); + + for (auto it = mSessions.begin(); it != mSessions.end(); it++) { + delete it->second; + } + mSessions.clear(); + mLastSessionId = std::nullopt; + + mDecryptionManager = nullptr; + mHost = nullptr; + + Release(); +} + +bool ClearKeySessionManager::MaybeDeferTillInitialized( + function<void()>&& aMaybeDefer) { + if (mPersistence->IsLoaded()) { + return false; + } + + mDeferredInitialize.emplace(std::move(aMaybeDefer)); + return true; +} + +void ClearKeySessionManager::OnQueryOutputProtectionStatus( + QueryResult aResult, uint32_t aLinkMask, uint32_t aOutputProtectionMask) { + MOZ_ASSERT(mHasOutstandingOutputProtectionQuery, + "Should only be called if a query is outstanding"); + CK_LOGD("ClearKeySessionManager::OnQueryOutputProtectionStatus"); + mHasOutstandingOutputProtectionQuery = false; + + if (aResult == QueryResult::kQueryFailed) { + // Indicate the query failed. This can happen if we're in shutdown. + NotifyOutputProtectionStatus(KeyStatus::kInternalError); + return; + } + + if (aLinkMask & OutputLinkTypes::kLinkTypeNetwork) { + NotifyOutputProtectionStatus(KeyStatus::kOutputRestricted); + return; + } + + NotifyOutputProtectionStatus(KeyStatus::kUsable); +} + +void ClearKeySessionManager::QueryOutputProtectionStatusIfNeeded() { + MOZ_ASSERT( + mHost, + "Should not query protection status if we're shutdown (mHost == null)!"); + CK_LOGD( + "ClearKeySessionManager::UpdateOutputProtectionStatusAndQueryIfNeeded"); + if (mLastOutputProtectionQueryTime.IsNull()) { + // We haven't perfomed a check yet, get a query going. + MOZ_ASSERT( + !mHasOutstandingOutputProtectionQuery, + "Shouldn't have an outstanding query if we haven't recorded a time"); + QueryOutputProtectionStatusFromHost(); + return; + } + + MOZ_ASSERT(!mLastOutputProtectionQueryTime.IsNull(), + "Should have already handled the case where we don't yet have a " + "previous check time"); + const mozilla::TimeStamp now = mozilla::TimeStamp::NowLoRes(); + const mozilla::TimeDuration timeSinceQuery = + now - mLastOutputProtectionQueryTime; + + // The time between output protection checks to the host. I.e. if this amount + // of time has passed since the last check with the host, another should be + // performed (provided the first check has been handled). + static const mozilla::TimeDuration kOutputProtectionQueryInterval = + mozilla::TimeDuration::FromSeconds(0.2); + // The number of kOutputProtectionQueryInterval intervals we can miss before + // we decide a check has failed. I.e. if this value is 2, if we have not + // received a reply to a check after kOutputProtectionQueryInterval * 2 + // time, we consider the check failed. + constexpr uint32_t kMissedIntervalsBeforeFailure = 2; + // The length of time after which we will restrict output until we get a + // query response. + static const mozilla::TimeDuration kTimeToWaitBeforeFailure = + kOutputProtectionQueryInterval * kMissedIntervalsBeforeFailure; + + if ((timeSinceQuery > kOutputProtectionQueryInterval) && + !mHasOutstandingOutputProtectionQuery) { + // We don't have an outstanding query and enough time has passed we should + // query again. + QueryOutputProtectionStatusFromHost(); + return; + } + + if ((timeSinceQuery > kTimeToWaitBeforeFailure) && + mHasOutstandingOutputProtectionQuery) { + // A reponse was not received fast enough, notify. + NotifyOutputProtectionStatus(KeyStatus::kInternalError); + } +} + +void ClearKeySessionManager::QueryOutputProtectionStatusFromHost() { + MOZ_ASSERT( + mHost, + "Should not query protection status if we're shutdown (mHost == null)!"); + CK_LOGD("ClearKeySessionManager::QueryOutputProtectionStatusFromHost"); + if (mHost) { + mLastOutputProtectionQueryTime = mozilla::TimeStamp::NowLoRes(); + mHost->QueryOutputProtectionStatus(); + mHasOutstandingOutputProtectionQuery = true; + } +} + +void ClearKeySessionManager::NotifyOutputProtectionStatus(KeyStatus aStatus) { + MOZ_ASSERT(aStatus == KeyStatus::kUsable || + aStatus == KeyStatus::kOutputRestricted || + aStatus == KeyStatus::kInternalError, + "aStatus should have an expected value"); + CK_LOGD("ClearKeySessionManager::NotifyOutputProtectionStatus"); + if (!mLastSessionId.has_value()) { + // If we don't have a session id, either because we're too early, or are + // shutting down, don't notify. + return; + } + + string& lastSessionId = mLastSessionId.value(); + + // Use 'output-protection' as the key ID. This helps tests disambiguate key + // status updates related to this. + const uint8_t kKeyId[] = {'o', 'u', 't', 'p', 'u', 't', '-', 'p', 'r', + 'o', 't', 'e', 'c', 't', 'i', 'o', 'n'}; + KeyInformation keyInfo = {}; + keyInfo.key_id = kKeyId; + keyInfo.key_id_size = std::size(kKeyId); + keyInfo.status = aStatus; + + vector<KeyInformation> keyInfos; + keyInfos.push_back(keyInfo); + + // At time of writing, Gecko's higher level handling doesn't use this arg. + // However, we set it to false to mimic Chromium's similar case. Since + // Clearkey is used to test the Chromium CDM path, it doesn't hurt to try + // and mimic their behaviour. + bool hasAdditionalUseableKey = false; + mHost->OnSessionKeysChange(lastSessionId.c_str(), lastSessionId.size(), + hasAdditionalUseableKey, keyInfos.data(), + keyInfos.size()); +} |