Backed out changeset 736a78915259 (bug 1310879)
authorSebastian Hengst <archaeopteryx@coole-files.de>
Wed, 19 Oct 2016 11:25:00 +0200
changeset 318496 9168ac4608163c233af299f6dc1b468c62199f85
parent 318495 d2d4f0ad9256faae13953dd3b422d9a7fe8e726f
child 318497 27eef31f9434af59344a313481ada7366c151f59
push id20725
push userphilringnalda@gmail.com
push dateThu, 20 Oct 2016 01:36:01 +0000
treeherderfx-team@998ad5a74da8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1310879
milestone52.0a1
backs out736a7891525950ecc45f8f7e60dd7b3b41dcdff8
Backed out changeset 736a78915259 (bug 1310879)
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
media/gmp-clearkey/0.1/moz.build
new file mode 100644
--- /dev/null
+++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
@@ -0,0 +1,260 @@
+/*
+ * 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()));
+}
new file mode 100644
--- /dev/null
+++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef __ClearKeyPersistence_h__
+#define __ClearKeyPersistence_h__
+
+#include <string>
+#include "gmp-api/gmp-decryption.h"
+
+class ClearKeySessionManager;
+
+class ClearKeyPersistence {
+public:
+  static void EnsureInitialized();
+
+  static std::string GetNewSessionId(GMPSessionType aSessionType);
+
+  static bool DeferCreateSessionIfNotReady(ClearKeySessionManager* aInstance,
+                                           uint32_t aCreateSessionToken,
+                                           uint32_t aPromiseId,
+                                           const std::string& aInitDataType,
+                                           const uint8_t* aInitData,
+                                           uint32_t aInitDataSize,
+                                           GMPSessionType aSessionType);
+
+  static bool DeferLoadSessionIfNotReady(ClearKeySessionManager* aInstance,
+                                         uint32_t aPromiseId,
+                                         const char* aSessionId,
+                                         uint32_t aSessionIdLength);
+
+  static bool IsPersistentSessionId(const std::string& aSid);
+
+  static void LoadSessionData(ClearKeySessionManager* aInstance,
+                              const std::string& aSid,
+                              uint32_t aPromiseId);
+
+  static void PersistentSessionRemoved(const std::string& aSid);
+};
+
+#endif // __ClearKeyPersistence_h__
--- a/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
@@ -17,16 +17,17 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <string.h>
 
 #include "ClearKeyDecryptionManager.h"
 #include "ClearKeySessionManager.h"
 #include "ClearKeyUtils.h"
 #include "ClearKeyStorage.h"
+#include "ClearKeyPersistence.h"
 #include "gmp-task-utils.h"
 #include <assert.h>
 
 using namespace std;
 
 ClearKeySessionManager::ClearKeySessionManager()
   : mDecryptionManager(ClearKeyDecryptionManager::Get())
 {
@@ -46,23 +47,17 @@ ClearKeySessionManager::~ClearKeySession
 
 void
 ClearKeySessionManager::Init(GMPDecryptorCallback* aCallback,
                              bool aDistinctiveIdentifierAllowed,
                              bool aPersistentStateAllowed)
 {
   CK_LOGD("ClearKeySessionManager::Init");
   mCallback = aCallback;
-}
-
-string
-ClearKeySessionManager::GetNewSessionId()
-{
-  static uint32_t sNextSessionId = 1;
-  return to_string(sNextSessionId++);
+  ClearKeyPersistence::EnsureInitialized();
 }
 
 void
 ClearKeySessionManager::CreateSession(uint32_t aCreateSessionToken,
                                       uint32_t aPromiseId,
                                       const char* aInitDataType,
                                       uint32_t aInitDataTypeSize,
                                       const uint8_t* aInitData,
@@ -77,17 +72,27 @@ ClearKeySessionManager::CreateSession(ui
       initDataType != "keyids" &&
       initDataType != "webm") {
     string message = "'" + initDataType + "' is an initDataType unsupported by ClearKey";
     mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError,
                              message.c_str(), message.size());
     return;
   }
 
-  string sessionId = GetNewSessionId();
+  if (ClearKeyPersistence::DeferCreateSessionIfNotReady(this,
+                                                        aCreateSessionToken,
+                                                        aPromiseId,
+                                                        initDataType,
+                                                        aInitData,
+                                                        aInitDataSize,
+                                                        aSessionType)) {
+    return;
+  }
+
+  string sessionId = ClearKeyPersistence::GetNewSessionId(aSessionType);
   assert(mSessions.find(sessionId) == mSessions.end());
 
   ClearKeySession* session = new ClearKeySession(sessionId, mCallback, aSessionType);
   session->Init(aCreateSessionToken, aPromiseId, initDataType, aInitData, aInitDataSize);
   mSessions[sessionId] = session;
 
   const vector<KeyId>& sessionKeys = session->GetKeyIds();
   vector<KeyId> neededKeys;
@@ -114,18 +119,89 @@ ClearKeySessionManager::CreateSession(ui
 }
 
 void
 ClearKeySessionManager::LoadSession(uint32_t aPromiseId,
                                     const char* aSessionId,
                                     uint32_t aSessionIdLength)
 {
   CK_LOGD("ClearKeySessionManager::LoadSession");
-  const char message[] = "Persistent sessions not supported.";
-  mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError, message, strlen(message));
+
+  if (!ClearKeyUtils::IsValidSessionId(aSessionId, aSessionIdLength)) {
+    mCallback->ResolveLoadSessionPromise(aPromiseId, false);
+    return;
+  }
+
+  if (ClearKeyPersistence::DeferLoadSessionIfNotReady(this,
+                                                      aPromiseId,
+                                                      aSessionId,
+                                                      aSessionIdLength)) {
+    return;
+  }
+
+  string sid(aSessionId, aSessionId + aSessionIdLength);
+  if (!ClearKeyPersistence::IsPersistentSessionId(sid)) {
+    mCallback->ResolveLoadSessionPromise(aPromiseId, false);
+    return;
+  }
+
+  // Callsback PersistentSessionDataLoaded with results...
+  ClearKeyPersistence::LoadSessionData(this, sid, aPromiseId);
+}
+
+void
+ClearKeySessionManager::PersistentSessionDataLoaded(GMPErr aStatus,
+                                                    uint32_t aPromiseId,
+                                                    const string& aSessionId,
+                                                    const uint8_t* aKeyData,
+                                                    uint32_t aKeyDataSize)
+{
+  CK_LOGD("ClearKeySessionManager::PersistentSessionDataLoaded");
+  if (GMP_FAILED(aStatus) ||
+      Contains(mSessions, aSessionId) ||
+      (aKeyDataSize % (2 * CENC_KEY_LEN)) != 0) {
+    mCallback->ResolveLoadSessionPromise(aPromiseId, false);
+    return;
+  }
+
+  ClearKeySession* session = new ClearKeySession(aSessionId,
+                                                 mCallback,
+                                                 kGMPPersistentSession);
+  mSessions[aSessionId] = session;
+
+  uint32_t numKeys = aKeyDataSize / (2 * CENC_KEY_LEN);
+
+  vector<GMPMediaKeyInfo> key_infos;
+  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);
+    key_infos.push_back(GMPMediaKeyInfo(&keyPairs[i].mKeyId[0],
+                                        keyPairs[i].mKeyId.size(),
+                                        kGMPUsable));
+  }
+  mCallback->BatchedKeyStatusChanged(&aSessionId[0], aSessionId.size(),
+                                     key_infos.data(), key_infos.size());
+
+  mCallback->ResolveLoadSessionPromise(aPromiseId, true);
 }
 
 void
 ClearKeySessionManager::UpdateSession(uint32_t aPromiseId,
                                       const char* aSessionId,
                                       uint32_t aSessionIdLength,
                                       const uint8_t* aResponse,
                                       uint32_t aResponseSize)
@@ -256,16 +332,18 @@ ClearKeySessionManager::RemoveSession(ui
   bool isPersistent = session->Type() == kGMPPersistentSession;
   ClearInMemorySessionData(session);
 
   if (!isPersistent) {
     mCallback->ResolvePromise(aPromiseId);
     return;
   }
 
+  ClearKeyPersistence::PersistentSessionRemoved(sid);
+
   // Overwrite the record storing the sessionId's key data with a zero
   // length record to delete it.
   vector<uint8_t> emptyKeydata;
   GMPTask* resolve = WrapTask(mCallback, &GMPDecryptorCallback::ResolvePromise, aPromiseId);
   static const char* message = "Could not remove session";
   GMPTask* reject = WrapTask(mCallback,
                              &GMPDecryptorCallback::RejectPromise,
                              aPromiseId,
--- a/media/gmp-clearkey/0.1/ClearKeySessionManager.h
+++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.h
@@ -68,21 +68,25 @@ public:
                                     const uint8_t* aServerCert,
                                     uint32_t aServerCertSize) override;
 
   virtual void Decrypt(GMPBuffer* aBuffer,
                        GMPEncryptedBufferMetadata* aMetadata) override;
 
   virtual void DecryptingComplete() override;
 
+  void PersistentSessionDataLoaded(GMPErr aStatus,
+                                   uint32_t aPromiseId,
+                                   const std::string& aSessionId,
+                                   const uint8_t* aKeyData,
+                                   uint32_t aKeyDataSize);
+
 private:
   ~ClearKeySessionManager();
 
-  std::string GetNewSessionId();
-
   void DoDecrypt(GMPBuffer* aBuffer, GMPEncryptedBufferMetadata* aMetadata);
   void Shutdown();
 
   void ClearInMemorySessionData(ClearKeySession* aSession);
   void Serialize(const ClearKeySession* aSession, std::vector<uint8_t>& aOutKeyData);
 
   RefPtr<ClearKeyDecryptionManager> mDecryptionManager;
 
--- a/media/gmp-clearkey/0.1/moz.build
+++ b/media/gmp-clearkey/0.1/moz.build
@@ -9,16 +9,17 @@ SharedLibrary('clearkey')
 FINAL_TARGET = 'dist/bin/gmp-clearkey/0.1'
 
 FINAL_TARGET_PP_FILES += ['clearkey.info.in']
 
 UNIFIED_SOURCES += [
     'ClearKeyAsyncShutdown.cpp',
     'ClearKeyBase64.cpp',
     'ClearKeyDecryptionManager.cpp',
+    'ClearKeyPersistence.cpp',
     'ClearKeySession.cpp',
     'ClearKeySessionManager.cpp',
     'ClearKeyStorage.cpp',
     'ClearKeyUtils.cpp',
     'gmp-clearkey.cpp',
 ]
 
 SOURCES += [