diff --git a/core/lib/Drupal/Component/Discovery/DiscoverableInterface.php b/core/lib/Drupal/Component/Discovery/DiscoverableInterface.php
index 02a5776..c04556f 100644
--- a/core/lib/Drupal/Component/Discovery/DiscoverableInterface.php
+++ b/core/lib/Drupal/Component/Discovery/DiscoverableInterface.php
@@ -16,7 +16,7 @@
    * Returns an array of discoverable items.
    *
    * @return array
-   *   An array of discovered data.
+   *   An array of discovered data keyed by provider.
    */
   public function findAll();
 
diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php
index ac2f0a0..faa9a72 100644
--- a/core/modules/simpletest/src/WebTestBase.php
+++ b/core/modules/simpletest/src/WebTestBase.php
@@ -647,7 +647,7 @@ protected function drupalCreateRole(array $permissions, $rid = NULL, $name = NUL
    *   TRUE if the permissions are valid, FALSE otherwise.
    */
   protected function checkPermissions(array $permissions) {
-    $available = array_keys(\Drupal::moduleHandler()->invokeAll('permission'));
+    $available = array_keys(\Drupal::service('user.permissions')->getPermissions());
     $valid = TRUE;
     foreach ($permissions as $permission) {
       if (!in_array($permission, $available)) {
diff --git a/core/modules/system/src/Tests/Menu/BreadcrumbTest.php b/core/modules/system/src/Tests/Menu/BreadcrumbTest.php
index f1bf640..b44f1f1 100644
--- a/core/modules/system/src/Tests/Menu/BreadcrumbTest.php
+++ b/core/modules/system/src/Tests/Menu/BreadcrumbTest.php
@@ -32,7 +32,7 @@ class BreadcrumbTest extends MenuTestBase {
   function setUp() {
     parent::setUp();
 
-    $perms = array_keys(\Drupal::moduleHandler()->invokeAll('permission'));
+    $perms = array_keys(\Drupal::service('user.permissions')->getPermissions());
     $this->admin_user = $this->drupalCreateUser($perms);
     $this->drupalLogin($this->admin_user);
 
diff --git a/core/modules/system/src/Tests/System/AdminTest.php b/core/modules/system/src/Tests/System/AdminTest.php
index 950194f..f64989f 100644
--- a/core/modules/system/src/Tests/System/AdminTest.php
+++ b/core/modules/system/src/Tests/System/AdminTest.php
@@ -44,7 +44,7 @@ function setUp() {
     // Create an administrator with all permissions, as well as a regular user
     // who can only access administration pages and perform some Locale module
     // administrative tasks, but not all of them.
-    $this->admin_user = $this->drupalCreateUser(array_keys(\Drupal::moduleHandler()->invokeAll('permission')));
+    $this->admin_user = $this->drupalCreateUser(array_keys(\Drupal::service('user.permissions')->getPermissions()));
     $this->web_user = $this->drupalCreateUser(array(
       'access administration pages',
       'translate interface',
diff --git a/core/modules/user/src/Form/UserPermissionsForm.php b/core/modules/user/src/Form/UserPermissionsForm.php
index bd4e473..60acd02 100644
--- a/core/modules/user/src/Form/UserPermissionsForm.php
+++ b/core/modules/user/src/Form/UserPermissionsForm.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Utility\String;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Form\FormBase;
+use Drupal\user\PermissionManagerInterface;
 use Drupal\user\RoleStorageInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -19,11 +20,11 @@
 class UserPermissionsForm extends FormBase {
 
   /**
-   * The module handler.
+   * The permission manager.
    *
-   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   * @var \Drupal\user\PermissionManagerInterface
    */
-  protected $moduleHandler;
+  protected $permissionManager;
 
   /**
    * The role storage.
@@ -35,13 +36,13 @@ class UserPermissionsForm extends FormBase {
   /**
    * Constructs a new UserPermissionsForm.
    *
-   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
-   *   The module handler.
+   * @param \Drupal\user\PermissionManagerInterface $permission_manager
+   *   The permission manager.
    * @param \Drupal\user\RoleStorageInterface $role_storage
    *   The role storage.
    */
-  public function __construct(ModuleHandlerInterface $module_handler, RoleStorageInterface $role_storage) {
-    $this->moduleHandler = $module_handler;
+  public function __construct(PermissionManagerInterface $permission_manager, RoleStorageInterface $role_storage) {
+    $this->permissionManager = $permission_manager;
     $this->roleStorage = $role_storage;
   }
 
@@ -50,7 +51,7 @@ public function __construct(ModuleHandlerInterface $module_handler, RoleStorageI
    */
   public static function create(ContainerInterface $container) {
     return new static(
-      $container->get('module_handler'),
+      $container->get('user.permissions'),
       $container->get('entity.manager')->getStorage('user_role')
     );
   }
@@ -95,14 +96,6 @@ public function buildForm(array $form, array &$form_state) {
     $module_info = system_rebuild_module_data();
     $hide_descriptions = system_admin_compact_mode();
 
-    // Get a list of all the modules implementing a hook_permission() and sort by
-    // display name.
-    $modules = array();
-    foreach ($this->moduleHandler->getImplementations('permission') as $module) {
-      $modules[$module] = $module_info[$module]->info['name'];
-    }
-    asort($modules);
-
     $form['system_compact_link'] = array(
       '#theme' => 'system_compact_link',
     );
@@ -120,55 +113,59 @@ public function buildForm(array $form, array &$form_state) {
       );
     }
 
-    foreach ($modules as $module => $display_name) {
-      if ($permissions = $this->moduleHandler->invoke($module, 'permission')) {
-        // Module name.
-        $form['permissions'][$module] = array(array(
+    $permissions = $this->permissionManager->getPermissions();
+    $permissions_by_provider = array();
+    foreach ($permissions as $permission_name => $permission) {
+      $permissions_by_provider[$permission['provider']][$permission_name] = $permission;
+    }
+
+    foreach ($permissions_by_provider as $provider => $permissions) {
+      // Module name.
+      $form['permissions'][$provider] = array(array(
+        '#wrapper_attributes' => array(
+          'colspan' => count($role_names) + 1,
+          'class' => array('module'),
+          'id' => 'module-' . $provider,
+        ),
+        '#markup' => $module_info[$provider]->info['name'],
+      ));
+      foreach ($permissions as $perm => $perm_item) {
+        // Fill in default values for the permission.
+        $perm_item += array(
+          'description' => '',
+          'restrict access' => FALSE,
+          'warning' => !empty($perm_item['restrict access']) ? $this->t('Warning: Give to trusted roles only; this permission has security implications.') : '',
+        );
+        $options[$perm] = $perm_item['title'];
+        // Show the permission description.
+        if (!$hide_descriptions) {
+          $user_permission_description = $perm_item['description'];
+          // Append warning message.
+          if (!empty($perm_item['warning'])) {
+            $user_permission_description .= ' <em class="permission-warning">' . $perm_item['warning'] . '</em>';
+          }
+        }
+        $form['permissions'][$perm]['description'] = array(
           '#wrapper_attributes' => array(
-            'colspan' => count($role_names) + 1,
-            'class' => array('module'),
-            'id' => 'module-' . $module,
+            'class' => array('permission'),
           ),
-          '#markup' => $module_info[$module]->info['name'],
-        ));
-        foreach ($permissions as $perm => $perm_item) {
-          // Fill in default values for the permission.
-          $perm_item += array(
-            'description' => '',
-            'restrict access' => FALSE,
-            'warning' => !empty($perm_item['restrict access']) ? $this->t('Warning: Give to trusted roles only; this permission has security implications.') : '',
-          );
-          $options[$perm] = $perm_item['title'];
-          // Show the permission description.
-          if (!$hide_descriptions) {
-            $user_permission_description = $perm_item['description'];
-            // Append warning message.
-            if (!empty($perm_item['warning'])) {
-              $user_permission_description .= ' <em class="permission-warning">' . $perm_item['warning'] . '</em>';
-            }
-          }
-          $form['permissions'][$perm]['description'] = array(
+          '#type' => 'item',
+          '#markup' => $perm_item['title'],
+          '#description' => $user_permission_description,
+        );
+        $options[$perm] = '';
+        foreach ($role_names as $rid => $name) {
+          $form['permissions'][$perm][$rid] = array(
+            '#title' => $name . ': ' . $perm_item['title'],
+            '#title_display' => 'invisible',
             '#wrapper_attributes' => array(
-              'class' => array('permission'),
+              'class' => array('checkbox'),
             ),
-            '#type' => 'item',
-            '#markup' => $perm_item['title'],
-            '#description' => $user_permission_description,
+            '#type' => 'checkbox',
+            '#default_value' => in_array($perm, $role_permissions[$rid]) ? 1 : 0,
+            '#attributes' => array('class' => array('rid-' . $rid)),
+            '#parents' => array($rid, $perm),
           );
-          $options[$perm] = '';
-          foreach ($role_names as $rid => $name) {
-            $form['permissions'][$perm][$rid] = array(
-              '#title' => $name . ': ' . $perm_item['title'],
-              '#title_display' => 'invisible',
-              '#wrapper_attributes' => array(
-                'class' => array('checkbox'),
-              ),
-              '#type' => 'checkbox',
-              '#default_value' => in_array($perm,$role_permissions[$rid]) ? 1 : 0,
-              '#attributes' => array('class' => array('rid-' . $rid)),
-              '#parents' => array($rid, $perm),
-            );
-          }
         }
       }
     }
diff --git a/core/modules/user/src/PermissionManager.php b/core/modules/user/src/PermissionManager.php
new file mode 100644
index 0000000..6028bc4
--- /dev/null
+++ b/core/modules/user/src/PermissionManager.php
@@ -0,0 +1,177 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\user\PermissionManager.
+ */
+
+namespace Drupal\user;
+
+use Drupal\Component\Discovery\YamlDiscovery;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\StringTranslation\TranslationInterface;
+
+/**
+ * Provides the available permissions based on hook_permission and yml files.
+ *
+ * To define permissions you can use a $module.permissions.yml file:
+ *
+ * @code
+ * 'access all views':
+ *   title: 'Bypass views access control'
+ *   description: 'Bypass access control when accessing views.'
+ *   'restrict access': TRUE
+ * @encode
+ *
+ * @see hook_permission()
+ */
+class PermissionManager implements PermissionManagerInterface {
+
+  use StringTranslationTrait;
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * Stores the available permissions.
+   *
+   * @var array
+   */
+  protected $permissions;
+
+  /**
+   * Constructs a new PermissionManager.
+   *
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
+   *   The string translation.
+   */
+  public function __construct(ModuleHandlerInterface $module_handler, TranslationInterface $string_translation) {
+    $this->moduleHandler = $module_handler;
+    // @todo Replace this with container.namespaces or something similar.
+    $this->ymlDiscovery = new YamlDiscovery('permissions', $this->moduleHandler->getModuleDirectories());
+    $this->stringTranslation = $string_translation;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPermissions() {
+    $this->permissions = $this->buildPermissions();
+    return $this->permissions;
+  }
+
+  /**
+   * Builds up all available permissions from YML files and hook_permission.
+   *
+   * @return array
+   *   An array of available permissions.
+   */
+  protected function buildPermissions() {
+    $all_permissions = $this->buildPermissionYaml();
+
+    $all_permissions += $this->buildPermissionsModules();
+
+    $all_permissions = $this->sortPermissionsByProviderName($all_permissions);
+
+    return $all_permissions;
+  }
+
+  /**
+   * Returns all module names.
+   *
+   * @return array
+   */
+  protected function getModuleNames() {
+    $modules = array();
+    $module_info = $this->systemRebuildModuleData();
+    foreach (array_keys($this->moduleHandler->getModuleList()) as $module) {
+      $modules[$module] = $module_info[$module]->info['name'];
+    }
+    asort($modules);
+    return $modules;
+  }
+
+  /**
+   * Builds all permissions provided by .permissions.yml files.
+   *
+   * @return array
+   *   Returns all permissions.
+   */
+  protected function buildPermissionYaml() {
+    $all_permissions = array();
+    foreach ($this->ymlDiscovery->findAll() as $provider => $permissions) {
+      foreach ($permissions as &$permission) {
+        if (!is_array($permission)) {
+          $permission = array(
+            'title' => $permission,
+          );
+        }
+        $permission['title'] = $this->t($permission['title']);
+        $permission['description'] = isset($permission['description']) ? $this->t($permission['description']) : NULL;
+        $permission['provider'] = $provider;
+      }
+      $all_permissions += $permissions;
+    }
+    return $all_permissions;
+  }
+
+  /**
+   * Builds all permissions provided by .module files.
+   *
+   * @return array
+   *   Returns all permissions.
+   */
+  protected function buildPermissionsModules() {
+    $all_permissions = array();
+    foreach ($this->moduleHandler->getImplementations('permission') as $provider) {
+      $permissions = $this->moduleHandler->invoke($provider, 'permission');
+      foreach ($permissions as &$permission) {
+        if (!is_array($permission)) {
+          $permission = array(
+            'title' => $permission,
+          );
+        }
+        $permission['provider'] = $provider;
+      }
+      $all_permissions += $permissions;
+    }
+    return $all_permissions;
+  }
+
+  /**
+   * Sorts the given permissions by provider name.
+   *
+   * @param array $all_permissions
+   *   The permissions to be sorted.
+   *
+   * @return array
+   *   The sorted permissions.
+   */
+  protected function sortPermissionsByProviderName(array $all_permissions = array()) {
+    // Get a list of all the modules providing permissions and sort by
+    // display name.
+    $modules = $this->getModuleNames();
+
+    uasort($all_permissions, function (array $permission_a, array $permission_b) use ($modules) {
+      return $modules[$permission_a['provider']] > $modules[$permission_b['provider']];
+    });
+    return $all_permissions;
+  }
+
+  /**
+   * Wraps system_rebuild_module_data()
+   *
+   * @return \Drupal\Core\Extension\Extension[]
+   */
+  protected function systemRebuildModuleData() {
+    return system_rebuild_module_data();
+  }
+
+}
diff --git a/core/modules/user/src/PermissionManagerInterface.php b/core/modules/user/src/PermissionManagerInterface.php
new file mode 100644
index 0000000..167597f
--- /dev/null
+++ b/core/modules/user/src/PermissionManagerInterface.php
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\user\PermissionManagerInterface.
+ */
+
+namespace Drupal\user;
+
+/**
+ * Defines an interface to list available permissions.
+ */
+interface PermissionManagerInterface {
+
+  /**
+   * Gets all available permissions.
+   *
+   * @return array
+   *   An array whose keys are permission names and whose corresponding values
+   *   are arrays containing the following key-value pairs:
+   *   - title: The human-readable name of the permission, to be shown on the
+   *     permission administration page. This should be wrapped in the t()
+   *     function so it can be translated.
+   *   - description: (optional) A description of what the permission does. This
+   *     should be wrapped in the t() function so it can be translated.
+   *   - restrict access: (optional) A boolean which can be set to TRUE to
+   *     indicate that site administrators should restrict access to this
+   *     permission to trusted users. This should be used for permissions that
+   *     have inherent security risks across a variety of potential use cases
+   *     (for example, the "administer filters" and "bypass node access"
+   *     permissions provided by Drupal core). When set to TRUE, a standard
+   *     warning message defined in user_admin_permissions() will be displayed
+   *     with the permission on the permission administration page. Defaults
+   *     to FALSE.
+   *   - warning: (optional) A translated warning message to display for this
+   *     permission on the permission administration page. This warning overrides
+   *     the automatic warning generated by 'restrict access' being set to TRUE.
+   *     This should rarely be used, since it is important for all permissions to
+   *     have a clear, consistent security warning that is the same across the
+   *     site. Use the 'description' key instead to provide any information that
+   *     is specific to the permission you are defining.
+   *   - provider: (optional) The provider name of the permission.
+   */
+  public function getPermissions();
+
+}
+
diff --git a/core/modules/user/src/Plugin/views/access/Permission.php b/core/modules/user/src/Plugin/views/access/Permission.php
index 7797913..e3e0569 100644
--- a/core/modules/user/src/Plugin/views/access/Permission.php
+++ b/core/modules/user/src/Plugin/views/access/Permission.php
@@ -44,7 +44,7 @@ public function alterRouteDefinition(Route $route) {
   }
 
   public function summaryTitle() {
-    $permissions = \Drupal::moduleHandler()->invokeAll('permission');
+    $permissions = \Drupal::service('user.permissions')->getPermissions();
     if (isset($permissions[$this->options['perm']])) {
       return $permissions[$this->options['perm']]['title'];
     }
diff --git a/core/modules/user/src/Plugin/views/field/Permissions.php b/core/modules/user/src/Plugin/views/field/Permissions.php
index 92d7171..ee4fd0c 100644
--- a/core/modules/user/src/Plugin/views/field/Permissions.php
+++ b/core/modules/user/src/Plugin/views/field/Permissions.php
@@ -83,7 +83,7 @@ public function preRender(&$values) {
     $uids = array();
     $this->items = array();
 
-    $permission_names = \Drupal::moduleHandler()->invokeAll('permission');
+    $permission_names = \Drupal::service('user.permissions')->getPermissions();
 
     $rids = array();
     foreach ($values as $result) {
diff --git a/core/modules/user/tests/src/PermissionManagerTest.php b/core/modules/user/tests/src/PermissionManagerTest.php
new file mode 100644
index 0000000..c06c164
--- /dev/null
+++ b/core/modules/user/tests/src/PermissionManagerTest.php
@@ -0,0 +1,225 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\user\Tests\PermissionManagerTest.
+ */
+
+namespace Drupal\user\Tests;
+
+use Drupal\Core\Extension\Extension;
+use Drupal\Tests\UnitTestCase;
+use Drupal\user\PermissionManager;
+use org\bovigo\vfs\vfsStream;
+use org\bovigo\vfs\vfsStreamDirectory;
+use org\bovigo\vfs\vfsStreamWrapper;
+
+/**
+ * Tests the permission manager.
+ *
+ * @group user
+ *
+ * @coversDefaultClass \Drupal\user\PermissionManager
+ */
+class PermissionManagerTest extends UnitTestCase {
+
+  /**
+   * The tested permission manager.
+   *
+   * @var \Drupal\user\Tests\TestPermissionManager|\Drupal\user\PermissionManager
+   */
+  protected $permissionManager;
+
+  /**
+   * The mocked module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $moduleHandler;
+
+  /**
+   * The mocked string translation.
+   *
+   * @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $stringTranslation;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    if (!isset($this->moduleHandler)) {
+      $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
+      $this->moduleHandler->expects($this->once())
+        ->method('getModuleDirectories')
+        ->willReturn(array());
+    }
+    $this->stringTranslation = $this->getStringTranslationStub();
+
+    $this->permissionManager = new TestPermissionManager($this->moduleHandler, $this->stringTranslation);
+  }
+
+  /**
+   * Provides an extension object for a given module with a human name.
+   *
+   * @param string $module
+   *   The module machine name.
+   * @param string $name
+   *   The module human name.
+   *
+   * @return \Drupal\Core\Extension\Extension
+   *   The extension object.
+   */
+  protected function mockModuleExtension($module, $name) {
+    $extension = new Extension($module, "modules/$module");
+    $extension->info['name'] = $name;
+    return $extension;
+  }
+
+  /**
+   * Tests permissions by hook_permission.
+   *
+   * @covers ::getPermissions
+   * @covers ::buildPermissions
+   * @covers ::buildPermissionsModules
+   * @covers ::sortPermissionsByProviderName
+   * @covers ::getModuleNames
+   */
+  public function testBuildPermissionsModules() {
+    $modules = array('module_a', 'module_b', 'module_c');
+    $extensions = array(
+      'module_a' => $this->mockModuleExtension('module_a', 'Module a'),
+      'module_b' => $this->mockModuleExtension('module_b', 'Moduleb'),
+      'module_c' => $this->mockModuleExtension('module_c', 'Module c'),
+    );
+    $permissions = array(
+      'module_a' => array('access_module_a' => 'single_description'),
+      'module_b' => array('access module b' => array('title' => 'Access B', 'description' => 'bla bla')),
+      'module_c' => array('access_module_c' => array('title' => 'Access C', 'description' => 'bla bla', 'restrict access' => TRUE)),
+    );
+    $this->moduleHandler->expects($this->at(0))
+      ->method('getImplementations')
+      ->with('permission')
+      ->willReturn($modules);
+
+    // Setup the module handler.
+    $i = 1;
+    foreach ($modules as $module_name) {
+      $this->moduleHandler->expects($this->at($i++))
+        ->method('invoke')
+        ->with($module_name)
+        ->willReturn($permissions[$module_name]);
+    }
+    $this->moduleHandler->expects($this->any())
+      ->method('getModuleList')
+      ->willReturn(array_flip($modules));
+
+    // Setup system_rebuild_module_data().
+    $this->permissionManager->setSystemRebuildModuleData($extensions);
+
+    $actual_permissions = $this->permissionManager->getPermissions();
+    $this->assertPermissions($actual_permissions);
+    // Ensure that the human name of the module is taken into account for the
+    // sorting.
+    $this->assertSame(array('access_module_a', 'access_module_c', 'access module b'), array_keys($actual_permissions));
+  }
+
+  /**
+   * Tests permissions provided by YML files.
+   *
+   * @covers ::getPermissions
+   * @covers ::buildPermissions
+   * @covers ::buildPermissionYaml
+   */
+  public function testBuildPermissionYaml() {
+    vfsStreamWrapper::register();
+    $root = new vfsStreamDirectory('modules');
+    vfsStreamWrapper::setRoot($root);
+
+    $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
+    $this->moduleHandler->expects($this->once())
+      ->method('getModuleDirectories')
+      ->willReturn(array(
+        'module_a' => vfsStream::url('modules/module_a'),
+        'module_b' => vfsStream::url('modules/module_b'),
+        'module_c' => vfsStream::url('modules/module_c'),
+      ));
+    $this->setUp();
+
+    $url = vfsStream::url('modules');
+    mkdir($url . '/module_a');
+    file_put_contents($url . '/module_a/module_a.permissions.yml',
+"access_module_a: single_description"
+    );
+    mkdir($url . '/module_b');
+    file_put_contents($url . '/module_b/module_b.permissions.yml',
+"'access module b':
+  title: 'Access B'
+  description: 'bla bla'
+");
+    mkdir($url . '/module_c');
+    file_put_contents($url . '/module_c/module_c.permissions.yml',
+"'access_module_c':
+  title: 'Access C'
+  description: 'bla bla'
+  'restrict access': TRUE
+");
+    $modules = array('module_a', 'module_b', 'module_c');
+    $extensions = array(
+      'module_a' => $this->mockModuleExtension('module_a', 'Module a'),
+      'module_b' => $this->mockModuleExtension('module_b', 'Module b'),
+      'module_c' => $this->mockModuleExtension('module_c', 'Module c'),
+    );
+    $this->moduleHandler->expects($this->any(0))
+      ->method('getImplementations')
+      ->with('permission')
+      ->willReturn(array());
+
+    $this->moduleHandler->expects($this->any())
+      ->method('getModuleList')
+      ->willReturn(array_flip($modules));
+
+    // Setup system_rebuild_module_data().
+    $this->permissionManager->setSystemRebuildModuleData($extensions);
+
+    $actual_permissions = $this->permissionManager->getPermissions();
+    $this->assertPermissions($actual_permissions);
+  }
+
+  /**
+   * Checks that the permissions are like expected.
+   *
+   * @param array $actual_permissions
+   *   The actual permissions
+   */
+  protected function assertPermissions(array $actual_permissions) {
+    $this->assertCount(3, $actual_permissions);
+    $this->assertEquals($actual_permissions['access_module_a']['title'], 'single_description');
+    $this->assertEquals($actual_permissions['access_module_a']['provider'], 'module_a');
+    $this->assertEquals($actual_permissions['access module b']['title'], 'Access B');
+    $this->assertEquals($actual_permissions['access module b']['provider'], 'module_b');
+    $this->assertEquals($actual_permissions['access_module_c']['title'], 'Access C');
+    $this->assertEquals($actual_permissions['access_module_c']['provider'], 'module_c');
+    $this->assertEquals($actual_permissions['access_module_c']['restrict access'], TRUE);
+  }
+
+}
+
+class TestPermissionManager extends PermissionManager {
+
+  /**
+   * Test module data.
+   *
+   * @var array
+   */
+  protected $systemModuleData;
+
+  protected function systemRebuildModuleData() {
+    return $this->systemModuleData;
+  }
+
+  public function setSystemRebuildModuleData(array $extensions) {
+    $this->systemModuleData = $extensions;
+  }
+
+}
diff --git a/core/modules/user/user.services.yml b/core/modules/user/user.services.yml
index c703c0b..8cb08b7 100644
--- a/core/modules/user/user.services.yml
+++ b/core/modules/user/user.services.yml
@@ -55,6 +55,9 @@ services:
   user.tempstore:
     class: Drupal\user\TempStoreFactory
     arguments: ['@serialization.phpserialize', '@database', '@lock', '%user.tempstore.expire%']
+  user.permissions:
+    class: Drupal\user\PermissionManager
+    arguments: ['@module_handler', '@string_translation']
 
 parameters:
   user.tempstore.expire: 604800
diff --git a/core/modules/views/views.module b/core/modules/views/views.module
index d9d9805..00044d5 100644
--- a/core/modules/views/views.module
+++ b/core/modules/views/views.module
@@ -292,19 +292,6 @@ function views_theme_suggestions_comment_alter(array &$suggestions, array $varia
 }
 
 /**
- * Implements hook_permission().
- */
-function views_permission() {
-  return array(
-    'access all views' => array(
-      'title' => t('Bypass views access control'),
-      'description' => t('Bypass access control when accessing views.'),
-      'restrict access' => TRUE,
-    ),
-  );
-}
-
-/**
  * Implements hook_menu_link_defaults_alter().
  */
 function views_menu_link_defaults_alter(array &$links) {
diff --git a/core/modules/views/views.permissions.yml b/core/modules/views/views.permissions.yml
new file mode 100644
index 0000000..db47ea5
--- /dev/null
+++ b/core/modules/views/views.permissions.yml
@@ -0,0 +1,4 @@
+'access all views':
+  title: 'Bypass views access control'
+  description: 'Bypass access control when accessing views.'
+  'restrict access': TRUE
diff --git a/core/modules/views_ui/views_ui.module b/core/modules/views_ui/views_ui.module
index 910e4be..fda25ed 100644
--- a/core/modules/views_ui/views_ui.module
+++ b/core/modules/views_ui/views_ui.module
@@ -124,19 +124,6 @@ function views_ui_theme() {
 }
 
 /**
- * Implements hook_permission().
- */
-function views_ui_permission() {
-  return array(
-    'administer views' => array(
-      'title' => t('Administer views'),
-      'description' => t('Access the views administration pages.'),
-      'restrict access' => TRUE,
-    ),
-  );
-}
-
-/**
  * Implements hook_preprocess_HOOK() for views templates.
  */
 function views_ui_preprocess_views_view(&$variables) {
diff --git a/core/modules/views_ui/views_ui.permissions.yml b/core/modules/views_ui/views_ui.permissions.yml
new file mode 100644
index 0000000..0881aab
--- /dev/null
+++ b/core/modules/views_ui/views_ui.permissions.yml
@@ -0,0 +1,4 @@
+'administer views':
+  title: 'Administer views'
+  description: 'Access the views administration pages.'
+  'restrict access': TRUE
diff --git a/core/profiles/standard/standard.install b/core/profiles/standard/standard.install
index 30656be..0d1a0b7 100644
--- a/core/profiles/standard/standard.install
+++ b/core/profiles/standard/standard.install
@@ -34,7 +34,7 @@ function standard_install() {
   user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array('access comments', 'post comments', 'skip comment approval'));
 
   // Enable all permissions for the administrator role.
-  user_role_grant_permissions('administrator', array_keys(\Drupal::moduleHandler()->invokeAll('permission')));
+  user_role_grant_permissions('administrator', array_keys(\Drupal::service('user.permissions')->getPermissions()));
   // Set this as the administrator role.
   $user_settings->set('admin_role', 'administrator')->save();
 
