diff --git a/core/authorize.php b/core/authorize.php
index f938f39..1d54182 100644
--- a/core/authorize.php
+++ b/core/authorize.php
@@ -47,7 +47,7 @@
  *   TRUE if the current user can run authorize.php, and FALSE if not.
  */
 function authorize_access_allowed() {
-  \Drupal::service('session_manager')->initialize();
+  \Drupal::service('session.storage')->initialize();
   return Settings::get('allow_authorize_operations', TRUE) && user_access('administer software updates');
 }
 
diff --git a/core/core.services.yml b/core/core.services.yml
index a8313a6..779e313 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -120,7 +120,7 @@ services:
     arguments: ['@typed_data_manager']
   cron:
     class: Drupal\Core\Cron
-    arguments: ['@module_handler', '@lock', '@queue', '@state', '@current_user', '@session_manager']
+    arguments: ['@module_handler', '@lock', '@queue', '@state', '@current_user', '@session.helper']
   diff.formatter:
     class: Drupal\Core\Diff\DiffFormatter
     arguments: ['@config.factory']
@@ -789,14 +789,35 @@ services:
   current_user:
     class: Drupal\Core\Session\AccountProxy
     arguments: ['@authentication', '@request']
-  session_manager:
-    class: Drupal\Core\Session\SessionManager
-    arguments: ['@request_stack', '@database', '@session_manager.metadata_bag', '@settings']
+  session:
+    class: Symfony\Component\HttpFoundation\Session\Session
+    arguments: ['@session.storage', '@session.attribute_bag', '@session.flash_bag']
+  session.attribute_bag:
+    class: Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag
+  session.flash_bag:
+    class: Symfony\Component\HttpFoundation\Session\Flash\FlashBag
+  session.storage:
+    class: Drupal\Core\Session\Storage\NativeSessionStorage
+    arguments: ['@request_stack', '@database', '@session_manager.metadata_bag', '@session.helper', '@session.handler']
     tags:
       - { name: persist }
+  session.handler:
+    class: Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler
+    arguments: ['@session.handler.database']
+  session.handler.database:
+    class: Drupal\Core\Session\Storage\Handler\SessionHandler
+    arguments: ['@session.helper', '@request_stack', '@database']
+  session.helper:
+    class: Drupal\Core\Session\SessionHelper
+    arguments: ['@settings']
   session_manager.metadata_bag:
     class: Drupal\Core\Session\MetadataBag
     arguments: ['@settings']
+  session_listener:
+    class: Drupal\Core\Session\SessionListener
+    arguments: ['@service_container']
+    tags:
+      - { name: event_subscriber }
   asset.css.collection_renderer:
     class: Drupal\Core\Asset\CssCollectionRenderer
     arguments: [ '@state' ]
diff --git a/core/includes/common.inc b/core/includes/common.inc
index 23f6ce3..4d69f89 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -2574,7 +2574,7 @@ function drupal_get_private_key() {
  *
  * @see drupal_get_hash_salt()
  * @see \Drupal\Core\Access\CsrfTokenGenerator
- * @see \Drupal\Core\Session\SessionManager::start()
+ * @see \Drupal\Core\Session\Storage\NativeSessionStorage::start()
  *
  * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
  *   Use \Drupal::csrfToken()->get().
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 7c0a3d0..aca2498 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -548,7 +548,7 @@ function install_run_task($task, &$install_state) {
       $response = batch_process(install_redirect_url($install_state), install_full_redirect_url($install_state));
       if ($response instanceof Response) {
         // Save $_SESSION data from batch.
-        \Drupal::service('session_manager')->save();
+        \Drupal::service('session.storage')->save();
         // Send the response.
         $response->send();
         exit;
@@ -1459,7 +1459,7 @@ function install_load_profile(&$install_state) {
  */
 function install_bootstrap_full() {
   drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
-  \Drupal::service('session_manager')->initialize();
+  \Drupal::service('session.storage')->initialize();
 }
 
 /**
diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php
index 57cf2ef..dacf2c8 100644
--- a/core/lib/Drupal.php
+++ b/core/lib/Drupal.php
@@ -587,7 +587,7 @@ public static function languageManager() {
    * @return \Drupal\Core\Access\CsrfTokenGenerator
    *   The CSRF token manager.
    *
-   * @see \Drupal\Core\Session\SessionManager::start()
+   * @see \Drupal\Core\Session\Storage\NativeSessionStorage::start()
    */
   public static function csrfToken() {
     return static::$container->get('csrf_token');
diff --git a/core/lib/Drupal/Core/Access/CsrfTokenGenerator.php b/core/lib/Drupal/Core/Access/CsrfTokenGenerator.php
index 4fa30e2..c4a3047 100644
--- a/core/lib/Drupal/Core/Access/CsrfTokenGenerator.php
+++ b/core/lib/Drupal/Core/Access/CsrfTokenGenerator.php
@@ -53,7 +53,7 @@ public function __construct(PrivateKey $private_key) {
    *   'drupal_private_key' configuration variable.
    *
    * @see drupal_get_hash_salt()
-   * @see \Drupal\Core\Session\SessionManager::start()
+   * @see \Drupal\Core\Session\Storage\NativeSessionStorage::start()
    */
   public function get($value = '') {
     if (empty($_SESSION['csrf_token_seed'])) {
diff --git a/core/lib/Drupal/Core/Authentication/Provider/Cookie.php b/core/lib/Drupal/Core/Authentication/Provider/Cookie.php
index 167a0b0..d33a59b 100644
--- a/core/lib/Drupal/Core/Authentication/Provider/Cookie.php
+++ b/core/lib/Drupal/Core/Authentication/Provider/Cookie.php
@@ -47,7 +47,7 @@ public function applies(Request $request) {
   public function authenticate(Request $request) {
     // Global $user is deprecated, but the session system is still based on it.
     global $user;
-    if ($this->sessionManager->initialize()->isStarted()) {
+    if ($this->sessionManager->isStarted()) {
       return $user;
     }
     return NULL;
diff --git a/core/lib/Drupal/Core/Cache/TimeZoneCacheContext.php b/core/lib/Drupal/Core/Cache/TimeZoneCacheContext.php
index 50c4676..dc11e36 100644
--- a/core/lib/Drupal/Core/Cache/TimeZoneCacheContext.php
+++ b/core/lib/Drupal/Core/Cache/TimeZoneCacheContext.php
@@ -10,7 +10,7 @@
 /**
  * Defines the TimeZoneCacheContext service, for "per time zone" caching.
  *
- * @see \Drupal\Core\Session\SessionManager::initialize()
+ * @see \Drupal\Core\Session\Storage\NativeSessionStorage::initialize()
  */
 class TimeZoneCacheContext implements CacheContextInterface {
 
@@ -25,8 +25,9 @@ public static function getLabel() {
    * {@inheritdoc}
    */
   public function getContext() {
-    // date_default_timezone_set() is called in SessionManager::initialize(), so
-    // we can safely retrieve the timezone.
+    // date_default_timezone_set() is called in
+    // NativeSessionStorage::initialize(), so we can safely retrieve the
+    // timezone.
     return date_default_timezone_get();
   }
 
diff --git a/core/lib/Drupal/Core/Cron.php b/core/lib/Drupal/Core/Cron.php
index b639f5c..cafb9d9 100644
--- a/core/lib/Drupal/Core/Cron.php
+++ b/core/lib/Drupal/Core/Cron.php
@@ -8,12 +8,12 @@
 namespace Drupal\Core;
 
 use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Session\SessionHelper;
 use Drupal\Core\State\StateInterface;
 use Drupal\Core\Lock\LockBackendInterface;
 use Drupal\Core\Queue\QueueFactory;
 use Drupal\Core\Session\AccountProxyInterface;
 use Drupal\Core\Session\AnonymousUserSession;
-use Drupal\Core\Session\SessionManagerInterface;
 use Drupal\Core\Queue\SuspendQueueException;
 
 /**
@@ -57,11 +57,11 @@ class Cron implements CronInterface {
   protected $currentUser;
 
   /**
-   * The session manager.
+   * The session helper.
    *
-   * @var \Drupal\Core\Session\SessionManagerInterface
+   * @var \Drupal\Core\Session\SessionHelper
    */
-  protected $sessionManager;
+  protected $sessionHelper;
 
   /**
    * Constructs a cron object.
@@ -76,16 +76,16 @@ class Cron implements CronInterface {
    *   The state service.
    * @param \Drupal\Core\Session\AccountProxyInterface $current_user
    *    The current user.
-   * @param \Drupal\Core\Session\SessionManagerInterface $session_manager
-   *   The session manager.
+   * @param Session\SessionHelper $session_helper
+   *    The session helper.
    */
-  public function __construct(ModuleHandlerInterface $module_handler, LockBackendInterface $lock, QueueFactory $queue_factory, StateInterface $state, AccountProxyInterface $current_user, SessionManagerInterface $session_manager) {
+  public function __construct(ModuleHandlerInterface $module_handler, LockBackendInterface $lock, QueueFactory $queue_factory, StateInterface $state, AccountProxyInterface $current_user, SessionHelper $session_helper) {
     $this->moduleHandler = $module_handler;
     $this->lock = $lock;
     $this->queueFactory = $queue_factory;
     $this->state = $state;
     $this->currentUser = $current_user;
-    $this->sessionManager = $session_manager;
+    $this->sessionHelper = $session_helper;
   }
 
   /**
@@ -96,8 +96,8 @@ public function run() {
     @ignore_user_abort(TRUE);
 
     // Prevent session information from being saved while cron is running.
-    $original_session_saving = $this->sessionManager->isEnabled();
-    $this->sessionManager->disable();
+    $original_session_saving = $this->sessionHelper->isEnabled();
+    $this->sessionHelper->disable();
 
     // Force the current user to anonymous to ensure consistent permissions on
     // cron runs.
@@ -131,7 +131,7 @@ public function run() {
     // Restore the user.
     $this->currentUser->setAccount($original_user);
     if ($original_session_saving) {
-      $this->sessionManager->enable();
+      $this->sessionHelper->enable();
     }
 
     return $return;
diff --git a/core/lib/Drupal/Core/Session/SessionHelper.php b/core/lib/Drupal/Core/Session/SessionHelper.php
new file mode 100644
index 0000000..bf382f4
--- /dev/null
+++ b/core/lib/Drupal/Core/Session/SessionHelper.php
@@ -0,0 +1,97 @@
+<?php
+/**
+ * @file
+ * @todo fill in file doc.
+ */
+
+namespace Drupal\Core\Session;
+
+
+use Drupal\Core\Site\Settings;
+
+class SessionHelper {
+
+  /**
+   * Whether session management is enabled or temporarily disabled.
+   *
+   * PHP session ID, session, and cookie handling happens in the global scope.
+   * This value has to persist, since a potentially wrong or disallowed session
+   * would be written otherwise.
+   *
+   * @var bool
+   */
+  protected $enabled = TRUE;
+
+  /**
+   * Whether or not the session manager is operating in mixed mode SSL.
+   *
+   * @var bool
+   */
+  protected $mixedMode;
+
+  public function __construct(Settings $settings) {
+    $this->mixedMode = $settings->get('mixed_mode_sessions', FALSE);
+  }
+
+  /**
+   * Determines whether to save session data of the current request.
+   *
+   * @return bool
+   *   FALSE if writing session data has been disabled. TRUE otherwise.
+   */
+  public function isEnabled() {
+    return $this->enabled;
+  }
+
+  /**
+   * Temporarily disables saving of session data.
+   *
+   * This function allows the caller to temporarily disable writing of
+   * session data, should the request end while performing potentially
+   * dangerous operations, such as manipulating the global $user object.
+   *
+   * @see https://drupal.org/node/218104
+   *
+   * @return $this
+   */
+  public function disable() {
+    $this->enabled = FALSE;
+    return $this;
+  }
+
+  /**
+   * Re-enables saving of session data.
+   *
+   * @return $this
+   */
+  public function enable() {
+    $this->enabled = TRUE;
+    return $this;
+  }
+
+  /**
+   * Returns whether mixed mode SSL sessions are enabled in the session manager.
+   *
+   * @return bool
+   *   Value of the mixed mode SSL sessions flag.
+   */
+  public function isMixedMode() {
+    return $this->mixedMode;
+  }
+
+  /**
+   * Returns the name of the insecure session when operating in mixed mode SSL.
+   *
+   * @param string $name
+   *   Current session name.
+   * @return string
+   *   The name of the insecure session.
+   */
+  public function getInsecureName($name) {
+    if (strpos($name, 'SS') === 0) {
+      $name = substr($name, 1);
+    }
+    return $name;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Session/SessionManagerInterface.php b/core/lib/Drupal/Core/Session/SessionManagerInterface.php
index dc9aa40..5da2756 100644
--- a/core/lib/Drupal/Core/Session/SessionManagerInterface.php
+++ b/core/lib/Drupal/Core/Session/SessionManagerInterface.php
@@ -29,56 +29,4 @@ public function initialize();
    */
   public function delete($uid);
 
-  /**
-   * Determines whether to save session data of the current request.
-   *
-   * @return bool
-   *   FALSE if writing session data has been disabled. TRUE otherwise.
-   */
-  public function isEnabled();
-
-  /**
-   * Temporarily disables saving of session data.
-   *
-   * This function allows the caller to temporarily disable writing of
-   * session data, should the request end while performing potentially
-   * dangerous operations, such as manipulating the global $user object.
-   *
-   * @see https://drupal.org/node/218104
-   *
-   * @return $this
-   */
-  public function disable();
-
-  /**
-   * Re-enables saving of session data.
-   *
-   * @return $this
-   */
-  public function enable();
-
-  /**
-   * Returns whether mixed mode SSL sessions are enabled in the session manager.
-   *
-   * @return bool
-   *   Value of the mixed mode SSL sessions flag.
-   */
-  public function isMixedMode();
-
-  /**
-   * Enables or disables mixed mode SSL sessions in the session manager.
-   *
-   * @param bool $mixed_mode
-   *   New value for the mixed mode SSL sessions flag.
-   */
-  public function setMixedMode($mixed_mode);
-
-  /**
-   * Returns the name of the insecure session when operating in mixed mode SSL.
-   *
-   * @return string
-   *   The name of the insecure session.
-   */
-  public function getInsecureName();
-
 }
diff --git a/core/lib/Drupal/Core/Session/SessionHandler.php b/core/lib/Drupal/Core/Session/Storage/Handler/SessionHandler.php
similarity index 81%
rename from core/lib/Drupal/Core/Session/SessionHandler.php
rename to core/lib/Drupal/Core/Session/Storage/Handler/SessionHandler.php
index 40d3c45..adb7e49 100644
--- a/core/lib/Drupal/Core/Session/SessionHandler.php
+++ b/core/lib/Drupal/Core/Session/Storage/Handler/SessionHandler.php
@@ -5,10 +5,13 @@
  * Contains \Drupal\Core\Session\SessionHandler.
  */
 
-namespace Drupal\Core\Session;
+namespace Drupal\Core\Session\Storage\Handler;
 
 use Drupal\Component\Utility\Crypt;
 use Drupal\Core\Database\Connection;
+use Drupal\Core\Session\AnonymousUserSession;
+use Drupal\Core\Session\SessionHelper;
+use Drupal\Core\Session\UserSession;
 use Drupal\Core\Site\Settings;
 use Drupal\Core\Utility\Error;
 use Symfony\Component\HttpFoundation\RequestStack;
@@ -20,11 +23,11 @@
 class SessionHandler extends AbstractProxy implements \SessionHandlerInterface {
 
   /**
-   * The session manager.
+   * The session helper.
    *
-   * @var \Drupal\Core\Session\SessionManagerInterface
+   * @var \Drupal\Core\Session\SessionHelper
    */
-  protected $sessionManager;
+  protected $sessionHelper;
 
   /**
    * The request stack.
@@ -43,15 +46,15 @@ class SessionHandler extends AbstractProxy implements \SessionHandlerInterface {
   /**
    * Constructs a new SessionHandler instance.
    *
-   * @param \Drupal\Core\Session\SessionManagerInterface $session_manager
-   *   The session manager.
+   * @param \Drupal\Core\Session\SessionHelper $session_helper
+   *   The session helper.
    * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
    *   The request stack.
    * @param \Drupal\Core\Database\Connection $connection
    *   The database connection.
    */
-  public function __construct(SessionManagerInterface $session_manager, RequestStack $request_stack, Connection $connection) {
-    $this->sessionManager = $session_manager;
+  public function __construct(SessionHelper $session_helper, RequestStack $request_stack, Connection $connection) {
+    $this->sessionHelper = $session_helper;
     $this->requestStack = $request_stack;
     $this->connection = $connection;
   }
@@ -71,7 +74,7 @@ public function read($sid) {
 
     // Handle the case of first time visitors and clients that don't store
     // cookies (eg. web crawlers).
-    $insecure_session_name = $this->sessionManager->getInsecureName();
+    $insecure_session_name = $this->sessionHelper->getInsecureName($this->getName());
     $cookies = $this->requestStack->getCurrentRequest()->cookies;
     if (!$cookies->has($this->getName()) && !$cookies->has($insecure_session_name)) {
       $user = new UserSession();
@@ -142,7 +145,7 @@ public function write($sid, $value) {
     // The exception handler is not active at this point, so we need to do it
     // manually.
     try {
-      if (!$this->sessionManager->isEnabled()) {
+      if (!$this->sessionHelper->isEnabled()) {
         // We don't have anything to do if we are not allowed to save the
         // session.
         return TRUE;
@@ -166,14 +169,14 @@ public function write($sid, $value) {
         // secure and insecure session cookies. If enabled and both cookies
         // are presented then use both keys. The session ID from the cookie is
         // hashed before being stored in the database as a security measure.
-        if ($this->sessionManager->isMixedMode()) {
-          $insecure_session_name = $this->sessionManager->getInsecureName();
+        if ($this->sessionHelper->isMixedMode()) {
+          $insecure_session_name = $this->sessionHelper->getInsecureName($this->getName());
           if ($cookies->has($insecure_session_name)) {
             $key['sid'] = Crypt::hashBase64($cookies->get($insecure_session_name));
           }
         }
       }
-      elseif ($this->sessionManager->isMixedMode()) {
+      elseif ($this->sessionHelper->isMixedMode()) {
         unset($key['ssid']);
       }
       $this->connection->merge('sessions')
@@ -218,7 +221,7 @@ public function destroy($sid) {
     global $user;
 
     // Nothing to do if we are not allowed to change the session.
-    if (!$this->sessionManager->isEnabled()) {
+    if (!$this->sessionHelper->isEnabled()) {
       return TRUE;
     }
     $is_https = $this->requestStack->getCurrentRequest()->isSecure();
@@ -232,14 +235,6 @@ public function destroy($sid) {
     $_SESSION = array();
     $user = new AnonymousUserSession();
 
-    // Unset the session cookies.
-    $this->deleteCookie($this->getName());
-    if ($is_https) {
-      $this->deleteCookie($this->sessionManager->getInsecureName(), FALSE);
-    }
-    elseif ($this->sessionManager->isMixedMode()) {
-      $this->deleteCookie('S' . $this->getName(), TRUE);
-    }
     return TRUE;
   }
 
@@ -258,24 +253,4 @@ public function gc($lifetime) {
     return TRUE;
   }
 
-  /**
-   * Deletes a session cookie.
-   *
-   * @param string $name
-   *   Name of session cookie to delete.
-   * @param bool $secure
-   *   Force the secure value of the cookie.
-   */
-  protected function deleteCookie($name, $secure = NULL) {
-    $cookies = $this->requestStack->getCurrentRequest()->cookies;
-    if ($cookies->has($name) || (!$this->requestStack->getCurrentRequest()->isSecure() && $secure === TRUE)) {
-      $params = session_get_cookie_params();
-      if ($secure !== NULL) {
-        $params['secure'] = $secure;
-      }
-      setcookie($name, '', REQUEST_TIME - 3600, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
-      $cookies->remove($name);
-    }
-  }
-
 }
diff --git a/core/lib/Drupal/Core/Session/SessionManager.php b/core/lib/Drupal/Core/Session/Storage/NativeSessionStorage.php
similarity index 77%
rename from core/lib/Drupal/Core/Session/SessionManager.php
rename to core/lib/Drupal/Core/Session/Storage/NativeSessionStorage.php
index f656b2d..8478e42 100644
--- a/core/lib/Drupal/Core/Session/SessionManager.php
+++ b/core/lib/Drupal/Core/Session/Storage/NativeSessionStorage.php
@@ -2,20 +2,19 @@
 
 /**
  * @file
- * Contains \Drupal\Core\Session\SessionManager.
+ * Contains \Drupal\Core\Session\Storage\NativeSessionStorage.
  */
 
-namespace Drupal\Core\Session;
+namespace Drupal\Core\Session\Storage;
 
 use Drupal\Component\Utility\Crypt;
 use Drupal\Core\Database\Connection;
 use Drupal\Core\Session\AnonymousUserSession;
-use Drupal\Core\Session\SessionHandler;
-use Drupal\Core\Site\Settings;
+use Drupal\Core\Session\SessionHelper;
+use Drupal\Core\Session\SessionManagerInterface;
 use Symfony\Component\HttpFoundation\RequestStack;
-use Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler;
 use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag as SymfonyMetadataBag;
-use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
+use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage as SymfonyNativeSessionStorage;
 
 /**
  * Manages user sessions.
@@ -35,14 +34,7 @@
  *   (e.g. mixed mode SSL, sid-hashing) or relocated to the authentication
  *   subsystem.
  */
-class SessionManager extends NativeSessionStorage implements SessionManagerInterface {
-
-  /**
-   * Whether or not the session manager is operating in mixed mode SSL.
-   *
-   * @var bool
-   */
-  protected $mixedMode;
+class NativeSessionStorage extends SymfonyNativeSessionStorage implements SessionManagerInterface {
 
   /**
    * The request stack.
@@ -66,17 +58,6 @@ class SessionManager extends NativeSessionStorage implements SessionManagerInter
   protected $lazySession;
 
   /**
-   * Whether session management is enabled or temporarily disabled.
-   *
-   * PHP session ID, session, and cookie handling happens in the global scope.
-   * This value has to persist, since a potentially wrong or disallowed session
-   * would be written otherwise.
-   *
-   * @var bool
-   */
-  protected static $enabled = TRUE;
-
-  /**
    * Constructs a new session manager instance.
    *
    * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
@@ -85,24 +66,30 @@ class SessionManager extends NativeSessionStorage implements SessionManagerInter
    *   The database connection.
    * @param \Symfony\Component\HttpFoundation\Session\Storage\MetadataBag $metadata_bag
    *   The session metadata bag.
-   * @param \Drupal\Core\Site\Settings $settings
-   *   The settings instance.
+   * @param \Drupal\Core\Session\SessionHelper $session_helper
+   *   The session helper.
+   * @param \SessionHandlerInterface $session_handler
+   *   The session handler.
    */
-  public function __construct(RequestStack $request_stack, Connection $connection, SymfonyMetadataBag $metadata_bag, Settings $settings) {
+  public function __construct(RequestStack $request_stack, Connection $connection, SymfonyMetadataBag $metadata_bag, SessionHelper $session_helper, \SessionHandlerInterface $session_handler) {
     parent::__construct();
     $this->requestStack = $request_stack;
     $this->connection = $connection;
+    $metadata_bag->getLastUsed();
     $this->setMetadataBag($metadata_bag);
 
-    $this->setMixedMode($settings->get('mixed_mode_sessions', FALSE));
+    $this->sessionHelper = $session_helper;
 
     // @todo When not using the Symfony Session object, the list of bags in the
     //   NativeSessionStorage will remain uninitialized. This will lead to
-    //   errors in NativeSessionHandler::loadSession. Remove this after
+    //   errors in NativeSessionStorage::loadSession. Remove this after
     //   https://drupal.org/node/2229145, when we will be using the Symfony
     //   session object (which registers an attribute bag with the
     //   manager upon instantiation).
     $this->bags = array();
+
+    // Register the default session handler.
+    $this->setSaveHandler($session_handler);
   }
 
   /**
@@ -111,16 +98,10 @@ public function __construct(RequestStack $request_stack, Connection $connection,
   public function initialize() {
     global $user;
 
-    // Register the default session handler.
-    // @todo Extract session storage from session handler into a service.
-    $save_handler = new SessionHandler($this, $this->requestStack, $this->connection);
-    $write_check_handler = new WriteCheckSessionHandler($save_handler);
-    $this->setSaveHandler($write_check_handler);
-
     $is_https = $this->requestStack->getCurrentRequest()->isSecure();
     $cookies = $this->requestStack->getCurrentRequest()->cookies;
-    $insecure_session_name = $this->getInsecureName();
-    if (($cookies->has($this->getName()) && ($session_name = $cookies->get($this->getName()))) || ($is_https && $this->isMixedMode() && ($cookies->has($insecure_session_name) && ($session_name = $cookies->get($insecure_session_name))))) {
+    $insecure_session_name = $this->sessionHelper->getInsecureName($this->getName());
+    if (($cookies->has($this->getName()) && ($session_name = $cookies->get($this->getName()))) || ($is_https && $this->sessionHelper->isMixedMode() && ($cookies->has($insecure_session_name) && ($session_name = $cookies->get($insecure_session_name))))) {
       // If a session cookie exists, initialize the session. Otherwise the
       // session is only started on demand in save(), making
       // anonymous users not use a session cookie unless something is stored in
@@ -138,7 +119,7 @@ public function initialize() {
       $this->lazySession = TRUE;
       $user = new AnonymousUserSession();
       $this->setId(Crypt::randomBytesBase64());
-      if ($is_https && $this->isMixedMode()) {
+      if ($is_https && $this->sessionHelper->isMixedMode()) {
         $session_id = Crypt::randomBytesBase64();
         $cookies->set($insecure_session_name, $session_id);
       }
@@ -152,7 +133,7 @@ public function initialize() {
    * {@inheritdoc}
    */
   public function start() {
-    if (!$this->isEnabled() || $this->isCli()) {
+    if (!$this->sessionHelper->isEnabled() || $this->isCli()) {
       return;
     }
     // Save current session data before starting it, as PHP will destroy it.
@@ -174,25 +155,18 @@ public function start() {
   public function save() {
     global $user;
 
-    if (!$this->isEnabled()) {
+    if (!$this->sessionHelper->isEnabled()) {
       // We don't have anything to do if we are not allowed to save the session.
       return;
     }
 
-    if ($user->isAnonymous() && $this->isSessionObsolete()) {
-      // There is no session data to store, destroy the session if it was
-      // previously started.
-      if ($this->getSaveHandler()->isActive()) {
-        session_destroy();
-      }
-    }
-    else {
+    if (!$user->isAnonymous() || !$this->isSessionObsolete()) {
       // There is session data to store. Start the session if it is not already
       // started.
       if (!$this->isStarted()) {
         $this->start();
-        if ($this->requestStack->getCurrentRequest()->isSecure() && $this->isMixedMode()) {
-          $insecure_session_name = $this->getInsecureName();
+        if ($this->requestStack->getCurrentRequest()->isSecure() && $this->sessionHelper->isMixedMode()) {
+          $insecure_session_name = $this->sessionHelper->getInsecureName($this->getName());
           $params = session_get_cookie_params();
           $expire = $params['lifetime'] ? REQUEST_TIME + $params['lifetime'] : 0;
           $cookie_params = $this->requestStack->getCurrentRequest()->cookies;
@@ -211,21 +185,21 @@ public function regenerate($destroy = FALSE, $lifetime = NULL) {
     global $user;
 
     // Nothing to do if we are not allowed to change the session.
-    if (!$this->isEnabled()) {
+    if (!$this->sessionHelper->isEnabled()) {
       return;
     }
 
     // We do not support the optional $destroy and $lifetime parameters as long
     // as #2238561 remains open.
     if ($destroy || isset($lifetime)) {
-      throw new \InvalidArgumentException('The optional parameters $destroy and $lifetime of SessionManager::regenerate() are not supported currently');
+      throw new \InvalidArgumentException('The optional parameters $destroy and $lifetime of NativeSessionStorage::regenerate() are not supported currently');
     }
 
     $is_https = $this->requestStack->getCurrentRequest()->isSecure();
     $cookies = $this->requestStack->getCurrentRequest()->cookies;
 
-    if ($is_https && $this->isMixedMode()) {
-      $insecure_session_name = $this->getInsecureName();
+    if ($is_https && $this->sessionHelper->isMixedMode()) {
+      $insecure_session_name = $this->sessionHelper->getInsecureName($this->getName());;
       if (!isset($this->lazySession) && $cookies->has($insecure_session_name)) {
         $old_insecure_session_id = $cookies->get($insecure_session_name);
       }
@@ -260,7 +234,7 @@ public function regenerate($destroy = FALSE, $lifetime = NULL) {
         $fields['ssid'] = Crypt::hashBase64($this->getId());
         // If the "secure pages" setting is enabled, use the newly-created
         // insecure session identifier as the regenerated sid.
-        if ($this->isMixedMode()) {
+        if ($this->sessionHelper->isMixedMode()) {
           $fields['sid'] = Crypt::hashBase64($session_id);
         }
       }
@@ -281,7 +255,7 @@ public function regenerate($destroy = FALSE, $lifetime = NULL) {
     else {
       // Start the session when it doesn't exist yet.
       // Preserve the logged in user, as it will be reset to anonymous
-      // by \Drupal\Core\Session\SessionHandler::read().
+      // by \Drupal\Core\Session\Storage\Handler\SessionHandler::read().
       $account = $user;
       $this->start();
       $user = $account;
@@ -294,7 +268,7 @@ public function regenerate($destroy = FALSE, $lifetime = NULL) {
    */
   public function delete($uid) {
     // Nothing to do if we are not allowed to change the session.
-    if (!$this->isEnabled()) {
+    if (!$this->sessionHelper->isEnabled()) {
       return;
     }
     $this->connection->delete('sessions')
@@ -303,50 +277,6 @@ public function delete($uid) {
   }
 
   /**
-   * {@inheritdoc}
-   */
-  public function isEnabled() {
-    return static::$enabled;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function disable() {
-    static::$enabled = FALSE;
-    return $this;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function enable() {
-    static::$enabled = TRUE;
-    return $this;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function isMixedMode() {
-    return $this->mixedMode;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setMixedMode($mixed_mode) {
-    $this->mixedMode = (bool) $mixed_mode;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getInsecureName() {
-    return substr($this->getName(), 1);
-  }
-
-  /**
    * Returns whether the current PHP process runs on CLI.
    *
    * Command line clients do not support cookies nor sessions.
@@ -364,7 +294,7 @@ protected function isCli() {
    *   TRUE when the session does not contain any values and therefore can be
    *   destroyed.
    */
-  protected function isSessionObsolete() {
+  public function isSessionObsolete() {
     $used_session_keys = array_filter($this->getSessionDataMask());
     return empty($used_session_keys);
   }
@@ -403,7 +333,7 @@ protected function getSessionDataMask() {
     // Ignore attribute bags when they do not contain any data.
     foreach ($this->bags as $bag) {
       $key = $bag->getStorageKey();
-      $mask[$key] = empty($_SESSION[$key]);
+      $mask[$key] = !empty($_SESSION[$key]);
     }
 
     return array_intersect_key($mask, $_SESSION);
diff --git a/core/modules/simpletest/src/TestBase.php b/core/modules/simpletest/src/TestBase.php
index 0987d3d..9ee48b9 100644
--- a/core/modules/simpletest/src/TestBase.php
+++ b/core/modules/simpletest/src/TestBase.php
@@ -1037,7 +1037,7 @@ private function prepareEnvironment() {
     $this->originalUser = isset($user) ? clone $user : NULL;
 
     // Ensure that the current session is not changed by the new environment.
-    \Drupal::service('session_manager')->disable();
+    \Drupal::service('session.helper')->disable();
 
     // Save and clean the shutdown callbacks array because it is static cached
     // and will be changed by the test run. Otherwise it will contain callbacks
@@ -1237,7 +1237,7 @@ private function restoreEnvironment() {
 
     // Restore original user session.
     $this->container->set('current_user', $this->originalUser);
-    \Drupal::service('session_manager')->enable();
+    \Drupal::service('session.helper')->enable();
   }
 
   /**
diff --git a/core/modules/system/src/Tests/Common/FormatDateTest.php b/core/modules/system/src/Tests/Common/FormatDateTest.php
index 7fd5683..afa3060 100644
--- a/core/modules/system/src/Tests/Common/FormatDateTest.php
+++ b/core/modules/system/src/Tests/Common/FormatDateTest.php
@@ -129,7 +129,7 @@ function testFormatDate() {
     $this->drupalPostForm('user/' . $test_user->id() . '/edit', $edit, t('Save'));
 
     // Disable session saving as we are about to modify the global $user.
-    \Drupal::service('session_manager')->disable();
+    \Drupal::service('session.helper')->disable();
     // Save the original user and language and then replace it with the test user and language.
     $real_user = $user;
     $user = user_load($test_user->id(), TRUE);
@@ -159,6 +159,6 @@ function testFormatDate() {
     $language_interface->id = $real_language;
     // Restore default time zone.
     date_default_timezone_set(drupal_get_user_timezone());
-    \Drupal::service('session_manager')->enable();
+    \Drupal::service('session.helper')->enable();
   }
 }
diff --git a/core/modules/system/src/Tests/Datetime/DrupalDateTimeTest.php b/core/modules/system/src/Tests/Datetime/DrupalDateTimeTest.php
index 781d6b0..34206e9 100644
--- a/core/modules/system/src/Tests/Datetime/DrupalDateTimeTest.php
+++ b/core/modules/system/src/Tests/Datetime/DrupalDateTimeTest.php
@@ -103,7 +103,7 @@ public function testDateTimezone() {
     $this->drupalPostForm('user/' . $test_user->id() . '/edit', $edit, t('Save'));
 
     // Disable session saving as we are about to modify the global $user.
-    \Drupal::service('session_manager')->disable();
+    \Drupal::service('session.helper')->disable();
     // Save the original user and then replace it with the test user.
     $real_user = $user;
     $user = user_load($test_user->id(), TRUE);
@@ -122,7 +122,7 @@ public function testDateTimezone() {
     $user = $real_user;
     // Restore default time zone.
     date_default_timezone_set(drupal_get_user_timezone());
-    \Drupal::service('session_manager')->enable();
+    \Drupal::service('session.helper')->enable();
 
 
   }
diff --git a/core/modules/system/src/Tests/Session/SessionTest.php b/core/modules/system/src/Tests/Session/SessionTest.php
index 720f16f..178eccd 100644
--- a/core/modules/system/src/Tests/Session/SessionTest.php
+++ b/core/modules/system/src/Tests/Session/SessionTest.php
@@ -32,13 +32,9 @@ public static function getInfo() {
   }
 
   /**
-   * Tests for \Drupal\Core\Session\SessionManager::isEnabled() and ::regenerate().
+   * Tests for \Drupal\Core\Session\SessionManager::regenerate().
    */
   function testSessionSaveRegenerate() {
-    $session_manager = $this->container->get('session_manager');
-    $this->assertFalse($session_manager->isEnabled(), 'SessionManager->isEnabled() initially returns FALSE (in testing framework).');
-    $this->assertFalse($session_manager->disable()->isEnabled(), 'SessionManager->isEnabled() returns FALSE after disabling.');
-    $this->assertTrue($session_manager->enable()->isEnabled(), 'SessionManager->isEnabled() returns TRUE after enabling.');
 
     // Test session hardening code from SA-2008-044.
     $user = $this->drupalCreateUser();
diff --git a/core/modules/system/tests/modules/session_test/src/Controller/SessionTestController.php b/core/modules/system/tests/modules/session_test/src/Controller/SessionTestController.php
index 5fbd412..8227e2a 100644
--- a/core/modules/system/tests/modules/session_test/src/Controller/SessionTestController.php
+++ b/core/modules/system/tests/modules/session_test/src/Controller/SessionTestController.php
@@ -35,11 +35,11 @@ public function get() {
    *   A notification message with session ID.
    */
   public function getId() {
-    // Set a value in $_SESSION, so that SessionManager::save() will start
+    // Set a value in $_SESSION, so that NativeSessionStorage::save() will start
     // a session.
     $_SESSION['test'] = 'test';
 
-    \Drupal::service('session_manager')->save();
+    \Drupal::service('session.storage')->save();
 
     return 'session_id:' . session_id() . "\n";
   }
@@ -83,7 +83,7 @@ public function set($test_value) {
    *   A notification message.
    */
   public function noSet($test_value) {
-    \Drupal::service('session_manager')->disable();
+    \Drupal::service('session.helper')->disable();
     $this->set($test_value);
     return $this->t('session saving was disabled, and then %val was set', array('%val' => $test_value));
   }
@@ -109,7 +109,7 @@ public function setMessage() {
    *   A notification message.
    */
   public function setMessageButDontSave() {
-    \Drupal::service('session_manager')->disable();
+    \Drupal::service('session.helper')->disable();
     $this->setMessage();
   }
 
diff --git a/core/modules/user/src/Entity/User.php b/core/modules/user/src/Entity/User.php
index 3acb5b4..38aa326 100644
--- a/core/modules/user/src/Entity/User.php
+++ b/core/modules/user/src/Entity/User.php
@@ -118,7 +118,7 @@ public function postSave(EntityStorageInterface $storage, $update = TRUE) {
     parent::postSave($storage, $update);
 
     if ($update) {
-      $session_manager = \Drupal::service('session_manager');
+      $session_manager = \Drupal::service('session.storage');
       // If the password has been changed, delete all open sessions for the
       // user and recreate the current one.
       if ($this->pass->value != $this->original->pass->value) {
diff --git a/core/modules/user/src/Tests/Views/ArgumentDefaultTest.php b/core/modules/user/src/Tests/Views/ArgumentDefaultTest.php
index c8de3a3..c2add26 100644
--- a/core/modules/user/src/Tests/Views/ArgumentDefaultTest.php
+++ b/core/modules/user/src/Tests/Views/ArgumentDefaultTest.php
@@ -37,7 +37,7 @@ public function test_plugin_argument_default_current_user() {
     $this->drupalLogin($account);
     global $user;
     $admin = $user;
-    $session_manager = \Drupal::service('session_manager')->disable();
+    $session_helper = \Drupal::service('session.helper')->disable();
     $user = $account;
 
     $view = Views::getView('test_plugin_argument_default_current_user');
@@ -46,7 +46,7 @@ public function test_plugin_argument_default_current_user() {
     $this->assertEqual($view->argument['null']->getDefaultArgument(), $account->id(), 'Uid of the current user is used.');
     // Switch back.
     $user = $admin;
-    $session_manager->enable();
+    $session_helper->enable();
   }
 
 }
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 5e072cb..41b7be2 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -785,7 +785,7 @@ function user_login_finalize(UserInterface $account) {
   // Regenerate the session ID to prevent against session fixation attacks.
   // This is called before hook_user in case one of those functions fails
   // or incorrectly does a redirect which would leave the old session in place.
-  \Drupal::service('session_manager')->regenerate();
+  \Drupal::service('session.storage')->regenerate();
 
   \Drupal::moduleHandler()->invokeAll('user_login', array($account));
 }
@@ -1000,7 +1000,7 @@ function _user_cancel($edit, $account, $method) {
 function _user_cancel_session_regenerate() {
   // Regenerate the users session instead of calling session_destroy() as we
   // want to preserve any messages that might have been set.
-  \Drupal::service('session_manager')->regenerate();
+  \Drupal::service('session.storage')->regenerate();
 }
 
 /**
diff --git a/core/modules/user/user.services.yml b/core/modules/user/user.services.yml
index 1e8f9b3..3814f20 100644
--- a/core/modules/user/user.services.yml
+++ b/core/modules/user/user.services.yml
@@ -17,7 +17,7 @@ services:
       - { name: access_check, applies_to: _user_is_logged_in }
   authentication.cookie:
     class: Drupal\Core\Authentication\Provider\Cookie
-    arguments: ['@session_manager']
+    arguments: ['@session.storage']
     tags:
       - { name: authentication_provider, priority: 0 }
   cache_context.user:
diff --git a/core/tests/Drupal/Tests/Core/Session/SessionHelperTest.php b/core/tests/Drupal/Tests/Core/Session/SessionHelperTest.php
new file mode 100644
index 0000000..8df9f91
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Session/SessionHelperTest.php
@@ -0,0 +1,87 @@
+<?php
+/**
+ * @file
+ * Contains Drupal\Test\Core\Session
+ */
+
+namespace Drupal\Tests\Core\Session;
+use Drupal\Core\Session\SessionHelper;
+use Drupal\Core\Site\Settings;
+use Drupal\Tests\UnitTestCase;
+
+
+/**
+ * Tests the SessionHelper class.
+ *
+ * @group Drupal
+ *
+ * @coversDefaultClass \Drupal\Core\Session\SessionHelper
+ */
+class SessionHelperTest extends UnitTestCase {
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Tests the SessionHelper class.',
+      'description' => 'Tests the SessionHelper class.',
+      'group' => 'Session',
+    );
+  }
+
+  /**
+   * @var \Drupal\Core\Session\SessionHelper
+   */
+  protected $sessionHelper;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+    $settings = new Settings(array());
+    $this->sessionHelper = new SessionHelper($settings);
+  }
+
+  /**
+   * Tests mixed mode setting.
+   *
+   * @covers ::__construct()
+   * @covers ::isMixedMode()
+   */
+  public function testIsMixedMode() {
+    $this->assertFalse($this->sessionHelper->isMixedMode(), 'Mixed mode defaults false.');
+
+    $settings = new Settings(array('mixed_mode_sessions' => TRUE));
+    $this->sessionHelper = new SessionHelper($settings);
+    $this->assertTrue($this->sessionHelper->isMixedMode(), 'Mixed mode set to true.');
+
+    $settings = new Settings(array('mixed_mode_sessions' => FALSE));
+    $this->sessionHelper = new SessionHelper($settings);
+    $this->assertFalse($this->sessionHelper->isMixedMode(), 'Mixed mode set to false.');
+  }
+
+  /**
+   * Tests mixed mode is enabled default.
+   *
+   * @covers ::isEnabled()
+   * @covers ::disable()
+   * @covers ::enable()
+   */
+  public function testIsEnabled() {
+    $this->assertTrue($this->sessionHelper->isEnabled(), '::isEnabled() initially returns TRUE.');
+    $this->assertFalse($this->sessionHelper->disable()->isEnabled(), '::isEnabled() returns FALSE after disabling.');
+    $this->assertTrue($this->sessionHelper->enable()->isEnabled(), '::isEnabled() returns TRUE after enabling.');
+  }
+
+  /**
+   * Tests converting session name to insecure name.
+   *
+   * @covers ::getInsecureName()
+   */
+  public function testGetInsecureName() {
+    $this->assertEquals('SFooBar', $this->sessionHelper->getInsecureName('SSFooBar'));
+    $this->assertEquals('SFooBar', $this->sessionHelper->getInsecureName('SFooBar'));
+  }
+
+}
diff --git a/core/update.php b/core/update.php
index b6accf3..0c434e2 100644
--- a/core/update.php
+++ b/core/update.php
@@ -342,7 +342,7 @@ function update_task_list($active = NULL) {
 // Determine if the current user has access to run update.php.
 drupal_bootstrap(DRUPAL_BOOTSTRAP_PAGE_CACHE);
 
-\Drupal::service('session_manager')->initialize();
+\Drupal::service('session.storage')->initialize();
 
 // Ensure that URLs generated for the home and admin pages don't have 'update.php'
 // in them.
@@ -450,7 +450,7 @@ function update_task_list($active = NULL) {
 }
 if (isset($output) && $output) {
   // Explicitly start a session so that the update.php token will be accepted.
-  \Drupal::service('session_manager')->start();
+  \Drupal::service('session.storage')->start();
   // We defer the display of messages until all updates are done.
   $progress_page = ($batch = batch_get()) && isset($batch['running']);
   if ($output instanceof Response) {
