Backed out changeset 736a78915259 (bug 1310879)
authorSebastian Hengst <archaeopteryx@coole-files.de>
Wed, 19 Oct 2016 11:25:00 +0200
changeset 318582 9168ac4608163c233af299f6dc1b468c62199f85
parent 318581 d2d4f0ad9256faae13953dd3b422d9a7fe8e726f
child 318583 27eef31f9434af59344a313481ada7366c151f59
push id82960
push usercbook@mozilla.com
push dateWed, 19 Oct 2016 15:04:04 +0000
treeherdermozilla-inbound@c48c53e3492e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1310879
milestone52.0a1
backs out736a7891525950ecc45f8f7e60dd7b3b41dcdff8
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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 += [