diff --git a/core/core.services.yml b/core/core.services.yml
index 8af072f..787c145 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -564,6 +564,15 @@ services:
   plugin.manager.condition:
     class: Drupal\Core\Condition\ConditionManager
     parent: default_plugin_manager
+  stream_wrapper_manager:
+    class: Drupal\Core\StreamWrapper\StreamWrapperManager
+    arguments: ['@module_handler']
+    calls:
+      - [setContainer, ['@service_container']]
+  stream_wrapper.public:
+    class: Drupal\Core\StreamWrapper\PublicStream
+    tags:
+      - { name: stream_wrapper, scheme: public }
   kernel_destruct_subscriber:
     class: Drupal\Core\EventSubscriber\KernelDestructionSubscriber
     tags:
diff --git a/core/includes/file.inc b/core/includes/file.inc
index e3e9326..87424fc 100644
--- a/core/includes/file.inc
+++ b/core/includes/file.inc
@@ -5,43 +5,11 @@
  * API for handling file uploads and server file management.
  */
 
-use Drupal\Core\StreamWrapper\LocalStream;
 use Drupal\Component\PhpStorage\MTimeProtectedFastFileStorage;
 use Drupal\Component\Utility\String;
+use Drupal\Core\StreamWrapper\LocalStream;
 use Drupal\Core\StreamWrapper\PublicStream;
-
-/**
- * Stream wrapper bit flags that are the basis for composite types.
- *
- * Note that 0x0002 is skipped, because it was the value of a constant that has
- * since been removed.
- */
-
-/**
- * Stream wrapper bit flag -- a filter that matches all wrappers.
- */
-const STREAM_WRAPPERS_ALL = 0x0000;
-
-/**
- * Stream wrapper bit flag -- refers to a local file system location.
- */
-const STREAM_WRAPPERS_LOCAL = 0x0001;
-
-/**
- * Stream wrapper bit flag -- wrapper is readable (almost always true).
- */
-const STREAM_WRAPPERS_READ = 0x0004;
-
-/**
- * Stream wrapper bit flag -- wrapper is writeable.
- */
-const STREAM_WRAPPERS_WRITE = 0x0008;
-
-/**
- * Stream wrapper bit flag -- exposed in the UI and potentially web accessible.
- */
-const STREAM_WRAPPERS_VISIBLE = 0x0010;
-
+use Drupal\Core\StreamWrapper\StreamWrapperInterface;
 
 /**
  * Default mode for new directories. See drupal_chmod().
@@ -54,45 +22,6 @@
 const FILE_CHMOD_FILE = 0664;
 
 /**
- * Composite stream wrapper bit flags that are usually used as the types.
- */
-
-/**
- * Stream wrapper type flag -- not visible in the UI or accessible via web,
- * but readable and writable. E.g. the temporary directory for uploads.
- */
-define('STREAM_WRAPPERS_HIDDEN', STREAM_WRAPPERS_READ | STREAM_WRAPPERS_WRITE);
-
-/**
- * Stream wrapper type flag -- hidden, readable and writeable using local files.
- */
-define('STREAM_WRAPPERS_LOCAL_HIDDEN', STREAM_WRAPPERS_LOCAL | STREAM_WRAPPERS_HIDDEN);
-
-/**
- * Stream wrapper type flag -- visible, readable and writeable.
- */
-define('STREAM_WRAPPERS_WRITE_VISIBLE', STREAM_WRAPPERS_READ | STREAM_WRAPPERS_WRITE | STREAM_WRAPPERS_VISIBLE);
-
-/**
- * Stream wrapper type flag -- visible and read-only.
- */
-define('STREAM_WRAPPERS_READ_VISIBLE', STREAM_WRAPPERS_READ | STREAM_WRAPPERS_VISIBLE);
-
-/**
- * Stream wrapper type flag -- the default when 'type' is omitted from
- * hook_stream_wrappers(). This does not include STREAM_WRAPPERS_LOCAL,
- * because PHP grants a greater trust level to local files (for example, they
- * can be used in an "include" statement, regardless of the "allow_url_include"
- * setting), so stream wrappers need to explicitly opt-in to this.
- */
-define('STREAM_WRAPPERS_NORMAL', STREAM_WRAPPERS_WRITE_VISIBLE);
-
-/**
- * Stream wrapper type flag -- visible, readable and writeable using local files.
- */
-define('STREAM_WRAPPERS_LOCAL_NORMAL', STREAM_WRAPPERS_LOCAL | STREAM_WRAPPERS_NORMAL);
-
-/**
  * @defgroup file File interface
  * @{
  * Common file handling functions.
@@ -150,125 +79,49 @@
 /**
  * Provides Drupal stream wrapper registry.
  *
- * A stream wrapper is an abstraction of a file system that allows Drupal to
- * use the same set of methods to access both local files and remote resources.
- *
- * Provide a facility for managing and querying user-defined stream wrappers
- * in PHP. PHP's internal stream_get_wrappers() doesn't return the class
- * registered to handle a stream, which we need to be able to find the handler
- * for class instantiation.
  *
- * If a module registers a scheme that is already registered with PHP, the
- * existing scheme will be unregistered and replaced with the specified class.
- *
- * A stream is referenced as "scheme://target".
- *
- * The optional $filter parameter can be used to retrieve only the stream
- * wrappers that are appropriate for particular usage. For example, this returns
- * only stream wrappers that use local file storage:
- * @code
- *   $local_stream_wrappers = file_get_stream_wrappers(STREAM_WRAPPERS_LOCAL);
- * @endcode
- *
- * The $filter parameter can only filter to types containing a particular flag.
- * In some cases, you may want to filter to types that do not contain a
- * particular flag. For example, you may want to retrieve all stream wrappers
- * that are not writable, or all stream wrappers that are not local. PHP's
- * array_diff_key() function can be used to help with this. For example, this
- * returns only stream wrappers that do not use local file storage:
- * @code
- *   $remote_stream_wrappers = array_diff_key(file_get_stream_wrappers(STREAM_WRAPPERS_ALL), file_get_stream_wrappers(STREAM_WRAPPERS_LOCAL));
- * @endcode
- *
- * @param $filter
+ * @param int $filter
  *   (Optional) Filters out all types except those with an on bit for each on
- *   bit in $filter. For example, if $filter is STREAM_WRAPPERS_WRITE_VISIBLE,
- *   which is equal to (STREAM_WRAPPERS_READ | STREAM_WRAPPERS_WRITE |
- *   STREAM_WRAPPERS_VISIBLE), then only stream wrappers with all three of these
- *   bits set are returned. Defaults to STREAM_WRAPPERS_ALL, which returns all
+ *   bit in $filter. For example, if $filter is StreamWrapperInterface::WRITE_VISIBLE,
+ *   which is equal to (StreamWrapperInterface::READ | StreamWrapperInterface::WRITE |
+ *   StreamWrapperInterface::VISIBLE), then only stream wrappers with all three of these
+ *   bits set are returned. Defaults to StreamWrapperInterface::ALL, which returns all
  *   registered stream wrappers.
  *
- * @return
+ * @return array
  *   An array keyed by scheme, with values containing an array of information
  *   about the stream wrapper, as returned by hook_stream_wrappers(). If $filter
- *   is omitted or set to STREAM_WRAPPERS_ALL, the entire Drupal stream wrapper
+ *   is omitted or set to StreamWrapperInterface::ALL, the entire Drupal stream wrapper
  *   registry is returned. Otherwise only the stream wrappers whose 'type'
  *   bitmask has an on bit for each bit specified in $filter are returned.
  *
- * @see hook_stream_wrappers()
  * @see hook_stream_wrappers_alter()
+ * @see \Drupal\Core\StreamWrapper\StreamWrapperManager
  */
-function file_get_stream_wrappers($filter = STREAM_WRAPPERS_ALL) {
-  $wrappers_storage = &drupal_static(__FUNCTION__);
-
-  if (!isset($wrappers_storage)) {
-    $wrappers = \Drupal::moduleHandler()->invokeAll('stream_wrappers');
-    foreach ($wrappers as $scheme => $info) {
-      // Add defaults.
-      $wrappers[$scheme] += array('type' => STREAM_WRAPPERS_NORMAL);
-    }
-    drupal_alter('stream_wrappers', $wrappers);
-    $existing = stream_get_wrappers();
-    foreach ($wrappers as $scheme => $info) {
-      // We only register classes that implement our interface.
-      if (in_array('Drupal\Core\StreamWrapper\StreamWrapperInterface', class_implements($info['class']), TRUE)) {
-        // Record whether we are overriding an existing scheme.
-        if (in_array($scheme, $existing, TRUE)) {
-          $wrappers[$scheme]['override'] = TRUE;
-          stream_wrapper_unregister($scheme);
-        }
-        else {
-          $wrappers[$scheme]['override'] = FALSE;
-        }
-        if (($info['type'] & STREAM_WRAPPERS_LOCAL) == STREAM_WRAPPERS_LOCAL) {
-          stream_wrapper_register($scheme, $info['class']);
-        }
-        else {
-          stream_wrapper_register($scheme, $info['class'], STREAM_IS_URL);
-        }
-      }
-      // Pre-populate the static cache with the filters most typically used.
-      $wrappers_storage[STREAM_WRAPPERS_ALL][$scheme] = $wrappers[$scheme];
-      if (($info['type'] & STREAM_WRAPPERS_WRITE_VISIBLE) == STREAM_WRAPPERS_WRITE_VISIBLE) {
-        $wrappers_storage[STREAM_WRAPPERS_WRITE_VISIBLE][$scheme] = $wrappers[$scheme];
-      }
-    }
-  }
-
-  if (!isset($wrappers_storage[$filter])) {
-    $wrappers_storage[$filter] = array();
-    foreach ($wrappers_storage[STREAM_WRAPPERS_ALL] as $scheme => $info) {
-      // Bit-wise filter.
-      if (($info['type'] & $filter) == $filter) {
-        $wrappers_storage[$filter][$scheme] = $info;
-      }
-    }
-  }
-
-  return $wrappers_storage[$filter];
+function file_get_stream_wrappers($filter = StreamWrapperInterface::ALL) {
+  return \Drupal::service('stream_wrapper_manager')->getWrappers($filter);
 }
 
 /**
  * Returns the stream wrapper class name for a given scheme.
  *
- * @param $scheme
+ * @param string $scheme
  *   Stream scheme.
  *
- * @return
+ * @return string|bool
  *   Return string if a scheme has a registered handler, or FALSE.
  */
 function file_stream_wrapper_get_class($scheme) {
-  $wrappers = file_get_stream_wrappers();
-  return empty($wrappers[$scheme]) ? FALSE : $wrappers[$scheme]['class'];
+  return \Drupal::service('stream_wrapper_manager')->getClass($scheme);
 }
 
 /**
  * Returns the scheme of a URI (e.g. a stream).
  *
- * @param $uri
+ * @param string $uri
  *   A stream, referenced as "scheme://target".
  *
- * @return
+ * @return string|bool
  *   A string containing the name of the scheme, or FALSE if none. For example,
  *   the URI "public://example.txt" would return "public".
  *
@@ -286,10 +139,10 @@ function file_uri_scheme($uri) {
  * and that it is callable. This is useful if you want to confirm a valid
  * scheme without creating a new instance of the registered handler.
  *
- * @param $scheme
+ * @param string $scheme
  *   A URI scheme, a stream is referenced as "scheme://target".
  *
- * @return
+ * @return bool
  *   Returns TRUE if the string is the name of a validated stream,
  *   or FALSE if the scheme does not have a registered handler.
  */
@@ -301,10 +154,10 @@ function file_stream_wrapper_valid_scheme($scheme) {
 /**
  * Returns the part of a URI after the schema.
  *
- * @param $uri
+ * @param string $uri
  *   A stream, referenced as "scheme://target".
  *
- * @return
+ * @return string|bool
  *   A string containing the target (path), or FALSE if none.
  *   For example, the URI "public://sample/test.txt" would return
  *   "sample/test.txt".
@@ -321,7 +174,7 @@ function file_uri_target($uri) {
 /**
  * Gets the default file stream implementation.
  *
- * @return
+ * @return string
  *   'public', 'private' or any other file scheme defined as the default.
  */
 function file_default_scheme() {
@@ -337,10 +190,10 @@ function file_default_scheme() {
  * - Remove trailing slashes from target
  * - Trim erroneous leading slashes from target. e.g. ":///" becomes "://".
  *
- * @param $uri
+ * @param string $uri
  *   String reference containing the URI to normalize.
  *
- * @return
+ * @return string
  *   The normalized URI.
  */
 function file_stream_wrapper_uri_normalize($uri) {
@@ -363,25 +216,17 @@ function file_stream_wrapper_uri_normalize($uri) {
  * The scheme determines the stream wrapper class that should be
  * used by consulting the stream wrapper registry.
  *
- * @param $uri
+ * @param string $uri
  *   A stream, referenced as "scheme://target".
  *
- * @return
+ * @return \Drupal\Core\StreamWrapper\StreamWrapperInterface|bool
  *   Returns a new stream wrapper object appropriate for the given URI or FALSE
  *   if no registered handler could be found. For example, a URI of
  *   "private://example.txt" would return a new private stream wrapper object
  *   (Drupal\Core\StreamWrapper\PrivateStream).
  */
 function file_stream_wrapper_get_instance_by_uri($uri) {
-  if ($scheme = file_uri_scheme($uri)) {
-    $class = file_stream_wrapper_get_class($scheme);
-    if (class_exists($class)) {
-      $instance = new $class();
-      $instance->setUri($uri);
-      return $instance;
-    }
-  }
-  return FALSE;
+  return \Drupal::service('stream_wrapper_manager')->getViaUri($uri);
 }
 
 /**
@@ -395,25 +240,17 @@ function file_stream_wrapper_get_instance_by_uri($uri) {
  * Note: the instance URI will be initialized to "scheme://" so that you can
  * make the customary method calls as if you had retrieved an instance by URI.
  *
- * @param $scheme
+ * @param string $scheme
  *   If the stream was "public://target", "public" would be the scheme.
  *
- * @return \Drupal\Core\StreamWrapper\StreamWrapperInterface
+ * @return \Drupal\Core\StreamWrapper\StreamWrapperInterface|bool
  *   Returns a new stream wrapper object appropriate for the given $scheme.
  *   For example, for the public scheme a stream wrapper object
  *   (Drupal\Core\StreamWrapper\PublicStream).
  *   FALSE is returned if no registered handler could be found.
  */
 function file_stream_wrapper_get_instance_by_scheme($scheme) {
-  $class = file_stream_wrapper_get_class($scheme);
-  if (class_exists($class)) {
-    $instance = new $class();
-    $instance->setUri($scheme . '://');
-    return $instance;
-  }
-  else {
-    return FALSE;
-  }
+  return \Drupal::service('stream_wrapper_manager')->getViaScheme($scheme);
 }
 
 /**
diff --git a/core/includes/module.inc b/core/includes/module.inc
index 429f5db..c9a13ff 100644
--- a/core/includes/module.inc
+++ b/core/includes/module.inc
@@ -30,7 +30,7 @@
  */
 function system_list($type) {
   $lists = &drupal_static(__FUNCTION__);
-  if ($cached = cache('bootstrap')->get('system_list')) {
+  if ($cached = \Drupal::cache('bootstrap')->get('system_list')) {
     $lists = $cached->data;
   }
   else {
@@ -92,7 +92,7 @@ function system_list($type) {
       // Set the theme engine prefix.
       $lists['theme'][$key]->prefix = ($lists['theme'][$key]->info['engine'] == 'theme') ? $base_key : $lists['theme'][$key]->info['engine'];
     }
-    cache('bootstrap')->set('system_list', $lists);
+    \Drupal::cache('bootstrap')->set('system_list', $lists);
   }
   // To avoid a separate database lookup for the filepath, prime the
   // drupal_get_filename() static cache with all enabled modules and themes.
@@ -110,8 +110,8 @@ function system_list_reset() {
   drupal_static_reset('system_list');
   drupal_static_reset('system_rebuild_module_data');
   drupal_static_reset('list_themes');
-  cache('bootstrap')->delete('system_list');
-  cache()->delete('system_info');
+  \Drupal::cache('bootstrap')->delete('system_list');
+  \Drupal::cache()->delete('system_info');
   // Remove last known theme data state.
   // This causes system_list() to call system_rebuild_theme_data() on its next
   // invocation. When enabling a module that implements hook_system_info_alter()
diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php
index 098b165..c4b72f4 100644
--- a/core/lib/Drupal/Core/CoreServiceProvider.php
+++ b/core/lib/Drupal/Core/CoreServiceProvider.php
@@ -8,27 +8,28 @@
 namespace Drupal\Core;
 
 use Drupal\Core\Cache\ListCacheBinsPass;
-use Drupal\Core\DependencyInjection\ServiceProviderInterface;
-use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\DependencyInjection\Compiler\ModifyServiceDefinitionsPass;
-use Drupal\Core\DependencyInjection\Compiler\RegisterKernelListenersPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterAccessChecksPass;
+use Drupal\Core\DependencyInjection\Compiler\RegisterAuthenticationPass;
+use Drupal\Core\DependencyInjection\Compiler\RegisterBreadcrumbBuilderPass;
+use Drupal\Core\DependencyInjection\Compiler\RegisterKernelListenersPass;
+use Drupal\Core\DependencyInjection\Compiler\RegisterParamConvertersPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterPathProcessorsPass;
-use Drupal\Core\DependencyInjection\Compiler\RegisterRouteProcessorsPass;
-use Drupal\Core\DependencyInjection\Compiler\RegisterRouteFiltersPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterRouteEnhancersPass;
-use Drupal\Core\DependencyInjection\Compiler\RegisterParamConvertersPass;
+use Drupal\Core\DependencyInjection\Compiler\RegisterRouteFiltersPass;
+use Drupal\Core\DependencyInjection\Compiler\RegisterRouteProcessorsPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterServicesForDestructionPass;
+use Drupal\Core\DependencyInjection\Compiler\RegisterStreamWrappersPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterStringTranslatorsPass;
-use Drupal\Core\DependencyInjection\Compiler\RegisterBreadcrumbBuilderPass;
-use Drupal\Core\DependencyInjection\Compiler\RegisterAuthenticationPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterTwigExtensionsPass;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\DependencyInjection\ServiceProviderInterface;
 use Drupal\Core\Theme\ThemeNegotiatorPass;
+use Symfony\Component\DependencyInjection\Compiler\PassConfig;
 use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\DependencyInjection\Reference;
 use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Reference;
 use Symfony\Component\DependencyInjection\Scope;
-use Symfony\Component\DependencyInjection\Compiler\PassConfig;
 
 /**
  * ServiceProvider class for mandatory core services.
@@ -54,6 +55,8 @@ public function register(ContainerBuilder $container) {
     $this->registerModuleHandler($container);
     $this->registerUuid($container);
 
+    $container->addCompilerPass(new RegisterStreamWrappersPass());
+
     $container->addCompilerPass(new RegisterRouteFiltersPass());
     // Add a compiler pass for registering event subscribers.
     $container->addCompilerPass(new RegisterKernelListenersPass(), PassConfig::TYPE_AFTER_REMOVING);
diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterStreamWrappersPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterStreamWrappersPass.php
new file mode 100644
index 0000000..01193d3
--- /dev/null
+++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterStreamWrappersPass.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\DependencyInjection\Compiler\RegisterStreamWrappersPass.
+ */
+
+namespace Drupal\Core\DependencyInjection\Compiler;
+
+use Drupal\Core\StreamWrapper\StreamWrapperInterface;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * Adds services tagged 'stream_wrapper' to the stream_wrapper_manager service.
+ */
+class RegisterStreamWrappersPass implements CompilerPassInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function process(ContainerBuilder $container) {
+    if (!$container->hasDefinition('stream_wrapper_manager')) {
+      return;
+    }
+
+    $stream_wrapper_manager = $container->getDefinition('stream_wrapper_manager');
+
+    foreach ($container->findTaggedServiceIds('stream_wrapper') as $id => $attributes) {
+      $class = $container->getDefinition($id)->getClass();
+      $scheme = $attributes[0]['scheme'];
+
+      $stream_wrapper_manager->addMethodCall('addStreamWrapper', array($id, $class, $scheme));
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
index 0160119..a3230c2 100644
--- a/core/lib/Drupal/Core/DrupalKernel.php
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -175,6 +175,7 @@ public function boot() {
       return;
     }
     $this->initializeContainer();
+    $this->container->get('stream_wrapper_manager')->register();
     $this->booted = TRUE;
     if ($this->containerNeedsDumping && !$this->dumpDrupalContainer($this->container, static::CONTAINER_BASE_CLASS)) {
       watchdog('DrupalKernel', 'Container cannot be written to disk');
@@ -188,6 +189,7 @@ public function shutdown() {
     if (FALSE === $this->booted) {
       return;
     }
+    $this->container->get('stream_wrapper_manager')->unregister();
     $this->booted = FALSE;
     $this->container = NULL;
   }
diff --git a/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php b/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php
index 65f26a0..cf2fa1f 100644
--- a/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php
+++ b/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php
@@ -42,8 +42,6 @@ public function getImplementations($hook) {
       // Forms do not render without the basic elements in
       // drupal_common_theme() called from system_theme().
       case 'theme':
-      // user_update_8011() uses public:// to create the image style directory.
-      case 'stream_wrappers':
         return array('system');
 
       // This is called during rebuild to find testing themes.
@@ -54,11 +52,6 @@ public function getImplementations($hook) {
       case 'user_role_load':
         return array();
 
-      // t() in system_stream_wrappers() needs this. Other schema calls aren't
-      // supported.
-      case 'schema':
-        return array('locale');
-
       default:
         throw new \LogicException("Invoking hooks $hook is not supported during updates");
     }
diff --git a/core/lib/Drupal/Core/StreamWrapper/LocalStream.php b/core/lib/Drupal/Core/StreamWrapper/LocalStream.php
index 5c1ee6f..b32d1e3 100644
--- a/core/lib/Drupal/Core/StreamWrapper/LocalStream.php
+++ b/core/lib/Drupal/Core/StreamWrapper/LocalStream.php
@@ -43,6 +43,13 @@
   protected $uri;
 
   /**
+   * {@inheritdoc}
+   */
+  public static function getType() {
+    return StreamWrapperInterface::NORMAL;
+  }
+
+  /**
    * Gets the path that the wrapper is responsible for.
    *
    * @todo Review this method name in D8 per http://drupal.org/node/701358.
diff --git a/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php b/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php
index 39b3f28..6bcd56b 100644
--- a/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php
+++ b/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php
@@ -16,6 +16,27 @@
 class PrivateStream extends LocalStream {
 
   /**
+   * {@inheritdoc}
+   */
+  public static function getType() {
+    return StreamWrapperInterface::LOCAL_NORMAL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getName() {
+    return t('Private files');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    return t('Private local files served by Drupal.');
+  }
+
+  /**
    * Implements Drupal\Core\StreamWrapper\LocalStream::getDirectoryPath()
    */
   public function getDirectoryPath() {
diff --git a/core/lib/Drupal/Core/StreamWrapper/PublicStream.php b/core/lib/Drupal/Core/StreamWrapper/PublicStream.php
index 90f17b3..e028ad0 100644
--- a/core/lib/Drupal/Core/StreamWrapper/PublicStream.php
+++ b/core/lib/Drupal/Core/StreamWrapper/PublicStream.php
@@ -18,6 +18,27 @@ class PublicStream extends LocalStream {
   /**
    * {@inheritdoc}
    */
+  public static function getType() {
+    return StreamWrapperInterface::LOCAL_NORMAL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getName() {
+    return t('Public files');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    return t('Public local files served by the webserver.');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getDirectoryPath() {
     return static::basePath();
   }
diff --git a/core/lib/Drupal/Core/StreamWrapper/StreamWrapperInterface.php b/core/lib/Drupal/Core/StreamWrapper/StreamWrapperInterface.php
index 9830a01..9992e1e 100644
--- a/core/lib/Drupal/Core/StreamWrapper/StreamWrapperInterface.php
+++ b/core/lib/Drupal/Core/StreamWrapper/StreamWrapperInterface.php
@@ -30,6 +30,100 @@
 interface StreamWrapperInterface extends PhpStreamWrapperInterface {
 
   /**
+   * Stream wrapper bit flags that are the basis for composite types.
+   *
+   * Note that 0x0002 is skipped, because it was the value of a constant that
+   * has since been removed.
+   */
+
+  /**
+   * A filter that matches all wrappers.
+   */
+  const ALL = 0x0000;
+
+  /**
+   * Refers to a local file system location.
+   */
+  const LOCAL = 0x0001;
+
+  /**
+   * Wrapper is readable (almost always true).
+   */
+  const READ = 0x0004;
+
+  /**
+   * Wrapper is writeable.
+   */
+  const WRITE = 0x0008;
+
+  /**
+   * Exposed in the UI and potentially web accessible.
+   */
+  const VISIBLE = 0x0010;
+
+  /**
+   * Composite stream wrapper bit flags that are usually used as the types.
+   */
+
+  /**
+   * Stream wrapper type flag -- not visible in the UI or accessible via web,
+   * but readable and writable. E.g. the temporary directory for uploads.
+   */
+  const HIDDEN = 0x0012;
+
+  /**
+   * Hidden, readable and writeable using local files.
+   */
+  const LOCAL_HIDDEN = 0x0013;
+
+  /**
+   * Visible, readable and writeable.
+   */
+  const WRITE_VISIBLE = 0x0022;
+
+  /**
+   * Visible and read-only.
+   */
+  const READ_VISIBLE = 0x0014;
+
+  /**
+   * Stream wrapper type flag -- This is the default 'type' falg.
+   * This does not include StreamWrapperInterface::LOCAL, because PHP grants a
+   * greater trust level to local files (for example, they can be used in an
+   * "include" statement, regardless of the "allow_url_include" setting), so
+   * stream wrappers need to explicitly opt-in to this.
+   */
+  const NORMAL = 0x0022;
+
+  /**
+   * Visible, readable and writeable using local files.
+   */
+  const LOCAL_NORMAL = 0x0023;
+
+  /**
+   * Returns the type of stream wrapper.
+   *
+   * @return int
+   */
+  public static function getType();
+
+  /**
+   * Returns the name of the stream wrapper for use in the UI.
+   *
+   * @return string
+   *   The stream wrapper name.
+   */
+  public function getName();
+
+  /**
+   * Returns the description of the stream wrapper for use in the UI.
+   *
+   * @return string
+   *   The stream wrapper description.
+   */
+  public function getDescription();
+
+  /**
    * Sets the absolute stream resource URI.
    *
    * This allows you to set the URI. Generally is only called by the factory
diff --git a/core/lib/Drupal/Core/StreamWrapper/StreamWrapperManager.php b/core/lib/Drupal/Core/StreamWrapper/StreamWrapperManager.php
new file mode 100644
index 0000000..4599085
--- /dev/null
+++ b/core/lib/Drupal/Core/StreamWrapper/StreamWrapperManager.php
@@ -0,0 +1,272 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\StreamWrapper\StreamWrapperManager.
+ */
+
+namespace Drupal\Core\StreamWrapper;
+
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Symfony\Component\DependencyInjection\ContainerAware;
+
+/**
+ * Provides a StreamWrapper manager.
+ *
+ * @see file_get_stream_wrappers()
+ * @see hook_stream_wrappers_alter()
+ * @see system_stream_wrappers()
+ * @see \Drupal\Core\StreamWrapper\StreamWrapperInterface
+ */
+class StreamWrapperManager extends ContainerAware {
+
+  /**
+   * @var array
+   */
+  protected $info = array();
+  protected $wrappers = array();
+  protected $services = array();
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * Constructs a StreamWrapperManager object.
+   *
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   */
+  public function __construct(ModuleHandlerInterface $module_handler) {
+    $this->moduleHandler = $module_handler;
+  }
+
+  /**
+   * Provides Drupal stream wrapper registry.
+   *
+   * A stream wrapper is an abstraction of a file system that allows Drupal to
+   * use the same set of methods to access both local files and remote resources.
+   *
+   * Provide a facility for managing and querying user-defined stream wrappers
+   * in PHP. PHP's internal stream_get_wrappers() doesn't return the class
+   * registered to handle a stream, which we need to be able to find the handler
+   * for class instantiation.
+   *
+   * If a module registers a scheme that is already registered with PHP, the
+   * existing scheme will be unregistered and replaced with the specified class.
+   *
+   * A stream is referenced as "scheme://target".
+   *
+   * The optional $filter parameter can be used to retrieve only the stream
+   * wrappers that are appropriate for particular usage. For example, this returns
+   * only stream wrappers that use local file storage:
+   *
+   * @code
+   *   $local_stream_wrappers = file_get_stream_wrappers(StreamWrapperInterface::LOCAL);
+   * @endcode
+   *
+   * The $filter parameter can only filter to types containing a particular flag.
+   * In some cases, you may want to filter to types that do not contain a
+   * particular flag. For example, you may want to retrieve all stream wrappers
+   * that are not writable, or all stream wrappers that are not local. PHP's
+   * array_diff_key() function can be used to help with this. For example, this
+   * returns only stream wrappers that do not use local file storage:
+   * @code
+   *   $remote_stream_wrappers = array_diff_key(file_get_stream_wrappers(StreamWrapperInterface::ALL), file_get_stream_wrappers(StreamWrapperInterface::LOCAL));
+   * @endcode
+   *
+   * @param int $filter
+   *   (Optional) Filters out all types except those with an on bit for each on
+   *   bit in $filter. For example, if $filter is StreamWrapperInterface::WRITE_VISIBLE,
+   *   which is equal to (StreamWrapperInterface::READ | StreamWrapperInterface::WRITE |
+   *   StreamWrapperInterface::VISIBLE), then only stream wrappers with all three of these
+   *   bits set are returned. Defaults to StreamWrapperInterface::ALL, which returns all
+   *   registered stream wrappers.
+   *
+   * @return array
+   *   An array keyed by scheme, with values containing an array of information
+   *   about the stream wrapper, as returned by hook_stream_wrappers(). If $filter
+   *   is omitted or set to StreamWrapperInterface::ALL, the entire Drupal stream wrapper
+   *   registry is returned. Otherwise only the stream wrappers whose 'type'
+   *   bitmask has an on bit for each bit specified in $filter are returned.
+   */
+  public function getWrappers($filter = StreamWrapperInterface::ALL) {
+    if (!isset($this->wrappers[$filter])) {
+      $this->wrappers[$filter] = array();
+      foreach ($this->wrappers[StreamWrapperInterface::ALL] as $scheme => $info) {
+        // Bit-wise filter.
+        if (($info['type'] & $filter) == $filter) {
+          $this->wrappers[$filter][$scheme] = $info;
+        }
+      }
+    }
+
+    return $this->wrappers[$filter];
+  }
+
+  /**
+   * @todo
+   */
+  public function getNames($filter = StreamWrapperInterface::ALL) {
+    $names = array();
+    foreach (array_keys($this->getWrappers($filter)) as $scheme) {
+      $name[$scheme] = $this->getViaScheme($scheme)->getName();
+    }
+
+    return $names;
+  }
+
+  /**
+   * @todo
+   */
+  public function getDescriptions($filter = StreamWrapperInterface::ALL) {
+    $names = array();
+    foreach (array_keys($this->getWrappers($filter)) as $scheme) {
+      $name[$scheme] = $this->getViaScheme($scheme)->getDescription();
+    }
+
+    return $names;
+  }
+
+  /**
+   * Returns a stream wrapper via scheme.
+   *
+   * @param string $scheme
+   *   The scheme of the stream wrapper.
+   *
+   * @return \Drupal\Core\StreamWrapper\StreamWrapperInterface|bool
+   *   A stream wrapper object, or false if the scheme is not available.
+   */
+  public function getViaScheme($scheme) {
+    return $this->getWrapper($scheme, $scheme . '://');
+  }
+
+  /**
+   * Returns a stream wrapper via URI.
+   *
+   * @param string $uri
+   *   The URI of the stream wrapper.
+   *
+   * @return \Drupal\Core\StreamWrapper\StreamWrapperInterface|bool
+   *   A stream wrapper object, or false if the scheme is not available.
+   */
+  public function getViaUri($uri) {
+    $scheme = file_uri_scheme($uri);
+    return $this->getWrapper($scheme, $uri);
+  }
+
+  /**
+   * Returns the stream wrapper class.
+   *
+   * @param string $scheme
+   *   The stream wrapper scheme.
+   *
+   * @return string|bool
+   *   The stream wrapper class, or false if the scheme does not exist.
+   */
+  public function getClass($scheme) {
+    if (isset($this->info[$scheme])) {
+      return $this->info[$scheme]['class'];
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Returns a stream wrapper instance.
+   *
+   * @param string $scheme
+   *   The scheme of the desired stream wrapper.
+   * @param string $uri
+   *   The URI of the stream.
+   *
+   * @return \Drupal\Core\StreamWrapper\StreamWrapperInterface|bool
+   *   A stream wrapper object, or false if the scheme is not available.
+   */
+  protected function getWrapper($scheme, $uri) {
+    if (isset($this->services[$scheme])) {
+      $instance = $this->container->get($this->services[$scheme]);
+      $instance->setUri($uri);
+      return $instance;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Adds a stream wrapper.
+   *
+   * Internal use only.
+   *
+   * @param string $service_id
+   *   The service id.
+   * @param string $class
+   *   The stream wrapper class.
+   */
+  public function addStreamWrapper($service_id, $class, $scheme) {
+    $info = array();
+    $info['class'] = $class;
+    $info['type'] = $class::getType();
+    $this->services[$scheme] = $service_id;
+    $this->info[$scheme] = $info;
+  }
+
+  /**
+   * Registers the tagged stream wrappers.
+   *
+   * Internal use only.
+   */
+  public function register() {
+    $this->moduleHandler->alter('stream_wrappers', $this->info);
+    $this->services = array_intersect_key($this->services, $this->info);
+
+    foreach ($this->info as $scheme => $info) {
+
+      $this->registerWrapper($scheme, $info['class'], $info['type']);
+
+      // Pre-populate the static cache with the filters most typically used.
+      $this->wrappers[StreamWrapperInterface::ALL][$scheme] = $info;
+      if (($info['type'] & StreamWrapperInterface::WRITE_VISIBLE) == StreamWrapperInterface::WRITE_VISIBLE) {
+        $this->wrappers[StreamWrapperInterface::WRITE_VISIBLE][$scheme] = $info;
+      }
+    }
+  }
+
+  /**
+   * Unregisters the tagged stream wrappers.
+   *
+   * Internal use only.
+   */
+  public function unregister() {
+    foreach (array_keys($this->wrappers[StreamWrapperInterface::ALL]) as $scheme) {
+      stream_wrapper_unregister($scheme);
+    }
+  }
+
+  /**
+   * Registers stream wrapper with PHP.
+   *
+   * @param string $scheme
+   *   The scheme of the stream wrapper.
+   * @param string $class
+   *   The class of the stream wrapper.
+   * @param int $type
+   *   The type of the stream wrapper.
+   */
+  protected function registerWrapper($scheme, $class, $type) {
+    if (in_array($scheme, stream_get_wrappers(), TRUE)) {
+      stream_wrapper_unregister($scheme);
+    }
+
+    if (($type & StreamWrapperInterface::LOCAL) == StreamWrapperInterface::LOCAL) {
+      stream_wrapper_register($scheme, $class);
+    }
+    else {
+      stream_wrapper_register($scheme, $class, STREAM_IS_URL);
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/StreamWrapper/TemporaryStream.php b/core/lib/Drupal/Core/StreamWrapper/TemporaryStream.php
index 42dd4fd..0ea466c 100644
--- a/core/lib/Drupal/Core/StreamWrapper/TemporaryStream.php
+++ b/core/lib/Drupal/Core/StreamWrapper/TemporaryStream.php
@@ -16,6 +16,27 @@
 class TemporaryStream extends LocalStream {
 
   /**
+   * {@inheritdoc}
+   */
+  public static function getType() {
+    return StreamWrapperInterface::LOCAL_HIDDEN;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getName() {
+    return t('Temporary files');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    return t('Temporary local files for upload and previews.');
+  }
+
+  /**
    * Implements Drupal\Core\StreamWrapper\LocalStream::getDirectoryPath()
    */
   public function getDirectoryPath() {
diff --git a/core/modules/editor/editor.admin.inc b/core/modules/editor/editor.admin.inc
index a29924b..80b4ec0 100644
--- a/core/modules/editor/editor.admin.inc
+++ b/core/modules/editor/editor.admin.inc
@@ -5,8 +5,9 @@
  * Administration functions for editor.module.
  */
 
-use Drupal\editor\Entity\Editor;
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\StreamWrapper\StreamWrapperInterface;
+use Drupal\editor\Entity\Editor;
 
 /**
  * Subform constructor to configure the text editor's image upload settings.
@@ -52,10 +53,7 @@ function editor_image_upload_settings_form(Editor $editor) {
 
   // Any visible, writable wrapper can potentially be used for uploads,
   // including a remote file system that integrates with a CDN.
-  $stream_wrappers = file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE);
-  foreach ($stream_wrappers as $scheme => $info) {
-    $options[$scheme] = $info['description'];
-  }
+  $stream_wrappers = \Drupal::service('stream_wrapper_manager')->getDescriptions(StreamWrapperInterface::WRITE_VISIBLE);
   if (!empty($options)) {
     $form['scheme'] = array(
       '#type' => 'radios',
diff --git a/core/modules/file/lib/Drupal/file/Plugin/Field/FieldType/FileItem.php b/core/modules/file/lib/Drupal/file/Plugin/Field/FieldType/FileItem.php
index 9bb9dd0..6f190a6 100644
--- a/core/modules/file/lib/Drupal/file/Plugin/Field/FieldType/FileItem.php
+++ b/core/modules/file/lib/Drupal/file/Plugin/Field/FieldType/FileItem.php
@@ -7,10 +7,11 @@
 
 namespace Drupal\file\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\ConfigFieldItemInterface;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
+use Drupal\Core\StreamWrapper\StreamWrapperInterface;
 use Drupal\Core\TypedData\DataDefinition;
-use Drupal\Core\Field\ConfigFieldItemInterface;
 
 /**
  * Plugin implementation of the 'file' field type.
@@ -128,10 +129,7 @@ public function settingsForm(array $form, array &$form_state, $has_data) {
       ),
     );
 
-    $scheme_options = array();
-    foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $stream_wrapper) {
-      $scheme_options[$scheme] = $stream_wrapper['name'];
-    }
+    $scheme_options = \Drupal::service('stream_wrapper_manager')->getNames(StreamWrapperInterface::WRITE_VISIBLE);
     $element['uri_scheme'] = array(
       '#type' => 'radios',
       '#title' => t('Upload destination'),
diff --git a/core/modules/file/tests/file_test/file_test.module b/core/modules/file/tests/file_test/file_test.module
index e5a10a0..9b25057 100644
--- a/core/modules/file/tests/file_test/file_test.module
+++ b/core/modules/file/tests/file_test/file_test.module
@@ -14,29 +14,6 @@
 const FILE_URL_TEST_CDN_2 = 'http://cdn2.example.com';
 
 /**
- * Implements hook_stream_wrappers().
- */
-function file_test_stream_wrappers() {
-  return array(
-    'dummy' => array(
-      'name' => t('Dummy files'),
-      'class' => 'Drupal\file_test\DummyStreamWrapper',
-      'description' => t('Dummy wrapper for simpletest.'),
-    ),
-    'dummy-remote' => array(
-      'name' => t('Dummy files (remote)'),
-      'class' => 'Drupal\file_test\DummyRemoteStreamWrapper',
-      'description' => t('Dummy wrapper for simpletest (remote).'),
-    ),
-    'dummy-readonly' => array(
-      'name' => t('Dummy files (readonly)'),
-      'class' => 'Drupal\file_test\DummyReadOnlyStreamWrapper',
-      'description' => t('Dummy wrapper for simpletest (readonly).'),
-    ),
-  );
-}
-
-/**
  * Reset/initialize the history of calls to the file_* hooks.
  *
  * @see file_test_get_calls()
diff --git a/core/modules/file/tests/file_test/file_test.services.yml b/core/modules/file/tests/file_test/file_test.services.yml
new file mode 100644
index 0000000..1e05c26
--- /dev/null
+++ b/core/modules/file/tests/file_test/file_test.services.yml
@@ -0,0 +1,13 @@
+services:
+  stream_wrapper.dummy_readonly:
+    class: Drupal\file_test\StreamWrapper\DummyReadOnlyStreamWrapper
+    tags:
+      - { name: stream_wrapper, scheme: dummy-readonly }
+  stream_wrapper.dummy_remote:
+    class: Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper
+    tags:
+      - { name: stream_wrapper, scheme: dummy-remote }
+  stream_wrapper.dummy:
+    class: Drupal\file_test\StreamWrapper\DummyStreamWrapper
+    tags:
+      - { name: stream_wrapper, scheme: dummy }
diff --git a/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyReadOnlyStreamWrapper.php b/core/modules/file/tests/file_test/lib/Drupal/file_test/StreamWrapper/DummyReadOnlyStreamWrapper.php
similarity index 59%
rename from core/modules/file/tests/file_test/lib/Drupal/file_test/DummyReadOnlyStreamWrapper.php
rename to core/modules/file/tests/file_test/lib/Drupal/file_test/StreamWrapper/DummyReadOnlyStreamWrapper.php
index b427d3f..ab2f7b8 100644
--- a/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyReadOnlyStreamWrapper.php
+++ b/core/modules/file/tests/file_test/lib/Drupal/file_test/StreamWrapper/DummyReadOnlyStreamWrapper.php
@@ -2,10 +2,10 @@
 
 /**
  * @file
- * Definition of Drupal\file_test\DummyReadOnlyStreamWrapper.
+ * Contains \Drupal\file_test\StreamWrapper\DummyReadOnlyStreamWrapper.
  */
 
-namespace Drupal\file_test;
+namespace Drupal\file_test\StreamWrapper;
 
 use Drupal\Core\StreamWrapper\LocalReadOnlyStream;
 
@@ -15,8 +15,23 @@
  * Dummy stream wrapper implementation (dummy-readonly://).
  */
 class DummyReadOnlyStreamWrapper extends LocalReadOnlyStream {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getName() {
+    return t('Dummy files (readonly)');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    return t('Dummy wrapper for simpletest (readonly).');
+  }
+
   function getDirectoryPath() {
-    return 'sites/default/files';
+    return variable_get('stream_public_path', 'sites/default/files');
   }
 
   /**
diff --git a/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyRemoteStreamWrapper.php b/core/modules/file/tests/file_test/lib/Drupal/file_test/StreamWrapper/DummyRemoteStreamWrapper.php
similarity index 52%
rename from core/modules/file/tests/file_test/lib/Drupal/file_test/DummyRemoteStreamWrapper.php
rename to core/modules/file/tests/file_test/lib/Drupal/file_test/StreamWrapper/DummyRemoteStreamWrapper.php
index 2741196..7e9f089 100644
--- a/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyRemoteStreamWrapper.php
+++ b/core/modules/file/tests/file_test/lib/Drupal/file_test/StreamWrapper/DummyRemoteStreamWrapper.php
@@ -2,10 +2,10 @@
 
 /**
  * @file
- * Definition of Drupal\file_test\DummyRemoteStreamWrapper.
+ * Contains \Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper.
  */
 
-namespace Drupal\file_test;
+namespace Drupal\file_test\StreamWrapper;
 
 use Drupal\Core\StreamWrapper\PublicStream;
 
@@ -17,6 +17,21 @@
  * Basically just the public scheme but not returning a local file for realpath.
  */
 class DummyRemoteStreamWrapper extends PublicStream {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getName() {
+    return t('Dummy files (remote)');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    return t('Dummy wrapper for simpletest (remote).');
+  }
+
   function realpath() {
     return FALSE;
   }
diff --git a/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyStreamWrapper.php b/core/modules/file/tests/file_test/lib/Drupal/file_test/StreamWrapper/DummyStreamWrapper.php
similarity index 65%
rename from core/modules/file/tests/file_test/lib/Drupal/file_test/DummyStreamWrapper.php
rename to core/modules/file/tests/file_test/lib/Drupal/file_test/StreamWrapper/DummyStreamWrapper.php
index 4836f09..295426d 100644
--- a/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyStreamWrapper.php
+++ b/core/modules/file/tests/file_test/lib/Drupal/file_test/StreamWrapper/DummyStreamWrapper.php
@@ -2,10 +2,10 @@
 
 /**
  * @file
- * Definition of Drupal\file_test\DummyStreamWrapper.
+ * Contains \Drupal\file_test\StreamWrapper\DummyStreamWrapper.
  */
 
-namespace Drupal\file_test;
+namespace Drupal\file_test\StreamWrapper;
 
 use Drupal\Core\StreamWrapper\LocalStream;
 
@@ -15,6 +15,21 @@
  * Dummy stream wrapper implementation (dummy://).
  */
 class DummyStreamWrapper extends LocalStream {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getName() {
+    return t('Dummy files');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    return t('Dummy wrapper for simpletest.');
+  }
+
   function getDirectoryPath() {
     return 'sites/default/files';
   }
diff --git a/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php b/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php
index 57869d4..402231e 100644
--- a/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php
+++ b/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php
@@ -7,13 +7,14 @@
 
 namespace Drupal\image\Entity;
 
+use Drupal\Component\Utility\Crypt;
+use Drupal\Component\Utility\Url;
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\StreamWrapper\StreamWrapperInterface;
 use Drupal\image\ImageEffectBag;
 use Drupal\image\ImageEffectInterface;
 use Drupal\image\ImageStyleInterface;
-use Drupal\Component\Utility\Crypt;
-use Drupal\Component\Utility\Url;
 use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
 
 /**
@@ -245,7 +246,7 @@ public function flush($path = NULL) {
     }
 
     // Delete the style directory in each registered wrapper.
-    $wrappers = file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE);
+    $wrappers = \Drupal::service('stream_wrapper_manager')->getWrappers(StreamWrapperInterface::WRITE_VISIBLE);
     foreach ($wrappers as $wrapper => $wrapper_data) {
       if (file_exists($directory = $wrapper . '://styles/' . $this->id())) {
         file_unmanaged_delete_recursive($directory);
diff --git a/core/modules/image/lib/Drupal/image/Plugin/Field/FieldType/ImageItem.php b/core/modules/image/lib/Drupal/image/Plugin/Field/FieldType/ImageItem.php
index be20c66..9685943 100644
--- a/core/modules/image/lib/Drupal/image/Plugin/Field/FieldType/ImageItem.php
+++ b/core/modules/image/lib/Drupal/image/Plugin/Field/FieldType/ImageItem.php
@@ -7,8 +7,9 @@
 
 namespace Drupal\image\Plugin\Field\FieldType;
 
-use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\StreamWrapper\StreamWrapperInterface;
+use Drupal\Core\TypedData\DataDefinition;
 use Drupal\file\Plugin\Field\FieldType\FileItem;
 
 /**
@@ -149,10 +150,7 @@ public function settingsForm(array $form, array &$form_state, $has_data) {
     // the field.
     $settings = $this->getFieldDefinition()->getField()->getSettings();
 
-    $scheme_options = array();
-    foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $stream_wrapper) {
-      $scheme_options[$scheme] = $stream_wrapper['name'];
-    }
+    $scheme_options = \Drupal::service('stream_wrapper_manager')->getNames(StreamWrapperInterface::WRITE_VISIBLE);
     $element['uri_scheme'] = array(
       '#type' => 'radios',
       '#title' => t('Upload destination'),
diff --git a/core/modules/locale/lib/Drupal/locale/TranslationsStream.php b/core/modules/locale/lib/Drupal/locale/StreamWrapper/TranslationsStream.php
similarity index 55%
rename from core/modules/locale/lib/Drupal/locale/TranslationsStream.php
rename to core/modules/locale/lib/Drupal/locale/StreamWrapper/TranslationsStream.php
index 5245bd4..44c8957 100644
--- a/core/modules/locale/lib/Drupal/locale/TranslationsStream.php
+++ b/core/modules/locale/lib/Drupal/locale/StreamWrapper/TranslationsStream.php
@@ -2,12 +2,15 @@
 
 /**
  * @file
- * Definition of Drupal\locale\TranslationStream.
+ * Contains \Drupal\locale\StreamWrapper\TranslationStream.
  */
 
-namespace Drupal\locale;
+namespace Drupal\locale\StreamWrapper;
 
+use Drupal\Core\Annotation\StreamWrapper;
+use Drupal\Core\Annotation\Translation;
 use Drupal\Core\StreamWrapper\LocalStream;
+use Drupal\Core\StreamWrapper\StreamWrapperInterface;
 
 /**
  * Defines a Drupal translations (translations://) stream wrapper class.
@@ -17,6 +20,27 @@
 class TranslationsStream extends LocalStream {
 
   /**
+   * {@inheritdoc}
+   */
+  public static function getType() {
+    return StreamWrapperInterface::LOCAL_HIDDEN;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getName() {
+    return t('Translation files');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    return t('Translation files');
+  }
+
+  /**
    * Implements Drupal\Core\StreamWrapper\LocalStream::getDirectoryPath()
    */
   function getDirectoryPath() {
diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module
index 7a7574a..53eceaf 100644
--- a/core/modules/locale/locale.module
+++ b/core/modules/locale/locale.module
@@ -222,21 +222,6 @@ function locale_theme() {
 }
 
 /**
- * Implements hook_stream_wrappers().
- */
-function locale_stream_wrappers() {
-  $wrappers = array(
-    'translations' => array(
-      'name' => t('Translation files'),
-      'class' => 'Drupal\locale\TranslationsStream',
-      'description' => t('Translation files'),
-      'type' => STREAM_WRAPPERS_LOCAL_HIDDEN,
-    ),
-  );
-  return $wrappers;
-}
-
-/**
  * Implements hook_language_insert().
  */
 function locale_language_insert($language) {
diff --git a/core/modules/locale/locale.services.yml b/core/modules/locale/locale.services.yml
index b5f4cf4..6765575 100644
--- a/core/modules/locale/locale.services.yml
+++ b/core/modules/locale/locale.services.yml
@@ -16,3 +16,7 @@ services:
     tags:
       - { name: string_translator }
       - { name: needs_destruction }
+  stream_wrapper.translations:
+    class: Drupal\locale\StreamWrapper\TranslationsStream
+    tags:
+      - { name: stream_wrapper, scheme: translations }
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
index 78b154e8..c077992 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
@@ -1146,10 +1146,6 @@ private function restoreEnvironment() {
       }
     }
 
-    // In case a fatal error occurred that was not in the test process read the
-    // log to pick up any fatal errors.
-    simpletest_log_read($this->testId, $this->databasePrefix, get_class($this), TRUE);
-
     // Delete temporary files directory.
     file_unmanaged_delete_recursive($this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10), array($this, 'filePreDeleteCallback'));
 
@@ -1179,6 +1175,17 @@ private function restoreEnvironment() {
     // Restore original statics and globals.
     \Drupal::setContainer($this->originalContainer);
     $GLOBALS['config_directories'] = $this->originalConfigDirectories;
+
+    // Re-initialize original stream wrappers of the parent site.
+    // This must happen after static variables have been reset and the original
+    // container and $config_directories are restored, as simpletest_log_read()
+    // uses the public stream wrapper to locate the error.log.
+    $this->originalContainer->get('stream_wrapper_manager')->register();
+
+    // In case a fatal error occurred that was not in the test process read the
+    // log to pick up any fatal errors.
+    simpletest_log_read($this->testId, $this->databasePrefix, get_class($this), TRUE);
+
     if (isset($this->originalPrefix)) {
       drupal_valid_test_ua($this->originalPrefix);
     }
diff --git a/core/modules/system/lib/Drupal/system/Form/FileSystemForm.php b/core/modules/system/lib/Drupal/system/Form/FileSystemForm.php
index 2d51684..139f1f7 100644
--- a/core/modules/system/lib/Drupal/system/Form/FileSystemForm.php
+++ b/core/modules/system/lib/Drupal/system/Form/FileSystemForm.php
@@ -7,8 +7,15 @@
 
 namespace Drupal\system\Form;
 
-use Drupal\Core\StreamWrapper\PublicStream;
+use Drupal\Component\Utility\String;
+use Drupal\Core\Config\ConfigFactory;
+use Drupal\Core\Config\Context\ContextInterface;
 use Drupal\Core\Form\ConfigFormBase;
+use Drupal\Core\StreamWrapper\PublicStream;
+use Drupal\Core\StreamWrapper\StreamWrapperInterface;
+use Drupal\Core\StreamWrapper\StreamWrapperManager;
+use Drupal\system\SystemConfigFormBase;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Configure file system settings for this site.
@@ -16,6 +23,37 @@
 class FileSystemForm extends ConfigFormBase {
 
   /**
+   * The stream wrapper manager.
+   *
+   * @var \Drupal\Core\StreamWrapper\StreamWrapperManager
+   */
+  protected $streamWrapperManager;
+
+  /**
+   * Constructs a FileSystemForm object.
+   *
+   * @param \Drupal\Core\Config\ConfigFactory $config_factory
+   *   The factory for configuration objects.
+   * @param \Drupal\Core\StreamWrapper\StreamWrapperManager $stream_wrapper_manager
+   *   The stream wrapper manager.
+   */
+  public function __construct(ConfigFactory $config_factory, StreamWrapperManager $stream_wrapper_manager) {
+    parent::__construct($config_factory);
+
+    $this->streamWrapperManager = $stream_wrapper_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('config.factory'),
+      $container->get('stream_wrapper_manager')
+    );
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function getFormId() {
@@ -54,9 +92,9 @@ public function buildForm(array $form, array &$form_state) {
     );
     // Any visible, writeable wrapper can potentially be used for the files
     // directory, including a remote file system that integrates with a CDN.
-    foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $info) {
-      $options[$scheme] = check_plain($info['description']);
-    }
+    $options = array_map(function ($wrapper) {
+      return String::checkPlain($wrapper['description']);
+    }, $this->streamWrapperManager->getWrappers(StreamWrapperInterface::WRITE_VISIBLE));
 
     if (!empty($options)) {
       $form['file_default_scheme'] = array(
diff --git a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php
index e5c5528..c892a35 100644
--- a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php
+++ b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php
@@ -7,9 +7,10 @@
 
 namespace Drupal\system\Plugin\ImageToolkit;
 
-use Drupal\Core\Image\ImageInterface;
-use Drupal\Core\ImageToolkit\ImageToolkitBase;
 use Drupal\Component\Utility\Image as ImageUtility;
+use Drupal\Core\ImageToolkit\ImageToolkitBase;
+use Drupal\Core\Image\ImageInterface;
+use Drupal\Core\StreamWrapper\StreamWrapperInterface;
 
 /**
  * Defines the GD2 toolkit for image manipulation within Drupal.
@@ -227,7 +228,7 @@ public function save(ImageInterface $image, $destination) {
     // Work around lack of stream wrapper support in imagejpeg() and imagepng().
     if ($scheme && file_stream_wrapper_valid_scheme($scheme)) {
       // If destination is not local, save image to temporary local file.
-      $local_wrappers = file_get_stream_wrappers(STREAM_WRAPPERS_LOCAL);
+      $local_wrappers = file_get_stream_wrappers(StreamWrapperInterface::LOCAL);
       if (!isset($local_wrappers[$scheme])) {
         $permanent_destination = $destination;
         $destination = drupal_tempnam('temporary://', 'gd_');
diff --git a/core/modules/system/lib/Drupal/system/Tests/File/ReadOnlyStreamWrapperTest.php b/core/modules/system/lib/Drupal/system/Tests/File/ReadOnlyStreamWrapperTest.php
index ef84796..db1a7e1 100644
--- a/core/modules/system/lib/Drupal/system/Tests/File/ReadOnlyStreamWrapperTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/File/ReadOnlyStreamWrapperTest.php
@@ -20,7 +20,7 @@ class ReadOnlyStreamWrapperTest extends FileTestBase {
   public static $modules = array('file_test');
 
   protected $scheme = 'dummy-readonly';
-  protected $classname = 'Drupal\file_test\DummyReadOnlyStreamWrapper';
+  protected $classname = 'Drupal\file_test\StreamWrapper\DummyReadOnlyStreamWrapper';
 
   public static function getInfo() {
     return array(
@@ -30,16 +30,6 @@ public static function getInfo() {
     );
   }
 
-  function setUp() {
-    parent::setUp();
-    drupal_static_reset('file_get_stream_wrappers');
-  }
-
-  function tearDown() {
-    parent::tearDown();
-    stream_wrapper_unregister($this->scheme);
-  }
-
   /**
    * Test write functionality of the read-only stream wrapper.
    */
diff --git a/core/modules/system/lib/Drupal/system/Tests/File/StreamWrapperTest.php b/core/modules/system/lib/Drupal/system/Tests/File/StreamWrapperTest.php
index 16bc592..9c729a4 100644
--- a/core/modules/system/lib/Drupal/system/Tests/File/StreamWrapperTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/File/StreamWrapperTest.php
@@ -23,7 +23,7 @@ class StreamWrapperTest extends WebTestBase {
   public static $modules = array('file_test');
 
   protected $scheme = 'dummy';
-  protected $classname = 'Drupal\file_test\DummyStreamWrapper';
+  protected $classname = 'Drupal\file_test\StreamWrapper\DummyStreamWrapper';
 
   public static function getInfo() {
     return array(
@@ -33,16 +33,6 @@ public static function getInfo() {
     );
   }
 
-  function setUp() {
-    parent::setUp();
-    drupal_static_reset('file_get_stream_wrappers');
-  }
-
-  function tearDown() {
-    parent::tearDown();
-    stream_wrapper_unregister($this->scheme);
-  }
-
   /**
    * Test the getClassName() function.
    */
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index 1de6b15..2865e56 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -1697,80 +1697,10 @@ function hook_modules_uninstalled($modules) {
 }
 
 /**
- * Registers PHP stream wrapper implementations associated with a module.
- *
- * Provide a facility for managing and querying user-defined stream wrappers
- * in PHP. PHP's internal stream_get_wrappers() doesn't return the class
- * registered to handle a stream, which we need to be able to find the handler
- * for class instantiation.
- *
- * If a module registers a scheme that is already registered with PHP, it will
- * be unregistered and replaced with the specified class.
- *
- * @return
- *   A nested array, keyed first by scheme name ("public" for "public://"),
- *   then keyed by the following values:
- *   - 'name' A short string to name the wrapper.
- *   - 'class' A string specifying the PHP class that implements the
- *     Drupal\Core\StreamWrapper\StreamWrapperInterface interface.
- *   - 'description' A string with a short description of what the wrapper does.
- *   - 'type' (Optional) A bitmask of flags indicating what type of streams this
- *     wrapper will access - local or remote, readable and/or writeable, etc.
- *     Many shortcut constants are defined in file.inc. Defaults to
- *     STREAM_WRAPPERS_NORMAL which includes all of these bit flags:
- *     - STREAM_WRAPPERS_READ
- *     - STREAM_WRAPPERS_WRITE
- *     - STREAM_WRAPPERS_VISIBLE
- *
- * @see file_get_stream_wrappers()
- * @see hook_stream_wrappers_alter()
- * @see system_stream_wrappers()
- */
-function hook_stream_wrappers() {
-  return array(
-    'public' => array(
-      'name' => t('Public files'),
-      'class' => 'Drupal\Core\StreamWrapper\PublicStream',
-      'description' => t('Public local files served by the webserver.'),
-      'type' => STREAM_WRAPPERS_LOCAL_NORMAL,
-    ),
-    'private' => array(
-      'name' => t('Private files'),
-      'class' => 'Drupal\Core\StreamWrapper\PrivateStream',
-      'description' => t('Private local files served by Drupal.'),
-      'type' => STREAM_WRAPPERS_LOCAL_NORMAL,
-    ),
-    'temp' => array(
-      'name' => t('Temporary files'),
-      'class' => 'Drupal\Core\StreamWrapper\TemporaryStream',
-      'description' => t('Temporary local files for upload and previews.'),
-      'type' => STREAM_WRAPPERS_LOCAL_HIDDEN,
-    ),
-    'cdn' => array(
-      'name' => t('Content delivery network files'),
-      // @todo: Fix the name of this class when we decide on module PSR-0 usage.
-      'class' => 'MyModuleCDNStream',
-      'description' => t('Files served by a content delivery network.'),
-      // 'type' can be omitted to use the default of STREAM_WRAPPERS_NORMAL
-    ),
-    'youtube' => array(
-      'name' => t('YouTube video'),
-      // @todo: Fix the name of this class when we decide on module PSR-0 usage.
-      'class' => 'MyModuleYouTubeStream',
-      'description' => t('Video streamed from YouTube.'),
-      // A module implementing YouTube integration may decide to support using
-      // the YouTube API for uploading video, but here, we assume that this
-      // particular module only supports playing YouTube video.
-      'type' => STREAM_WRAPPERS_READ_VISIBLE,
-    ),
-  );
-}
-
-/**
  * Alters the list of PHP stream wrapper implementations.
  *
  * @see file_get_stream_wrappers()
- * @see hook_stream_wrappers()
+ * @see \Drupal\Core\StreamWrapper\StreamWrapperManager
  */
 function hook_stream_wrappers_alter(&$wrappers) {
   // Change the name of private files to reflect the performance.
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index c2cf620..5aaa865 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -1894,35 +1894,13 @@ function system_library_info() {
 }
 
 /**
- * Implements hook_stream_wrappers().
+ * Implements hook_stream_wrappers_alter().
  */
-function system_stream_wrappers() {
-  $wrappers = array(
-    'public' => array(
-      'name' => t('Public files'),
-      'class' => 'Drupal\Core\StreamWrapper\PublicStream',
-      'description' => t('Public local files served by the webserver.'),
-      'type' => STREAM_WRAPPERS_LOCAL_NORMAL,
-    ),
-    'temporary' => array(
-      'name' => t('Temporary files'),
-      'class' => 'Drupal\Core\StreamWrapper\TemporaryStream',
-      'description' => t('Temporary local files for upload and previews.'),
-      'type' => STREAM_WRAPPERS_LOCAL_HIDDEN,
-    ),
-  );
-
+function system_stream_wrappers_alter(&$wrappers) {
   // Only register the private file stream wrapper if a file path has been set.
-  if (\Drupal::config('system.file')->get('path.private')) {
-    $wrappers['private'] = array(
-      'name' => t('Private files'),
-      'class' => 'Drupal\Core\StreamWrapper\PrivateStream',
-      'description' => t('Private local files served by Drupal.'),
-      'type' => STREAM_WRAPPERS_LOCAL_NORMAL,
-    );
+  if (!Drupal::config('system.file')->get('path.private')) {
+    unset($wrappers['private']);
   }
-
-  return $wrappers;
 }
 
 /**
diff --git a/core/modules/system/system.services.yml b/core/modules/system/system.services.yml
index 54e5b11..2708078 100644
--- a/core/modules/system/system.services.yml
+++ b/core/modules/system/system.services.yml
@@ -20,3 +20,11 @@ services:
     arguments: ['@batch.storage']
     tags:
       - { name: theme_negotiator, priority: 1000 }
+  stream_wrapper.private:
+    class: Drupal\Core\StreamWrapper\PrivateStream
+    tags:
+      - { name: stream_wrapper, scheme: private }
+  stream_wrapper.temporary:
+    class: Drupal\Core\StreamWrapper\TemporaryStream
+    tags:
+      - { name: stream_wrapper, scheme: temporary }
