diff --git a/core/includes/entity.api.php b/core/includes/entity.api.php
index 88e370a3cc11f20a057f0f1b31c5fc9b42373ecc..7c9c5396875dbb806ff714f0113111dccb17ae82 100644
--- a/core/includes/entity.api.php
+++ b/core/includes/entity.api.php
@@ -24,11 +24,114 @@
  * @see entity_get_info()
  */
 function hook_entity_info(&$entity_info) {
-  // Add the 'Print' view mode for nodes.
-  $entity_info['node']['view_modes']['print'] = array(
-    'label' => t('Print'),
-    'custom_settings' => FALSE,
+  // Add a form controller for a custom node form without overriding the default
+  // node form. To override the default node form, use hook_entity_info_alter()
+  // to alter $entity_info['node']['form_controller_class']['default'].
+  $entity_info['node']['form_controller_class']['mymodule_foo'] = 'Drupal\mymodule\NodeFooFormController';
+}
+
+/**
+ * Describe the view modes for entity types.
+ *
+ * View modes let entities be displayed differently depending on the context.
+ * For instance, a node can be displayed differently on its own page ('full'
+ * mode), on the home page or taxonomy listings ('teaser' mode), or in an RSS
+ * feed ('rss' mode). Modules taking part in the display of the entity (notably
+ * the Field API) can adjust their behavior depending on the requested view
+ * mode. An additional 'default' view mode is available for all entity types.
+ * This view mode is not intended for actual entity display, but holds default
+ * display settings. For each available view mode, administrators can configure
+ * whether it should use its own set of field display settings, or just
+ * replicate the settings of the 'default' view mode, thus reducing the amount
+ * of display configurations to keep track of.
+ *
+ * @return array
+ *   An associative array of all entity view modes, keyed by the entity
+ *   type name, and then the view mode name, with the following keys:
+ *   - label: The human-readable name of the view mode.
+ *   - custom_settings: A boolean specifying whether the view mode should by
+ *     default use its own custom field display settings. If FALSE, entities
+ *     displayed in this view mode will reuse the 'default' display settings
+ *     by default (e.g. right after the module exposing the view mode is
+ *     enabled), but administrators can later use the Field UI to apply custom
+ *     display settings specific to the view mode.
+ *
+ * @see entity_get_view_modes()
+ * @see hook_entity_view_mode_info_alter()
+ */
+function hook_entity_view_mode_info() {
+  $view_modes['user']['full'] = array(
+    'label' => t('User account'),
+  );
+  $view_modes['user']['compact'] = array(
+    'label' => t('Compact'),
+    'custom_settings' => TRUE,
+  );
+  return $view_modes;
+}
+
+/**
+ * Alter the view modes for entity types.
+ *
+ * @param array $view_modes
+ *   An array of view modes, keyed first by entity type, then by view mode name.
+ *
+ * @see entity_get_view_modes()
+ * @see hook_entity_view_mode_info()
+ */
+function hook_entity_view_mode_info_alter(&$view_modes) {
+  $view_modes['user']['full']['custom_settings'] = TRUE;
+}
+
+/**
+ * Describe the bundles for entity types.
+ *
+ * @return array
+ *   An associative array of all entity bundles, keyed by the entity
+ *   type name, and then the bundle name, with the following keys:
+ *   - label: The human-readable name of the bundle.
+ *   - uri_callback: The same as the 'uri_callback' key defined for the entity
+ *     type in the EntityManager, but for the bundle only. When determining
+ *     the URI of an entity, if a 'uri_callback' is defined for both the
+ *     entity type and the bundle, the one for the bundle is used.
+ *   - admin: An array of information that allows Field UI pages to attach
+ *     themselves to the existing administration pages for the bundle.
+ *     Elements:
+ *     - path: the path of the bundle's main administration page, as defined
+ *       in hook_menu(). If the path includes a placeholder for the bundle,
+ *       the 'bundle argument', 'bundle helper' and 'real path' keys below
+ *       are required.
+ *     - bundle argument: The position of the placeholder in 'path', if any.
+ *     - real path: The actual path (no placeholder) of the bundle's main
+ *       administration page. This will be used to generate links.
+ *     - access callback: As in hook_menu(). 'user_access' will be assumed if
+ *       no value is provided.
+ *     - access arguments: As in hook_menu().
+ *
+ * @see entity_get_bundles()
+ * @see hook_entity_bundle_info_alter()
+ */
+function hook_entity_bundle_info() {
+  $bundles['user']['user'] = array(
+    'label' => t('User'),
+    'admin' => array(
+      'path' => 'admin/config/people/accounts',
+    ),
   );
+  return $bundles;
+}
+
+/**
+ * Alter the bundles for entity types.
+ *
+ * @param array $bundles
+ *   An array of bundles, keyed first by entity type, then by bundle name.
+ *
+ * @see entity_get_bundles()
+ * @see hook_entity_bundle_info()
+ */
+function hook_entity_bundle_info_alter(&$bundles) {
+  $bundles['user']['user']['label'] = t('Full account');
 }
 
 /**
diff --git a/core/includes/entity.inc b/core/includes/entity.inc
index e5d2ae51205a1f509ddba119366642f342d2eff2..2d21e8222c7b18e98c63b3c242b4cb42c5131795 100644
--- a/core/includes/entity.inc
+++ b/core/includes/entity.inc
@@ -49,23 +49,91 @@ function entity_get_info($entity_type = NULL) {
  */
 function entity_info_cache_clear() {
   drupal_static_reset('entity_get_info');
+  drupal_static_reset('entity_get_view_modes');
+  drupal_static_reset('entity_get_bundles');
   // Clear all languages.
   cache()->deleteTags(array('entity_info' => TRUE));
 }
 
 /**
- * Returns the defined bundles for the given entity type.
+ * Returns the entity bundle info.
  *
- * @param string $entity_type
- *   The entity type whose bundles should be returned.
+ * @param string|null $entity_type
+ *   The entity type whose bundle info should be returned, or NULL for all
+ *   bundles info. Defaults to NULL.
  *
  * @return array
- *   An array containing the bundle names or the entity type name itself if no
- *   bundle is defined.
+ *   The bundle info for a specific entity type, or all entity types.
  */
-function entity_get_bundles($entity_type) {
-  $entity_info = entity_get_info($entity_type);
-  return isset($entity_info['bundles']) ? array_keys($entity_info['bundles']) : array($entity_type);
+function entity_get_bundles($entity_type = NULL) {
+  $bundles = &drupal_static(__FUNCTION__);
+  if (!$bundles) {
+    $langcode = language(LANGUAGE_TYPE_INTERFACE)->langcode;
+    if ($cache = cache()->get("entity_bundle_info:$langcode")) {
+      $bundles = $cache->data;
+    }
+    else {
+      $bundles = module_invoke_all('entity_bundle_info');
+      // If no bundles are provided, use the entity type name and label.
+      foreach (entity_get_info() as $type => $entity_info) {
+        if (!isset($bundles[$type])) {
+          $bundles[$type][$type]['label'] = $entity_info['label'];
+        }
+      }
+      drupal_alter('entity_bundle_info', $bundles);
+      cache()->set("entity_bundle_info:$langcode", $bundles, CacheBackendInterface::CACHE_PERMANENT, array('entity_info' => TRUE));
+    }
+  }
+
+  if (empty($entity_type)) {
+    return $bundles;
+  }
+  elseif (isset($bundles[$entity_type])) {
+    return $bundles[$entity_type];
+  }
+
+  return array();
+}
+
+/**
+ * Returns the entity view mode info.
+ *
+ * @param string|null $entity_type
+ *   The entity type whose view mode info should be returned, or NULL for all
+ *   view mode info. Defaults to NULL.
+ *
+ * @return array
+ *   The view mode info for a specific entity type, or all entity types.
+ */
+function entity_get_view_modes($entity_type = NULL) {
+  $view_modes = &drupal_static(__FUNCTION__);
+  if (!$view_modes) {
+    $langcode = language(LANGUAGE_TYPE_INTERFACE)->langcode;
+    if ($cache = cache()->get("entity_view_mode_info:$langcode")) {
+      $view_modes = $cache->data;
+    }
+    else {
+      $view_modes = module_invoke_all('entity_view_mode_info');
+      foreach ($view_modes as $type => $entity_info) {
+        foreach ($entity_info as $view_mode => $view_mode_info) {
+          $view_modes[$type][$view_mode] += array(
+            'custom_settings' => FALSE,
+          );
+        }
+      }
+      drupal_alter('entity_view_mode_info', $view_modes);
+      cache()->set("entity_view_mode_info:$langcode", $view_modes, CacheBackendInterface::CACHE_PERMANENT, array('entity_info' => TRUE));
+    }
+  }
+
+  if (empty($entity_type)) {
+    return $view_modes;
+  }
+  elseif (isset($view_modes[$entity_type])) {
+    return $view_modes[$entity_type];
+  }
+
+  return array();
 }
 
 /**
diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index 7ea923f33643c3fd1669189b693389f1b196b2d2..938908588b1ad65f5902414d70d8343bbe50e410 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -154,8 +154,9 @@ public function uri() {
     // A bundle-specific callback takes precedence over the generic one for the
     // entity type.
     $entity_info = $this->entityInfo();
-    if (isset($entity_info['bundles'][$bundle]['uri_callback'])) {
-      $uri_callback = $entity_info['bundles'][$bundle]['uri_callback'];
+    $bundles = entity_get_bundles($this->entityType);
+    if (isset($bundles[$bundle]['uri_callback'])) {
+      $uri_callback = $bundles[$bundle]['uri_callback'];
     }
     elseif (isset($entity_info['uri_callback'])) {
       $uri_callback = $entity_info['uri_callback'];
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index 659043e938d203c724b5d90b3e91cfd34cd431a6..c760707834a8245e1a78248178ff4bec1ce6a1f6 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -109,49 +109,6 @@
  *   Elements:
  *   - bundle: The name of the property that contains the name of the bundle
  *     object.
- * - bundles: An array describing all bundles for this object type. Keys are
- *   bundle machine names, as found in the objects' 'bundle' property
- *   (defined in the 'entity_keys' entry for the entity type in the
- *   EntityManager). Elements:
- *   - label: The human-readable name of the bundle.
- *   - uri_callback: The same as the 'uri_callback' key defined for the entity
- *     type in the EntityManager, but for the bundle only. When determining
- *     the URI of an entity, if a 'uri_callback' is defined for both the
- *     entity type and the bundle, the one for the bundle is used.
- *   - admin: An array of information that allows Field UI pages to attach
- *     themselves to the existing administration pages for the bundle.
- *     Elements:
- *     - path: the path of the bundle's main administration page, as defined
- *       in hook_menu(). If the path includes a placeholder for the bundle,
- *       the 'bundle argument', 'bundle helper' and 'real path' keys below
- *       are required.
- *     - bundle argument: The position of the placeholder in 'path', if any.
- *     - real path: The actual path (no placeholder) of the bundle's main
- *       administration page. This will be used to generate links.
- *     - access callback: As in hook_menu(). 'user_access' will be assumed if
- *       no value is provided.
- *     - access arguments: As in hook_menu().
- * - view_modes: An array describing the view modes for the entity type. View
- *   modes let entities be displayed differently depending on the context.
- *   For instance, a node can be displayed differently on its own page
- *   ('full' mode), on the home page or taxonomy listings ('teaser' mode), or
- *   in an RSS feed ('rss' mode). Modules taking part in the display of the
- *   entity (notably the Field API) can adjust their behavior depending on
- *   the requested view mode. An additional 'default' view mode is available
- *   for all entity types. This view mode is not intended for actual entity
- *   display, but holds default display settings. For each available view
- *   mode, administrators can configure whether it should use its own set of
- *   field display settings, or just replicate the settings of the 'default'
- *   view mode, thus reducing the amount of display configurations to keep
- *   track of. Keys of the array are view mode names. Each view mode is
- *   described by an array with the following key/value pairs:
- *   - label: The human-readable name of the view mode.
- *   - custom_settings: A boolean specifying whether the view mode should by
- *     default use its own custom field display settings. If FALSE, entities
- *     displayed in this view mode will reuse the 'default' display settings
- *     by default (e.g. right after the module exposing the view mode is
- *     enabled), but administrators can later use the Field UI to apply custom
- *     display settings specific to the view mode.
  * - menu_base_path: (optional) The base menu router path to which the entity
  *   administration user interface responds. It can be used to generate UI
  *   links and to attach additional router items to the entity UI in a generic
@@ -229,8 +186,6 @@ class EntityManager extends PluginManagerBase {
     'access_controller_class' => 'Drupal\Core\Entity\EntityAccessController',
     'static_cache' => TRUE,
     'translation' => array(),
-    'bundles' => array(),
-    'view_modes' => array(),
   );
 
   /**
@@ -286,17 +241,6 @@ public function processDefinition(&$definition, $plugin_id) {
       return;
     }
 
-    foreach ($definition['view_modes'] as $view_mode => $view_mode_info) {
-      $definition['view_modes'][$view_mode] += array(
-        'custom_settings' => FALSE,
-      );
-    }
-
-    // If no bundle key is provided, assume a single bundle, named after
-    // the entity type.
-    if (empty($definition['entity_keys']['bundle']) && empty($definition['bundles'])) {
-      $definition['bundles'] = array($plugin_id => array('label' => $definition['label']));
-    }
     // Prepare entity schema fields SQL info for
     // Drupal\Core\Entity\DatabaseStorageControllerInterface::buildQuery().
     if (isset($definition['base_table'])) {
diff --git a/core/modules/book/book.module b/core/modules/book/book.module
index af436d481a1a4180fc4e144aa3bad59f87ebce1b..248759fff54c9ee40d8e14684c8fad1447dcb206 100644
--- a/core/modules/book/book.module
+++ b/core/modules/book/book.module
@@ -250,14 +250,14 @@ function book_admin_paths() {
 }
 
 /**
- * Implements hook_entity_info().
+ * Implements hook_entity_view_mode_info().
  */
-function book_entity_info(&$info) {
+function book_entity_view_mode_info() {
   // Add the 'Print' view mode for nodes.
-  $info['node']['view_modes']['print'] = array(
+  $view_modes['node']['print'] = array(
     'label' => t('Print'),
-    'custom_settings' => FALSE,
   );
+  return $view_modes;
 }
 
 /**
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index 3bcabfeb940e482aae7f410c365f4f7e24884858..554b635ee24ea0fde1e74af89c6f3be57254c77f 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -99,11 +99,22 @@ function comment_help($path, $arg) {
 }
 
 /**
- * Implements hook_entity_info().
+ * Implements hook_entity_view_mode_info().
  */
-function comment_entity_info(&$info) {
+function comment_entity_view_mode_info() {
+  $view_modes['comment']['full'] = array(
+    'label' => t('Full comment'),
+  );
+  return $view_modes;
+}
+
+/**
+ * Implements hook_entity_bundle_info().
+ */
+function comment_entity_bundle_info() {
+  $bundles = array();
   foreach (node_type_get_names() as $type => $name) {
-    $info['comment']['bundles']['comment_node_' . $type] = array(
+    $bundles['comment']['comment_node_' . $type] = array(
       'label' => t('@node_type comment', array('@node_type' => $name)),
       // Provide the node type/bundle name for other modules, so it does not
       // have to be extracted manually from the bundle name.
@@ -121,6 +132,7 @@ function comment_entity_info(&$info) {
       ),
     );
   }
+  return $bundles;
 }
 
 /**
@@ -289,7 +301,7 @@ function comment_menu_alter(&$items) {
   $items['admin/content']['description'] = 'Administer content and comments.';
 
   // Adjust the Field UI tabs on admin/structure/types/manage/[node-type].
-  // See comment_entity_info().
+  // See comment_entity_bundle_info().
   $items['admin/structure/types/manage/%comment_node_type/comment/fields']['title'] = 'Comment fields';
   $items['admin/structure/types/manage/%comment_node_type/comment/fields']['weight'] = 3;
   $items['admin/structure/types/manage/%comment_node_type/comment/display']['title'] = 'Comment display';
@@ -1389,7 +1401,7 @@ function comment_delete_multiple($cids) {
  *   (optional) An array of entity IDs. If omitted, all entities are loaded.
  * @param bool $reset
  *   Whether to reset the internal static entity cache. Note that the static
- *   cache is disabled in comment_entity_info() by default.
+ *   cache is disabled by default.
  *
  * @return array
  *   An array of comment objects, indexed by comment ID.
@@ -1408,7 +1420,7 @@ function comment_load_multiple(array $cids = NULL, $reset = FALSE) {
  *   The ID of the comment to be loaded.
  * @param bool $reset
  *   Whether to reset the internal static entity cache. Note that the static
- *   cache is disabled in comment_entity_info() by default.
+ *   cache is disabled by default.
  *
  * @return
  *   The comment object.
diff --git a/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php b/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php
index c6175af94c3501c04b149176f957a66f02da7f67..94934881b50b653c79d9f317b6afa131040af1d3 100644
--- a/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php
+++ b/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php
@@ -35,12 +35,6 @@
  *     "bundle" = "node_type",
  *     "label" = "subject",
  *     "uuid" = "uuid"
- *   },
- *   view_modes = {
- *     "full" = {
- *       "label" = "Full comment",
- *       "custom_settings" = FALSE
- *     }
  *   }
  * )
  */
diff --git a/core/modules/comment/lib/Drupal/comment/Plugin/views/row/Rss.php b/core/modules/comment/lib/Drupal/comment/Plugin/views/row/Rss.php
index 3afa8023b45465e84767487e0b795aaa4ee551d8..96809ef4f87039ebe2361c7c0270b64eaeb689db 100644
--- a/core/modules/comment/lib/Drupal/comment/Plugin/views/row/Rss.php
+++ b/core/modules/comment/lib/Drupal/comment/Plugin/views/row/Rss.php
@@ -77,12 +77,10 @@ function pre_render($result) {
    * in views_plugin_row_comment|node_rss.inc
    */
   function options_form_summary_options() {
-    $entity_info = entity_get_info('node');
+    $view_modes = entity_get_view_modes('node');
     $options = array();
-    if (!empty($entity_info['view_modes'])) {
-      foreach ($entity_info['view_modes'] as $mode => $settings) {
-        $options[$mode] = $settings['label'];
-      }
+    foreach ($view_modes as $mode => $settings) {
+      $options[$mode] = $settings['label'];
     }
     $options['title'] = t('Title only');
     $options['default'] = t('Use site default RSS settings');
diff --git a/core/modules/contact/contact.module b/core/modules/contact/contact.module
index 44a6f5af1abbbf6f2e3b710f2e1c4c0455569d29..2686b80e4cc7f12fe6f2eae40fe11a39c77ea113 100644
--- a/core/modules/contact/contact.module
+++ b/core/modules/contact/contact.module
@@ -174,12 +174,13 @@ function _contact_personal_tab_access($account) {
 }
 
 /**
- * Implements hook_entity_info().
+ * Implements hook_entity_bundle_info().
  */
-function contact_entity_info(&$types) {
+function contact_entity_bundle_info() {
+  $bundles = array();
   foreach (config_get_storage_names_with_prefix('contact.category.') as $config_name) {
     $config = config($config_name);
-    $types['contact_message']['bundles'][$config->get('id')] = array(
+    $bundles['contact_message'][$config->get('id')] = array(
       'label' => $config->get('label'),
       'admin' => array(
         'path' => 'admin/structure/contact/manage/%contact_category',
@@ -189,6 +190,7 @@ function contact_entity_info(&$types) {
       ),
     );
   }
+  return $bundles;
 }
 
 /**
@@ -196,8 +198,7 @@ function contact_entity_info(&$types) {
  */
 function contact_field_extra_fields() {
   $fields = array();
-  $entity_info = entity_get_info('contact_message');
-  foreach (array_keys($entity_info['bundles']) as $bundle) {
+  foreach (array_keys(entity_get_bundles('contact_message')) as $bundle) {
     $fields['contact_message'][$bundle]['form']['name'] = array(
       'label' => t('Sender name'),
       'description' => t('Text'),
diff --git a/core/modules/field/field.info.inc b/core/modules/field/field.info.inc
index 688d6116e6c87b27ad4ae53cd44ec992c3c7832c..b422b475813c67b9130ac11679c9bcc0c581ba7d 100644
--- a/core/modules/field/field.info.inc
+++ b/core/modules/field/field.info.inc
@@ -275,31 +275,6 @@ function field_info_storage_types($storage_type = NULL) {
   }
 }
 
-/**
- * Returns information about existing bundles.
- *
- * @param $entity_type
- *   The type of entity; e.g. 'node' or 'user'.
- *
- * @return
- *   An array of bundles for the $entity_type keyed by bundle name, or, if no
- *   $entity_type was provided, the array of all existing bundles, keyed by
- *   entity type.
- */
-function field_info_bundles($entity_type = NULL) {
-  $info = entity_get_info();
-
-  if ($entity_type) {
-    return isset($info[$entity_type]['bundles']) ? $info[$entity_type]['bundles'] : array();
-  }
-
-  $bundles = array();
-  foreach ($info as $type => $entity_info) {
-    $bundles[$type] = $entity_info['bundles'];
-  }
-  return $bundles;
-}
-
 /**
  * Returns all field definitions.
  *
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index e350a2e367c8524780337461f34644528c57377d..7673ea7d9127b465817209584ec057c46f86301f 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -746,8 +746,8 @@ function field_view_mode_settings($entity_type, $bundle) {
     // Include view modes for which nothing has been stored yet, but whose
     // definition in hook_entity_info_alter() specify they should use custom
     // settings by default.
-    $entity_info = entity_get_info($entity_type);
-    foreach ($entity_info['view_modes'] as $view_mode => $view_mode_info) {
+    $view_modes = entity_get_view_modes($entity_type);
+    foreach ($view_modes as $view_mode => $view_mode_info) {
       if (!isset($settings[$view_mode]['custom_settings']) && $view_mode_info['custom_settings']) {
         $settings[$view_mode]['custom_settings'] = TRUE;
       }
diff --git a/core/modules/field/tests/modules/field_test/field_test.entity.inc b/core/modules/field/tests/modules/field_test/field_test.entity.inc
index aeb1634feb4f48321dea56f6c33e75ab7715f8c0..1f8bd4b44f8eab4a13cc04afbb4c407dfaf6d1e8 100644
--- a/core/modules/field/tests/modules/field_test/field_test.entity.inc
+++ b/core/modules/field/tests/modules/field_test/field_test.entity.inc
@@ -19,8 +19,17 @@ function field_test_entity_info_alter(&$entity_info) {
   // Disable the entity type translation handler.
   foreach ($entity_info as $entity_type => $info) {
     $entity_info[$entity_type]['translation'][$entity_type] = FALSE;
-    if ($info['module'] == 'field_test') {
-      $entity_info[$entity_type]['view_modes'] = array(
+  }
+}
+
+/**
+ * Implements hook_entity_view_mode_info_alter().
+ */
+function field_test_entity_view_mode_info_alter(&$view_modes) {
+  $entity_info = entity_get_info();
+  foreach ($entity_info as $entity_type => $info) {
+    if ($entity_info[$entity_type]['module'] == 'field_test') {
+      $view_modes[$entity_type] = array(
         'full' => array(
           'label' => t('Full object'),
           'custom_settings' => TRUE,
@@ -30,12 +39,23 @@ function field_test_entity_info_alter(&$entity_info) {
           'custom_settings' => TRUE,
         ),
       );
-      $entity_info[$entity_type]['bundles'] = state()->get('field_test_bundles') ?: array('test_bundle' => array('label' => 'Test Bundle'));
+    }
+  }
+}
+
+/**
+ * Implements hook_entity_bundle_info_alter().
+ */
+function field_test_entity_bundle_info_alter(&$bundles) {
+  $entity_info = entity_get_info();
+  foreach ($bundles as $entity_type => $info) {
+    if ($entity_info[$entity_type]['module'] == 'field_test') {
+      $bundles[$entity_type] = state()->get('field_test_bundles') ?: array('test_bundle' => array('label' => 'Test Bundle'));
       if ($entity_type == 'test_entity_bundle') {
-        $entity_info[$entity_type]['bundles'] += array('test_entity_2' => array('label' => 'Test entity 2'));
+        $bundles[$entity_type] += array('test_entity_2' => array('label' => 'Test entity 2'));
       }
       if ($entity_type == 'test_entity_bundle_key') {
-        $entity_info[$entity_type]['bundles'] += array('bundle1' => array('label' => 'Bundle1'), 'bundle2' => array('label' => 'Bundle2'));
+        $bundles[$entity_type] += array('bundle1' => array('label' => 'Bundle1'), 'bundle2' => array('label' => 'Bundle2'));
       }
     }
   }
diff --git a/core/modules/field/tests/modules/field_test/field_test.module b/core/modules/field/tests/modules/field_test/field_test.module
index 741dd607cf853e998f3a5cce3b5f7eb64d877a61..03094dd532f15a19d179756712607aab13fe4fc0 100644
--- a/core/modules/field/tests/modules/field_test/field_test.module
+++ b/core/modules/field/tests/modules/field_test/field_test.module
@@ -45,9 +45,8 @@ function field_test_permission() {
  */
 function field_test_menu() {
   $items = array();
-  $bundles = field_info_bundles('test_entity');
 
-  foreach ($bundles as $bundle_name => $bundle_info) {
+  foreach (entity_get_bundles('test_entity') as $bundle_name => $bundle_info) {
     $items['test-entity/add/' . $bundle_name] = array(
       'title' => t('Add %bundle test_entity', array('%bundle' => $bundle_info['label'])),
       'page callback' => 'field_test_entity_add',
@@ -309,7 +308,7 @@ function field_test_field_extra_fields_alter(&$info) {
  * @todo Remove when http://drupal.org/node/1822458 is fixed.
  */
 function field_test_module_implements_alter(&$implementations, $hook) {
-  if ($hook == 'entity_info_alter' && isset($implementations['field_test']) && isset($implementations['rdf'])) {
+  if ($hook == 'entity_bundle_info_alter' && isset($implementations['field_test']) && isset($implementations['rdf'])) {
     foreach (array('field_test', 'rdf') as $module) {
       $group = $implementations[$module];
       unset($implementations[$module]);
diff --git a/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Tables.php b/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Tables.php
index bf02e2b69a863d36d0d5993cece191655cef6f92..68a5701dfca340c52cd15b43abe0048372c379ab 100644
--- a/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Tables.php
+++ b/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Tables.php
@@ -174,8 +174,7 @@ function addField($field, $type, $langcode) {
           // If there are bundles, pick one. It does not matter which,
           // properties exist on all bundles.
           if (!empty($entity_info['entity keys']['bundle'])) {
-            $bundles = array_keys($entity_info['bundles']);
-            $values[$entity_info['entity keys']['bundle']] = reset($bundles);
+            $values[$entity_info['entity keys']['bundle']] = key(entity_get_bundles('node'));
           }
           $entity = entity_create($entity_type, $values);
           $propertyDefinitions = $entity->$specifier->getPropertyDefinitions();
diff --git a/core/modules/field_ui/field_ui.admin.inc b/core/modules/field_ui/field_ui.admin.inc
index 6da0f6947debb5728193c8935b9796f04592053f..fd225cae61b5399745c660ad0fb6650d13f9d087 100644
--- a/core/modules/field_ui/field_ui.admin.inc
+++ b/core/modules/field_ui/field_ui.admin.inc
@@ -17,7 +17,7 @@
 function field_ui_fields_list() {
   $instances = field_info_instances();
   $field_types = field_info_field_types();
-  $bundles = field_info_bundles();
+  $bundles = entity_get_bundles();
 
   $modules = system_rebuild_module_data();
 
@@ -654,7 +654,7 @@ function field_ui_widget_type_form($form, &$form_state, FieldInstance $instance)
   $field_name = $instance['field_name'];
 
   $field = field_info_field($field_name);
-  $bundles = field_info_bundles();
+  $bundles = entity_get_bundles();
   $bundle_label = $bundles[$entity_type][$bundle]['label'];
 
   $form = array(
@@ -759,7 +759,7 @@ function field_ui_field_delete_form_submit($form, &$form_state) {
 
   $field = field_info_field($field_name);
   $instance = field_info_instance($entity_type, $field_name, $bundle);
-  $bundles = field_info_bundles();
+  $bundles = entity_get_bundles();
   $bundle_label = $bundles[$entity_type][$bundle]['label'];
 
   if (!empty($bundle) && $field && !$field['locked'] && $form_values['confirm']) {
@@ -795,7 +795,7 @@ function field_ui_field_edit_form($form, &$form_state, $instance) {
   $bundle = $instance['bundle'];
   $entity_type = $instance['entity_type'];
   $field = field_info_field($instance['field_name']);
-  $bundles = field_info_bundles();
+  $bundles = entity_get_bundles();
 
   drupal_set_title(t('%instance settings for %bundle', array(
     '%instance' => $instance['label'],
diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module
index dd59df9cb3f78db2843687a3691bbdf63e63bef8..b9d1a1c24f8fb76b93c2018b851331c89845b942 100644
--- a/core/modules/field_ui/field_ui.module
+++ b/core/modules/field_ui/field_ui.module
@@ -71,7 +71,7 @@ function field_ui_menu() {
   // Create tabs for all possible bundles.
   foreach (entity_get_info() as $entity_type => $entity_info) {
     if ($entity_info['fieldable']) {
-      foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
+      foreach (entity_get_bundles($entity_type) as $bundle_name => $bundle_info) {
         if (isset($bundle_info['admin'])) {
           // Extract path information from the bundle.
           $path = $bundle_info['admin']['path'];
@@ -173,7 +173,7 @@ function field_ui_menu() {
           // view modes, and use an access callback to determine which ones are
           // actually visible for a given bundle.
           $weight = 0;
-          $view_modes = array('default' => array('label' => t('Default'))) + $entity_info['view_modes'];
+          $view_modes = array('default' => array('label' => t('Default'))) + entity_get_view_modes($entity_type);
           foreach ($view_modes as $view_mode => $view_mode_info) {
             $items["$path/display/$view_mode"] = array(
               'title' => $view_mode_info['label'],
@@ -328,7 +328,7 @@ function field_ui_field_attach_create_bundle($entity_type, $bundle) {
  * Determines the adminstration path for a bundle.
  */
 function field_ui_bundle_admin_path($entity_type, $bundle_name) {
-  $bundles = field_info_bundles($entity_type);
+  $bundles = entity_get_bundles($entity_type);
   $bundle_info = $bundles[$bundle_name];
   if (isset($bundle_info['admin'])) {
     return isset($bundle_info['admin']['real path']) ? $bundle_info['admin']['real path'] : $bundle_info['admin']['path'];
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php
index 4ead6e7dd6f68951cd18390342a041b01eb4e657..fe725cc43703a37bc439ab946e6be350625b197a 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php
@@ -346,8 +346,7 @@ public function form(array $form, array &$form_state) {
 
     // Custom display settings.
     if ($this->view_mode == 'default') {
-      $entity_info = entity_get_info($this->entity_type);
-      $view_modes = $entity_info['view_modes'];
+      $view_modes = entity_get_view_modes($this->entity_type);
       // Only show the settings if there is more than one view mode.
       if (count($view_modes) > 1) {
         $form['modes'] = array(
@@ -478,6 +477,7 @@ public function submit(array $form, array &$form_state) {
     // Handle the 'view modes' checkboxes if present.
     if ($this->view_mode == 'default' && !empty($form_values['view_modes_custom'])) {
       $entity_info = entity_get_info($this->entity_type);
+      $view_modes = entity_get_view_modes($this->entity_type);
       $bundle_settings = field_bundle_settings($this->entity_type, $this->bundle);
       $view_mode_settings = field_view_mode_settings($this->entity_type, $this->bundle);
 
@@ -491,7 +491,7 @@ public function submit(array $form, array &$form_state) {
             $display->save();
           }
 
-          $view_mode_label = $entity_info['view_modes'][$view_mode]['label'];
+          $view_mode_label = $view_modes[$view_mode]['label'];
           $path = field_ui_bundle_admin_path($this->entity_type, $this->bundle) . "/display/$view_mode";
           drupal_set_message(t('The %view_mode mode now uses custom display settings. You might want to <a href="@url">configure them</a>.', array('%view_mode' => $view_mode_label, '@url' => url($path))));
         }
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index b9e604ede12bc83d87898cae777e9854ed182d3d..913f75a3bfcb4d4dee021cb6b95045b0ab276c25 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -89,6 +89,16 @@ function file_element_info() {
   return $types;
 }
 
+/**
+ * Implements hook_entity_view_mode_info().
+ */
+function file_entity_view_mode_info() {
+  $view_modes['file']['full'] = array(
+    'label' => t('File default'),
+  );
+  return $view_modes;
+}
+
 /**
  * Loads file entities from the database.
  *
diff --git a/core/modules/file/lib/Drupal/file/Plugin/Core/Entity/File.php b/core/modules/file/lib/Drupal/file/Plugin/Core/Entity/File.php
index bc54514f685d539b161aee0e14ee5e9c9222546b..d7f2a6ba418abfae9d146aa095140c6dddcc32e6 100644
--- a/core/modules/file/lib/Drupal/file/Plugin/Core/Entity/File.php
+++ b/core/modules/file/lib/Drupal/file/Plugin/Core/Entity/File.php
@@ -26,12 +26,6 @@
  *     "id" = "fid",
  *     "label" = "filename",
  *     "uuid" = "uuid"
- *   },
- *   view_modes = {
- *     "full" = {
- *       "label" = "File default",
- *       "custom_settings" = FALSE
- *     }
  *   }
  * )
  */
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index e9cd043365cdd9a47b5f81fa48348b24c69c6489..ce8397da2e9f64ab75a4bf5defebe1a775402087 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -217,13 +217,13 @@ function forum_menu_local_tasks_alter(&$data, $router_item, $root_path) {
 }
 
 /**
- * Implements hook_entity_info_alter().
+ * Implements hook_entity_bundle_info_alter().
  */
-function forum_entity_info_alter(&$info) {
+function forum_entity_bundle_info_alter(&$bundles) {
   // Take over URI construction for taxonomy terms that are forums.
   if ($vid = config('forum.settings')->get('vocabulary')) {
-    if (isset($info['taxonomy_term']['bundles'][$vid])) {
-      $info['taxonomy_term']['bundles'][$vid]['uri_callback'] = 'forum_uri';
+    if (isset($bundles['taxonomy_term'][$vid])) {
+      $bundles['taxonomy_term'][$vid]['uri_callback'] = 'forum_uri';
     }
   }
 }
diff --git a/core/modules/image/image.module b/core/modules/image/image.module
index e5c2b9b746682b24fda83b75e1f4878cd51c0d79..50f1081c8fa3d3140853ebb73cbacb77f04c822c 100644
--- a/core/modules/image/image.module
+++ b/core/modules/image/image.module
@@ -352,8 +352,8 @@ function image_image_style_update(ImageStyle $style) {
     // Loop through all fields searching for image fields.
     foreach ($instances as $instance) {
       if ($instance['widget']['module'] == 'image') {
-        $entity_info = entity_get_info($instance['entity_type']);
-        $view_modes = array('default') + array_keys($entity_info['view_modes']);
+        $view_modes = entity_get_view_modes($instance['entity_type']);
+        $view_modes = array('default') + array_keys($view_modes);
         foreach ($view_modes as $view_mode) {
           $display = entity_get_display($instance['entity_type'], $instance['bundle'], $view_mode);
           $display_options = $display->getComponent($instance['field_name']);
diff --git a/core/modules/language/language.admin.inc b/core/modules/language/language.admin.inc
index 1e6cdc4017988d431ef1c022a358130877152723..b078d2392d744407cd453c2e1f449941366bb814 100644
--- a/core/modules/language/language.admin.inc
+++ b/core/modules/language/language.admin.inc
@@ -1002,7 +1002,7 @@ function language_content_settings_form(array $form, array $form_state, array $s
     $default[$entity_type] = FALSE;
 
     // Check whether we have any custom setting.
-    foreach (entity_get_bundles($entity_type) as $bundle) {
+    foreach (entity_get_bundles($entity_type) as $bundle => $bundle_info) {
       $conf = language_get_default_configuration($entity_type, $bundle);
       if (!empty($conf['language_show']) || $conf['langcode'] != 'site_default') {
         $default[$entity_type] = $entity_type;
@@ -1043,10 +1043,10 @@ function language_content_settings_form(array $form, array $form_state, array $s
       ),
     );
 
-    foreach (entity_get_bundles($entity_type) as $bundle) {
+    foreach (entity_get_bundles($entity_type) as $bundle => $bundle_info) {
       $form['settings'][$entity_type][$bundle]['settings'] = array(
         '#type' => 'item',
-        '#label' => isset($info['bundles'][$bundle]) ? $info['bundles'][$bundle]['label'] : $labels[$entity_type],
+        '#label' => $bundle_info['label'],
         'language' => array(
           '#type' => 'language_configuration',
           '#entity_information' => array(
diff --git a/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php b/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php
index 6cf9fac395570f996207fa8f171da26cc42c70ba..a78309ba004fa26d527088213970f983e6b636a4 100644
--- a/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php
+++ b/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php
@@ -39,20 +39,6 @@
  *   },
  *   bundle_keys = {
  *     "bundle" = "type"
- *   },
- *   view_modes = {
- *     "full" = {
- *       "label" = "Full content",
- *       "custom_settings" = FALSE
- *     },
- *     "teaser" = {
- *       "label" = "Teaser",
- *       "custom_settings" = TRUE
- *     },
- *     "rss" = {
- *       "label" = "RSS",
- *       "custom_settings" = FALSE
- *     }
  *   }
  * )
  */
diff --git a/core/modules/node/lib/Drupal/node/Plugin/views/row/Rss.php b/core/modules/node/lib/Drupal/node/Plugin/views/row/Rss.php
index 31dab0b6a36e2db26f13635be045ec316e9359f6..20c8f12091ba2ce817bfd3af2448940be8fc76c9 100644
--- a/core/modules/node/lib/Drupal/node/Plugin/views/row/Rss.php
+++ b/core/modules/node/lib/Drupal/node/Plugin/views/row/Rss.php
@@ -64,12 +64,10 @@ public function buildOptionsForm(&$form, &$form_state) {
    * Return the main options, which are shown in the summary title.
    */
   public function buildOptionsForm_summary_options() {
-    $entity_info = entity_get_info('node');
+    $view_modes = entity_get_view_modes('node');
     $options = array();
-    if (!empty($entity_info['view_modes'])) {
-      foreach ($entity_info['view_modes'] as $mode => $settings) {
-        $options[$mode] = $settings['label'];
-      }
+    foreach ($view_modes as $mode => $settings) {
+      $options[$mode] = $settings['label'];
     }
     $options['title'] = t('Title only');
     $options['default'] = t('Use site default RSS settings');
diff --git a/core/modules/node/lib/Drupal/node/Plugin/views/wizard/Node.php b/core/modules/node/lib/Drupal/node/Plugin/views/wizard/Node.php
index fec2bfe41d92a61cdec4f297460eafbbd967ebbb..a7f4f8872a1e9b6cc8e40d9375ef548d2308a249 100644
--- a/core/modules/node/lib/Drupal/node/Plugin/views/wizard/Node.php
+++ b/core/modules/node/lib/Drupal/node/Plugin/views/wizard/Node.php
@@ -248,7 +248,6 @@ protected  function display_options_row(&$display_options, $row_plugin, $row_opt
    */
   protected function build_filters(&$form, &$form_state) {
     parent::build_filters($form, $form_state);
-    $entity_info = $this->entity_info;
 
     $selected_bundle = static::getSelected($form_state, array('show', 'type'), 'all', $form['displays']['show']['type']);
 
@@ -270,7 +269,7 @@ protected function build_filters(&$form, &$form_state) {
     // entities. If a particular entity type (i.e., bundle) has been
     // selected above, then we only search for taxonomy fields associated
     // with that bundle. Otherwise, we use all bundles.
-    $bundles = array_keys($entity_info['bundles']);
+    $bundles = array_keys(entity_get_bundles($this->entity_type));
     // Double check that this is a real bundle before using it (since above
     // we added a dummy option 'all' to the bundle list on the form).
     if (isset($selected_bundle) && in_array($selected_bundle, $bundles)) {
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index e6bc1c6558546e390112e594eb31a79b02a7f474..b64a1dcc3c338612581947d07124a0c67741c088 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -175,32 +175,52 @@ function node_theme() {
 }
 
 /**
- * Implements hook_entity_info().
+ * Implements hook_entity_info_alter().
  */
-function node_entity_info(&$info) {
+function node_entity_info_alter(&$info) {
   // Add a translation handler for fields if the language module is enabled.
   if (module_exists('language')) {
     $info['node']['translation']['node'] = TRUE;
   }
+}
 
+/**
+ * Implements hook_entity_view_mode_info().
+ */
+function node_entity_view_mode_info() {
+  $view_modes['node']['full'] = array(
+    'label' => t('Full content'),
+  );
+  $view_modes['node']['teaser'] = array(
+    'label' => t('Teaser'),
+    'custom_settings' => TRUE,
+  );
+  $view_modes['node']['rss'] = array(
+    'label' => t('RSS'),
+  );
   // Search integration is provided by node.module, so search-related
   // view modes for nodes are defined here and not in search.module.
   if (module_exists('search')) {
-    $info['node']['view_modes']['search_index'] = array(
+    $view_modes['node']['search_index'] = array(
       'label' => t('Search index'),
-      'custom_settings' => FALSE,
     );
-    $info['node']['view_modes']['search_result'] = array(
+    $view_modes['node']['search_result'] = array(
       'label' => t('Search result'),
-      'custom_settings' => FALSE,
     );
   }
+  return $view_modes;
+}
 
+/**
+ * Implements hook_entity_bundle_info().
+ */
+function node_entity_bundle_info() {
+  $bundles = array();
   // Bundles must provide a human readable name so we can create help and error
   // messages, and the path to attach Field admin pages to.
   node_type_cache_reset();
   foreach (node_type_get_names() as $type => $name) {
-    $info['node']['bundles'][$type] = array(
+    $bundles['node'][$type] = array(
       'label' => $name,
       'admin' => array(
         'path' => 'admin/structure/types/manage/%node_type',
@@ -209,6 +229,7 @@ function node_entity_info(&$info) {
       ),
     );
   }
+  return $bundles;
 }
 
 /**
diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaController.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaController.php
index b60eb84b85cb0f363f5621ce2a3951fb989ce044..acd3f8e85de48a293792c6da48e1a9a9321c9ea1 100644
--- a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaController.php
+++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaController.php
@@ -54,7 +54,7 @@ public function bundle($entity_type, $bundle, $schema_path) {
     if (!$entity_info = entity_get_info($entity_type)) {
       throw new NotFoundHttpException(t('Entity type @entity_type not found', array('@entity_type' => $entity_type)));
     }
-    if (!array_key_exists($bundle, $entity_info['bundles'])) {
+    if (!array_key_exists($bundle, entity_get_bundles($entity_type))) {
       throw new NotFoundHttpException(t('Bundle @bundle not found', array('@bundle' => $bundle)));
     }
 
diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchemaManager.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchemaManager.php
index 7ca9ca8817e537500a55e979ff0c3fbe0095e114..e42b868eddcd5f29fa8268fb2ae877de24e38ed5 100644
--- a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchemaManager.php
+++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchemaManager.php
@@ -38,14 +38,16 @@ public function writeCache() {
 
     // Type URIs correspond to bundles. Iterate through the bundles to get the
     // URI and data for them.
-    foreach (entity_get_info() as $entity_type => $entity_info) {
+    $entity_info = entity_get_info();
+    foreach (entity_get_bundles() as $entity_type => $bundles) {
       // Only content entities are supported currently.
       // @todo Consider supporting config entities.
-      $reflection = new ReflectionClass($entity_info['class']);
+      $entity_type_info = $entity_info[$entity_type];
+      $reflection = new ReflectionClass($entity_type_info['class']);
       if ($reflection->implementsInterface('\Drupal\Core\Config\Entity\ConfigEntityInterface')) {
         continue;
       }
-      foreach ($entity_info['bundles'] as $bundle => $bundle_info) {
+      foreach ($bundles as $bundle => $bundle_info) {
         // Get a type URI for the bundle in each of the defined schemas.
         foreach ($this->siteSchemas as $schema) {
           $bundle_uri = $schema->bundle($entity_type, $bundle)->getUri();
diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module
index cff64e528a7195ea652942fbea7397eb3616bed6..8f41a85ced9c0077c5c3368a305fceae0b1d3052 100644
--- a/core/modules/rdf/rdf.module
+++ b/core/modules/rdf/rdf.module
@@ -126,9 +126,9 @@ function rdf_get_namespaces() {
  */
 function rdf_mapping_load($type, $bundle = RDF_DEFAULT_BUNDLE) {
   // Retrieves the bundle-specific mapping from the entity info.
-  $entity_info = entity_get_info($type);
-  if (!empty($entity_info['bundles'][$bundle]['rdf_mapping'])) {
-    return $entity_info['bundles'][$bundle]['rdf_mapping'];
+  $bundles = entity_get_bundles($type);
+  if (!empty($bundles[$bundle]['rdf_mapping'])) {
+    return $bundles[$bundle]['rdf_mapping'];
   }
   // If there is no mapping defined for this bundle, we return the default
   // mapping that is defined for this entity type.
@@ -368,7 +368,7 @@ function rdf_modules_uninstalled($modules) {
 }
 
 /**
- * Implements hook_entity_info_alter().
+ * Implements hook_entity_bundle_info_alter().
  *
  * Adds the proper RDF mapping to each entity type/bundle pair.
  *
@@ -379,22 +379,20 @@ function rdf_modules_uninstalled($modules) {
  * would override the user defined mapping as well.
  *
  */
-function rdf_entity_info_alter(&$entity_info) {
+function rdf_entity_bundle_info_alter(&$bundles) {
   // Loop through each entity type and its bundles.
-  foreach ($entity_info as $entity_type => $entity_type_info) {
-    if (!empty($entity_type_info['bundles'])) {
-      $bundles = array_keys($entity_type_info['bundles']);
-      $mappings = _rdf_mapping_load_multiple($entity_type, $bundles);
-
-      foreach ($bundles as $bundle) {
-        if (isset($mappings[$bundle])) {
-          $entity_info[$entity_type]['bundles'][$bundle]['rdf_mapping'] = $mappings[$bundle];
-        }
-        else {
-          // If no mapping was found in the database, assign the default RDF
-          // mapping for this entity type.
-          $entity_info[$entity_type]['bundles'][$bundle]['rdf_mapping'] = _rdf_get_default_mapping($entity_type);
-        }
+  foreach ($bundles as $entity_type => $bundle_info) {
+    $bundle_names = array_keys($bundle_info);
+    $mappings = _rdf_mapping_load_multiple($entity_type, $bundle_names);
+
+    foreach ($bundle_names as $bundle) {
+      if (isset($mappings[$bundle])) {
+        $bundles[$entity_type][$bundle]['rdf_mapping'] = $mappings[$bundle];
+      }
+      else {
+        // If no mapping was found in the database, assign the default RDF
+        // mapping for this entity type.
+        $bundles[$entity_type][$bundle]['rdf_mapping'] = _rdf_get_default_mapping($entity_type);
       }
     }
   }
diff --git a/core/modules/system/lib/Drupal/system/Plugin/views/row/EntityRow.php b/core/modules/system/lib/Drupal/system/Plugin/views/row/EntityRow.php
index bb13b25f77b2d3be8b362fc5b81bdae27c4f26e2..0a5ef91c13c02f2cee3890f5e05420707c0379f1 100644
--- a/core/modules/system/lib/Drupal/system/Plugin/views/row/EntityRow.php
+++ b/core/modules/system/lib/Drupal/system/Plugin/views/row/EntityRow.php
@@ -94,10 +94,9 @@ public function buildOptionsForm(&$form, &$form_state) {
    */
   protected function buildViewModeOptions() {
     $options = array();
-    if (!empty($this->entityInfo['view_modes'])) {
-      foreach ($this->entityInfo['view_modes'] as $mode => $settings) {
-        $options[$mode] = $settings['label'];
-      }
+    $view_modes = entity_get_view_modes($this->entityType);
+    foreach ($view_modes as $mode => $settings) {
+      $options[$mode] = $settings['label'];
     }
 
     return $options;
diff --git a/core/modules/system/tests/modules/entity_cache_test/entity_cache_test.module b/core/modules/system/tests/modules/entity_cache_test/entity_cache_test.module
index ed60fe814cf390b18bb2fc880ded8543aa295eb0..6f23ebbb1e7781732eec22351bdab7a85b2e4c28 100644
--- a/core/modules/system/tests/modules/entity_cache_test/entity_cache_test.module
+++ b/core/modules/system/tests/modules/entity_cache_test/entity_cache_test.module
@@ -15,7 +15,6 @@
  * dependency module.
  *
  * @see EnableDisableTestCase::testEntityCache()
- * @see entity_cache_test_dependency_entity_info()
  */
 function entity_cache_test_watchdog($log_entry) {
   if ($log_entry['type'] == 'system' && $log_entry['message'] == '%module module installed.') {
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Term.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Term.php
index d669336245a49620542e230bb0666adb3e15283d..be46f2ab808dcf8ae9092d470392a2263d91429c 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Term.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Term.php
@@ -38,12 +38,6 @@
  *   bundle_keys = {
  *     "bundle" = "vid"
  *   },
- *   view_modes = {
- *     "full" = {
- *       "label" = "Taxonomy term page",
- *       "custom_settings" = FALSE
- *     }
- *   },
  *   menu_base_path = "taxonomy/term/%taxonomy_term"
  * )
  */
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Vocabulary.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Vocabulary.php
index 9c65552c6750e63fba56cd683e1303741f7aa4ca..ec2607c9a23618300331174680222a8659d54541 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Vocabulary.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Vocabulary.php
@@ -26,12 +26,6 @@
  *   entity_keys = {
  *     "id" = "vid",
  *     "label" = "name"
- *   },
- *   view_modes = {
- *     "full" = {
- *       "label" = "Taxonomy vocabulary",
- *       "custom_settings" = FALSE
- *     }
  *   }
  * )
  */
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php
index 1395014e0e85a6e27381a3a05a654dd0cd7106f4..d73302b18daf8dc1e5dfe1f90e163b09aa58889d 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php
@@ -160,9 +160,9 @@ function testTaxonomyVocabularyChangeMachineName() {
     taxonomy_vocabulary_save($this->vocabulary);
 
     // Check that entity bundles are properly updated.
-    $info = entity_get_info('taxonomy_term');
-    $this->assertFalse(isset($info['bundles'][$old_name]), 'The old bundle name does not appear in entity_get_info().');
-    $this->assertTrue(isset($info['bundles'][$new_name]), 'The new bundle name appears in entity_get_info().');
+    $info = entity_get_bundles('taxonomy_term');
+    $this->assertFalse(isset($info[$old_name]), 'The old bundle name does not appear in entity_get_bundles().');
+    $this->assertTrue(isset($info[$new_name]), 'The new bundle name appears in entity_get_bundles().');
 
     // Check that the field instance is still attached to the vocabulary.
     $this->assertTrue(field_info_instance('taxonomy_term', 'field_test', $new_name), 'The bundle name was updated correctly.');
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyStorageController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyStorageController.php
index 3d2853d9e8a3ed2ed92734c40950fe71407062c8..b14fc2f3c42984049aba58d7c4c54f6eaafebc47 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyStorageController.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyStorageController.php
@@ -100,7 +100,8 @@ protected function postDelete($entities) {
   public function resetCache(array $ids = NULL) {
     drupal_static_reset('taxonomy_vocabulary_get_names');
     parent::resetCache($ids);
-    cache_invalidate_tags(array('content' => TRUE, 'entity_info' => TRUE));
-    drupal_static_reset('entity_get_info');
+    cache_invalidate_tags(array('content' => TRUE));
+    entity_info_cache_clear();
   }
+
 }
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index 8a1381027bc52d2b46ece07a5e1a61eb66614756..16d101983ba91c18d43d6b4e1cebfea69a265459 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -106,12 +106,26 @@ function taxonomy_permission() {
 }
 
 /**
- * Implements hook_entity_info().
+ * Implements hook_entity_view_mode_info().
  */
-function taxonomy_entity_info(&$info) {
+function taxonomy_entity_view_mode_info() {
+  $view_modes['taxonomy_term']['full'] = array(
+    'label' => t('Taxonomy term page'),
+  );
+  $view_modes['taxonomy_vocabulary']['full'] = array(
+    'label' => t('Taxonomy vocabulary'),
+  );
+  return $view_modes;
+}
+
+/**
+ * Implements hook_entity_bundle_info().
+ */
+function taxonomy_entity_bundle_info() {
+  $bundles = array();
   foreach (taxonomy_vocabulary_get_names() as $id) {
     $config = config('taxonomy.vocabulary.' . $id);
-    $info['taxonomy_term']['bundles'][$id] = array(
+    $bundles['taxonomy_term'][$id] = array(
       'label' => $config->get('name'),
       'admin' => array(
         'path' => 'admin/structure/taxonomy/%taxonomy_vocabulary',
@@ -120,6 +134,7 @@ function taxonomy_entity_info(&$info) {
       ),
     );
   }
+  return $bundles;
 }
 
 /**
@@ -136,8 +151,7 @@ function taxonomy_term_uri($term) {
  */
 function taxonomy_field_extra_fields() {
   $return = array();
-  $info = entity_get_info('taxonomy_term');
-  foreach (array_keys($info['bundles']) as $bundle) {
+  foreach (entity_get_bundles('taxonomy_term') as $bundle => $bundle_info) {
     $return['taxonomy_term'][$bundle] = array(
       'form' => array(
         'name' => array(
diff --git a/core/modules/translation_entity/translation_entity.admin.inc b/core/modules/translation_entity/translation_entity.admin.inc
index c4f5277fb098a874ffff1c143e0457fceedcdd28..aca0dd061eb23a5852df4b59faabdf11cea2a27f 100644
--- a/core/modules/translation_entity/translation_entity.admin.inc
+++ b/core/modules/translation_entity/translation_entity.admin.inc
@@ -28,7 +28,7 @@ function _translation_entity_form_language_content_settings_form_alter(array &$f
   $form['#attached']['library'][] = array('translation_entity', 'drupal.translation_entity.admin');
 
   foreach ($form['#labels'] as $entity_type => $label) {
-    foreach (entity_get_bundles($entity_type) as $bundle) {
+    foreach (entity_get_bundles($entity_type) as $bundle => $bundle_info) {
       $form['settings'][$entity_type][$bundle]['translatable'] = array(
         '#type' => 'checkbox',
         '#default_value' => translation_entity_enabled($entity_type, $bundle),
diff --git a/core/modules/translation_entity/translation_entity.module b/core/modules/translation_entity/translation_entity.module
index b5108ed1f5f6602cf0a17835d826892568fd3a88..ee16a9c8a7b1bae51f79612ae455b122d15049c0 100644
--- a/core/modules/translation_entity/translation_entity.module
+++ b/core/modules/translation_entity/translation_entity.module
@@ -74,6 +74,7 @@ function translation_entity_language_types_info_alter(array &$language_types) {
 function translation_entity_entity_info_alter(array &$entity_info) {
   $edit_form_info = array();
 
+  $bundles_info = entity_get_bundles();
   // Provide defaults for translation info.
   foreach ($entity_info as $entity_type => &$info) {
     if (!isset($info['translation']['translation_entity'])) {
@@ -90,7 +91,7 @@ function translation_entity_entity_info_alter(array &$entity_info) {
     // use translation_entity_enabled() here since it would cause infinite
     // recursion, as it relies on entity info.
     $enabled = FALSE;
-    $bundles = isset($info['bundles']) ? array_keys($info['bundles']) : array($entity_type);
+    $bundles = isset($bundles_info[$entity_type]) ? array_keys($bundles_info[$entity_type]) : array($entity_type);
     foreach ($bundles as $bundle) {
       if (translation_entity_get_config($entity_type, $bundle, 'enabled')) {
         $enabled = TRUE;
@@ -379,7 +380,7 @@ function translation_entity_set_config($entity_type, $bundle, $setting, $value)
  */
 function translation_entity_enabled($entity_type, $bundle = NULL, $skip_handler = FALSE) {
   $enabled = FALSE;
-  $bundles = !empty($bundle) ? array($bundle) : entity_get_bundles($entity_type);
+  $bundles = !empty($bundle) ? array($bundle) : array_keys(entity_get_bundles($entity_type));
 
   foreach ($bundles as $bundle) {
     if (translation_entity_get_config($entity_type, $bundle, 'enabled')) {
@@ -630,7 +631,7 @@ function translation_entity_field_extra_fields() {
   $extra = array();
 
   foreach (entity_get_info() as $entity_type => $info) {
-    foreach (entity_get_bundles($entity_type) as $bundle) {
+    foreach (entity_get_bundles($entity_type) as $bundle => $bundle_info) {
       if (translation_entity_enabled($entity_type, $bundle)) {
         $extra[$entity_type][$bundle]['form']['translation'] = array(
           'label' => t('Translation'),
diff --git a/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/User.php b/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/User.php
index 9b68355f8964b06ddb06d9e1abb153a5fa8b236a..c4f2c92417ecb624816c37296d9c5b0df8cdce2f 100644
--- a/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/User.php
+++ b/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/User.php
@@ -32,24 +32,6 @@
  *   entity_keys = {
  *     "id" = "uid",
  *     "uuid" = "uuid"
- *   },
- *   bundles = {
- *     "user" = {
- *       "label" = "User",
- *       "admin" = {
- *         "path" = "admin/config/people/accounts",
- *       }
- *     }
- *   },
- *   view_modes = {
- *     "full" = {
- *       "label" = "User account",
- *       "custom_settings" = FALSE
- *     },
- *     "compact" = {
- *       "label" = "Compact",
- *       "custom_settings" = TRUE
- *     }
  *   }
  * )
  */
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 89bc6c1675316c502c54d3253f25682cb588ab82..7eb634aa9597f6397c107e8eaeada3cbb17a5792 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -123,6 +123,33 @@ function user_page_build(&$page) {
   $page['#attached']['css'][$path . '/user.css'] = array('every_page' => TRUE);
 }
 
+/**
+ * Implements hook_entity_view_mode_info().
+ */
+function user_entity_view_mode_info() {
+  $view_modes['user']['full'] = array(
+    'label' => t('User account'),
+  );
+  $view_modes['user']['compact'] = array(
+    'label' => t('Compact'),
+    'custom_settings' => TRUE,
+  );
+  return $view_modes;
+}
+
+/**
+ * Implements hook_entity_bundle_info().
+ */
+function user_entity_bundle_info() {
+  $bundles['user']['user'] = array(
+    'label' => t('User'),
+    'admin' => array(
+      'path' => 'admin/config/people/accounts',
+    ),
+  );
+  return $bundles;
+}
+
 /**
  * Entity URI callback.
  */
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/wizard/WizardPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/wizard/WizardPluginBase.php
index 91da4d7ca7f1b12c8041b4ad91ca429767ac8fbd..50013dc2c219d25047ac7a3a103faf3b25d7b41f 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/wizard/WizardPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/wizard/WizardPluginBase.php
@@ -558,12 +558,12 @@ protected function build_filters(&$form, &$form_state) {
     // Find all the fields we are allowed to filter by.
     $fields = views_fetch_fields($this->base_table, 'filter');
 
-    $entity_info = $this->entity_info;
+    $bundles = entity_get_bundles($this->entity_type);
     // If the current base table support bundles and has more than one (like user).
-    if (isset($entity_info['bundle_keys']) && isset($entity_info['bundles'])) {
+    if (isset($this->entity_info['bundle_keys']) && !empty($bundles)) {
       // Get all bundles and their human readable names.
       $options = array('all' => t('All'));
-      foreach ($entity_info['bundles'] as $type => $bundle) {
+      foreach ($bundles as $type => $bundle) {
         $options[$type] = $bundle['label'];
       }
       $form['displays']['show']['type'] = array(