Bug 1318965 - Adds code for deferring the initialization of sessions until persistent sessions have been loaded r?cpearce draft
authorJay Harris <jharris@mozilla.com>
Wed, 21 Dec 2016 08:40:42 +1300
changeset 452463 b0ffd4b63027dbea7f166abe52143e88ec8cd691
parent 452462 e8e5bc029cb19bd448aeddfd014b8674e9f18135
child 452464 5f95b1f524b98b83fc0d962e84ca2325275068a2
push id39414
push userbmo:jharris@mozilla.com
push dateWed, 21 Dec 2016 20:40:40 +0000
reviewerscpearce
bugs1318965
milestone53.0a1
Bug 1318965 - Adds code for deferring the initialization of sessions until persistent sessions have been loaded r?cpearce MozReview-Commit-ID: F46wvAmlbep
media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
media/gmp-clearkey/0.1/ClearKeyPersistence.h
media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
media/gmp-clearkey/0.1/ClearKeySessionManager.h
--- a/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
@@ -24,22 +24,22 @@
 #include <assert.h>
 #include <stdint.h>
 #include <sstream>
 #include <string.h>
 
 using namespace std;
 using namespace cdm;
 
-void ClearKeyPersistence::ReadAllRecordsFromIndex() {
+void ClearKeyPersistence::ReadAllRecordsFromIndex(function<void()>&& aOnComplete) {
   //Clear what we think the index file contains, we're about to read it again
   mPersistentSessionIds.clear();
 
   function<void(const uint8_t*, uint32_t)> onIndexSuccess =
-    [this]
+    [this, aOnComplete]
   (const uint8_t* data, uint32_t size) -> void {
     CK_LOGD("ClearKeyPersistence: Loaded index file!");
     const char* charData = (const char*)data;
 
     uint32_t start = 0;
     for (uint32_t end = 0; end < size; ++end) {
       if (start == end) {
         continue;
@@ -62,23 +62,25 @@ void ClearKeyPersistence::ReadAllRecords
         mPersistentSessionIds.insert(atoi(fileName.c_str()));
 
         // Increment end past the '\n' and set start to the new value
         start = ++end;
       }
     }
 
     mPersistentKeyState = PersistentKeyState::LOADED;
+    aOnComplete();
   };
 
   function<void()> onIndexFailed =
-    [this]
+    [this, aOnComplete]
   () -> void {
     CK_LOGD("ClearKeyPersistence: Failed to load index file (it might not exist");
     mPersistentKeyState = PersistentKeyState::LOADED;
+    aOnComplete();
   };
 
   string filename = "index";
   ReadData(mHost, filename, move(onIndexSuccess), move(onIndexFailed));
 }
 
 void ClearKeyPersistence::WriteIndex() {
   function <void()> onIndexSuccess =
@@ -115,24 +117,36 @@ void ClearKeyPersistence::WriteIndex() {
 
 
 ClearKeyPersistence::ClearKeyPersistence(Host_8* aHost)
 {
   this->mHost = aHost;
 }
 
 void
-ClearKeyPersistence::EnsureInitialized()
+ClearKeyPersistence::EnsureInitialized(bool aPersistentStateAllowed,
+                                       function<void()>&& aOnInitialized)
 {
-  if (mPersistentKeyState == PersistentKeyState::UNINITIALIZED) {
+  if (aPersistentStateAllowed &&
+      mPersistentKeyState == PersistentKeyState::UNINITIALIZED) {
     mPersistentKeyState = LOADING;
-    ReadAllRecordsFromIndex();
+    ReadAllRecordsFromIndex(move(aOnInitialized));
+  }
+  else {
+    mPersistentKeyState = PersistentKeyState::LOADED;
+    aOnInitialized();
   }
 }
 
+
+bool ClearKeyPersistence::IsLoaded()
+{
+  return mPersistentKeyState == PersistentKeyState::LOADED;
+}
+
 string
 ClearKeyPersistence::GetNewSessionId(SessionType aSessionType)
 {
   static uint32_t sNextSessionId = 1;
 
   // Ensure we don't re-use a session id that was persisted.
   while (Contains(mPersistentSessionIds, sNextSessionId)) {
     sNextSessionId++;
--- a/media/gmp-clearkey/0.1/ClearKeyPersistence.h
+++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.h
@@ -19,45 +19,49 @@
 
 // This include is required in order for content_decryption_module to work
 // on Unix systems
 #include "stddef.h"
 #include "content_decryption_module.h"
 #include "gmp-api/gmp-decryption.h"
 #include "RefCounted.h"
 
+#include <functional>
 #include <set>
 #include <string>
 #include <vector>
 
 
 class ClearKeySessionManager;
 
 // Whether we've loaded the persistent session ids yet.
 enum PersistentKeyState {
   UNINITIALIZED,
   LOADING,
   LOADED
 };
 
 class ClearKeyPersistence : public RefCounted {
 public:
-  ClearKeyPersistence(cdm::Host_8* aHost);
+  explicit ClearKeyPersistence(cdm::Host_8* aHost);
 
-  void EnsureInitialized();
+  void EnsureInitialized(bool aPersistentStateAllowed,
+                         std::function<void()>&& aOnInitialized);
+
+  bool IsLoaded();
 
   std::string GetNewSessionId(cdm::SessionType aSessionType);
 
   bool IsPersistentSessionId(const std::string& aSid);
 
   void PersistentSessionRemoved(std::string& aSid);
 private:
   cdm::Host_8* mHost = nullptr;
 
   PersistentKeyState mPersistentKeyState = PersistentKeyState::UNINITIALIZED;
 
   std::set<uint32_t> mPersistentSessionIds;
 
-  void ReadAllRecordsFromIndex();
+  void ReadAllRecordsFromIndex(std::function<void()>&& aOnComplete);
   void WriteIndex();
 };
 
 #endif // __ClearKeyPersistence_h__
--- a/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
@@ -49,28 +49,54 @@ void
 ClearKeySessionManager::Init(Host_8* aHost,
                              bool aDistinctiveIdentifierAllowed,
                              bool aPersistentStateAllowed)
 {
   CK_LOGD("ClearKeySessionManager::Init");
   mHost = aHost;
   mPersistence = new ClearKeyPersistence(mHost);
 
-  if (aPersistentStateAllowed) {
-    mPersistence->EnsureInitialized();
-  }
+  function<void()> onPersistentStateLoaded =
+    [this]
+  () -> void {
+    for (uint32_t i = 0; i < mDefferedInitialize.size(); ++i) {
+      mDefferedInitialize[i]();
+    }
+    mDefferedInitialize.clear();
+  };
+
+  mPersistence->EnsureInitialized(aPersistentStateAllowed,
+                                  move(onPersistentStateLoaded));
 }
 
 void
 ClearKeySessionManager::CreateSession(uint32_t aPromiseId,
                                       InitDataType aInitDataType,
                                       const uint8_t* aInitData,
                                       uint32_t aInitDataSize,
                                       SessionType aSessionType)
 {
+  // Copy the init data so it is correctly captured by the lambda
+  vector<uint8_t> initData(aInitData, aInitData + aInitDataSize);
+  function<void()> deferrer =
+    [this, aPromiseId, aInitDataType, initData, aSessionType]
+  () -> void {
+    CreateSession(aPromiseId,
+                  aInitDataType,
+                  initData.data(),
+                  initData.size(),
+                  aSessionType);
+  };
+
+  // If we haven't loaded, don't do this yet
+  if (MaybeDeferTillInitialized(deferrer)) {
+    return;
+  }
+
+
   CK_LOGD("ClearKeySessionManager::CreateSession type:%s", aInitDataType);
   // 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,
@@ -139,44 +165,55 @@ ClearKeySessionManager::CreateSession(ui
                           0);
 }
 
 void
 ClearKeySessionManager::LoadSession(uint32_t aPromiseId,
                                     const char* aSessionId,
                                     uint32_t aSessionIdLength)
 {
+  // Copy the sessionId into a string so the lambda captures it properly
+  string sessionId(aSessionId, aSessionId + aSessionIdLength);
+  function<void()> deferrer =
+    [this, aPromiseId, sessionId]
+  () -> void {
+    LoadSession(aPromiseId, sessionId.data(), sessionId.size());
+  };
+
+  if (MaybeDeferTillInitialized(deferrer)) {
+    return;
+  }
+
   CK_LOGD("ClearKeySessionManager::LoadSession");
 
   if (!ClearKeyUtils::IsValidSessionId(aSessionId, aSessionIdLength)) {
     mHost->OnResolveNewSessionPromise(aPromiseId, nullptr, 0);
     return;
   }
 
-  string sid(aSessionId, aSessionId + aSessionIdLength);
-  if (!mPersistence->IsPersistentSessionId(sid)) {
+  if (!mPersistence->IsPersistentSessionId(sessionId)) {
     mHost->OnResolveNewSessionPromise(aPromiseId, nullptr, 0);
     return;
   }
 
   function<void(const uint8_t*, uint32_t)> success =
-    [this, sid, aPromiseId]
+    [this, sessionId, aPromiseId]
   (const uint8_t* data, uint32_t size) -> void {
     PersistentSessionDataLoaded(aPromiseId,
-                                sid,
+                                sessionId,
                                 data,
                                 size);
   };
 
-  function<void()> failure = [this, sid, aPromiseId]() -> void {
+  function<void()> failure = [this, sessionId, aPromiseId]() -> void {
     // As per the API described in ContentDecryptionModule_8
     mHost->OnResolveNewSessionPromise(aPromiseId, nullptr, 0);
   };
 
-  ReadData(mHost, sid, move(success), move(failure));
+  ReadData(mHost, sessionId, move(success), move(failure));
 }
 
 void
 ClearKeySessionManager::PersistentSessionDataLoaded(uint32_t aPromiseId,
                                                     const string& aSessionId,
                                                     const uint8_t* aKeyData,
                                                     uint32_t aKeyDataSize)
 {
@@ -237,18 +274,35 @@ ClearKeySessionManager::PersistentSessio
 
 void
 ClearKeySessionManager::UpdateSession(uint32_t aPromiseId,
                                       const char* aSessionId,
                                       uint32_t aSessionIdLength,
                                       const uint8_t* aResponse,
                                       uint32_t aResponseSize)
 {
+  // Copy the method arguments so we can capture them in the lambda
+  string sessionId(aSessionId, aSessionId + aSessionIdLength);
+  vector<uint8_t> response(aResponse, aResponse + aResponseSize);
+  function<void()> deferrer =
+    [this, aPromiseId, sessionId, response]
+  () -> void {
+    UpdateSession(aPromiseId,
+                  sessionId.data(),
+                  sessionId.size(),
+                  response.data(),
+                  response.size());
+  };
+
+  // If we haven't fully loaded, defer calling this method
+  if (MaybeDeferTillInitialized(deferrer)) {
+    return;
+  }
+
   CK_LOGD("ClearKeySessionManager::UpdateSession");
-  string sessionId(aSessionId, aSessionId + aSessionIdLength);
   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,
                            Error::kInvalidAccessError,
@@ -353,19 +407,31 @@ ClearKeySessionManager::Serialize(const 
   }
 }
 
 void
 ClearKeySessionManager::CloseSession(uint32_t aPromiseId,
                                      const char* aSessionId,
                                      uint32_t aSessionIdLength)
 {
+  // Copy the sessionId into a string so we capture it properly
+  string sessionId(aSessionId, aSessionId + aSessionIdLength);
+  function<void()> deferrer =
+    [this, aPromiseId, sessionId]
+  () -> void {
+    CloseSession(aPromiseId, sessionId.data(), sessionId.size());
+  };
+
+  // If we haven't loaded, call this method later
+  if (MaybeDeferTillInitialized(deferrer)) {
+    return;
+  }
+
   CK_LOGD("ClearKeySessionManager::CloseSession");
 
-  string sessionId(aSessionId, aSessionId + aSessionIdLength);
   auto itr = mSessions.find(sessionId);
   if (itr == mSessions.end()) {
     CK_LOGW("ClearKey CDM couldn't close non-existent session.");
     mHost->OnRejectPromise(aPromiseId,
                            Error::kInvalidAccessError,
                            0,
                            nullptr,
                            0);
@@ -389,18 +455,30 @@ ClearKeySessionManager::ClearInMemorySes
   delete aSession;
 }
 
 void
 ClearKeySessionManager::RemoveSession(uint32_t aPromiseId,
                                       const char* aSessionId,
                                       uint32_t aSessionIdLength)
 {
+  // Copy the sessionId into a string so it can be captured for the lambda
+  string sessionId(aSessionId, aSessionId + aSessionIdLength);
+  function<void()> deferrer =
+    [this, aPromiseId, sessionId]
+  () -> void {
+    RemoveSession(aPromiseId, sessionId.data(), sessionId.size());
+  };
+
+  // If we haven't fully loaded, defer calling this method
+  if (MaybeDeferTillInitialized(deferrer)) {
+    return;
+  }
+
   CK_LOGD("ClearKeySessionManager::RemoveSession");
-  string sessionId(aSessionId, aSessionId + aSessionIdLength);
   auto itr = mSessions.find(sessionId);
   if (itr == mSessions.end()) {
     CK_LOGW("ClearKey CDM couldn't remove non-existent session.");
 
     mHost->OnRejectPromise(aPromiseId,
                            Error::kInvalidAccessError,
                            0,
                            nullptr,
@@ -494,8 +572,18 @@ void
 ClearKeySessionManager::DecryptingComplete()
 {
   CK_LOGD("ClearKeySessionManager::DecryptingComplete %p", this);
 
   Shutdown();
   mDecryptionManager = nullptr;
   Release();
 }
+
+bool ClearKeySessionManager::MaybeDeferTillInitialized(function<void()> aMaybeDefer)
+{
+  if (mPersistence->IsLoaded()) {
+    return false;
+  }
+
+  mDefferedInitialize.push_back(move(aMaybeDefer));
+  return true;
+}
--- a/media/gmp-clearkey/0.1/ClearKeySessionManager.h
+++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.h
@@ -23,16 +23,17 @@
 #include "ClearKeyUtils.h"
 // This include is required in order for content_decryption_module to work
 // on Unix systems
 #include "stddef.h"
 #include "content_decryption_module.h"
 #include "gmp-api/gmp-decryption.h"
 #include "RefCounted.h"
 
+#include <functional>
 #include <map>
 #include <set>
 #include <string>
 #include <vector>
 
 class ClearKeySessionManager final : public RefCounted
 {
 public:
@@ -81,21 +82,24 @@ public:
                                    uint32_t aKeyDataSize);
 
 private:
   ~ClearKeySessionManager();
 
   void Shutdown();
 
   void ClearInMemorySessionData(ClearKeySession* aSession);
+  bool MaybeDeferTillInitialized(std::function<void()> aMaybeDefer);
   void Serialize(const ClearKeySession* aSession,
                  std::vector<uint8_t>& aOutKeyData);
 
   RefPtr<ClearKeyDecryptionManager> mDecryptionManager;
   RefPtr<ClearKeyPersistence> mPersistence;
 
   cdm::Host_8* mHost = nullptr;
 
   std::set<KeyId> mKeyIds;
   std::map<std::string, ClearKeySession*> mSessions;
+
+  std::vector<std::function<void()>> mDefferedInitialize;
 };
 
 #endif // __ClearKeyDecryptor_h__