diff --git a/core/includes/config.inc b/core/includes/config.inc
index af62fda..c7fca03 100644
--- a/core/includes/config.inc
+++ b/core/includes/config.inc
@@ -7,6 +7,7 @@
 use Drupal\Core\Config\Context\FreeConfigContext;
 use Drupal\Core\Config\FileStorage;
 use Drupal\Core\Config\StorageInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Symfony\Component\Yaml\Dumper;
 
 /**
@@ -202,8 +203,8 @@ function config_context_leave() {
  *   Either the entity type name, or NULL if none match.
  */
 function config_get_entity_type_by_name($name) {
-  $entities = array_filter(entity_get_info(), function($entity_info) use ($name) {
-    return (isset($entity_info['config_prefix']) && strpos($name, $entity_info['config_prefix'] . '.') === 0);
+  $entities = array_filter(\Drupal::entityManager()->getDefinitions(), function (EntityTypeInterface $entity_info) use ($name) {
+    return ($config_prefix = $entity_info->getConfigPrefix()) && strpos($name, $config_prefix . '.') === 0;
   });
   return key($entities);
 }
diff --git a/core/includes/entity.inc b/core/includes/entity.inc
index 4dddce1..28d2025 100644
--- a/core/includes/entity.inc
+++ b/core/includes/entity.inc
@@ -266,11 +266,10 @@ function entity_revision_delete($entity_type, $revision_id) {
  * @see \Drupal\Core\Entity\EntityManagerInterface
  */
 function entity_load_by_uuid($entity_type, $uuid, $reset = FALSE) {
-  $entity_info = entity_get_info($entity_type);
-  if (empty($entity_info['entity_keys']['uuid'])) {
+  $entity_info = \Drupal::entityManager()->getDefinition($entity_type);
+  if (!$uuid_key = $entity_info->getKey('uuid')) {
     throw new EntityStorageException("Entity type $entity_type does not support UUIDs.");
   }
-  $uuid_key = $entity_info['entity_keys']['uuid'];
 
   $controller = \Drupal::entityManager()->getStorageController($entity_type);
   if ($reset) {
diff --git a/core/lib/Drupal/Component/Annotation/AnnotationBase.php b/core/lib/Drupal/Component/Annotation/AnnotationBase.php
new file mode 100644
index 0000000..83e8539
--- /dev/null
+++ b/core/lib/Drupal/Component/Annotation/AnnotationBase.php
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Component\Annotation\AnnotationBase.
+ */
+
+namespace Drupal\Component\Annotation;
+
+/**
+ * Provides a base class for classed annotations.
+ */
+abstract class AnnotationBase implements AnnotationInterface {
+
+  /**
+   * The plugin ID.
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The class used for this plugin.
+   *
+   * @var string
+   */
+  protected $class;
+
+  /**
+   * The provider of the plugin.
+   *
+   * @var string
+   */
+  protected $provider;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getProvider() {
+    return $this->provider;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setProvider($provider) {
+    $this->provider = $provider;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getId() {
+    return $this->id;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getClass() {
+    return $this->class;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setClass($class) {
+    $this->class = $class;
+  }
+
+}
diff --git a/core/lib/Drupal/Component/Annotation/AnnotationInterface.php b/core/lib/Drupal/Component/Annotation/AnnotationInterface.php
index e3aacc6..a0dd75d 100644
--- a/core/lib/Drupal/Component/Annotation/AnnotationInterface.php
+++ b/core/lib/Drupal/Component/Annotation/AnnotationInterface.php
@@ -17,4 +17,39 @@
    */
   public function get();
 
+  /**
+   * Returns the name of the provider of the plugin.
+   *
+   * @return string
+   */
+  public function getProvider();
+
+  /**
+   * Sets the name of the provider of the plugin.
+   *
+   * @param string $provider
+   */
+  public function setProvider($provider);
+
+  /**
+   * Returns the unique ID for this plugin.
+   *
+   * @return string
+   */
+  public function getId();
+
+  /**
+   * Returns the class of the plugin.
+   *
+   * @return string
+   */
+  public function getClass();
+
+  /**
+   * Sets the class of the plugin.
+   *
+   * @param string $class
+   */
+  public function setClass($class);
+
 }
diff --git a/core/lib/Drupal/Component/Annotation/Plugin.php b/core/lib/Drupal/Component/Annotation/Plugin.php
index b67092b..115e94b 100644
--- a/core/lib/Drupal/Component/Annotation/Plugin.php
+++ b/core/lib/Drupal/Component/Annotation/Plugin.php
@@ -71,10 +71,45 @@ protected function parse(array $values) {
   }
 
   /**
-   * Implements Drupal\Core\Annotation\AnnotationInterface::get().
+   * {@inheritdoc}
    */
   public function get() {
     return $this->definition;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getProvider() {
+    return isset($this->definition['provider']) ? $this->definition['provider'] : FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setProvider($provider) {
+    $this->definition['provider'] = $provider;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getId() {
+    return $this->definition['id'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getClass() {
+    return $this->definition['class'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setClass($class) {
+    $this->definition['class'] = $class;
+  }
+
 }
diff --git a/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php b/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php
index b9236be..b04e39d 100644
--- a/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php
+++ b/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Component\Annotation\Plugin\Discovery;
 
+use Drupal\Component\Annotation\AnnotationInterface;
 use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
 use Drupal\Component\Annotation\Reflection\MockFileFinder;
 use Doctrine\Common\Annotations\SimpleAnnotationReader;
@@ -115,12 +116,12 @@ public function getDefinitions() {
               $finder = MockFileFinder::create($fileinfo->getPathName());
               $parser = new StaticReflectionParser($class, $finder, TRUE);
 
+              /** @var $annotation \Drupal\Component\Annotation\AnnotationInterface */
               if ($annotation = $reader->getClassAnnotation($parser->getReflectionClass(), $this->pluginDefinitionAnnotationName)) {
+                $this->prepareAnnotationDefinition($annotation, $class);
                 // AnnotationInterface::get() returns the array definition
                 // instead of requiring us to work with the annotation object.
-                $definition = $annotation->get();
-                $definition['class'] = $class;
-                $definitions[$definition['id']] = $definition;
+                $definitions[$annotation->getId()] = $annotation->get();
               }
             }
           }
@@ -135,6 +136,18 @@ public function getDefinitions() {
   }
 
   /**
+   * Prepares the annotation definition.
+   *
+   * @param \Drupal\Component\Annotation\AnnotationInterface $annotation
+   *   The annotation derived from the plugin.
+   * @param string $class
+   *   The class used for the plugin.
+   */
+  protected function prepareAnnotationDefinition(AnnotationInterface $annotation, $class) {
+    $annotation->setClass($class);
+  }
+
+  /**
    * Returns an array of PSR-0 namespaces to search for plugin classes.
    */
   protected function getPluginNamespaces() {
diff --git a/core/lib/Drupal/Component/Annotation/PluginID.php b/core/lib/Drupal/Component/Annotation/PluginID.php
index dcb27aa..fe8e599 100644
--- a/core/lib/Drupal/Component/Annotation/PluginID.php
+++ b/core/lib/Drupal/Component/Annotation/PluginID.php
@@ -12,7 +12,7 @@
  *
  * @Annotation
  */
-class PluginID implements AnnotationInterface {
+class PluginID extends AnnotationBase {
 
   /**
    * The plugin ID.
@@ -29,7 +29,16 @@ class PluginID implements AnnotationInterface {
   public function get() {
     return array(
       'id' => $this->value,
+      'class' => $this->class,
+      'provider' => $this->provider,
     );
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getId() {
+    return $this->value;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Annotation/Translation.php b/core/lib/Drupal/Core/Annotation/Translation.php
index 96ffa9c..ebb2655 100644
--- a/core/lib/Drupal/Core/Annotation/Translation.php
+++ b/core/lib/Drupal/Core/Annotation/Translation.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\Core\Annotation;
 
-use Drupal\Component\Annotation\AnnotationInterface;
+use Drupal\Component\Annotation\AnnotationBase;
 
 /**
  * @defgroup plugin_translatable Translatable plugin metadata
@@ -45,7 +45,7 @@
  *
  * @ingroup plugin_translatable
  */
-class Translation implements AnnotationInterface {
+class Translation extends AnnotationBase {
 
   /**
    * The translation of the value passed to the constructor of the class.
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityListController.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityListController.php
index b363cc4..979dddd 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityListController.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityListController.php
@@ -23,7 +23,7 @@ public function load() {
 
     // Sort the entities using the entity class's sort() method.
     // See \Drupal\Core\Config\Entity\ConfigEntityBase::sort().
-    uasort($entities, array($this->entityInfo['class'], 'sort'));
+    uasort($entities, array($this->entityInfo->getClass(), 'sort'));
     return $entities;
   }
 
@@ -41,7 +41,7 @@ public function getOperations(EntityInterface $entity) {
       $operations['edit']['href'] = $uri['path'];
     }
 
-    if (isset($this->entityInfo['entity_keys']['status'])) {
+    if ($this->entityInfo->hasKey('status')) {
       if (!$entity->status()) {
         $operations['enable'] = array(
           'title' => t('Enable'),
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php b/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
index 0ee55e2..458d765 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
@@ -13,6 +13,7 @@
 use Drupal\Core\Config\Config;
 use Drupal\Core\Config\ConfigFactory;
 use Drupal\Core\Config\StorageInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Entity\Query\QueryFactory;
 use Drupal\Component\Uuid\UuidInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -79,10 +80,8 @@ class ConfigStorageController extends EntityStorageControllerBase {
   /**
    * Constructs a ConfigStorageController object.
    *
-   * @param string $entity_type
-   *   The entity type for which the instance is created.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    * @param \Drupal\Core\Config\ConfigFactory $config_factory
    *   The config factory service.
    * @param \Drupal\Core\Config\StorageInterface $config_storage
@@ -92,17 +91,11 @@ class ConfigStorageController extends EntityStorageControllerBase {
    * @param \Drupal\Component\Uuid\UuidInterface $uuid_service
    *   The UUID service.
    */
-  public function __construct($entity_type, array $entity_info, ConfigFactory $config_factory, StorageInterface $config_storage, QueryFactory $entity_query_factory, UuidInterface $uuid_service) {
-    parent::__construct($entity_type, $entity_info);
+  public function __construct(EntityTypeInterface $entity_info, ConfigFactory $config_factory, StorageInterface $config_storage, QueryFactory $entity_query_factory, UuidInterface $uuid_service) {
+    parent::__construct($entity_info);
 
-    $this->idKey = $this->entityInfo['entity_keys']['id'];
-
-    if (isset($this->entityInfo['entity_keys']['status'])) {
-      $this->statusKey = $this->entityInfo['entity_keys']['status'];
-    }
-    else {
-      $this->statusKey = FALSE;
-    }
+    $this->idKey = $this->entityInfo->getKey('id');
+    $this->statusKey = $this->entityInfo->getKey('status');
 
     $this->configFactory = $config_factory;
     $this->configStorage = $config_storage;
@@ -113,9 +106,8 @@ public function __construct($entity_type, array $entity_info, ConfigFactory $con
   /**
    * {@inheritdoc}
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
     return new static(
-      $entity_type,
       $entity_info,
       $container->get('config.factory'),
       $container->get('config.storage'),
@@ -208,7 +200,7 @@ public function getQuery($conjunction = 'AND') {
    *   The full configuration prefix, for example 'views.view.'.
    */
   public function getConfigPrefix() {
-    return $this->entityInfo['config_prefix'] . '.';
+    return $this->entityInfo->getConfigPrefix() . '.';
   }
 
   /**
@@ -249,7 +241,7 @@ public static function getIDFromConfigName($config_name, $config_prefix) {
    *   A SelectQuery object for loading the entity.
    */
   protected function buildQuery($ids, $revision_id = FALSE) {
-    $config_class = $this->entityInfo['class'];
+    $config_class = $this->entityInfo->getClass();
     $prefix = $this->getConfigPrefix();
 
     // Get the names of the configuration entities we are going to load.
@@ -276,7 +268,7 @@ protected function buildQuery($ids, $revision_id = FALSE) {
    * Implements Drupal\Core\Entity\EntityStorageControllerInterface::create().
    */
   public function create(array $values) {
-    $class = $this->entityInfo['class'];
+    $class = $this->entityInfo->getClass();
     $class::preCreate($this, $values);
 
     // Set default language to site default if not provided.
@@ -314,7 +306,7 @@ public function delete(array $entities) {
       return;
     }
 
-    $entity_class = $this->entityInfo['class'];
+    $entity_class = $this->entityInfo->getClass();
     $entity_class::preDelete($this, $entities);
     foreach ($entities as $entity) {
       $this->invokeHook('predelete', $entity);
@@ -458,7 +450,7 @@ public function importCreate($name, Config $new_config, Config $old_config) {
    *   A configuration object containing the old configuration data.
    */
   public function importUpdate($name, Config $new_config, Config $old_config) {
-    $id = static::getIDFromConfigName($name, $this->entityInfo['config_prefix']);
+    $id = static::getIDFromConfigName($name, $this->entityInfo->getConfigPrefix());
     $entity = $this->load($id);
     $entity->setSyncing(TRUE);
     $entity->original = clone $entity;
@@ -489,7 +481,7 @@ public function importUpdate($name, Config $new_config, Config $old_config) {
    *   A configuration object containing the old configuration data.
    */
   public function importDelete($name, Config $new_config, Config $old_config) {
-    $id = static::getIDFromConfigName($name, $this->entityInfo['config_prefix']);
+    $id = static::getIDFromConfigName($name, $this->entityInfo->getConfigPrefix());
     $entity = $this->load($id);
     $entity->setSyncing(TRUE);
     $entity->delete();
diff --git a/core/lib/Drupal/Core/Config/Entity/DraggableListController.php b/core/lib/Drupal/Core/Config/Entity/DraggableListController.php
index 2cb3f43..8e61eb4 100644
--- a/core/lib/Drupal/Core/Config/Entity/DraggableListController.php
+++ b/core/lib/Drupal/Core/Config/Entity/DraggableListController.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Form\FormInterface;
 
@@ -41,12 +42,12 @@
   /**
    * {@inheritdoc}
    */
-  public function __construct($entity_type, array $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler) {
-    parent::__construct($entity_type, $entity_info, $storage, $module_handler);
+  public function __construct(EntityTypeInterface $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler) {
+    parent::__construct($entity_info, $storage, $module_handler);
 
     // Check if the entity type supports weighting.
-    if (!empty($this->entityInfo['entity_keys']['weight'])) {
-      $this->weightKey = $this->entityInfo['entity_keys']['weight'];
+    if ($this->entityInfo->hasKey('weight')) {
+      $this->weightKey = $this->entityInfo->getKey('weight');
     }
   }
 
@@ -99,7 +100,7 @@ public function buildForm(array $form, array &$form_state) {
     $form[$this->entitiesKey] = array(
       '#type' => 'table',
       '#header' => $this->buildHeader(),
-      '#empty' => t('There is no @label yet.', array('@label' => $this->entityInfo['label'])),
+      '#empty' => t('There is no @label yet.', array('@label' => $this->entityInfo->getLabel())),
       '#tabledrag' => array(
         array(
           'action' => 'order',
diff --git a/core/lib/Drupal/Core/Config/Entity/Query/Query.php b/core/lib/Drupal/Core/Config/Entity/Query/Query.php
index 8789445..6676ade 100644
--- a/core/lib/Drupal/Core/Config/Entity/Query/Query.php
+++ b/core/lib/Drupal/Core/Config/Entity/Query/Query.php
@@ -77,7 +77,7 @@ public function condition($property, $value = NULL, $operator = NULL, $langcode
   public function execute() {
     // Load all config files.
     $entity_info = $this->entityManager->getDefinition($this->getEntityType());
-    $prefix = $entity_info['config_prefix'] . '.';
+    $prefix = $entity_info->getConfigPrefix() . '.';
     $prefix_length = strlen($prefix);
     $names = $this->configStorage->listAll($prefix);
     $configs = array();
diff --git a/core/lib/Drupal/Core/Entity/Annotation/EntityType.php b/core/lib/Drupal/Core/Entity/Annotation/EntityType.php
index 3dbbb39..a6fe28f 100644
--- a/core/lib/Drupal/Core/Entity/Annotation/EntityType.php
+++ b/core/lib/Drupal/Core/Entity/Annotation/EntityType.php
@@ -17,261 +17,33 @@
 class EntityType extends Plugin {
 
   /**
-   * The name of the entity type class.
+   * The class used to represent the entity type.
    *
-   * This is not provided manually, it will be added by the discovery mechanism.
+   * It must implement \Drupal\Core\Entity\EntityTypeInterfaceInterface.
    *
    * @var string
    */
-  public $class;
+  public $entity_type_class = 'Drupal\Core\Entity\EntityType';
 
   /**
-   * The name of the entity type's base table.
-   *
-   * @todo This is only used by \Drupal\Core\Entity\DatabaseStorageController.
-   *
-   * @var string
-   */
-  public $base_table;
-
-  /**
-   * An associative array where the keys are the names of different controller
-   * types (listed below) and the values are the names of the classes that
-   * implement that controller:
-   * - storage: The name of the class that is used to load the objects. The
-   *   class must implement \Drupal\Core\Entity\EntityStorageControllerInterface.
-   * - form: An associative array where the keys are the names of the different
-   *   form operations (such as 'create', 'edit', or 'delete') and the values
-   *   are the names of the controller classes for those operations. The name of
-   *   the operation is passed also to the form controller's constructor, so
-   *   that one class can be used for multiple entity forms when the forms are
-   *   similar. The classes must implement
-   *   \Drupal\Core\Entity\EntityFormControllerInterface
-   * - list: The name of the class that provides listings of the entities. The
-   *   class must implement \Drupal\Core\Entity\EntityListControllerInterface.
-   * - render: The name of the class that is used to render the entities. The
-   *   class must implement \Drupal\Core\Entity\EntityViewBuilderInterface.
-   * - access: The name of the class that is used for access checks. The class
-   *   must implement \Drupal\Core\Entity\EntityAccessControllerInterface.
-   *   Defaults to \Drupal\Core\Entity\EntityAccessController.
-   * - translation: The name of the controller class that should be used to
-   *   handle the translation process. The class must implement
-   *   \Drupal\content_translation\ContentTranslationControllerInterface.
-   *
-   * @todo Interfaces from outside \Drupal\Core or \Drupal\Component should not
-   *   be used here.
-   *
-   * @var array
-   */
-  public $controllers = array(
-    'access' => 'Drupal\Core\Entity\EntityAccessController',
-  );
-
-  /**
-   * The name of the default administrative permission.
-   *
-   * The default \Drupal\Core\Entity\EntityAccessController class checks this
-   * permission for all operations in its checkAccess() method. Entities with
-   * more complex permissions can extend this class to do their own access
-   * checks.
-   *
-   * @var string (optional)
-   */
-  public $admin_permission;
-
-  /**
-   * Boolean indicating whether fields can be attached to entities of this type.
-   *
-   * @var bool (optional)
-   */
-  public $fieldable = FALSE;
-
-  /**
-   * Boolean indicating if the persistent cache of field data should be used.
-   *
-   * The persistent cache should usually only be disabled if a higher level
-   * persistent cache is available for the entity type. Defaults to TRUE.
-   *
-   * @var bool (optional)
-   */
-  public $field_cache = TRUE;
-
-  /**
-   * The human-readable name of the type.
-   *
-   * @ingroup plugin_translatable
-   *
-   * @var \Drupal\Core\Annotation\Translation
-   */
-  public $label;
-
-  /**
-   * The human-readable name of the entity bundles, e.g. Vocabulary.
-   *
-   * @ingroup plugin_translatable
-   *
-   * @var \Drupal\Core\Annotation\Translation
-   */
-  public $bundle_label;
-
-  /**
-   * The name of a function that returns the label of the entity.
-   *
-   * The function takes an entity and optional langcode argument, and returns
-   * the label of the entity. If langcode is omitted, the entity's default
-   * language is used. The entity label is the main string associated with an
-   * entity; for example, the title of a node or the subject of a comment. If
-   * there is an entity object property that defines the label, use the 'label'
-   * element of the 'entity_keys' return value component to provide this
-   * information (see below). If more complex logic is needed to determine the
-   * label of an entity, you can instead specify a callback function here, which
-   * will be called to determine the entity label. See also the
-   * \Drupal\Core\Entity\EntityInterface::label() method, which implements this
-   * logic.
-   *
-   * @var string (optional)
-   */
-  public $label_callback;
-
-  /**
-   * Boolean indicating whether entities should be statically cached during a page request.
-   *
-   * @todo This is only used by \Drupal\Core\Entity\DatabaseStorageController.
-   *
-   * @var bool (optional)
-   */
-  public $static_cache = TRUE;
-
-  /**
-   * Boolean indicating whether the rendered output of entities should be
-   * cached.
-   *
-   * @var bool (optional)
-   */
-  public $render_cache = TRUE;
-
-  /**
-   * Boolean indicating whether entities of this type have multilingual support.
-   *
-   * At an entity level, this indicates language support and at a bundle level
-   * this indicates translation support.
-   *
-   * @var bool (optional)
-   */
-  public $translatable = FALSE;
-
-  /**
-   * @todo content_translation_entity_info_alter() uses this but it is undocumented.
+   * @todo content_translation_entity_info_alter() uses this but it is
+   *   undocumented. Fix in https://drupal.org/node/1968970.
    *
    * @var array
    */
   public $translation = array();
 
   /**
-   * The name of the entity type for which bundles are provided.
-   *
-   * It can be used by other modules to act accordingly; for example,
-   * the Field UI module uses it to add operation links to manage fields and
-   * displays.
-   *
-   * @var string
-   */
-  public $bundle_of;
-
-  /**
-   * An array describing how the Field API can extract certain information from
-   * objects of this entity type:
-   * - id: The name of the property that contains the primary ID of the entity.
-   *   Every entity object passed to the Field API must have this property and
-   *   its value must be numeric.
-   * - revision: (optional) The name of the property that contains the revision
-   *   ID of the entity. The Field API assumes that all revision IDs are unique
-   *   across all entities of a type. This entry can be omitted if the entities
-   *   of this type are not versionable.
-   * - bundle: (optional) The name of the property that contains the bundle name
-   *   for the entity. The bundle name defines which set of fields are attached
-   *   to the entity (e.g. what nodes call "content type"). This entry can be
-   *   omitted if this entity type exposes a single bundle (such that all
-   *   entities have the same collection of fields). The name of this single
-   *   bundle will be the same as the entity type.
-   * - label: The name of the property that contains the entity label. For
-   *   example, if the entity's label is located in $entity->subject, then
-   *   'subject' should be specified here. If complex logic is required to build
-   *   the label, a 'label_callback' should be defined instead (see the
-   *   $label_callback block above for details).
-   * - uuid (optional): The name of the property that contains the universally
-   *   unique identifier of the entity, which is used to distinctly identify an
-   *   entity across different systems.
-   *
-   * @var array
-   */
-  public $entity_keys = array(
-    'revision' => '',
-    'bundle' => '',
-  );
-
-  /**
-   * An array describing how the Field API can extract the information it needs
-   * from the bundle objects for this type (e.g Vocabulary objects for terms;
-   * not applicable for nodes):
-   * - bundle: The name of the property that contains the name of the bundle
-   *   object.
-   *
-   * This entry can be omitted if this type's bundles do not exist as standalone
-   * objects.
-   *
-   * @var array
+   * {@inheritdoc}
    */
-  public $bundle_keys;
+  public function get() {
+    $values = $this->definition;
 
-  /**
-   * The name of the entity type which provides bundles.
-   *
-   * @var string (optional)
-   */
-  public $bundle_entity_type = 'bundle';
+    // Use the specified entity type class, and remove it before instantiating.
+    $class = $values['entity_type_class'];
+    unset($values['entity_type_class']);
 
-  /**
-   * Link templates using the URI template syntax.
-   *
-   * Links are an array of standard link relations to the URI template that
-   * should be used for them. Where possible, link relationships should use
-   * established IANA relationships rather than custom relationships.
-   *
-   * Every entity type should, at minimum, define "canonical", which is the
-   * pattern for URIs to that entity. Even if the entity will have no HTML page
-   * exposed to users it should still have a canonical URI in order to be
-   * compatible with web services. Entities that will be user-editable via an
-   * HTML page must also define an "edit-form" relationship.
-   *
-   * By default, the following placeholders are supported:
-   * - entityType: The machine name of the entity type.
-   * - bundle: The bundle machine name of the entity.
-   * - id: The unique ID of the entity.
-   * - uuid: The UUID of the entity.
-   * - [entityType]: The entity type itself will also be a valid token for the
-   *   ID of the entity. For instance, a placeholder of {node} used on the Node
-   *   class would have the same value as {id}. This is generally preferred
-   *   over "id" for better self-documentation.
-   *
-   * Specific entity types may also expand upon this list by overriding the
-   * uriPlaceholderReplacements() method.
-   *
-   * @link http://www.iana.org/assignments/link-relations/link-relations.xml @endlink
-   * @link http://tools.ietf.org/html/rfc6570 @endlink
-   *
-   * @var array
-   */
-  public $links = array();
-
-  /**
-   * Specifies whether a module exposing permissions for the current entity type
-   * should use entity-type level granularity, bundle level granularity or just
-   * skip this entity. The allowed values are respectively "entity_type",
-   * "bundle" or FALSE.
-   *
-   * @var string|bool (optional)
-   */
-  public $permission_granularity = 'entity_type';
+    return new $class($values);
+  }
 
 }
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
index 8bdaed8..7c68f0a 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
@@ -179,8 +179,7 @@ public function setNewRevision($value = TRUE) {
    * {@inheritdoc}
    */
   public function isNewRevision() {
-    $info = $this->entityInfo();
-    return $this->newRevision || (!empty($info['entity_keys']['revision']) && !$this->getRevisionId());
+    return $this->newRevision || ($this->entityInfo()->hasKey('revision') && !$this->getRevisionId());
   }
 
   /**
@@ -712,7 +711,7 @@ public function addTranslation($langcode, array $values = array()) {
     // Instantiate a new empty entity so default values will be populated in the
     // specified language.
     $info = $this->entityInfo();
-    $default_values = array($info['entity_keys']['bundle'] => $this->bundle, 'langcode' => $langcode);
+    $default_values = array($info->getKey('bundle') => $this->bundle, 'langcode' => $langcode);
     $entity = \Drupal::entityManager()
       ->getStorageController($this->entityType())
       ->create($default_values);
@@ -898,17 +897,17 @@ public function createDuplicate() {
 
     $duplicate = clone $this;
     $entity_info = $this->entityInfo();
-    $duplicate->{$entity_info['entity_keys']['id']}->value = NULL;
+    $duplicate->{$entity_info->getKey('id')}->value = NULL;
 
     // Check if the entity type supports UUIDs and generate a new one if so.
-    if (!empty($entity_info['entity_keys']['uuid'])) {
+    if ($entity_info->hasKey('uuid')) {
       // @todo Inject the UUID service into the Entity class once possible.
-      $duplicate->{$entity_info['entity_keys']['uuid']}->value = \Drupal::service('uuid')->generate();
+      $duplicate->{$entity_info->getKey('uuid')}->value = \Drupal::service('uuid')->generate();
     }
 
     // Check whether the entity type supports revisions and initialize it if so.
-    if (!empty($entity_info['entity_keys']['revision'])) {
-      $duplicate->{$entity_info['entity_keys']['revision']}->value = NULL;
+    if ($entity_info->hasKey('revision')) {
+      $duplicate->{$entity_info->getKey('revision')}->value = NULL;
     }
 
     return $duplicate;
@@ -954,11 +953,12 @@ public function label($langcode = NULL) {
     if (!isset($langcode)) {
       $langcode = $this->activeLangcode;
     }
-    if (isset($entity_info['label_callback']) && function_exists($entity_info['label_callback'])) {
-      $label = $entity_info['label_callback']($this, $langcode);
+    // @todo Convert to is_callable() and call_user_func().
+    if (($label_callback = $entity_info->getLabelCallback()) && function_exists($label_callback)) {
+      $label = $label_callback($this, $langcode);
     }
-    elseif (!empty($entity_info['entity_keys']['label']) && isset($this->{$entity_info['entity_keys']['label']})) {
-      $label = $this->{$entity_info['entity_keys']['label']}->value;
+    elseif (($label_key = $entity_info->getKey('label')) && isset($this->{$label_key})) {
+      $label = $this->{$label_key}->value;
     }
     return $label;
   }
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityFormController.php b/core/lib/Drupal/Core/Entity/ContentEntityFormController.php
index 7f613bc..de83f56 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityFormController.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityFormController.php
@@ -50,8 +50,7 @@ public function form(array $form, array &$form_state) {
     $entity = $this->entity;
     // @todo Exploit the Field API to generate the default widgets for the
     // entity fields.
-    $info = $entity->entityInfo();
-    if (!empty($info['fieldable'])) {
+    if ($entity->entityInfo()->isFieldable()) {
       field_attach_form($entity, $form, $form_state, $this->getFormLangcode($form_state));
     }
 
@@ -132,14 +131,14 @@ public function isDefaultFormLangcode(array $form_state) {
   public function buildEntity(array $form, array &$form_state) {
     $entity = clone $this->entity;
     $entity_type = $entity->entityType();
-    $info = entity_get_info($entity_type);
+    $info = \Drupal::entityManager()->getDefinition($entity_type);
 
     // @todo Exploit the Entity Field API to process the submitted field values.
     // Copy top-level form values that are entity fields but not handled by
     // field API without changing existing entity fields that are not being
     // edited by this form. Values of fields handled by field API are copied
     // by field_attach_extract_form_values() below.
-    $values_excluding_fields = $info['fieldable'] ? array_diff_key($form_state['values'], field_info_instances($entity_type, $entity->bundle())) : $form_state['values'];
+    $values_excluding_fields = $info->isFieldable() ? array_diff_key($form_state['values'], field_info_instances($entity_type, $entity->bundle())) : $form_state['values'];
     $definitions = $entity->getPropertyDefinitions();
     foreach ($values_excluding_fields as $key => $value) {
       if (isset($definitions[$key])) {
@@ -155,7 +154,7 @@ public function buildEntity(array $form, array &$form_state) {
     }
 
     // Invoke field API for copying field values.
-    if ($info['fieldable']) {
+    if ($info->isFieldable()) {
       field_attach_extract_form_values($entity, $form, $form_state, array('langcode' => $this->getFormLangcode($form_state)));
     }
     return $entity;
diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
index f258353..bfbdfb4 100644
--- a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
+++ b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
@@ -9,6 +9,7 @@
 
 use Drupal\Component\Uuid\UuidInterface;
 use Drupal\Core\Database\Connection;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Entity\Query\QueryInterface;
 use Drupal\Core\Language\Language;
 use Drupal\Component\Utility\NestedArray;
@@ -60,9 +61,8 @@ class DatabaseStorageController extends EntityStorageControllerBase {
   /**
    * {@inheritdoc}
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
     return new static(
-      $entity_type,
       $entity_info,
       $container->get('database'),
       $container->get('uuid')
@@ -72,36 +72,24 @@ public static function createInstance(ContainerInterface $container, $entity_typ
   /**
    * Constructs a DatabaseStorageController object.
    *
-   * @param string $entity_type
-   *   The entity type for which the instance is created.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    * @param \Drupal\Core\Database\Connection $database
    *   The database connection to be used.
    * @param \Drupal\Component\Uuid\UuidInterface $uuid_service
    *   The UUID service.
    */
-  public function __construct($entity_type, array $entity_info, Connection $database, UuidInterface $uuid_service) {
-    parent::__construct($entity_type, $entity_info);
+  public function __construct(EntityTypeInterface $entity_info, Connection $database, UuidInterface $uuid_service) {
+    parent::__construct($entity_info);
 
     $this->database = $database;
     $this->uuidService = $uuid_service;
 
     // Check if the entity type supports IDs.
-    if (isset($this->entityInfo['entity_keys']['id'])) {
-      $this->idKey = $this->entityInfo['entity_keys']['id'];
-    }
-    else {
-      $this->idKey = FALSE;
-    }
+    $this->idKey = $this->entityInfo->getKey('id');
 
     // Check if the entity type supports UUIDs.
-    if (!empty($this->entityInfo['entity_keys']['uuid'])) {
-      $this->uuidKey = $this->entityInfo['entity_keys']['uuid'];
-    }
-    else {
-      $this->uuidKey = FALSE;
-    }
+    $this->uuidKey = $this->entityInfo->getKey('uuid');
   }
 
   /**
@@ -133,11 +121,11 @@ public function loadMultiple(array $ids = NULL) {
       // Build and execute the query.
       $query_result = $this->buildQuery($ids)->execute();
 
-      if (!empty($this->entityInfo['class'])) {
+      if ($class = $this->entityInfo->getClass()) {
         // We provide the necessary arguments for PDO to create objects of the
         // specified entity class.
         // @see \Drupal\Core\Entity\EntityInterface::__construct()
-        $query_result->setFetchMode(\PDO::FETCH_CLASS, $this->entityInfo['class'], array(array(), $this->entityType));
+        $query_result->setFetchMode(\PDO::FETCH_CLASS, $class, array(array(), $this->entityType));
       }
       $queried_entities = $query_result->fetchAllAssoc($this->idKey);
     }
@@ -229,12 +217,12 @@ protected function buildPropertyQuery(QueryInterface $entity_query, array $value
    *   A SelectQuery object for loading the entity.
    */
   protected function buildQuery($ids, $revision_id = FALSE) {
-    $query = $this->database->select($this->entityInfo['base_table'], 'base');
+    $query = $this->database->select($this->entityInfo->getBaseTable(), 'base');
 
     $query->addTag($this->entityType . '_load_multiple');
 
     // Add fields from the {entity} table.
-    $entity_fields = drupal_schema_fields_sql($this->entityInfo['base_table']);
+    $entity_fields = drupal_schema_fields_sql($this->entityInfo->getBaseTable());
     $query->fields('base', $entity_fields);
 
     if ($ids) {
@@ -248,7 +236,7 @@ protected function buildQuery($ids, $revision_id = FALSE) {
    * {@inheritdoc}
    */
   public function create(array $values) {
-    $entity_class = $this->entityInfo['class'];
+    $entity_class = $this->entityInfo->getClass();
     $entity_class::preCreate($this, $values);
 
     $entity = new $entity_class($values, $this->entityType);
@@ -277,14 +265,14 @@ public function delete(array $entities) {
     $transaction = $this->database->startTransaction();
 
     try {
-      $entity_class = $this->entityInfo['class'];
+      $entity_class = $this->entityInfo->getClass();
       $entity_class::preDelete($this, $entities);
       foreach ($entities as $entity) {
         $this->invokeHook('predelete', $entity);
       }
       $ids = array_keys($entities);
 
-      $this->database->delete($this->entityInfo['base_table'])
+      $this->database->delete($this->entityInfo->getBaseTable())
         ->condition($this->idKey, $ids, 'IN')
         ->execute();
 
@@ -320,13 +308,13 @@ public function save(EntityInterface $entity) {
       $this->invokeHook('presave', $entity);
 
       if (!$entity->isNew()) {
-        $return = drupal_write_record($this->entityInfo['base_table'], $entity, $this->idKey);
+        $return = drupal_write_record($this->entityInfo->getBaseTable(), $entity, $this->idKey);
         $this->resetCache(array($entity->id()));
         $entity->postSave($this, TRUE);
         $this->invokeHook('update', $entity);
       }
       else {
-        $return = drupal_write_record($this->entityInfo['base_table'], $entity);
+        $return = drupal_write_record($this->entityInfo->getBaseTable(), $entity);
         // Reset general caches, but keep caches specific to certain entities.
         $this->resetCache(array());
 
diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index b0b905d..178fe51 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -109,11 +109,12 @@ public function bundle() {
   public function label($langcode = NULL) {
     $label = NULL;
     $entity_info = $this->entityInfo();
-    if (isset($entity_info['label_callback']) && function_exists($entity_info['label_callback'])) {
-      $label = $entity_info['label_callback']($this, $langcode);
+    // @todo Convert to is_callable() and call_user_func().
+    if (($label_callback = $entity_info->getLabelCallback()) && function_exists($label_callback)) {
+      $label = $label_callback($this, $langcode);
     }
-    elseif (!empty($entity_info['entity_keys']['label']) && isset($this->{$entity_info['entity_keys']['label']})) {
-      $label = $this->{$entity_info['entity_keys']['label']};
+    elseif (($label_key = $entity_info->getKey('label')) && isset($this->{$label_key})) {
+      $label = $this->{$label_key};
     }
     return $label;
   }
@@ -150,7 +151,7 @@ public function uri($rel = 'canonical') {
     $entity_info = $this->entityInfo();
 
     // The links array might contain URI templates set in annotations.
-    $link_templates = isset($entity_info['links']) ? $entity_info['links'] : array();
+    $link_templates = $entity_info->getLinkTemplates();
 
     $template = NULL;
     if (isset($link_templates[$rel])) {
@@ -185,12 +186,13 @@ public function uri($rel = 'canonical') {
     if (isset($bundles[$bundle]['uri_callback'])) {
       $uri_callback = $bundles[$bundle]['uri_callback'];
     }
-    elseif (isset($entity_info['uri_callback'])) {
-      $uri_callback = $entity_info['uri_callback'];
+    elseif ($entity_uri_callback = $entity_info->getUriCallback()) {
+      $uri_callback = $entity_uri_callback;
     }
 
     // Invoke the callback to get the URI. If there is no callback, use the
     // default URI format.
+    // @todo Convert to is_callable() and call_user_func().
     if (isset($uri_callback) && function_exists($uri_callback)) {
       $uri = $uri_callback($this);
     }
@@ -243,8 +245,7 @@ protected function uriPlaceholderReplacements() {
    *   An array of link relationships supported by this entity.
    */
   public function uriRelationships() {
-    $entity_info = $this->entityInfo();
-    return isset($entity_info['links']) ? array_keys($entity_info['links']) : array();
+    return array_keys($this->entityInfo()->getLinkTemplates());
   }
 
 
@@ -296,12 +297,12 @@ public function delete() {
   public function createDuplicate() {
     $duplicate = clone $this;
     $entity_info = $this->entityInfo();
-    $duplicate->{$entity_info['entity_keys']['id']} = NULL;
+    $duplicate->{$entity_info->getKey('id')} = NULL;
 
     // Check if the entity type supports UUIDs and generate a new one if so.
-    if (!empty($entity_info['entity_keys']['uuid'])) {
+    if ($entity_info->hasKey('uuid')) {
       // @todo Inject the UUID service into the Entity class once possible.
-      $duplicate->{$entity_info['entity_keys']['uuid']} = \Drupal::service('uuid')->generate();
+      $duplicate->{$entity_info->getKey('uuid')} = \Drupal::service('uuid')->generate();
     }
     return $duplicate;
   }
diff --git a/core/lib/Drupal/Core/Entity/EntityAccessController.php b/core/lib/Drupal/Core/Entity/EntityAccessController.php
index c747cca..30f8c88 100644
--- a/core/lib/Drupal/Core/Entity/EntityAccessController.php
+++ b/core/lib/Drupal/Core/Entity/EntityAccessController.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Entity;
 
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Field\FieldDefinitionInterface;
@@ -35,7 +36,7 @@ class EntityAccessController implements EntityAccessControllerInterface {
   /**
    * The entity info array.
    *
-   * @var array
+   * @var \Drupal\Core\Entity\EntityTypeInterface
    */
   protected $entityInfo;
 
@@ -49,13 +50,11 @@ class EntityAccessController implements EntityAccessControllerInterface {
   /**
    * Constructs an access controller instance.
    *
-   * @param string $entity_type
-   *   The entity type of the access controller instance.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    */
-  public function __construct($entity_type, array $entity_info) {
-    $this->entityType = $entity_type;
+  public function __construct(EntityTypeInterface $entity_info) {
+    $this->entityType = $entity_info->getId();
     $this->entityInfo = $entity_info;
   }
 
@@ -137,8 +136,8 @@ protected function processAccessHookResults(array $access) {
    *   could not be determined.
    */
   protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
-    if (!empty($this->entityInfo['admin_permission'])) {
-      return $account->hasPermission($this->entityInfo['admin_permission']);
+    if ($admin_permission = $this->entityInfo->getAdminPermission()) {
+      return $account->hasPermission($admin_permission);
     }
     else {
       return NULL;
@@ -258,8 +257,8 @@ public function createAccess($entity_bundle = NULL, AccountInterface $account =
    *   could not be determined.
    */
   protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
-    if (!empty($this->entityInfo['admin_permission'])) {
-      return $account->hasPermission($this->entityInfo['admin_permission']);
+    if ($admin_permission = $this->entityInfo->getAdminPermission()) {
+      return $account->hasPermission($admin_permission);
     }
     else {
       return NULL;
diff --git a/core/lib/Drupal/Core/Entity/EntityControllerInterface.php b/core/lib/Drupal/Core/Entity/EntityControllerInterface.php
index 79232c1..cdcffd3 100644
--- a/core/lib/Drupal/Core/Entity/EntityControllerInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityControllerInterface.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Entity;
 
 use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -30,14 +31,12 @@
    *
    * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
    *   The service container this object should use.
-   * @param string $entity_type
-   *   The entity type which the controller handles.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    *
    * @return static
    *   A new instance of the entity controller.
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info);
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info);
 
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityFormController.php b/core/lib/Drupal/Core/Entity/EntityFormController.php
index 8057ca7..2ea0fbe 100644
--- a/core/lib/Drupal/Core/Entity/EntityFormController.php
+++ b/core/lib/Drupal/Core/Entity/EntityFormController.php
@@ -138,8 +138,7 @@ public function form(array $form, array &$form_state) {
     $entity = $this->entity;
     // @todo Exploit the Field API to generate the default widgets for the
     // entity properties.
-    $info = $entity->entityInfo();
-    if (!empty($info['fieldable'])) {
+    if ($entity->entityInfo()->isFieldable()) {
       field_attach_form($entity, $form, $form_state, $this->getFormLangcode($form_state));
     }
 
diff --git a/core/lib/Drupal/Core/Entity/EntityInterface.php b/core/lib/Drupal/Core/Entity/EntityInterface.php
index bf01326..66ed701 100644
--- a/core/lib/Drupal/Core/Entity/EntityInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityInterface.php
@@ -228,7 +228,7 @@ public function createDuplicate();
   /**
    * Returns the info of the type of the entity.
    *
-   * @see entity_get_info()
+   * @return \Drupal\Core\Entity\EntityTypeInterface
    */
   public function entityInfo();
 
diff --git a/core/lib/Drupal/Core/Entity/EntityListController.php b/core/lib/Drupal/Core/Entity/EntityListController.php
index 64c1c43..80eb1ed 100644
--- a/core/lib/Drupal/Core/Entity/EntityListController.php
+++ b/core/lib/Drupal/Core/Entity/EntityListController.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Entity;
 
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\StringTranslation\TranslationInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -41,9 +42,7 @@ class EntityListController implements EntityListControllerInterface, EntityContr
   /**
    * The entity info array.
    *
-   * @var array
-   *
-   * @see entity_get_info()
+   * @var \Drupal\Core\Entity\EntityTypeInterface
    */
   protected $entityInfo;
 
@@ -57,11 +56,10 @@ class EntityListController implements EntityListControllerInterface, EntityContr
   /**
    * {@inheritdoc}
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
     return new static(
-      $entity_type,
       $entity_info,
-      $container->get('entity.manager')->getStorageController($entity_type),
+      $container->get('entity.manager')->getStorageController($entity_info->getId()),
       $container->get('module_handler')
     );
   }
@@ -69,17 +67,15 @@ public static function createInstance(ContainerInterface $container, $entity_typ
   /**
    * Constructs a new EntityListController object.
    *
-   * @param string $entity_type
-   *   The type of entity to be listed.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    * @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage
    *   The entity storage controller class.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    *   The module handler to invoke hooks on.
    */
-  public function __construct($entity_type, array $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler) {
-    $this->entityType = $entity_type;
+  public function __construct(EntityTypeInterface $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler) {
+    $this->entityType = $entity_info->getId();
     $this->storage = $storage;
     $this->entityInfo = $entity_info;
     $this->moduleHandler = $module_handler;
@@ -204,7 +200,7 @@ public function render() {
       '#header' => $this->buildHeader(),
       '#title' => $this->getTitle(),
       '#rows' => array(),
-      '#empty' => $this->t('There is no @label yet.', array('@label' => $this->entityInfo['label'])),
+      '#empty' => $this->t('There is no @label yet.', array('@label' => $this->entityInfo->getLabel())),
     );
     foreach ($this->load() as $entity) {
       if ($row = $this->buildRow($entity)) {
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index 83a671c..fe4c211 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -34,7 +34,7 @@
  *
  * @see \Drupal\Core\Entity\Annotation\EntityType
  * @see \Drupal\Core\Entity\EntityInterface
- * @see entity_get_info()
+ * @see \Drupal\Core\Entity\EntityTypeInterface
  * @see hook_entity_info_alter()
  */
 class EntityManager extends PluginManagerBase implements EntityManagerInterface {
@@ -160,28 +160,26 @@ public function clearCachedDefinitions() {
    * {@inheritdoc}
    */
   public function hasController($entity_type, $controller_type) {
-    $definition = $this->getDefinition($entity_type);
-    return !empty($definition['controllers'][$controller_type]);
+    if ($definition = $this->getDefinition($entity_type)) {
+      return $definition->hasController($controller_type);
+    }
+    return FALSE;
   }
 
   /**
    * {@inheritdoc}
    */
   public function getControllerClass($entity_type, $controller_type, $nested = NULL) {
-    $definition = $this->getDefinition($entity_type);
-    if (!$definition) {
+    $info = $this->getDefinition($entity_type);
+    if (!$info) {
       throw new \InvalidArgumentException(sprintf('The %s entity type does not exist.', $entity_type));
     }
-    $definition = $definition['controllers'];
-    if (!$definition) {
-      throw new \InvalidArgumentException(sprintf('The entity type (%s) does not exist.', $entity_type));
-    }
 
-    if (empty($definition[$controller_type])) {
+    if (!$info->hasController($controller_type)) {
       throw new \InvalidArgumentException(sprintf('The entity type (%s) did not specify a %s controller.', $entity_type, $controller_type));
     }
 
-    $class = $definition[$controller_type];
+    $class = $info->getController($controller_type);
 
     // Some class definitions can be nested.
     if (isset($nested)) {
@@ -213,7 +211,7 @@ public function getListController($entity_type) {
     if (!isset($this->controllers['listing'][$entity_type])) {
       $class = $this->getControllerClass($entity_type, 'list');
       if (in_array('Drupal\Core\Entity\EntityControllerInterface', class_implements($class))) {
-        $this->controllers['listing'][$entity_type] = $class::createInstance($this->container, $entity_type, $this->getDefinition($entity_type));
+        $this->controllers['listing'][$entity_type] = $class::createInstance($this->container, $this->getDefinition($entity_type));
       }
       else {
         $this->controllers['listing'][$entity_type] = new $class($entity_type, $this->getStorageController($entity_type));
@@ -277,10 +275,10 @@ protected function getController($entity_type, $controller_type) {
     if (!isset($this->controllers[$controller_type][$entity_type])) {
       $class = $this->getControllerClass($entity_type, $controller_type);
       if (in_array('Drupal\Core\Entity\EntityControllerInterface', class_implements($class))) {
-        $this->controllers[$controller_type][$entity_type] = $class::createInstance($this->container, $entity_type, $this->getDefinition($entity_type));
+        $this->controllers[$controller_type][$entity_type] = $class::createInstance($this->container, $this->getDefinition($entity_type));
       }
       else {
-        $this->controllers[$controller_type][$entity_type] = new $class($entity_type, $this->getDefinition($entity_type));
+        $this->controllers[$controller_type][$entity_type] = new $class($this->getDefinition($entity_type));
       }
     }
     return $this->controllers[$controller_type][$entity_type];
@@ -302,9 +300,9 @@ public function getAdminPath($entity_type, $bundle) {
     $admin_path = '';
     $entity_info = $this->getDefinition($entity_type);
     // Check for an entity type's admin base path.
-    if (isset($entity_info['links']['admin-form'])) {
-      $route_parameters[$entity_info['bundle_entity_type']] = $bundle;
-      $admin_path = \Drupal::urlGenerator()->getPathFromRoute($entity_info['links']['admin-form'], $route_parameters);
+    if ($admin_form = $entity_info->getLinkTemplate('admin-form')) {
+      $route_parameters[$entity_info->getBundleEntityType()] = $bundle;
+      $admin_path = \Drupal::urlGenerator()->getPathFromRoute($admin_form, $route_parameters);
     }
 
     return $admin_path;
@@ -318,7 +316,7 @@ public function getAdminRouteInfo($entity_type, $bundle) {
     return array(
       'route_name' => "field_ui.overview_$entity_type",
       'route_parameters' => array(
-        $entity_info['bundle_entity_type'] => $bundle,
+        $entity_info->getBundleEntityType() => $bundle,
       )
     );
   }
@@ -336,7 +334,9 @@ public function getFieldDefinitions($entity_type, $bundle = NULL) {
       else {
         // @todo: Refactor to allow for per-bundle overrides.
         // See https://drupal.org/node/2114707.
-        $class = $this->factory->getPluginClass($entity_type, $this->getDefinition($entity_type));
+        $entity_info = $this->getDefinition($entity_type);
+        $definition = array('class' => $entity_info->getClass());
+        $class = $this->factory->getPluginClass($entity_type, $definition);
 
         $this->entityFieldInfo[$entity_type] = array(
           'definitions' => $class::baseFieldDefinitions($entity_type),
@@ -367,8 +367,7 @@ public function getFieldDefinitions($entity_type, $bundle = NULL) {
         $this->moduleHandler->alter($hooks, $this->entityFieldInfo[$entity_type], $entity_type);
 
         // Ensure all basic fields are not defined as translatable.
-        $entity_info = $this->getDefinition($entity_type);
-        $keys = array_intersect_key(array_filter($entity_info['entity_keys']), array_flip(array('id', 'revision', 'uuid', 'bundle')));
+        $keys = array_intersect_key(array_filter($entity_info->getKeys()), array_flip(array('id', 'revision', 'uuid', 'bundle')));
         $untranslatable_fields = array_flip(array('langcode') + $keys);
         foreach (array('definitions', 'optional') as $key) {
           foreach ($this->entityFieldInfo[$entity_type][$key] as $field_name => &$definition) {
@@ -436,7 +435,7 @@ public function getAllBundleInfo() {
         // If no bundles are provided, use the entity type name and label.
         foreach ($this->getDefinitions() as $type => $entity_info) {
           if (!isset($this->bundleInfo[$type])) {
-            $this->bundleInfo[$type][$type]['label'] = $entity_info['label'];
+            $this->bundleInfo[$type][$type]['label'] = $entity_info->getLabel();
           }
         }
         $this->moduleHandler->alter('entity_bundle_info', $this->bundleInfo);
@@ -453,7 +452,7 @@ public function getAllBundleInfo() {
   public function getEntityTypeLabels() {
     $options = array();
     foreach ($this->getDefinitions() as $entity_type => $definition) {
-      $options[$entity_type] = $definition['label'];
+      $options[$entity_type] = $definition->getLabel();
     }
 
     return $options;
@@ -492,4 +491,18 @@ public function getTranslationFromContext(EntityInterface $entity, $langcode = N
     return $translation;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefinition($entity_type_name) {
+    return parent::getDefinition($entity_type_name);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefinitions() {
+    return parent::getDefinitions();
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
index fdc9666..cb962e0 100644
--- a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
@@ -262,4 +262,23 @@ public function getBundleInfo($entity_type);
    */
   public function getTranslationFromContext(EntityInterface $entity, $langcode = NULL, $context = array());
 
+  /**
+   * Returns the entity type info for a specific entity type.
+   *
+   * @param string $entity_type_name
+   *   The name of the entity type.
+   *
+   * @return \Drupal\Core\Entity\EntityTypeInterface|null
+   *   Either the entity type object, or NULL if the name does not exist.
+   */
+  public function getDefinition($entity_type_name);
+
+  /**
+   * Returns an array of entity type info, keyed by entity type name.
+   *
+   * @return \Drupal\Core\Entity\EntityTypeInterface[]
+   *   An array of entity type objects.
+   */
+  public function getDefinitions();
+
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php b/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php
index e9d7fcb..e6ea484 100644
--- a/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php
+++ b/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php
@@ -39,9 +39,7 @@
   /**
    * Array of information about the entity.
    *
-   * @var array
-   *
-   * @see entity_get_info()
+   * @var \Drupal\Core\Entity\EntityTypeInterface
    */
   protected $entityInfo;
 
@@ -64,16 +62,14 @@
   /**
    * Constructs an EntityStorageControllerBase instance.
    *
-   * @param string $entity_type
-   *   The entity type for which the instance is created.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    */
-  public function __construct($entity_type, $entity_info) {
-    $this->entityType = $entity_type;
+  public function __construct(EntityTypeInterface $entity_info) {
+    $this->entityType = $entity_info->getId();
     $this->entityInfo = $entity_info;
     // Check if the entity type supports static caching of loaded entities.
-    $this->cache = !empty($this->entityInfo['static_cache']);
+    $this->cache = $this->entityInfo->isStaticallyCacheable();
   }
 
   /**
@@ -165,7 +161,7 @@ protected function invokeHook($hook, EntityInterface $entity) {
    *   Associative array of query results, keyed on the entity ID.
    */
   protected function postLoad(array &$queried_entities) {
-    $entity_class = $this->entityInfo['class'];
+    $entity_class = $this->entityInfo->getClass();
     $entity_class::postLoad($this, $queried_entities);
     // Call hook_entity_load().
     foreach (\Drupal::moduleHandler()->getImplementations('entity_load') as $module) {
diff --git a/core/lib/Drupal/Core/Entity/EntityType.php b/core/lib/Drupal/Core/Entity/EntityType.php
new file mode 100644
index 0000000..d6c50eb
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/EntityType.php
@@ -0,0 +1,537 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\EntityType.
+ */
+
+namespace Drupal\Core\Entity;
+
+use Drupal\Component\Utility\Unicode;
+
+/**
+ * Provides an implementation of an entity type and its metadata.
+ */
+class EntityType implements EntityTypeInterface {
+
+  /**
+   * Indicates whether entities should be statically cached.
+   *
+   * @var bool
+   */
+  protected $static_cache;
+
+  /**
+   * Indicates whether the rendered output of entities should be cached.
+   *
+   * @var bool
+   */
+  protected $render_cache;
+
+  /**
+   * Indicates if the persistent cache of field data should be used.
+   *
+   * @var bool
+   */
+  protected $field_cache;
+
+  /**
+   * An array of entity keys.
+   *
+   * @var array
+   */
+  protected $entity_keys = array();
+
+  /**
+   * The unique identifier of this entity type.
+   *
+   * @var string
+   */
+  protected $id;
+
+  /**
+   * The name of the provider of this entity type.
+   *
+   * @var string
+   */
+  protected $provider;
+
+  /**
+   * The name of the entity type class.
+   *
+   * @var string
+   */
+  protected $class;
+
+  /**
+   * An array of controllers.
+   *
+   * @var array
+   */
+  protected $controllers = array();
+
+  /**
+   * The name of the default administrative permission.
+   *
+   * @var string
+   */
+  protected $admin_permission;
+
+  /**
+   * The permission granularity level.
+   *
+   * The allowed values are respectively "entity_type", "bundle" or FALSE.
+   *
+   * @var string|bool
+   */
+  protected $permission_granularity;
+
+  /**
+   * An array describing how the Field API can extract the information it needs
+   * from the bundle objects for this type (e.g Vocabulary objects for terms;
+   * not applicable for nodes):
+   * - bundle: The name of the property that contains the name of the bundle
+   *   object.
+   *
+   * This entry can be omitted if this type's bundles do not exist as standalone
+   * objects.
+   *
+   * @var array
+   */
+  protected $bundle_keys = array();
+
+  /**
+   * Indicates whether fields can be attached to entities of this type.
+   *
+   * @var bool (optional)
+   */
+  protected $fieldable;
+
+  /**
+   * Link templates using the URI template syntax.
+   *
+   * @var array
+   */
+  protected $links = array();
+
+  /**
+   * The name of a callback that returns the label of the entity.
+   *
+   * @var string
+   */
+  protected $label_callback;
+
+  /**
+   * The name of the entity type which provides bundles.
+   *
+   * @var string
+   */
+  protected $bundle_entity_type;
+
+  /**
+   * The name of the entity type for which bundles are provided.
+   *
+   * @var string
+   */
+  protected $bundle_of;
+
+  /**
+   * The human-readable name of the entity bundles, e.g. Vocabulary.
+   *
+   * @var string
+   */
+  protected $bundle_label;
+
+  /**
+   * The name of the entity type's base table.
+   *
+   * @var string
+   */
+  protected $base_table;
+
+  /**
+   * The name of the entity type's revision data table.
+   *
+   * @var string
+   */
+  protected $revision_data_table;
+
+  /**
+   * The name of the entity type's revision table.
+   *
+   * @var string
+   */
+  protected $revision_table;
+
+  /**
+   * The name of the entity type's data table.
+   *
+   * @var string
+   */
+  protected $data_table;
+
+  /**
+   * Indicates whether entities of this type have multilingual support.
+   *
+   * @var bool
+   */
+  protected $translatable = FALSE;
+
+  /**
+   * Returns the config prefix used by the configuration entity type.
+   *
+   * @var string
+   */
+  protected $config_prefix;
+
+  /**
+   * The human-readable name of the type.
+   *
+   * @var string
+   */
+  protected $label;
+
+  /**
+   * A callable that can be used to provide the entity URI.
+   *
+   * @var callable
+   */
+  protected $uri_callback;
+
+  /**
+   * Constructs a new EntityType.
+   *
+   * @param array $definition
+   *   An array of values from the annotation.
+   */
+  public function __construct($definition) {
+    foreach ($definition as $key => $value) {
+      $this->{$key} = $value;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function get($key) {
+    return isset($this->{$key}) ? $this->{$key} : NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function set($key, $value) {
+    $this->{$key} = $value;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isStaticallyCacheable() {
+    return isset($this->static_cache) ? $this->static_cache: TRUE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isRenderCacheable() {
+    return isset($this->render_cache) ? $this->render_cache: TRUE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isFieldDataCacheable() {
+    return isset($this->field_cache) ? $this->field_cache: TRUE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getKeys() {
+    return $this->entity_keys + array('revision' => '', 'bundle' => '');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getKey($key) {
+    $keys = $this->getKeys();
+    return isset($keys[$key]) ? $keys[$key] : FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasKey($key) {
+    $keys = $this->getKeys();
+    return !empty($keys[$key]);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getId() {
+    return $this->id;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getProvider() {
+    return $this->provider;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getClass() {
+    return $this->class;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setClass($class) {
+    $this->class = $class;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isSubclassOf($class) {
+    return is_subclass_of($this->getClass(), $class);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getControllers() {
+    return $this->controllers + array(
+      'access' => 'Drupal\Core\Entity\EntityAccessController',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getController($controller_type) {
+    $controllers = $this->getControllers();
+    return $controllers[$controller_type];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setController($controller_type, $value) {
+    $this->controllers[$controller_type] = $value;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasController($controller_type) {
+    $controllers = $this->getControllers();
+    return isset($controllers[$controller_type]);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setForm($operation, $class) {
+    $this->controllers['form'][$operation] = $class;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setList($class) {
+    $this->controllers['list'] = $class;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAdminPermission() {
+    return $this->admin_permission ?: FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPermissionGranularity() {
+    return isset($this->permission_granularity) ? $this->permission_granularity : 'entity_type';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getBundleKeys() {
+    return isset($this->bundle_keys) ? $this->bundle_keys : array();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getBundleKey($name) {
+    return isset($this->bundle_keys[$name]) ? $this->bundle_keys[$name] : FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isFieldable() {
+    return isset($this->fieldable) ? $this->fieldable : FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLinkTemplates() {
+    return isset($this->links) ? $this->links : array();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLinkTemplate($key) {
+    $links = $this->getLinkTemplates();
+    return isset($links[$key]) ? $links[$key] : FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasLinkTemplate($key) {
+    $links = $this->getLinkTemplates();
+    return isset($links[$key]);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setLinkTemplate($key, $route_name) {
+    $this->links[$key] = $route_name;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLabelCallback() {
+    return isset($this->label_callback) ? $this->label_callback : FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setLabelCallback($callback) {
+    $this->label_callback = $callback;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasLabelCallback() {
+    return isset($this->label_callback);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getBundleEntityType() {
+    return isset($this->bundle_entity_type) ? $this->bundle_entity_type : 'bundle';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getBundleOf() {
+    return isset($this->bundle_of) ? $this->bundle_of : FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getBundleLabel() {
+    return isset($this->bundle_label) ? $this->bundle_label : FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getBaseTable() {
+    return isset($this->base_table) ? $this->base_table : FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isTranslatable() {
+    return !empty($this->translatable);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfigPrefix() {
+    return isset($this->config_prefix) ? $this->config_prefix : FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRevisionDataTable() {
+    return isset($this->revision_data_table) ? $this->revision_data_table : FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRevisionTable() {
+    return isset($this->revision_table) ? $this->revision_table : FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDataTable() {
+    return isset($this->data_table) ? $this->data_table : FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLabel() {
+    return isset($this->label) ? $this->label : '';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLowercaseLabel() {
+    return Unicode::strtolower($this->getLabel());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getUriCallback() {
+    return isset($this->uri_callback) ? $this->uri_callback : FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUriCallback($callback) {
+    $this->uri_callback = $callback;
+    return $this;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/EntityTypeInterface.php b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
new file mode 100644
index 0000000..18405f5
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
@@ -0,0 +1,531 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\EntityTypeInterfaceInterface.
+ */
+
+namespace Drupal\Core\Entity;
+
+/**
+ * Provides an interface for an entity type and its metadata.
+ */
+interface EntityTypeInterface {
+
+  /**
+   * Gets any arbitrary key.
+   *
+   * @param string $key
+   *   The key to retrieve.
+   *
+   * @return mixed
+   *   The value for that key, or NULL if the key did not exist.
+   */
+  public function get($key);
+
+  /**
+   * Sets a value to an arbitrary key.
+   *
+   * @param string $key
+   *   The key to use for the value.
+   * @param mixed $value
+   *   The value to set.
+   *
+   * @return static
+   */
+  public function set($key, $value);
+
+  /**
+   * Indicates if a given entity key exists.
+   *
+   * @param string $key
+   *   The name of the entity key to check.
+   *
+   * @return bool
+   *   TRUE if a given entity key exists, FALSE otherwise.
+   */
+  public function hasKey($key);
+
+  /**
+   * Returns the unique identifier of the entity type.
+   *
+   * @return string
+   *   The unique identifier of the entity type.
+   */
+  public function getId();
+
+  /**
+   * Returns the name of the provider of this entity type.
+   *
+   * @return string
+   *   The name of the provider of this entity type.
+   */
+  public function getProvider();
+
+  /**
+   * Returns the name of the entity type class.
+   *
+   * @return string
+   *   The name of the entity type class.
+   */
+  public function getClass();
+
+  /**
+   * Returns a specific entity key.
+   *
+   * @param string $key
+   *   The name of the entity key to return.
+   *
+   * @return string|bool
+   *   The entity key, or FALSE if it does not exist.
+   *
+   * @see self::getKeys()
+   */
+  public function getKey($key);
+
+  /**
+   * Indicates whether entities should be statically cached.
+   *
+   * @return bool
+   *   TRUE if static caching should be used; FALSE otherwise.
+   */
+  public function isStaticallyCacheable();
+
+  /**
+   * Indicates whether the rendered output of entities should be cached.
+   *
+   * @return bool
+   */
+  public function isRenderCacheable();
+
+  /**
+   * Indicates if the persistent cache of field data should be used.
+   *
+   * @todo Used by FieldableEntityStorageControllerBase only.
+   *
+   * The persistent cache should usually only be disabled if a higher level
+   * persistent cache is available for the entity type.
+   *
+   * @return bool
+   */
+  public function isFieldDataCacheable();
+
+  /**
+   * Returns an array of entity keys.
+   *
+   * @return array
+   *   An array describing how the Field API can extract certain information
+   *   from objects of this entity type:
+   *   - id: The name of the property that contains the primary ID of the
+   *     entity. Every entity object passed to the Field API must have this
+   *     property and its value must be numeric.
+   *   - revision: (optional) The name of the property that contains the
+   *     revision ID of the entity. The Field API assumes that all revision IDs
+   *     are unique across all entities of a type. This entry can be omitted if
+   *     the entities of this type are not versionable.
+   *   - bundle: (optional) The name of the property that contains the bundle
+   *     name for the entity. The bundle name defines which set of fields are
+   *     attached to the entity (e.g. what nodes call "content type"). This
+   *     entry can be omitted if this entity type exposes a single bundle (such
+   *     that all entities have the same collection of fields). The name of this
+   *     single bundle will be the same as the entity type.
+   *   - label: The name of the property that contains the entity label. For
+   *     example, if the entity's label is located in $entity->subject, then
+   *     'subject' should be specified here. If complex logic is required to
+   *     build the label, a 'label_callback' should be defined instead (see the
+   *     $label_callback block above for details).
+   *   - uuid (optional): The name of the property that contains the universally
+   *     unique identifier of the entity, which is used to distinctly identify
+   *     an entity across different systems.
+   */
+  public function getKeys();
+
+  /**
+   * Sets the name of the entity type class.
+   *
+   * @param string $class
+   *   The name of the entity type class.
+   *
+   * @return static
+   */
+  public function setClass($class);
+
+  /**
+   * Determines if there is a controller for a given type.
+   *
+   * @param string $controller_type
+   *   The type of controller to check.
+   *
+   * @return bool
+   *   TRUE if a controller of this type exists, FALSE otherwise.
+   */
+  public function hasController($controller_type);
+
+  /**
+   * @param string $controller_type
+   *   The controller type to get.
+   *
+   * @return array|string
+   *   The controllers for a given type.
+   */
+  public function getController($controller_type);
+
+  /**
+   * Returns an array of controllers.
+   *
+   * @return array
+   *   An associative array where the keys are the names of different controller
+   *   types (listed below) and the values are the names of the classes that
+   *   implement that controller:
+   *   - storage: The name of the class used to load the objects. The class must
+   *     implement \Drupal\Core\Entity\EntityStorageControllerInterface.
+   *   - form: An associative array where the keys are the names of the
+   *     different form operations (such as 'create', 'edit', or 'delete') and
+   *     the values are the names of the controller classes for those
+   *     operations. The name of the operation is passed also to the form
+   *     controller's constructor, so that one class can be used for multiple
+   *     entity forms when the forms are similar. The classes must implement
+   *     \Drupal\Core\Entity\EntityFormControllerInterface.
+   *   - list: The name of the class that provides listings of the entities. The
+   *     class must implement \Drupal\Core\Entity\EntityListControllerInterface.
+   *   - render: The name of the class that is used to render the entities. The
+   *     class must implement \Drupal\Core\Entity\EntityViewBuilderInterface.
+   *   - access: The name of the class that is used for access checks. The class
+   *     must implement \Drupal\Core\Entity\EntityAccessControllerInterface.
+   *     Defaults to \Drupal\Core\Entity\EntityAccessController.
+   *   - translation: The name of the controller class that should be used to
+   *     handle the translation process. The class must implement
+   *     \Drupal\content_translation\ContentTranslationControllerInterface.
+   */
+  public function getControllers();
+
+  /**
+   * Sets a form class for a specific operation.
+   *
+   * @param string $operation
+   *   The operation to use this form class for.
+   * @param string $class
+   *   The form class to use for the operation.
+   *
+   * @return static
+   */
+  public function setForm($operation, $class);
+
+  /**
+   * Sets the listing class.
+   *
+   * @param string $class
+   *   The list class to use for the operation.
+   *
+   * @return static
+   */
+  public function setList($class);
+
+  /**
+   * Indicates if the entity type is a subclass of the given class or interface.
+   *
+   * @param string $class
+   *   The class or interface to check.
+   *
+   * @return bool
+   *   TRUE if the entity type is a subclass of the class or interface.
+   */
+  public function isSubclassOf($class);
+
+  /**
+   * Sets the controllers for a given type.
+   *
+   * @param string $controller_type
+   *   The type of controller to set.
+   * @param array|string $value
+   *   The value for a controller type.
+   *
+   * @return static
+   */
+  public function setController($controller_type, $value);
+
+  /**
+   * Returns the name of the default administrative permission.
+   *
+   * The default \Drupal\Core\Entity\EntityAccessController class checks this
+   * permission for all operations in its checkAccess() method. Entities with
+   * more complex permissions can extend this class to do their own access
+   * checks.
+   *
+   * @return string|bool
+   */
+  public function getAdminPermission();
+
+  /**
+   * Returns the permission granularity level.
+   *
+   * The allowed values are respectively "entity_type", "bundle" or FALSE.
+   *
+   * @return string|bool
+   *   Whether a module exposing permissions for the current entity type
+   *   should use entity-type level granularity, bundle level granularity or
+   *   just skip this entity.
+   */
+  public function getPermissionGranularity();
+
+  /**
+   * Get all bundle keys defined on the annotation.
+   *
+   * @return array
+   *   An array describing how the Field API can extract the information it
+   *   needs from the bundle objects for this type (e.g Vocabulary objects for
+   *   terms; not applicable for nodes):
+   *   - bundle: The name of the property that contains the name of the bundle
+   *     object.
+   */
+  public function getBundleKeys();
+
+  /**
+   * Returns a single bundle key.
+   *
+   * @param string $name
+   *   The name of the bundle key.
+   *
+   * @return string|bool
+   *   The value of the bundle key.
+   */
+  public function getBundleKey($name);
+
+  /**
+   * Indicates whether fields can be attached to entities of this type.
+   *
+   * @return bool
+   *   Returns TRUE if the entity type can has fields, otherwise FALSE.
+   */
+  public function isFieldable();
+
+  /**
+   * Returns link templates using the URI template syntax.
+   *
+   * Links are an array of standard link relations to the URI template that
+   * should be used for them. Where possible, link relationships should use
+   * established IANA relationships rather than custom relationships.
+   *
+   * Every entity type should, at minimum, define "canonical", which is the
+   * pattern for URIs to that entity. Even if the entity will have no HTML page
+   * exposed to users it should still have a canonical URI in order to be
+   * compatible with web services. Entities that will be user-editable via an
+   * HTML page must also define an "edit-form" relationship.
+   *
+   * By default, the following placeholders are supported:
+   * - entityType: The machine name of the entity type.
+   * - bundle: The bundle machine name of the entity.
+   * - id: The unique ID of the entity.
+   * - uuid: The UUID of the entity.
+   * - [entityType]: The entity type itself will also be a valid token for the
+   *   ID of the entity. For instance, a placeholder of {node} used on the Node
+   *   class would have the same value as {id}. This is generally preferred
+   *   over "id" for better self-documentation.
+   *
+   * Specific entity types may also expand upon this list by overriding the
+   * Entity::uriPlaceholderReplacements() method.
+   *
+   * @link http://www.iana.org/assignments/link-relations/link-relations.xml @endlink
+   * @link http://tools.ietf.org/html/rfc6570 @endlink
+   *
+   * @return array
+   */
+  public function getLinkTemplates();
+
+  /**
+   * Returns the link template for a given key.
+   *
+   * @param string $key
+   *   The link type.
+   *
+   * @return string|bool
+   *   The route name for this link, or FALSE if it doesn't exist.
+   */
+  public function getLinkTemplate($key);
+
+  /**
+   * Indicates if a link template exists for a given key.
+   *
+   * @param string $key
+   *   The link type.
+   *
+   * @return bool
+   *   TRUE if the link template exists, FALSE otherwise.
+   */
+  public function hasLinkTemplate($key);
+
+  /**
+   * Sets a single link template.
+   *
+   * @param string $key
+   *   The name of a link.
+   * @param string $route_name
+   *   The route name to use for the link.
+   *
+   * @return static
+   */
+  public function setLinkTemplate($key, $route_name);
+
+  /**
+   * Gets the callback for the label of the entity.
+   *
+   * The function takes an entity and optional langcode argument, and returns
+   * the label of the entity. If langcode is omitted, the entity's default
+   * language is used. The entity label is the main string associated with an
+   * entity; for example, the title of a node or the subject of a comment. If
+   * there is an entity object property that defines the label, use the 'label'
+   * element of the 'entity_keys' return value component to provide this
+   * information (see below). If more complex logic is needed to determine the
+   * label of an entity, you can instead specify a callback function here, which
+   * will be called to determine the entity label. See also the
+   * \Drupal\Core\Entity\EntityInterface::label() method, which implements this
+   * logic.
+   *
+   * @return callable|bool
+   *   The callback, or FALSE if none exists.
+   */
+  public function getLabelCallback();
+
+  /**
+   * Sets the label callback.
+   *
+   * @param callable $callback
+   *   A callable that returns the label of the entity.
+   *
+   * @return static
+   */
+  public function setLabelCallback($callback);
+
+  /**
+   * Indicates if a label callback exists.
+   *
+   * @return bool
+   */
+  public function hasLabelCallback();
+
+  /**
+   * Returns the name of the entity type which provides bundles.
+   *
+   * @return string
+   */
+  public function getBundleEntityType();
+
+  /**
+   * Returns the entity type for which this entity provides bundles.
+   *
+   * It can be used by other modules to act accordingly; for example,
+   * the Field UI module uses it to add operation links to manage fields and
+   * displays.
+   *
+   * @return string|bool
+   *   The entity type for which this entity provides bundles, or FALSE if does
+   *   not provide bundles for another entity type.
+   */
+  public function getBundleOf();
+
+  /**
+   * Returns the label for the bundle.
+   *
+   * @return string|bool
+   *   The bundle label, or FALSE if none exists.
+   */
+  public function getBundleLabel();
+
+  /**
+   * Returns the name of the entity's base table.
+   *
+   * @todo Used by DatabaseStorageController only.
+   *
+   * @return string|bool
+   *   The name of the entity's base table, or FALSE if none exists.
+   */
+  public function getBaseTable();
+
+  /**
+   * Indicates whether entities of this type have multilingual support.
+   *
+   * At an entity level, this indicates language support and at a bundle level
+   * this indicates translation support.
+   *
+   * @return bool
+   */
+  public function isTranslatable();
+
+  /**
+   * Returns the config prefix used by the configuration entity type.
+   *
+   * @todo Used for configuration entities only.
+   *
+   * @return string|bool
+   *   The config prefix, or FALSE if not a configuration entity type.
+   */
+  public function getConfigPrefix();
+
+  /**
+   * Returns the name of the entity's revision data table.
+   *
+   * @todo Used by FieldableDatabaseStorageController only.
+   *
+   * @return string|bool
+   *   The name of the entity type's revision data table.
+   */
+  public function getRevisionDataTable();
+
+  /**
+   * Returns the name of the entity's revision table.
+   *
+   * @todo Used by FieldableDatabaseStorageController only.
+   *
+   * @return string|bool
+   *   The name of the entity type's revision table.
+   */
+  public function getRevisionTable();
+
+  /**
+   * Returns the name of the entity's data table.
+   *
+   * @todo Used by FieldableDatabaseStorageController only.
+   *
+   * @return string|bool
+   *   The name of the entity type's data table.
+   */
+  public function getDataTable();
+
+  /**
+   * Returns the human-readable name of the entity type.
+   *
+   * @return string
+   *   The human-readable name of the entity type.
+   */
+  public function getLabel();
+
+  /**
+   * Returns the lowercase form of the human-readable entity type name.
+   *
+   * @return string
+   *   The lowercase form of the human-readable entity type name.
+   */
+  public function getLowercaseLabel();
+
+  /**
+   * Returns a callable that can be used to provide the entity URI.
+   *
+   * This is only called if there is no matching link template for the link
+   * relationship type, and there is no bundle-specific callback provided.
+   *
+   * @return callable|bool
+   *   A valid callback that is passed the entity or FALSE if none is specified.
+   */
+  public function getUriCallback();
+
+  /**
+   * Sets a callable to use to provide the entity URI.
+   *
+   * @param callable $callback
+   *   A callback to use to provide a URI for the entity.
+   *
+   * @return static
+   */
+  public function setUriCallback($callback);
+
+}
diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
index c2db6b0..4705243 100644
--- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
+++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
@@ -26,9 +26,7 @@ class EntityViewBuilder implements EntityControllerInterface, EntityViewBuilderI
   /**
    * The entity info array.
    *
-   * @var array
-   *
-   * @see entity_get_info()
+   * @var \Drupal\Core\Entity\EntityTypeInterface
    */
   protected $entityInfo;
 
@@ -60,25 +58,26 @@ class EntityViewBuilder implements EntityControllerInterface, EntityViewBuilderI
   /**
    * Constructs a new EntityViewBuilder.
    *
-   * @param string $entity_type
-   *   The entity type.
-   * @param array $entity_info
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
    *   The entity information array.
-   * @param \Drupal\Core\Entity\EntityManager $entity_manager
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager service.
    */
-  public function __construct($entity_type, array $entity_info, EntityManager $entity_manager) {
-    $this->entityType = $entity_type;
+  public function __construct(EntityTypeInterface $entity_info, EntityManagerInterface $entity_manager) {
+    $this->entityType = $entity_info->getId();
     $this->entityInfo = $entity_info;
     $this->entityManager = $entity_manager;
-    $this->viewModesInfo = entity_get_view_modes($entity_type);
+    $this->viewModesInfo = entity_get_view_modes($this->entityType);
   }
 
   /**
    * {@inheritdoc}
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
-    return new static($entity_type, $entity_info, $container->get('entity.manager'));
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
+    return new static(
+      $entity_info,
+      $container->get('entity.manager')
+    );
   }
 
   /**
@@ -140,7 +139,7 @@ protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langco
     // type configuration. The isset() checks below are necessary because
     // 'default' is not an actual view mode.
     $view_mode_is_cacheable = !isset($this->viewModesInfo[$view_mode]) || (isset($this->viewModesInfo[$view_mode]) && $this->viewModesInfo[$view_mode]['cache']);
-    if ($view_mode_is_cacheable && !$entity->isNew() && !isset($entity->in_preview) && $this->entityInfo['render_cache']) {
+    if ($view_mode_is_cacheable && !$entity->isNew() && !isset($entity->in_preview) && $this->entityInfo->isRenderCacheable()) {
       $return['#cache'] = array(
         'keys' => array('entity_view', $this->entityType, $entity->id(), $view_mode),
         'granularity' => DRUPAL_CACHE_PER_ROLE,
diff --git a/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php
index c78453c..cb8b4cb 100644
--- a/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php
+++ b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Entity;
 
 use Drupal\Core\Database\Connection;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Entity\Query\QueryInterface;
 use Drupal\Core\Language\Language;
 use Drupal\Component\Utility\NestedArray;
@@ -99,9 +100,8 @@ class FieldableDatabaseStorageController extends FieldableEntityStorageControlle
   /**
    * {@inheritdoc}
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
     return new static(
-      $entity_type,
       $entity_info,
       $container->get('database'),
       $container->get('field.info')
@@ -111,49 +111,42 @@ public static function createInstance(ContainerInterface $container, $entity_typ
   /**
    * Constructs a DatabaseStorageController object.
    *
-   * @param string $entity_type
-   *   The entity type for which the instance is created.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    * @param \Drupal\Core\Database\Connection $database
    *   The database connection to be used.
    * @param \Drupal\field\FieldInfo $field_info
    *   The field info service.
    */
-  public function __construct($entity_type, array $entity_info, Connection $database, FieldInfo $field_info) {
-    parent::__construct($entity_type, $entity_info);
+  public function __construct(EntityTypeInterface $entity_info, Connection $database, FieldInfo $field_info) {
+    parent::__construct($entity_info);
 
     $this->database = $database;
     $this->fieldInfo = $field_info;
-    $this->bundleKey = !empty($this->entityInfo['entity_keys']['bundle']) ? $this->entityInfo['entity_keys']['bundle'] : FALSE;
-    $this->entityClass = $this->entityInfo['class'];
+    $this->bundleKey = $this->entityInfo->getKey('bundle');
+    $this->entityClass = $this->entityInfo->getClass();
 
     // Check if the entity type supports IDs.
-    if (isset($this->entityInfo['entity_keys']['id'])) {
-      $this->idKey = $this->entityInfo['entity_keys']['id'];
+    if ($this->entityInfo->hasKey('id')) {
+      $this->idKey = $this->entityInfo->getKey('id');
     }
 
     // Check if the entity type supports UUIDs.
-    if (!empty($this->entityInfo['entity_keys']['uuid'])) {
-      $this->uuidKey = $this->entityInfo['entity_keys']['uuid'];
-    }
-    else {
-      $this->uuidKey = FALSE;
-    }
+    $this->uuidKey = $this->entityInfo->getKey('uuid');
 
     // Check if the entity type supports revisions.
-    if (!empty($this->entityInfo['entity_keys']['revision'])) {
-      $this->revisionKey = $this->entityInfo['entity_keys']['revision'];
-      $this->revisionTable = $this->entityInfo['revision_table'];
+    if ($this->entityInfo->hasKey('revision')) {
+      $this->revisionKey = $this->entityInfo->getKey('revision');
+      $this->revisionTable = $this->entityInfo->getRevisionTable();
     }
 
     // Check if the entity type has a dedicated table for fields.
-    if (!empty($this->entityInfo['data_table'])) {
-      $this->dataTable = $this->entityInfo['data_table'];
+    if ($data_table = $this->entityInfo->getDataTable()) {
+      $this->dataTable = $data_table;
       // Entity types having both revision and translation support should always
       // define a revision data table.
-      if ($this->revisionTable && !empty($this->entityInfo['revision_data_table'])) {
-        $this->revisionDataTable = $this->entityInfo['revision_data_table'];
+      if ($this->revisionTable && $revision_data_table = $this->entityInfo->getRevisionDataTable()) {
+        $this->revisionDataTable = $revision_data_table;
       }
     }
   }
@@ -336,10 +329,10 @@ protected function attachPropertyData(array &$entities) {
       $field_definitions = \Drupal::entityManager()->getFieldDefinitions($this->entityType);
       $translations = array();
       if ($this->revisionDataTable) {
-        $data_column_names = array_flip(array_diff(drupal_schema_fields_sql($this->entityInfo['revision_data_table']), drupal_schema_fields_sql($this->entityInfo['base_table'])));
+        $data_column_names = array_flip(array_diff(drupal_schema_fields_sql($this->entityInfo->getRevisionDataTable()), drupal_schema_fields_sql($this->entityInfo->getBaseTable())));
       }
       else {
-        $data_column_names = array_flip(drupal_schema_fields_sql($this->entityInfo['data_table']));
+        $data_column_names = array_flip(drupal_schema_fields_sql($this->entityInfo->getDataTable()));
       }
 
       foreach ($data as $values) {
@@ -459,7 +452,7 @@ protected function buildPropertyQuery(QueryInterface $entity_query, array $value
    *   A SelectQuery object for loading the entity.
    */
   protected function buildQuery($ids, $revision_id = FALSE) {
-    $query = $this->database->select($this->entityInfo['base_table'], 'base');
+    $query = $this->database->select($this->entityInfo->getBaseTable(), 'base');
 
     $query->addTag($this->entityType . '_load_multiple');
 
@@ -471,11 +464,11 @@ protected function buildQuery($ids, $revision_id = FALSE) {
     }
 
     // Add fields from the {entity} table.
-    $entity_fields = drupal_schema_fields_sql($this->entityInfo['base_table']);
+    $entity_fields = drupal_schema_fields_sql($this->entityInfo->getBaseTable());
 
     if ($this->revisionTable) {
       // Add all fields from the {entity_revision} table.
-      $entity_revision_fields = drupal_map_assoc(drupal_schema_fields_sql($this->entityInfo['revision_table']));
+      $entity_revision_fields = drupal_map_assoc(drupal_schema_fields_sql($this->entityInfo->getRevisionTable()));
       // The ID field is provided by entity, so remove it.
       unset($entity_revision_fields[$this->idKey]);
 
@@ -522,7 +515,7 @@ protected function postLoad(array &$queried_entities) {
     $queried_entities = $this->mapFromStorageRecords($queried_entities);
 
     // Attach field values.
-    if ($this->entityInfo['fieldable']) {
+    if ($this->entityInfo->isFieldable()) {
       $this->loadFieldItems($queried_entities);
     }
 
@@ -548,7 +541,7 @@ public function delete(array $entities) {
       }
       $ids = array_keys($entities);
 
-      $this->database->delete($this->entityInfo['base_table'])
+      $this->database->delete($this->entityInfo->getBaseTable())
         ->condition($this->idKey, $ids)
         ->execute();
 
@@ -615,7 +608,7 @@ public function save(EntityInterface $entity) {
 
       if (!$entity->isNew()) {
         if ($entity->isDefaultRevision()) {
-          $return = drupal_write_record($this->entityInfo['base_table'], $record, $this->idKey);
+          $return = drupal_write_record($this->entityInfo->getBaseTable(), $record, $this->idKey);
         }
         else {
           // @todo, should a different value be returned when saving an entity
@@ -645,7 +638,7 @@ public function save(EntityInterface $entity) {
         // Ensure the entity is still seen as new after assigning it an id,
         // while storing its data.
         $entity->enforceIsNew();
-        $return = drupal_write_record($this->entityInfo['base_table'], $record);
+        $return = drupal_write_record($this->entityInfo->getBaseTable(), $record);
         $entity->{$this->idKey}->value = (string) $record->{$this->idKey};
         if ($this->revisionTable) {
           $entity->setNewRevision();
@@ -691,7 +684,7 @@ public function save(EntityInterface $entity) {
    *   'data_table'.
    */
   protected function savePropertyData(EntityInterface $entity, $table_key = 'data_table') {
-    $table_name = $this->entityInfo[$table_key];
+    $table_name = $this->entityInfo->get($table_key);
     $revision = $table_key != 'data_table';
 
     if (!$revision || !$entity->isNewRevision()) {
@@ -733,11 +726,11 @@ protected function mapToStorageRecord(EntityInterface $entity, $table_key = 'bas
     $record = new \stdClass();
     $values = array();
     $definitions = $entity->getPropertyDefinitions();
-    $schema = drupal_get_schema($this->entityInfo[$table_key]);
+    $schema = drupal_get_schema($this->entityInfo->get($table_key));
     $is_new = $entity->isNew();
 
     $multi_column_fields = array();
-    foreach (drupal_schema_fields_sql($this->entityInfo[$table_key]) as $name) {
+    foreach (drupal_schema_fields_sql($this->entityInfo->get($table_key)) as $name) {
       // Check for fields which store data in multiple columns and process them
       // separately.
       if ($field = strstr($name, '__', TRUE)) {
@@ -814,7 +807,7 @@ protected function saveRevision(EntityInterface $entity) {
     if ($entity->isNewRevision()) {
       drupal_write_record($this->revisionTable, $record);
       if ($entity->isDefaultRevision()) {
-        $this->database->update($this->entityInfo['base_table'])
+        $this->database->update($this->entityInfo->getBaseTable())
           ->fields(array($this->revisionKey => $record->{$this->revisionKey}))
           ->condition($this->idKey, $record->{$this->idKey})
           ->execute();
diff --git a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php
index d30c972..9aa3a63 100644
--- a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php
+++ b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php
@@ -44,8 +44,7 @@ protected function loadFieldItems(array $entities) {
     // Only the most current revision of non-deleted fields for cacheable entity
     // types can be cached.
     $load_current = $age == static::FIELD_LOAD_CURRENT;
-    $info = entity_get_info($this->entityType);
-    $use_cache = $load_current && $info['field_cache'];
+    $use_cache = $load_current && $this->entityInfo->isFieldDataCacheable();
 
     // Assume all entities will need to be queried. Entities found in the cache
     // will be removed from the list.
@@ -130,7 +129,7 @@ protected function saveFieldItems(EntityInterface $entity, $update = TRUE) {
 
     if ($update) {
       $entity_info = $entity->entityInfo();
-      if ($entity_info['field_cache']) {
+      if ($entity_info->isFieldDataCacheable()) {
         cache('field')->delete('field:' . $entity->entityType() . ':' . $entity->id());
       }
     }
@@ -150,7 +149,7 @@ protected function deleteFieldItems(EntityInterface $entity) {
     $this->doDeleteFieldItems($entity);
 
     $entity_info = $entity->entityInfo();
-    if ($entity_info['field_cache']) {
+    if ($entity_info->isFieldDataCacheable()) {
       cache('field')->delete('field:' . $entity->entityType() . ':' . $entity->id());
     }
   }
diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/Deriver/EntityDeriver.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/Deriver/EntityDeriver.php
index 7828a99..508959c 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/DataType/Deriver/EntityDeriver.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/Deriver/EntityDeriver.php
@@ -82,8 +82,8 @@ public function getDerivativeDefinitions(array $base_plugin_definition) {
     // Add definitions for each entity type and bundle.
     foreach ($this->entityManager->getDefinitions() as $entity_type => $info) {
       $this->derivatives[$entity_type] = array(
-        'label' => $info['label'],
-        'class' => $info['class'],
+        'label' => $info->getLabel(),
+        'class' => $info->getClass(),
         'constraints' => array('EntityType' => $entity_type),
       ) + $base_plugin_definition;
 
@@ -92,7 +92,7 @@ public function getDerivativeDefinitions(array $base_plugin_definition) {
         if ($bundle !== $entity_type) {
           $this->derivatives[$entity_type . ':' . $bundle] = array(
             'label' => $bundle_info['label'],
-            'class' => $info['class'],
+            'class' => $info->getClass(),
             'constraints' => array(
               'EntityType' => $entity_type,
               'Bundle' => $bundle,
diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/Query.php b/core/lib/Drupal/Core/Entity/Query/Sql/Query.php
index 987a322..f5c84b4 100644
--- a/core/lib/Drupal/Core/Entity/Query/Sql/Query.php
+++ b/core/lib/Drupal/Core/Entity/Query/Sql/Query.php
@@ -22,9 +22,7 @@ class Query extends QueryBase implements QueryInterface {
   /**
    * Contains the entity info for the entity type of that query.
    *
-   * @var array
-   *
-   * @see \Drupal\Core\Entity\EntityManagerInterface
+   * @var \Drupal\Core\Entity\EntityTypeInterface
    */
   protected $entityInfo;
 
@@ -111,19 +109,18 @@ public function execute() {
   protected function prepare() {
     $entity_type = $this->entityType;
     $this->entityInfo = $this->entityManager->getDefinition($entity_type);
-    if (!isset($this->entityInfo['base_table'])) {
+    if (!$base_table = $this->entityInfo->getBaseTable()) {
       throw new QueryException("No base table, invalid query.");
     }
-    $base_table = $this->entityInfo['base_table'];
     $simple_query = TRUE;
-    if (isset($this->entityInfo['data_table'])) {
+    if ($this->entityInfo->getDataTable()) {
       $simple_query = FALSE;
     }
     $this->sqlQuery = $this->connection->select($base_table, 'base_table', array('conjunction' => $this->conjunction));
     $this->sqlQuery->addMetaData('entity_type', $entity_type);
-    $id_field = $this->entityInfo['entity_keys']['id'];
+    $id_field = $this->entityInfo->getKey('id');
     // Add the key field for fetchAllKeyed().
-    if (empty($this->entityInfo['entity_keys']['revision'])) {
+    if (!$revision_field = $this->entityInfo->getKey('revision')) {
       // When there is no revision support, the key field is the entity key.
       $this->sqlFields["base_table.$id_field"] = array('base_table', $id_field);
       // Now add the value column for fetchAllKeyed(). This is always the
@@ -132,7 +129,6 @@ protected function prepare() {
     }
     else {
       // When there is revision support, the key field is the revision key.
-      $revision_field = $this->entityInfo['entity_keys']['revision'];
       $this->sqlFields["base_table.$revision_field"] = array('base_table', $revision_field);
       // Now add the value column for fetchAllKeyed(). This is always the
       // entity id.
diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
index 8098849..8ec71e1 100644
--- a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
+++ b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
@@ -83,16 +83,16 @@ public function addField($field, $type, $langcode) {
     for ($key = 0; $key <= $count; $key ++) {
       // If there is revision support and only the current revision is being
       // queried then use the revision id. Otherwise, the entity id will do.
-      if (!empty($entity_info['entity_keys']['revision']) && $age == EntityStorageControllerInterface::FIELD_LOAD_CURRENT) {
+      if (($revision_key = $entity_info->getKey('revision')) && $age == EntityStorageControllerInterface::FIELD_LOAD_CURRENT) {
         // This contains the relevant SQL field to be used when joining entity
         // tables.
-        $entity_id_field = $entity_info['entity_keys']['revision'];
+        $entity_id_field = $revision_key;
         // This contains the relevant SQL field to be used when joining field
         // tables.
         $field_id_field = 'revision_id';
       }
       else {
-        $entity_id_field = $entity_info['entity_keys']['id'];
+        $entity_id_field = $entity_info->getKey('id');
         $field_id_field = 'entity_id';
       }
       // This can either be the name of an entity property (non-configurable
@@ -138,8 +138,8 @@ public function addField($field, $type, $langcode) {
             $values = array();
             $field_name = $field->getName();
             // If there are bundles, pick one.
-            if (!empty($entity_info['entity_keys']['bundle'])) {
-              $values[$entity_info['entity_keys']['bundle']] = reset($field_map[$entity_type][$field_name]['bundles']);
+            if ($bundle_key = $entity_info->getKey('bundle')) {
+              $values[$bundle_key] = reset($field_map[$entity_type][$field_name]['bundles']);
             }
             $entity = $entity_manager
               ->getStorageController($entity_type)
@@ -172,11 +172,12 @@ public function addField($field, $type, $langcode) {
         // finds the property first. The data table is preferred, which is why
         // it gets added before the base table.
         $entity_tables = array();
-        if (isset($entity_info['data_table'])) {
+        if ($data_table = $entity_info->getDataTable()) {
           $this->sqlQuery->addMetaData('simple_query', FALSE);
-          $entity_tables[$entity_info['data_table']] = drupal_get_schema($entity_info['data_table']);
+          $entity_tables[$data_table] = drupal_get_schema($data_table);
         }
-        $entity_tables[$entity_info['base_table']] = drupal_get_schema($entity_info['base_table']);
+        $entity_base_table = $entity_info->getBaseTable();
+        $entity_tables[$entity_base_table] = drupal_get_schema($entity_base_table);
         $sql_column = $specifier;
         $table = $this->ensureEntityTable($index_prefix, $specifier, $type, $langcode, $base_table, $entity_id_field, $entity_tables);
       }
@@ -190,9 +191,9 @@ public function addField($field, $type, $langcode) {
           $values = array();
           // If there are bundles, pick one. It does not matter which,
           // properties exist on all bundles.
-          if (!empty($entity_info['entity_keys']['bundle'])) {
+          if ($bundle_key = $entity_info->getKey('bundle')) {
             $bundles = entity_get_bundles($entity_type);
-            $values[$entity_info['entity_keys']['bundle']] = key($bundles);
+            $values[$bundle_key] = key($bundles);
           }
           $entity = $entity_manager
             ->getStorageController($entity_type)
@@ -207,8 +208,8 @@ public function addField($field, $type, $langcode) {
           $entity_type = $propertyDefinitions[$relationship_specifier]->getConstraint('EntityType');
           $entity_info = $entity_manager->getDefinition($entity_type);
           // Add the new entity base table using the table and sql column.
-          $join_condition= '%alias.' . $entity_info['entity_keys']['id'] . " = $table.$sql_column";
-          $base_table = $this->sqlQuery->leftJoin($entity_info['base_table'], NULL, $join_condition);
+          $join_condition= '%alias.' . $entity_info->getKey('id') . " = $table.$sql_column";
+          $base_table = $this->sqlQuery->leftJoin($entity_info->getBaseTable(), NULL, $join_condition);
           $propertyDefinitions = array();
           $key++;
           $index_prefix .= "$next_index_prefix.";
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
index 655f4fd..40f7a3a 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
@@ -51,7 +51,7 @@ public function getPropertyDefinitions() {
 
     if (!isset(static::$propertyDefinitions[$key])) {
       $target_type_info = \Drupal::entityManager()->getDefinition($target_type);
-      if (is_subclass_of($target_type_info['class'], '\Drupal\Core\Entity\ContentEntityInterface')) {
+      if ($target_type_info->isSubclassOf('\Drupal\Core\Entity\ContentEntityInterface')) {
         // @todo: Lookup the entity type's ID data type and use it here.
         // https://drupal.org/node/2107249
         static::$propertyDefinitions[$key]['target_id'] = DataDefinition::create('integer')
diff --git a/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php b/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php
index ffafdf6..02e96d1 100644
--- a/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php
+++ b/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Plugin\Discovery;
 
+use Drupal\Component\Annotation\AnnotationInterface;
 use Drupal\Component\Annotation\Plugin\Discovery\AnnotatedClassDiscovery as ComponentAnnotatedClassDiscovery;
 
 /**
@@ -71,15 +72,12 @@ protected function getAnnotationReader() {
   /**
    * {@inheritdoc}
    */
-  public function getDefinitions() {
-    $definitions = parent::getDefinitions();
-    foreach ($definitions as &$definition) {
-      // Extract the module name from the class namespace if it's not set.
-      if (!isset($definition['provider'])) {
-        $definition['provider'] = $this->getProviderFromNamespace($definition['class']);
-      }
+  protected function prepareAnnotationDefinition(AnnotationInterface $annotation, $class) {
+    parent::prepareAnnotationDefinition($annotation, $class);
+
+    if (!$annotation->getProvider()) {
+      $annotation->setProvider($this->getProviderFromNamespace($class));
     }
-    return $definitions;
   }
 
   /**
diff --git a/core/modules/action/action.module b/core/modules/action/action.module
index 5ecfb63..c7de85c 100644
--- a/core/modules/action/action.module
+++ b/core/modules/action/action.module
@@ -60,9 +60,11 @@ function action_menu() {
  * Implements hook_entity_info().
  */
 function action_entity_info(&$entity_info) {
-  $entity_info['action']['controllers']['form']['add'] = 'Drupal\action\ActionAddFormController';
-  $entity_info['action']['controllers']['form']['edit'] = 'Drupal\action\ActionEditFormController';
-  $entity_info['action']['controllers']['form']['delete'] = 'Drupal\action\Form\ActionDeleteForm';
-  $entity_info['action']['controllers']['list'] = 'Drupal\action\ActionListController';
-  $entity_info['action']['links']['edit-form'] = 'action.admin_configure';
+  /** @var $entity_info \Drupal\Core\Entity\EntityTypeInterface[] */
+  $entity_info['action']
+    ->setForm('add', 'Drupal\action\ActionAddFormController')
+    ->setForm('edit', 'Drupal\action\ActionEditFormController')
+    ->setForm('delete', 'Drupal\action\Form\ActionDeleteForm')
+    ->setList('Drupal\action\ActionListController')
+    ->setLinkTemplate('edit-form', 'action.admin_configure');
 }
diff --git a/core/modules/action/lib/Drupal/action/ActionListController.php b/core/modules/action/lib/Drupal/action/ActionListController.php
index 405f224..ad5cfea 100644
--- a/core/modules/action/lib/Drupal/action/ActionListController.php
+++ b/core/modules/action/lib/Drupal/action/ActionListController.php
@@ -12,6 +12,7 @@
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Config\Entity\ConfigEntityListController;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -35,10 +36,8 @@ class ActionListController extends ConfigEntityListController implements EntityC
   /**
    * Constructs a new ActionListController object.
    *
-   * @param string $entity_type
-   *   The entity type.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    * @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage
    *   The action storage controller.
    * @param \Drupal\Core\Action\ActionManager $action_manager
@@ -46,8 +45,8 @@ class ActionListController extends ConfigEntityListController implements EntityC
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    *   The module handler to invoke hooks on.
    */
-  public function __construct($entity_type, array $entity_info, EntityStorageControllerInterface $storage, ActionManager $action_manager, ModuleHandlerInterface $module_handler) {
-    parent::__construct($entity_type, $entity_info, $storage, $module_handler);
+  public function __construct(EntityTypeInterface $entity_info, EntityStorageControllerInterface $storage, ActionManager $action_manager, ModuleHandlerInterface $module_handler) {
+    parent::__construct($entity_info, $storage, $module_handler);
 
     $this->actionManager = $action_manager;
   }
@@ -55,11 +54,10 @@ public function __construct($entity_type, array $entity_info, EntityStorageContr
   /**
    * {@inheritdoc}
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
     return new static(
-      $entity_type,
       $entity_info,
-      $container->get('entity.manager')->getStorageController($entity_type),
+      $container->get('entity.manager')->getStorageController($entity_info->getId()),
       $container->get('plugin.manager.action'),
       $container->get('module_handler')
     );
diff --git a/core/modules/block/custom_block/custom_block.module b/core/modules/block/custom_block/custom_block.module
index 4cf2e68..21aa308 100644
--- a/core/modules/block/custom_block/custom_block.module
+++ b/core/modules/block/custom_block/custom_block.module
@@ -125,10 +125,13 @@ function custom_block_load($id) {
 /**
  * Implements hook_entity_info_alter().
  */
-function custom_block_entity_info_alter(&$types) {
+function custom_block_entity_info_alter(&$entity_info) {
+  /** @var $entity_info \Drupal\Core\Entity\EntityTypeInterface[] */
   // Add a translation handler for fields if the language module is enabled.
   if (\Drupal::moduleHandler()->moduleExists('language')) {
-    $types['custom_block']['translation']['custom_block'] = TRUE;
+    $translation = $entity_info['custom_block']->get('translation');
+    $translation['custom_block'] = TRUE;
+    $entity_info['custom_block']->set('translation', $translation);
   }
 }
 
diff --git a/core/modules/block/lib/Drupal/block/BlockAccessController.php b/core/modules/block/lib/Drupal/block/BlockAccessController.php
index a82de8b..315e4f6 100644
--- a/core/modules/block/lib/Drupal/block/BlockAccessController.php
+++ b/core/modules/block/lib/Drupal/block/BlockAccessController.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Entity\EntityAccessController;
 use Drupal\Core\Entity\EntityControllerInterface;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Path\AliasManagerInterface;
 use Drupal\Component\Utility\Unicode;
@@ -30,24 +31,21 @@ class BlockAccessController extends EntityAccessController implements EntityCont
   /**
    * Constructs a BlockAccessController object.
    *
-   * @param string $entity_type
-   *   The entity type of the access controller instance.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    * @param \Drupal\Core\Path\AliasManagerInterface $alias_manager
    *   The alias manager.
    */
-  public function __construct($entity_type, array $entity_info, AliasManagerInterface $alias_manager) {
-    parent::__construct($entity_type, $entity_info);
+  public function __construct(EntityTypeInterface $entity_info, AliasManagerInterface $alias_manager) {
+    parent::__construct($entity_info);
     $this->aliasManager = $alias_manager;
   }
 
   /**
    * {@inheritdoc}
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
     return new static(
-      $entity_type,
       $entity_info,
       $container->get('path.alias_manager')
     );
diff --git a/core/modules/block/lib/Drupal/block/BlockListController.php b/core/modules/block/lib/Drupal/block/BlockListController.php
index 6db9219..7a0c9a8 100644
--- a/core/modules/block/lib/Drupal/block/BlockListController.php
+++ b/core/modules/block/lib/Drupal/block/BlockListController.php
@@ -14,6 +14,7 @@
 use Drupal\Core\Entity\EntityControllerInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Form\FormInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -55,10 +56,8 @@ class BlockListController extends ConfigEntityListController implements FormInte
   /**
    * Constructs a new BlockListController object.
    *
-   * @param string $entity_type
-   *   The type of entity to be listed.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    * @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage
    *   The entity storage controller class.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
@@ -66,8 +65,8 @@ class BlockListController extends ConfigEntityListController implements FormInte
    * @param \Drupal\Component\Plugin\PluginManagerInterface $block_manager
    *   The block manager.
    */
-  public function __construct($entity_type, array $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler, PluginManagerInterface $block_manager) {
-    parent::__construct($entity_type, $entity_info, $storage, $module_handler);
+  public function __construct(EntityTypeInterface $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler, PluginManagerInterface $block_manager) {
+    parent::__construct($entity_info, $storage, $module_handler);
 
     $this->blockManager = $block_manager;
   }
@@ -75,11 +74,10 @@ public function __construct($entity_type, array $entity_info, EntityStorageContr
   /**
    * {@inheritdoc}
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
     return new static(
-      $entity_type,
       $entity_info,
-      $container->get('entity.manager')->getStorageController($entity_type),
+      $container->get('entity.manager')->getStorageController($entity_info->getId()),
       $container->get('module_handler'),
       $container->get('plugin.manager.block')
     );
@@ -102,7 +100,7 @@ public function load() {
     $entities = _block_rehash($this->theme);
 
     // Sort the blocks using \Drupal\block\Entity\Block::sort().
-    uasort($entities, array($this->entityInfo['class'], 'sort'));
+    uasort($entities, array($this->entityInfo->getClass(), 'sort'));
     return $entities;
   }
 
diff --git a/core/modules/book/book.module b/core/modules/book/book.module
index ec8cf4c..f49a3e2 100644
--- a/core/modules/book/book.module
+++ b/core/modules/book/book.module
@@ -115,7 +115,8 @@ function book_permission() {
  * Implements hook_entity_info().
  */
 function book_entity_info(&$entity_info) {
-  $entity_info['node']['controllers']['form']['book_outline'] = '\Drupal\book\Form\BookOutlineForm';
+  /** @var $entity_info \Drupal\Core\Entity\EntityTypeInterface[] */
+  $entity_info['node']->setForm('book_outline', 'Drupal\book\Form\BookOutlineForm');
 }
 
 /**
diff --git a/core/modules/breakpoint/breakpoint.module b/core/modules/breakpoint/breakpoint.module
index a2450f9..9306fec 100644
--- a/core/modules/breakpoint/breakpoint.module
+++ b/core/modules/breakpoint/breakpoint.module
@@ -75,11 +75,12 @@ function breakpoint_modules_uninstalled($modules) {
  */
 function _breakpoint_delete_breakpoints($list, $source_type) {
   $ids = config_get_storage_names_with_prefix('breakpoint.breakpoint_group.' . $source_type . '.');
-  $entity_info = \Drupal::service('entity.manager')->getDefinition('breakpoint_group');
+  $entity_manager = \Drupal::entityManager();
+  $entity_info = $entity_manager->getDefinition('breakpoint_group');
 
   // Remove the breakpoint.breakpoint part of the breakpoint identifier.
   foreach ($ids as &$id) {
-    $id = ConfigStorageController::getIDFromConfigName($id, $entity_info['config_prefix']);
+    $id = ConfigStorageController::getIDFromConfigName($id, $entity_info->getConfigPrefix());
   }
   $breakpoint_groups = entity_load_multiple('breakpoint_group', $ids);
 
@@ -90,11 +91,11 @@ function _breakpoint_delete_breakpoints($list, $source_type) {
 
       // Get all breakpoints defined by this theme/module.
       $breakpoint_ids = \Drupal::service('config.storage')->listAll('breakpoint.breakpoint.' . $source_type . '.' . $breakpoint_group->id() . '.');
-      $entity_info = \Drupal::service('entity.manager')->getDefinition('breakpoint');
+      $entity_info = $entity_manager->getDefinition('breakpoint');
 
       // Remove the breakpoint.breakpoint part of the breakpoint identifier.
       foreach ($breakpoint_ids as &$breakpoint_id) {
-        $breakpoint_id = ConfigStorageController::getIDFromConfigName($breakpoint_id, $entity_info['config_prefix']);
+        $breakpoint_id = ConfigStorageController::getIDFromConfigName($breakpoint_id, $entity_info->getConfigPrefix());
       }
       $breakpoints = entity_load_multiple('breakpoint', $breakpoint_ids);
 
diff --git a/core/modules/comment/comment.views.inc b/core/modules/comment/comment.views.inc
index ed8f85a..63b9b48 100644
--- a/core/modules/comment/comment.views.inc
+++ b/core/modules/comment/comment.views.inc
@@ -353,19 +353,19 @@ function comment_views_data() {
 
   // Provide a relationship for each entity type except comment.
   foreach ($entities_info as $type => $entity_info) {
-    if ($type == 'comment' || empty($entity_info['fieldable']) || !isset($entity_info['base_table'])) {
+    if ($type == 'comment' || !$entity_info->isFieldable() || !$entity_info->getBaseTable()) {
       continue;
     }
     if ($fields = \Drupal::service('comment.manager')->getFields($type)) {
       $data['comment'][$type] = array(
         'relationship' => array(
-          'title' => $entity_info['label'],
-          'help' => t('The @entity_type to which the comment is a reply to.', array('@entity_type' => $entity_info['label'])),
-          'base' => $entity_info['base_table'],
-          'base field' => $entity_info['entity_keys']['id'],
+          'title' => $entity_info->getLabel(),
+          'help' => t('The @entity_type to which the comment is a reply to.', array('@entity_type' => $entity_info->getLabel())),
+          'base' => $entity_info->getBaseTable(),
+          'base field' => $entity_info->getKey('id'),
           'relationship field' => 'entity_id',
           'id' => 'standard',
-          'label' => $entity_info['label'],
+          'label' => $entity_info->getLabel(),
           'extra' => array(
             array(
               'field' => 'entity_type',
@@ -432,7 +432,7 @@ function comment_views_data() {
 
   // Provide a relationship for each entity type except comment.
   foreach ($entities_info as $type => $entity_info) {
-    if ($type == 'comment' || empty($entity_info['fieldable']) || !isset($entity_info['base_table'])) {
+    if ($type == 'comment' || !$entity_info->isFieldable() || !$entity_info->getBaseTable()) {
       continue;
     }
     // This relationship does not use the 'field id' column, if the entity has
@@ -442,9 +442,9 @@ function comment_views_data() {
     // {comment_entity_statistics} for each field as multiple joins between
     // the same two tables is not supported.
     if (\Drupal::service('comment.manager')->getFields($type)) {
-      $data['comment_entity_statistics']['table']['join'][$entity_info['base_table']] = array(
+      $data['comment_entity_statistics']['table']['join'][$entity_info->getBaseTable()] = array(
         'type' => 'INNER',
-        'left_field' => $entity_info['entity_keys']['id'],
+        'left_field' => $entity_info->getKey('id'),
         'field' => 'entity_id',
         'extra' => array(
           array(
@@ -604,11 +604,11 @@ function comment_views_data_alter(&$data) {
 
   // Provide a integration for each entity type except comment.
   foreach (\Drupal::entityManager()->getDefinitions() as $entity_type => $entity_info) {
-    if ($entity_type == 'comment' || empty($entity_info['fieldable']) || !isset($entity_info['base_table'])) {
+    if ($entity_type == 'comment' || !$entity_info->isFieldable() || !$entity_info->getBaseTable()) {
       continue;
     }
     $fields = \Drupal::service('comment.manager')->getFields($entity_type);
-    $base_table = $entity_info['base_table'];
+    $base_table = $entity_info->getBaseTable();
     $args = array('@entity_type' => $entity_type);
 
     if ($fields) {
@@ -620,9 +620,9 @@ function comment_views_data_alter(&$data) {
         ),
       );
 
-      if ($entity_info['id'] == 'node') {
+      if ($entity_type == 'node') {
         // Node properties lives in data_table.
-        $table = $entity_info['data_table'];
+        $table = $entity_info->getDataTable();
       }
       else {
         $table = $base_table;
@@ -637,7 +637,7 @@ function comment_views_data_alter(&$data) {
           'id' => 'argument_comment_user_uid',
           'no group by' => TRUE,
           'entity_type' => $entity_type,
-          'entity_id' => $entity_info['entity_keys']['id'],
+          'entity_id' => $entity_info->getKey('id'),
         ),
         'filter' => array(
           'field' => 'uid',
@@ -645,7 +645,7 @@ function comment_views_data_alter(&$data) {
           'name field' => 'name',
           'id' => 'comment_user_uid',
           'entity_type' => $entity_type,
-          'entity_id' => $entity_info['entity_keys']['id'],
+          'entity_id' => $entity_info->getKey('id'),
         ),
       );
 
@@ -658,7 +658,7 @@ function comment_views_data_alter(&$data) {
             'label' => t('Comments'),
             'base' => 'comment',
             'base field' => 'entity_id',
-            'relationship field' => $entity_info['entity_keys']['id'],
+            'relationship field' => $entity_info->getKey('id'),
             'id' => 'standard',
             'extra' => array(
               array(
diff --git a/core/modules/comment/lib/Drupal/comment/CommentManager.php b/core/modules/comment/lib/Drupal/comment/CommentManager.php
index fc833b8..9bca3c5 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentManager.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentManager.php
@@ -58,7 +58,7 @@ public function getParentEntityUri(CommentInterface $comment) {
    */
   public function getFields($entity_type) {
     $info = $this->entityManager->getDefinition($entity_type);
-    if (!is_subclass_of($info['class'], '\Drupal\Core\Entity\ContentEntityInterface')) {
+    if (!$info->isSubclassOf('\Drupal\Core\Entity\ContentEntityInterface')) {
       return array();
     }
 
diff --git a/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php b/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php
index dc4cd61..7dd6517 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php
@@ -12,6 +12,7 @@
 use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Entity\EntityViewBuilderInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Entity\EntityViewBuilder;
@@ -47,9 +48,8 @@ class CommentViewBuilder extends EntityViewBuilder implements EntityViewBuilderI
   /**
    * {@inheritdoc}
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
     return new static(
-      $entity_type,
       $entity_info,
       $container->get('entity.manager'),
       $container->get('field.info'),
@@ -61,9 +61,7 @@ public static function createInstance(ContainerInterface $container, $entity_typ
   /**
    * Constructs a new CommentViewBuilder.
    *
-   * @param string $entity_type
-   *   The entity type.
-   * @param array $entity_info
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
    *   The entity information array.
    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager service.
@@ -74,8 +72,8 @@ public static function createInstance(ContainerInterface $container, $entity_typ
    * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
    *   The CSRF token manager service.
    */
-  public function __construct($entity_type, array $entity_info, EntityManagerInterface $entity_manager, FieldInfo $field_info, ModuleHandlerInterface $module_handler, CsrfTokenGenerator $csrf_token) {
-    parent::__construct($entity_type, $entity_info, $entity_manager);
+  public function __construct(EntityTypeInterface $entity_info, EntityManagerInterface $entity_manager, FieldInfo $field_info, ModuleHandlerInterface $module_handler, CsrfTokenGenerator $csrf_token) {
+    parent::__construct($entity_info, $entity_manager);
     $this->fieldInfo = $field_info;
     $this->moduleHandler = $module_handler;
     $this->csrfToken = $csrf_token;
diff --git a/core/modules/comment/lib/Drupal/comment/Controller/AdminController.php b/core/modules/comment/lib/Drupal/comment/Controller/AdminController.php
index 451a62d..22963bd 100644
--- a/core/modules/comment/lib/Drupal/comment/Controller/AdminController.php
+++ b/core/modules/comment/lib/Drupal/comment/Controller/AdminController.php
@@ -121,7 +121,7 @@ public function overviewBundles() {
           }
         }
 
-        $row['data']['type']['data'] = String::checkPlain($entity_types[$entity_type]['label']);
+        $row['data']['type']['data'] = String::checkPlain($entity_types[$entity_type]->getLabel());
 
         if ($field_ui_enabled) {
           if ($this->currentUser()->hasPermission('administer comment fields')) {
@@ -189,7 +189,7 @@ public function bundleInfo($commented_entity_type, $field_name) {
 
     $build['usage'] = array(
       '#theme' => 'item_list',
-      '#title' => String::checkPlain($entity_type_info['label']),
+      '#title' => String::checkPlain($entity_type_info->getLabel()),
       '#items' => array(),
     );
     // Loop over all of bundles to which this comment field is attached.
diff --git a/core/modules/comment/tests/modules/comment_test/comment_test.module b/core/modules/comment/tests/modules/comment_test/comment_test.module
index da43a04..110e8c5 100644
--- a/core/modules/comment/tests/modules/comment_test/comment_test.module
+++ b/core/modules/comment/tests/modules/comment_test/comment_test.module
@@ -11,10 +11,13 @@
 /**
  * Implements hook_entity_info_alter().
  */
-function comment_test_entity_info_alter(&$info) {
+function comment_test_entity_info_alter(&$entity_info) {
+  /** @var $entity_info \Drupal\Core\Entity\EntityTypeInterface[] */
   if (language_multilingual()) {
     // Enable language handling for comment fields.
-    $info['comment']['translation']['comment_test'] = TRUE;
+    $translation = $entity_info['comment']->get('translation');
+    $translation['comment_test'] = TRUE;
+    $entity_info['comment']->set('translation', $translation);
   }
 }
 
diff --git a/core/modules/config/lib/Drupal/config/Form/ConfigSingleExportForm.php b/core/modules/config/lib/Drupal/config/Form/ConfigSingleExportForm.php
index 5ac759b..2f146c6 100644
--- a/core/modules/config/lib/Drupal/config/Form/ConfigSingleExportForm.php
+++ b/core/modules/config/lib/Drupal/config/Form/ConfigSingleExportForm.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Utility\MapArray;
 use Drupal\Core\Config\StorageInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Form\FormBase;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -35,7 +36,7 @@ class ConfigSingleExportForm extends FormBase {
   /**
    * Tracks the valid config entity type definitions.
    *
-   * @var array
+   * @var \Drupal\Core\Entity\EntityTypeInterface[]
    */
   protected $definitions = array();
 
@@ -74,12 +75,12 @@ public function getFormID() {
    */
   public function buildForm(array $form, array &$form_state, $config_type = NULL, $config_name = NULL) {
     foreach ($this->entityManager->getDefinitions() as $entity_type => $definition) {
-      if (isset($definition['config_prefix']) && isset($definition['entity_keys']['uuid'])) {
+      if ($definition->getConfigPrefix() && $definition->hasKey('uuid')) {
         $this->definitions[$entity_type] = $definition;
       }
     }
-    $entity_types = array_map(function ($definition) {
-      return $definition['label'];
+    $entity_types = array_map(function (EntityTypeInterface $definition) {
+      return $definition->getLabel();
     }, $this->definitions);
     // Sort the entity types by label, then add the simple config to the top.
     uasort($entity_types, 'strnatcasecmp');
@@ -144,7 +145,7 @@ public function updateExport($form, &$form_state) {
     // Determine the full config name for the selected config entity.
     if ($form_state['values']['config_type'] !== 'system.simple') {
       $definition = $this->entityManager->getDefinition($form_state['values']['config_type']);
-      $name = $definition['config_prefix'] . '.' . $form_state['values']['config_name'];
+      $name = $definition->getConfigPrefix() . '.' . $form_state['values']['config_name'];
     }
     // The config name is used directly for simple configuration.
     else {
@@ -176,8 +177,8 @@ protected function findConfiguration($config_type) {
     // Handle simple configuration.
     else {
       // Gather the config entity prefixes.
-      $config_prefixes = array_map(function ($definition) {
-        return $definition['config_prefix'] . '.';
+      $config_prefixes = array_map(function (EntityTypeInterface $definition) {
+        return $definition->getConfigPrefix() . '.';
       }, $this->definitions);
 
       // Find all config, and then filter our anything matching a config prefix.
diff --git a/core/modules/config/lib/Drupal/config/Form/ConfigSingleImportForm.php b/core/modules/config/lib/Drupal/config/Form/ConfigSingleImportForm.php
index 89f71be..b89f68b 100644
--- a/core/modules/config/lib/Drupal/config/Form/ConfigSingleImportForm.php
+++ b/core/modules/config/lib/Drupal/config/Form/ConfigSingleImportForm.php
@@ -90,12 +90,12 @@ public function getCancelRoute() {
   public function getQuestion() {
     if ($this->data['config_type'] === 'system.simple') {
       $name = $this->data['config_name'];
-      $type = $this->t('Simple configuration');
+      $type = $this->t('simple configuration');
     }
     else {
       $definition = $this->entityManager->getDefinition($this->data['config_type']);
-      $name = $this->data['import'][$definition['entity_keys']['id']];
-      $type = $definition['label'];
+      $name = $this->data['import'][$definition->getKey('id')];
+      $type = $definition->getLowercaseLabel();
     }
 
     $args = array(
@@ -122,8 +122,8 @@ public function buildForm(array $form, array &$form_state) {
 
     $entity_types = array();
     foreach ($this->entityManager->getDefinitions() as $entity_type => $definition) {
-      if (isset($definition['config_prefix']) && isset($definition['entity_keys']['uuid'])) {
-        $entity_types[$entity_type] = $definition['label'];
+      if ($definition->getConfigPrefix() && $definition->hasKey('uuid')) {
+        $entity_types[$entity_type] = $definition->getLabel();
       }
     }
     // Sort the entity types by label, then add the simple config to the top.
@@ -180,14 +180,14 @@ public function validateForm(array &$form, array &$form_state) {
     // Validate for config entities.
     if ($form_state['values']['config_type'] !== 'system.simple') {
       $definition = $this->entityManager->getDefinition($form_state['values']['config_type']);
-      $id_key = $definition['entity_keys']['id'];
+      $id_key = $definition->getKey('id');
       $entity_storage = $this->entityManager->getStorageController($form_state['values']['config_type']);
       // If an entity ID was not specified, set an error.
       if (!isset($data[$id_key])) {
-        $this->setFormError('import', $form_state, $this->t('Missing ID key "@id_key" for this @entity_type import.', array('@id_key' => $id_key, '@entity_type' => $definition['label'])));
+        $this->setFormError('import', $form_state, $this->t('Missing ID key "@id_key" for this @entity_type import.', array('@id_key' => $id_key, '@entity_type' => $definition->getLabel())));
         return;
       }
-      $uuid_key = $definition['entity_keys']['uuid'];
+      $uuid_key = $definition->getKey('uuid');
       // If there is an existing entity, ensure matching ID and UUID.
       if ($entity = $entity_storage->load($data[$id_key])) {
         $this->configExists = $entity;
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityUnitTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityUnitTest.php
index a7ba6dc..262fd46 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityUnitTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityUnitTest.php
@@ -49,16 +49,16 @@ protected function setUp() {
    * Tests storage controller methods.
    */
   public function testStorageControllerMethods() {
-    $info = entity_get_info('config_test');
+    $info = \Drupal::entityManager()->getDefinition('config_test');
 
-    $expected = $info['config_prefix'] . '.';
+    $expected = $info->getConfigPrefix() . '.';
     $this->assertIdentical($this->storage->getConfigPrefix(), $expected);
 
     // Test the static extractID() method.
     $expected_id = 'test_id';
-    $config_name = $info['config_prefix'] . '.' . $expected_id;
+    $config_name = $info->getConfigPrefix() . '.' . $expected_id;
     $storage = $this->storage;
-    $this->assertIdentical($storage::getIDFromConfigName($config_name, $info['config_prefix']), $expected_id);
+    $this->assertIdentical($storage::getIDFromConfigName($config_name, $info->getConfigPrefix()), $expected_id);
 
     // Create three entities, two with the same style.
     $style = $this->randomName(8);
diff --git a/core/modules/config/tests/config_test/config_test.module b/core/modules/config/tests/config_test/config_test.module
index 9c0b042..0c3b782 100644
--- a/core/modules/config/tests/config_test/config_test.module
+++ b/core/modules/config/tests/config_test/config_test.module
@@ -67,13 +67,19 @@ function config_test_config_test_create(ConfigTest $config_test) {
  * Implements hook_entity_info_alter().
  */
 function config_test_entity_info_alter(&$entity_info) {
+  /** @var $entity_info \Drupal\Core\Entity\EntityTypeInterface[] */
   // The 'translatable' entity key is not supposed to change over time. In this
   // case we can safely do it because we set it once and we do not change it for
   // all the duration of the test session.
-  $entity_info['config_test']['translatable'] = \Drupal::service('state')->get('config_test.translatable');
+  $entity_info['config_test']->set('translatable', \Drupal::service('state')->get('config_test.translatable'));
 
   // Create a clone of config_test that does not have a status.
-  $entity_info['config_test_no_status'] = $entity_info['config_test'];
-  unset($entity_info['config_test_no_status']['entity_keys']['status']);
-  $entity_info['config_test_no_status']['config_prefix'] = 'config_test.no_status';
+  $entity_info['config_test_no_status'] = clone $entity_info['config_test'];
+  $config_test_no_status = &$entity_info['config_test_no_status'];
+
+  $keys = $config_test_no_status->getKeys();
+  unset($keys['status']);
+  $config_test_no_status->set('id', 'config_test_no_status');
+  $config_test_no_status->set('entity_keys', $keys);
+  $config_test_no_status->set('config_prefix', 'config_test.no_status');
 }
diff --git a/core/modules/config_translation/config_translation.api.php b/core/modules/config_translation/config_translation.api.php
index 6f98634..aa1f7f0 100644
--- a/core/modules/config_translation/config_translation.api.php
+++ b/core/modules/config_translation/config_translation.api.php
@@ -49,7 +49,7 @@ function hook_config_translation_info(&$info) {
       }
 
       // Make sure entity type is fieldable and has a base route.
-      if ($entity_info['fieldable'] && !empty($base_route)) {
+      if ($entity_info->isFieldable() && !empty($base_route)) {
         $info[$entity_type . '_fields'] = array(
           'base_route_name' => 'field_ui.instance_edit_' . $entity_type,
           'entity_type' => 'field_instance',
diff --git a/core/modules/config_translation/config_translation.module b/core/modules/config_translation/config_translation.module
index 0fc1170..63a8f8d 100644
--- a/core/modules/config_translation/config_translation.module
+++ b/core/modules/config_translation/config_translation.module
@@ -90,7 +90,7 @@ function config_translation_config_translation_info(&$info) {
       }
 
       // Make sure entity type is fieldable and has a base route.
-      if ($entity_info['fieldable'] && !empty($base_route)) {
+      if ($entity_info->isFieldable() && !empty($base_route)) {
         $info[$entity_type . '_fields'] = array(
           'base_route_name' => 'field_ui.instance_edit_' . $entity_type,
           'entity_type' => 'field_instance',
@@ -109,8 +109,8 @@ function config_translation_config_translation_info(&$info) {
     // Determine base path for entities automatically if provided via the
     // configuration entity.
     if (
-      !in_array('Drupal\Core\Config\Entity\ConfigEntityInterface', class_implements($entity_info['class'])) ||
-      !isset($entity_info['links']['edit-form'])
+      !$entity_info->isSubclassOf('Drupal\Core\Config\Entity\ConfigEntityInterface') ||
+      !$entity_info->hasLinkTemplate('edit-form')
     ) {
       // Do not record this entity mapper if the entity type does not
       // provide a base route. We'll surely not be able to do anything with
@@ -122,7 +122,7 @@ function config_translation_config_translation_info(&$info) {
     // Use the entity type as the plugin ID.
     $info[$entity_type] = array(
       'class' => '\Drupal\config_translation\ConfigEntityMapper',
-      'base_route_name' => $entity_info['links']['edit-form'],
+      'base_route_name' => $entity_info->getLinkTemplate('edit-form'),
       'title' => '!label !entity_type',
       'names' => array(),
       'entity_type' => $entity_type,
diff --git a/core/modules/config_translation/lib/Drupal/config_translation/ConfigEntityMapper.php b/core/modules/config_translation/lib/Drupal/config_translation/ConfigEntityMapper.php
index 44e0d7f..d8d3297 100644
--- a/core/modules/config_translation/lib/Drupal/config_translation/ConfigEntityMapper.php
+++ b/core/modules/config_translation/lib/Drupal/config_translation/ConfigEntityMapper.php
@@ -7,10 +7,9 @@
 
 namespace Drupal\config_translation;
 
-use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Config\ConfigFactory;
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Entity\EntityManager;
+use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Routing\RouteProviderInterface;
 use Drupal\Core\StringTranslation\TranslationInterface;
 use Drupal\locale\LocaleConfigManager;
@@ -25,7 +24,7 @@ class ConfigEntityMapper extends ConfigNamesMapper {
   /**
    * The entity manager.
    *
-   * @var \Drupal\Core\Entity\EntityManager
+   * @var \Drupal\Core\Entity\EntityManagerInterface
    */
   protected $entityManager;
 
@@ -69,10 +68,10 @@ class ConfigEntityMapper extends ConfigNamesMapper {
    *   The route provider.
    * @param \Drupal\Core\StringTranslation\TranslationInterface $translation_manager
    *   The string translation manager.
-   * @param \Drupal\Core\Entity\EntityManager $entity_manager
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager.
    */
-  public function __construct($plugin_id, array $plugin_definition, ConfigFactory $config_factory, LocaleConfigManager $locale_config_manager, ConfigMapperManagerInterface $config_mapper_manager, RouteProviderInterface $route_provider, TranslationInterface $translation_manager, EntityManager $entity_manager) {
+  public function __construct($plugin_id, array $plugin_definition, ConfigFactory $config_factory, LocaleConfigManager $locale_config_manager, ConfigMapperManagerInterface $config_mapper_manager, RouteProviderInterface $route_provider, TranslationInterface $translation_manager, EntityManagerInterface $entity_manager) {
     parent::__construct($plugin_id, $plugin_definition, $config_factory, $locale_config_manager, $config_mapper_manager, $route_provider, $translation_manager);
     $this->setType($plugin_definition['entity_type']);
 
@@ -134,7 +133,7 @@ public function setEntity(EntityInterface $entity) {
     // entity. This is not a Drupal 8 best practice (ideally the configuration
     // would have pluggable components), but this may happen as well.
     $entity_type_info = $this->entityManager->getDefinition($this->entityType);
-    $this->addConfigName($entity_type_info['config_prefix'] . '.' . $entity->id());
+    $this->addConfigName($entity_type_info->getConfigPrefix() . '.' . $entity->id());
 
     return TRUE;
   }
@@ -147,7 +146,7 @@ public function getTitle() {
     // current page language. The title placeholder is later escaped for
     // display.
     $entity_type_info = $this->entityManager->getDefinition($this->entityType);
-    return $this->t($this->pluginDefinition['title'], array('!label' => $this->entity->label(), '!entity_type' => Unicode::strtolower($entity_type_info['label'])));
+    return $this->t($this->pluginDefinition['title'], array('!label' => $this->entity->label(), '!entity_type' => $entity_type_info->getLowercaseLabel()));
   }
 
   /**
@@ -194,7 +193,7 @@ public function getType() {
    */
   public function getTypeName() {
     $entity_type_info = $this->entityManager->getDefinition($this->entityType);
-    return $entity_type_info['label'];
+    return $entity_type_info->getLabel();
   }
 
   /**
@@ -202,7 +201,7 @@ public function getTypeName() {
    */
   public function getTypeLabel() {
     $entityType = $this->entityManager->getDefinition($this->entityType);
-    return $entityType['label'];
+    return $entityType->getLabel();
   }
 
   /**
diff --git a/core/modules/config_translation/lib/Drupal/config_translation/ConfigFieldInstanceMapper.php b/core/modules/config_translation/lib/Drupal/config_translation/ConfigFieldInstanceMapper.php
index 44df444..8323461 100644
--- a/core/modules/config_translation/lib/Drupal/config_translation/ConfigFieldInstanceMapper.php
+++ b/core/modules/config_translation/lib/Drupal/config_translation/ConfigFieldInstanceMapper.php
@@ -31,7 +31,7 @@ class ConfigFieldInstanceMapper extends ConfigEntityMapper {
   public function getBaseRouteParameters() {
     $parameters = parent::getBaseRouteParameters();
     $base_entity_info = $this->entityManager->getDefinition($this->pluginDefinition['base_entity_type']);
-    $parameters[$base_entity_info['bundle_entity_type']] = $this->entity->targetBundle();
+    $parameters[$base_entity_info->getBundleEntityType()] = $this->entity->targetBundle();
     return $parameters;
   }
 
@@ -40,7 +40,7 @@ public function getBaseRouteParameters() {
    */
   public function getTypeLabel() {
     $base_entity_info = $this->entityManager->getDefinition($this->pluginDefinition['base_entity_type']);
-    return $this->t('@label fields', array('@label' => $base_entity_info['label']));
+    return $this->t('@label fields', array('@label' => $base_entity_info->getLabel()));
   }
 
 }
diff --git a/core/modules/config_translation/lib/Drupal/config_translation/Controller/ConfigTranslationBlockListController.php b/core/modules/config_translation/lib/Drupal/config_translation/Controller/ConfigTranslationBlockListController.php
index bd0f6f5..35c10cc 100644
--- a/core/modules/config_translation/lib/Drupal/config_translation/Controller/ConfigTranslationBlockListController.php
+++ b/core/modules/config_translation/lib/Drupal/config_translation/Controller/ConfigTranslationBlockListController.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Utility\String;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 
 /**
@@ -27,8 +28,8 @@ class ConfigTranslationBlockListController extends ConfigTranslationEntityListCo
   /**
    * {@inheritdoc}
    */
-  public function __construct($entity_type, array $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler) {
-    parent::__construct($entity_type, $entity_info, $storage, $module_handler);
+  public function __construct(EntityTypeInterface $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler) {
+    parent::__construct($entity_info, $storage, $module_handler);
     $this->themes = list_themes();
   }
 
diff --git a/core/modules/config_translation/lib/Drupal/config_translation/Controller/ConfigTranslationFieldInstanceListController.php b/core/modules/config_translation/lib/Drupal/config_translation/Controller/ConfigTranslationFieldInstanceListController.php
index d7ad320..a1b79ef 100644
--- a/core/modules/config_translation/lib/Drupal/config_translation/Controller/ConfigTranslationFieldInstanceListController.php
+++ b/core/modules/config_translation/lib/Drupal/config_translation/Controller/ConfigTranslationFieldInstanceListController.php
@@ -10,8 +10,9 @@
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Entity\EntityManager;
+use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\field\Field;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -45,7 +46,7 @@ class ConfigTranslationFieldInstanceListController extends ConfigTranslationEnti
   /**
    * The entity manager.
    *
-   * @var \Drupal\Core\Entity\EntityManager
+   * @var \Drupal\Core\Entity\EntityManagerInterface
    */
   protected $entityManager;
 
@@ -54,21 +55,18 @@ class ConfigTranslationFieldInstanceListController extends ConfigTranslationEnti
    *
    * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
    *   The service container this object should use.
-   * @param string $entity_type
-   *   The entity type which the controller handles.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    * @param array $definition
    *   (optional) The plugin definition of the config translation mapper.
    *
    * @return static
    *   A new instance of the entity controller.
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info, array $definition = array()) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info, array $definition = array()) {
     return new static(
-      $entity_type,
       $entity_info,
-      $container->get('entity.manager')->getStorageController($entity_type),
+      $container->get('entity.manager')->getStorageController($entity_info->getId()),
       $container->get('module_handler'),
       $container->get('entity.manager'),
       $definition
@@ -78,21 +76,19 @@ public static function createInstance(ContainerInterface $container, $entity_typ
   /**
    * Constructs a new ConfigTranslationFieldInstanceListController object.
    *
-   * @param string $entity_type
-   *   The type of entity to be listed.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    * @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage
    *   The entity storage controller class.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    *   The module handler to invoke hooks on.
-   * @param \Drupal\Core\Entity\EntityManager $entity_manager
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager.
    * @param array $definition
    *   The plugin definition of the config translation mapper.
    */
-  public function __construct($entity_type, array $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler, EntityManager $entity_manager, array $definition) {
-    parent::__construct($entity_type, $entity_info, $storage, $module_handler);
+  public function __construct(EntityTypeInterface $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler, EntityManagerInterface $entity_manager, array $definition) {
+    parent::__construct($entity_info, $storage, $module_handler);
     $this->entityManager = $entity_manager;
     $this->baseEntityType = $definition['base_entity_type'];
     $this->baseEntityInfo = $this->entityManager->getDefinition($this->baseEntityType);
@@ -117,7 +113,7 @@ public function load() {
    */
   public function getFilterLabels() {
     $info = parent::getFilterLabels();
-    $bundle = isset($this->baseEntityInfo['bundle_label']) ? $this->baseEntityInfo['bundle_label'] : $this->t('Bundle');
+    $bundle = $this->baseEntityInfo->getBundleLabel() ?: $this->t('Bundle');
     $bundle = Unicode::strtolower($bundle);
 
     $info['placeholder'] = $this->t('Enter field or @bundle', array('@bundle' => $bundle));
@@ -152,7 +148,7 @@ public function buildRow(EntityInterface $entity) {
   public function buildHeader() {
     $header['label'] = $this->t('Field');
     if ($this->displayBundle()) {
-      $header['bundle'] = isset($this->baseEntityInfo['bundle_label']) ? $this->baseEntityInfo['bundle_label'] : $this->t('Bundle');
+      $header['bundle'] = $this->baseEntityInfo->getBundleLabel() ?: $this->t('Bundle');
     }
     return $header + parent::buildHeader();
   }
@@ -165,7 +161,7 @@ public function buildHeader() {
    */
   public function displayBundle() {
     // The bundle key is explicitly defined in the entity definition.
-    if (isset($this->baseEntityInfo['bundle_keys']['bundle'])) {
+    if ($this->baseEntityInfo->getBundleKey('bundle')) {
       return TRUE;
     }
 
diff --git a/core/modules/config_translation/lib/Drupal/config_translation/Controller/ConfigTranslationListController.php b/core/modules/config_translation/lib/Drupal/config_translation/Controller/ConfigTranslationListController.php
index 48ecad7..14bd763 100644
--- a/core/modules/config_translation/lib/Drupal/config_translation/Controller/ConfigTranslationListController.php
+++ b/core/modules/config_translation/lib/Drupal/config_translation/Controller/ConfigTranslationListController.php
@@ -76,7 +76,7 @@ public function listing() {
     // list controller.
     $class = $this->mapperDefinition['list_controller'];
     /** @var \Drupal\config_translation\Controller\ConfigTranslationEntityListControllerInterface $controller */
-    $controller = new $class($entity_type, $this->entityManager()->getDefinition($entity_type), $this->entityManager()->getStorageController($entity_type), $this->moduleHandler(), $this->entityManager(), $this->mapperDefinition);
+    $controller = new $class($this->entityManager()->getDefinition($entity_type), $this->entityManager()->getStorageController($entity_type), $this->moduleHandler(), $this->entityManager(), $this->mapperDefinition);
     $build = $controller->render();
     $build['#title'] = $this->mapper->getTypeLabel();
     return $build;
diff --git a/core/modules/config_translation/lib/Drupal/config_translation/Tests/ConfigTranslationOverviewTest.php b/core/modules/config_translation/lib/Drupal/config_translation/Tests/ConfigTranslationOverviewTest.php
index 9ed4a20..a471ce9 100644
--- a/core/modules/config_translation/lib/Drupal/config_translation/Tests/ConfigTranslationOverviewTest.php
+++ b/core/modules/config_translation/lib/Drupal/config_translation/Tests/ConfigTranslationOverviewTest.php
@@ -8,7 +8,6 @@
 namespace Drupal\config_translation\Tests;
 
 use Drupal\Component\Utility\String;
-use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Language\Language;
 use Drupal\simpletest\WebTestBase;
 
@@ -97,13 +96,13 @@ public function testMapperListPage() {
       $entity_info = \Drupal::entityManager()->getDefinition($test_entity->entityType());
       $this->drupalGet($base_url . '/translate');
 
-      $title = t('!label !entity_type', array('!label' => $test_entity->label(), '!entity_type' => Unicode::strtolower($entity_info['label'])));
+      $title = t('!label !entity_type', array('!label' => $test_entity->label(), '!entity_type' => $entity_info->getLowercaseLabel()));
       $title = t('Translations for %label', array('%label' => $title));
       $this->assertRaw($title);
       $this->assertRaw('<th>' . t('Language') . '</th>');
 
       $this->drupalGet($base_url);
-      $this->assertLink(t('Translate @title', array('@title' => Unicode::strtolower($entity_info['label']))));
+      $this->assertLink(t('Translate @title', array('@title' => $entity_info->getLowercaseLabel())));
     }
   }
 
diff --git a/core/modules/config_translation/tests/Drupal/config_translation/Tests/ConfigEntityMapperTest.php b/core/modules/config_translation/tests/Drupal/config_translation/Tests/ConfigEntityMapperTest.php
index df9aa0c..b7ed1f5 100644
--- a/core/modules/config_translation/tests/Drupal/config_translation/Tests/ConfigEntityMapperTest.php
+++ b/core/modules/config_translation/tests/Drupal/config_translation/Tests/ConfigEntityMapperTest.php
@@ -59,9 +59,7 @@ public static function getInfo() {
   }
 
   public function setUp() {
-    $this->entityManager = $this->getMockBuilder('Drupal\Core\Entity\EntityManager')
-      ->disableOriginalConstructor()
-      ->getMock();
+    $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
 
     $this->entity = $this->getMock('Drupal\Core\Entity\EntityInterface');
 
@@ -107,11 +105,12 @@ public function testSetEntity() {
       ->with()
       ->will($this->returnValue('entity_id'));
 
+    $entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
     $this->entityManager
       ->expects($this->once())
       ->method('getDefinition')
       ->with('language_entity')
-      ->will($this->returnValue(array('config_prefix' => 'config_prefix')));
+      ->will($this->returnValue($entity_type));
 
     $result = $this->configEntityMapper->setEntity($this->entity);
     $this->assertTrue($result);
@@ -125,6 +124,12 @@ public function testSetEntity() {
    * Tests ConfigEntityMapper::getOverviewRouteParameters().
    */
   public function testGetOverviewRouteParameters() {
+    $entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
+    $this->entityManager
+      ->expects($this->once())
+      ->method('getDefinition')
+      ->with('language_entity')
+      ->will($this->returnValue($entity_type));
     $this->configEntityMapper->setEntity($this->entity);
 
     $this->entity
@@ -150,11 +155,15 @@ public function testGetType() {
    * Tests ConfigEntityMapper::getTypeName().
    */
   public function testGetTypeName() {
+    $entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
+    $entity_type->expects($this->once())
+      ->method('getLabel')
+      ->will($this->returnValue('test'));
     $this->entityManager
       ->expects($this->once())
       ->method('getDefinition')
       ->with('language_entity')
-      ->will($this->returnValue(array('label' => 'test')));
+      ->will($this->returnValue($entity_type));
 
     $result = $this->configEntityMapper->getTypeName();
     $this->assertSame('test', $result);
@@ -164,11 +173,15 @@ public function testGetTypeName() {
    * Tests ConfigEntityMapper::getTypeLabel().
    */
   public function testGetTypeLabel() {
+    $entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
+    $entity_type->expects($this->once())
+      ->method('getLabel')
+      ->will($this->returnValue('test'));
     $this->entityManager
       ->expects($this->once())
       ->method('getDefinition')
       ->with('language_entity')
-      ->will($this->returnValue(array('label' => 'test')));
+      ->will($this->returnValue($entity_type));
 
     $result = $this->configEntityMapper->getTypeLabel();
     $this->assertSame('test', $result);
diff --git a/core/modules/config_translation/tests/modules/config_translation_test/config_translation_test.module b/core/modules/config_translation/tests/modules/config_translation_test/config_translation_test.module
index 4d651f4..ac6fb3f 100644
--- a/core/modules/config_translation/tests/modules/config_translation_test/config_translation_test.module
+++ b/core/modules/config_translation/tests/modules/config_translation_test/config_translation_test.module
@@ -8,10 +8,10 @@
 /**
  * Implements hook_entity_info_alter().
  */
-function config_translation_test_entity_info_alter(&$info) {
+function config_translation_test_entity_info_alter(&$entity_info) {
   // Remove entity definition for these entity types from config_test module.
-  unset($info['config_test_no_status']);
-  unset($info['config_query_test']);
+  unset($entity_info['config_test_no_status']);
+  unset($entity_info['config_query_test']);
 }
 
 /**
diff --git a/core/modules/content_translation/content_translation.admin.inc b/core/modules/content_translation/content_translation.admin.inc
index 9f07191..a39d8f4 100644
--- a/core/modules/content_translation/content_translation.admin.inc
+++ b/core/modules/content_translation/content_translation.admin.inc
@@ -76,7 +76,7 @@ function _content_translation_form_language_content_settings_form_alter(array &$
   $dependent_options_settings = array();
   $entity_manager = Drupal::entityManager();
   foreach ($form['#labels'] as $entity_type => $label) {
-    $entity_info = entity_get_info($entity_type);
+    $entity_info = \Drupal::entityManager()->getDefinition($entity_type);
     foreach (entity_get_bundles($entity_type) as $bundle => $bundle_info) {
       // Here we do not want the widget to be altered and hold also the "Enable
       // translation" checkbox, which would be redundant. Hence we add this key
@@ -85,7 +85,7 @@ function _content_translation_form_language_content_settings_form_alter(array &$
 
       // Only show the checkbox to enable translation if the bundles in the
       // entity might have fields and if there are fields to translate.
-      if (!empty($entity_info['fieldable'])) {
+      if ($entity_info->isFieldable()) {
         $fields = $entity_manager->getFieldDefinitions($entity_type, $bundle);
         if ($fields) {
           $form['settings'][$entity_type][$bundle]['translatable'] = array(
diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module
index 0d476cb..9edb599 100644
--- a/core/modules/content_translation/content_translation.module
+++ b/core/modules/content_translation/content_translation.module
@@ -78,23 +78,28 @@ function content_translation_language_types_info_alter(array &$language_types) {
  */
 function content_translation_entity_info_alter(array &$entity_info) {
   // Provide defaults for translation info.
+  /** @var $entity_info \Drupal\Core\Entity\EntityTypeInterface[] */
   foreach ($entity_info as $entity_type => &$info) {
-    if (!empty($info['translatable'])) {
-      $info['controllers'] += array('translation' => 'Drupal\content_translation\ContentTranslationController');
+    if ($info->isTranslatable()) {
+      if (!$info->hasController('translation')) {
+        $info->setController('translation', 'Drupal\content_translation\ContentTranslationController');
+      }
 
-      if (!isset($info['translation']['content_translation'])) {
-        $info['translation']['content_translation'] = array();
+      $translation = $info->get('translation');
+      if (!$translation || !isset($translation['content_translation'])) {
+        $translation['content_translation'] = array();
       }
 
-      if (!empty($info['links']['canonical'])) {
+      if ($info->hasLinkTemplate('canonical')) {
         // Provide default route names for the translation paths.
-        $info['links'] += array(
-          'drupal:content-translation-overview' => "content_translation.translation_overview_$entity_type",
-        );
-        $info['translation']['content_translation'] += array(
+        if (!$info->hasLinkTemplate('drupal:content-translation-overview')) {
+          $info->setLinkTemplate('drupal:content-translation-overview', "content_translation.translation_overview_$entity_type");
+        }
+        $translation['content_translation'] += array(
           'access_callback' => 'content_translation_translate_access',
         );
       }
+      $info->set('translation', $translation);
     }
   }
 }
@@ -163,13 +168,14 @@ function content_translation_menu() {
   $items = array();
 
   // Create tabs for all possible entity types.
-  foreach (entity_get_info() as $entity_type => $info) {
+  foreach (\Drupal::entityManager()->getDefinitions() as $entity_type => $info) {
     // Provide the translation UI only for enabled types.
     if (content_translation_enabled($entity_type)) {
-      $path = _content_translation_link_to_router_path($entity_type, $info['links']['canonical']);
+      $path = _content_translation_link_to_router_path($entity_type, $info->getLinkTemplate('canonical'));
       $entity_position = count(explode('/', $path)) - 1;
       $keys = array_flip(array('load_arguments'));
-      $menu_info = array_intersect_key($info['translation']['content_translation'], $keys) + array('file' => 'content_translation.pages.inc');
+      $translation = $info->get('translation');
+      $menu_info = array_intersect_key($translation['content_translation'], $keys) + array('file' => 'content_translation.pages.inc');
       $item = array();
 
       // Plugin annotations cannot contain spaces, thus we need to restore them
@@ -220,9 +226,9 @@ function content_translation_menu_alter(array &$items) {
   $items['admin/config/regional/content-language']['description'] = 'Configure language and translation support for content.';
 
   // Check that the declared menu base paths are actually valid.
-  foreach (entity_get_info() as $entity_type => $info) {
+  foreach (\Drupal::entityManager()->getDefinitions() as $entity_type => $info) {
     if (content_translation_enabled($entity_type)) {
-      $path = _content_translation_link_to_router_path($entity_type, $info['links']['canonical']);
+      $path = _content_translation_link_to_router_path($entity_type, $info->getLinkTemplate('canonical'));
 
       // If the base path is not defined we cannot provide the translation UI
       // for this entity type. In some cases the actual base path might not have
@@ -235,7 +241,7 @@ function content_translation_menu_alter(array &$items) {
           $items["$path/translations/add/%language/%language"],
           $items["$path/translations/delete/%language"]
         );
-        $t_args = array('@entity_type' => isset($info['label']) ? $info['label'] : $entity_type);
+        $t_args = array('@entity_type' => $info->getLabel() ?: $entity_type);
         watchdog('content translation', 'The entities of type @entity_type do not define a valid base path: it will not be possible to translate them.', $t_args, WATCHDOG_WARNING);
       }
       else {
@@ -327,7 +333,7 @@ function content_translation_view_access(EntityInterface $entity, $langcode, Acc
   $entity_type = $entity->entityType();
   $info = $entity->entityInfo();
   $permission = "translate $entity_type";
-  if (!empty($info['permission_granularity']) && $info['permission_granularity'] == 'bundle') {
+  if ($info->getPermissionGranularity() == 'bundle') {
     $permission = "translate {$entity->bundle()} $entity_type";
   }
   return !empty($entity->translation[$langcode]['status']) || user_access('translate any entity', $account) || user_access($permission, $account);
@@ -512,9 +518,10 @@ function content_translation_enabled($entity_type, $bundle = NULL) {
  * @todo Move to \Drupal\content_translation\ContentTranslationManager.
  */
 function content_translation_controller($entity_type) {
-  $entity_info = entity_get_info($entity_type);
+  $entity_info = \Drupal::entityManager()->getDefinition($entity_type);
   // @todo Throw an exception if the key is missing.
-  return new $entity_info['controllers']['translation']($entity_type, $entity_info);
+  $class = $entity_info->getController('translation');
+  return new $class($entity_info);
 }
 
 /**
@@ -579,11 +586,11 @@ function content_translation_permission() {
 
   // Create a translate permission for each enabled entity type and (optionally)
   // bundle.
-  foreach (entity_get_info() as $entity_type => $info) {
-    if (!empty($info['permission_granularity'])) {
-      $t_args = array('@entity_label' => drupal_strtolower(t($info['label'])));
+  foreach (\Drupal::entityManager()->getDefinitions() as $entity_type => $info) {
+    if ($permission_granularity = $info->getPermissionGranularity()) {
+      $t_args = array('@entity_label' => $info->getLowercaseLabel());
 
-      switch ($info['permission_granularity']) {
+      switch ($permission_granularity) {
         case 'bundle':
           foreach (entity_get_bundles($entity_type) as $bundle => $bundle_info) {
             if (content_translation_enabled($entity_type, $bundle)) {
@@ -773,7 +780,7 @@ function content_translation_entity_update(EntityInterface $entity) {
 function content_translation_field_extra_fields() {
   $extra = array();
 
-  foreach (entity_get_info() as $entity_type => $info) {
+  foreach (\Drupal::entityManager()->getDefinitions() as $entity_type => $info) {
     foreach (entity_get_bundles($entity_type) as $bundle => $bundle_info) {
       if (content_translation_enabled($entity_type, $bundle)) {
         $extra[$entity_type][$bundle]['form']['translation'] = array(
diff --git a/core/modules/content_translation/content_translation.pages.inc b/core/modules/content_translation/content_translation.pages.inc
index 2e5fa6b..390063d 100644
--- a/core/modules/content_translation/content_translation.pages.inc
+++ b/core/modules/content_translation/content_translation.pages.inc
@@ -191,13 +191,11 @@ function content_translation_add_page(EntityInterface $entity, Language $source
   $target = !empty($target) ? $target : language(Language::TYPE_CONTENT);
   // @todo Exploit the upcoming hook_entity_prepare() when available.
   content_translation_prepare_translation($entity, $source, $target);
-  $info = $entity->entityInfo();
-  $operation = isset($info['default_operation']) ? $info['default_operation'] : 'default';
   $form_state['langcode'] = $target->id;
   $form_state['content_translation']['source'] = $source;
   $form_state['content_translation']['target'] = $target;
   $form_state['content_translation']['translation_form'] = !$entity->access('update');
-  return \Drupal::entityManager()->getForm($entity, $operation, $form_state);
+  return \Drupal::entityManager()->getForm($entity, 'default', $form_state);
 }
 
 /**
@@ -216,11 +214,9 @@ function content_translation_add_page(EntityInterface $entity, Language $source
  */
 function content_translation_edit_page(EntityInterface $entity, Language $language = NULL) {
   $language = !empty($language) ? $language : language(Language::TYPE_CONTENT);
-  $info = $entity->entityInfo();
-  $operation = isset($info['default_operation']) ? $info['default_operation'] : 'default';
   $form_state['langcode'] = $language->id;
   $form_state['content_translation']['translation_form'] = TRUE;
-  return \Drupal::entityManager()->getForm($entity, $operation, $form_state);
+  return \Drupal::entityManager()->getForm($entity, 'default', $form_state);
 }
 
 /**
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Access/ContentTranslationManageAccessCheck.php b/core/modules/content_translation/lib/Drupal/content_translation/Access/ContentTranslationManageAccessCheck.php
index 16cc31a..2135486 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/Access/ContentTranslationManageAccessCheck.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/Access/ContentTranslationManageAccessCheck.php
@@ -52,7 +52,7 @@ public function access(Route $route, Request $request, AccountInterface $account
       $route_requirements = $route->getRequirements();
       $operation = $route_requirements['_access_content_translation_manage'];
       $controller_class = $this->entityManager->getControllerClass($entity_type, 'translation');
-      $controller = new $controller_class($entity_type, $entity->entityInfo());
+      $controller = new $controller_class($entity->entityInfo());
 
       // Load translation.
       $translations = $entity->getTranslationLanguages();
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Access/ContentTranslationOverviewAccess.php b/core/modules/content_translation/lib/Drupal/content_translation/Access/ContentTranslationOverviewAccess.php
index 5086e79..3620c1b 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/Access/ContentTranslationOverviewAccess.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/Access/ContentTranslationOverviewAccess.php
@@ -52,15 +52,16 @@ public function access(Route $route, Request $request, AccountInterface $account
       $bundle = $entity->bundle();
 
       // Get entity access callback.
-      $definitions = $this->entityManager->getDefinitions();
-      $access_callback = $definitions[$entity_type]['translation']['content_translation']['access_callback'];
+      $definition = $this->entityManager->getDefinition($entity_type);
+      $translation = $definition->get('translation');
+      $access_callback = $translation['content_translation']['access_callback'];
       if (call_user_func($access_callback, $entity)) {
         return static::ALLOW;
       }
 
       // Check per entity permission.
       $permission = "translate {$entity_type}";
-      if ($definitions[$entity_type]['permission_granularity'] == 'bundle') {
+      if ($definition->getPermissionGranularity() == 'bundle') {
         $permission = "translate {$bundle} {$entity_type}";
       }
       if ($account->hasPermission($permission)) {
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php b/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php
index 6d14c1b..17358de 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php
@@ -25,20 +25,18 @@ class ContentTranslationController implements ContentTranslationControllerInterf
   /**
    * The entity info of the entity being translated.
    *
-   * @var array
+   * @var \Drupal\Core\Entity\EntityTypeInterface
    */
   protected $entityInfo;
 
   /**
    * Initializes an instance of the content translation controller.
    *
-   * @param string $entity_type
-   *   The type of the entity being translated.
-   * @param array $entity_info
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
    *   The info array of the given entity type.
    */
-  public function __construct($entity_type, $entity_info) {
-    $this->entityType = $entity_type;
+  public function __construct($entity_info) {
+    $this->entityType = $entity_info->getId();
     $this->entityInfo = $entity_info;
   }
 
@@ -63,8 +61,8 @@ public function getTranslationAccess(EntityInterface $entity, $op) {
     $translate_permission = TRUE;
     // If no permission granularity is defined this entity type does not need an
     // explicit translate permission.
-    if (!user_access('translate any entity') && !empty($info['permission_granularity'])) {
-      $translate_permission = user_access($info['permission_granularity'] == 'bundle' ? "translate {$entity->bundle()} {$entity->entityType()}" : "translate {$entity->entityType()}");
+    if (!user_access('translate any entity') && $permission_granularity = $info->getPermissionGranularity()) {
+      $translate_permission = user_access($permission_granularity == 'bundle' ? "translate {$entity->bundle()} {$entity->entityType()}" : "translate {$entity->entityType()}");
     }
     return $translate_permission && user_access("$op content translations");
   }
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationManager.php b/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationManager.php
index 1f98147..b575310 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationManager.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationManager.php
@@ -36,7 +36,7 @@ public function __construct(EntityManagerInterface $manager) {
    */
   public function isSupported($entity_type) {
     $info = $this->entityManager->getDefinition($entity_type);
-    return !empty($info['translatable']) && !empty($info['links']['drupal:content-translation-overview']);
+    return $info->isTranslatable() && $info->hasLinkTemplate('drupal:content-translation-overview');
   }
 
   /**
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationManagerInterface.php b/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationManagerInterface.php
index 92ff0fd..d3aa162 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationManagerInterface.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationManagerInterface.php
@@ -15,7 +15,7 @@
   /**
    * Gets the entity types that support content translation.
    *
-   * @return array
+   * @return \Drupal\Core\Entity\EntityTypeInterface[]
    *   An array of entity types that support content translation.
    */
   public function getSupportedEntityTypes();
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Form/ContentTranslationDeleteForm.php b/core/modules/content_translation/lib/Drupal/content_translation/Form/ContentTranslationDeleteForm.php
index 0ddbfc6..5d33cdf 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/Form/ContentTranslationDeleteForm.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/Form/ContentTranslationDeleteForm.php
@@ -63,9 +63,8 @@ public function getQuestion() {
    * {@inheritdoc}
    */
   public function getCancelRoute() {
-    $entity_info = $this->entity->entityInfo();
     return array(
-      'route_name' => $entity_info['links']['drupal:content-translation-overview'],
+      'route_name' => $this->entity->entityInfo()->getLinkTemplate('drupal:content-translation-overview'),
       'route_parameters' => array(
         $this->entity->entityType() => $this->entity->id(),
       ),
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Plugin/Derivative/ContentTranslationContextualLinks.php b/core/modules/content_translation/lib/Drupal/content_translation/Plugin/Derivative/ContentTranslationContextualLinks.php
index 98956ab..6238799 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/Plugin/Derivative/ContentTranslationContextualLinks.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/Plugin/Derivative/ContentTranslationContextualLinks.php
@@ -8,7 +8,7 @@
 namespace Drupal\content_translation\Plugin\Derivative;
 
 use Drupal\Component\Plugin\Derivative\DerivativeBase;
-use Drupal\Core\Entity\EntityManager;
+use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Plugin\Discovery\ContainerDerivativeInterface;
 use Drupal\Core\Routing\RouteProviderInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -23,17 +23,17 @@ class ContentTranslationContextualLinks extends DerivativeBase implements Contai
   /**
    * The entity manager.
    *
-   * @var \Drupal\Core\Entity\EntityManager
+   * @var \Drupal\Core\Entity\EntityManagerInterface
    */
   protected $entityManager;
 
   /**
    * Constructs a new ContentTranslationContextualLinks.
    *
-   * @param \Drupal\Core\Entity\EntityManager $entity_manager
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager.
    */
-  public function __construct(EntityManager $entity_manager) {
+  public function __construct(EntityManagerInterface $entity_manager) {
     $this->entityManager = $entity_manager;
   }
 
@@ -52,9 +52,9 @@ public static function create(ContainerInterface $container, $base_plugin_id) {
   public function getDerivativeDefinitions(array $base_plugin_definition) {
     // Create contextual links for all possible entity types.
     foreach ($this->entityManager->getDefinitions() as $entity_type => $entity_info) {
-      if ($entity_info['translatable'] && isset($entity_info['translation'])) {
+      if ($entity_info->isTranslatable()) {
         $this->derivatives[$entity_type]['title'] = t('Translate');
-        $this->derivatives[$entity_type]['route_name'] = $entity_info['links']['drupal:content-translation-overview'];
+        $this->derivatives[$entity_type]['route_name'] = $entity_info->getLinkTemplate('drupal:content-translation-overview');
         $this->derivatives[$entity_type]['group'] = $entity_type;
       }
     }
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Plugin/Derivative/ContentTranslationLocalTasks.php b/core/modules/content_translation/lib/Drupal/content_translation/Plugin/Derivative/ContentTranslationLocalTasks.php
index e906fd9..fdc9b37 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/Plugin/Derivative/ContentTranslationLocalTasks.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/Plugin/Derivative/ContentTranslationLocalTasks.php
@@ -61,7 +61,7 @@ public function getDerivativeDefinitions(array $base_plugin_definition) {
     // Create tabs for all possible entity types.
     foreach ($this->contentTranslationManager->getSupportedEntityTypes() as $entity_type => $entity_info) {
       // Find the route name for the translation overview.
-      $translation_route_name = $entity_info['links']['drupal:content-translation-overview'];
+      $translation_route_name = $entity_info->getLinkTemplate('drupal:content-translation-overview');
 
       $this->derivatives[$translation_route_name] = array(
         'entity_type' => $entity_type,
@@ -78,10 +78,10 @@ public function getDerivativeDefinitions(array $base_plugin_definition) {
   public function alterLocalTasks(array &$local_tasks) {
     foreach ($this->contentTranslationManager->getSupportedEntityTypes() as $entity_info) {
       // Find the route name for the entity page.
-      $entity_route_name = $entity_info['links']['canonical'];
+      $entity_route_name = $entity_info->getLinkTemplate('canonical');
 
       // Find the route name for the translation overview.
-      $translation_route_name = $entity_info['links']['drupal:content-translation-overview'];
+      $translation_route_name = $entity_info->getLinkTemplate('drupal:content-translation-overview');
       $translation_tab = $this->basePluginId . ':' . $translation_route_name;
 
       $local_tasks[$translation_tab]['tab_root_id'] = $this->getPluginIdFromRoute($entity_route_name, $local_tasks);
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Routing/ContentTranslationRouteSubscriber.php b/core/modules/content_translation/lib/Drupal/content_translation/Routing/ContentTranslationRouteSubscriber.php
index 2390090..2b18a13 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/Routing/ContentTranslationRouteSubscriber.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/Routing/ContentTranslationRouteSubscriber.php
@@ -41,7 +41,7 @@ public function __construct(ContentTranslationManagerInterface $content_translat
   protected function alterRoutes(RouteCollection $collection, $provider) {
     foreach ($this->contentTranslationManager->getSupportedEntityTypes() as $entity_type => $entity_info) {
       // Try to get the route from the current collection.
-      if (!$entity_route = $collection->get($entity_info['links']['canonical'])) {
+      if (!$entity_route = $collection->get($entity_info->getLinkTemplate('canonical'))) {
         continue;
       }
       $path = $entity_route->getPath() . '/translations';
@@ -67,7 +67,7 @@ protected function alterRoutes(RouteCollection $collection, $provider) {
           ),
         )
       );
-      $collection->add($entity_info['links']['drupal:content-translation-overview'], $route);
+      $collection->add($entity_info->getLinkTemplate('drupal:content-translation-overview'), $route);
 
       $route = new Route(
         $path . '/add/{source}/{target}',
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationTestBase.php b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationTestBase.php
index d1485d2..6e18d0f 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationTestBase.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationTestBase.php
@@ -110,9 +110,9 @@ protected function getTranslatorPermissions() {
    * Returns the translate permissions for the current entity and bundle.
    */
   protected function getTranslatePermission() {
-    $info = entity_get_info($this->entityType);
-    if (!empty($info['permission_granularity'])) {
-      return $info['permission_granularity'] == 'bundle' ? "translate {$this->bundle} {$this->entityType}" : "translate {$this->entityType}";
+    $info = \Drupal::entityManager()->getDefinition($this->entityType);
+    if ($permission_granularity = $info->getPermissionGranularity()) {
+      return $permission_granularity == 'bundle' ? "translate {$this->bundle} {$this->entityType}" : "translate {$this->entityType}";
     }
   }
 
@@ -206,9 +206,9 @@ protected function setupTestFields() {
   protected function createEntity($values, $langcode, $bundle_name = NULL) {
     $entity_values = $values;
     $entity_values['langcode'] = $langcode;
-    $info = entity_get_info($this->entityType);
-    if (!empty($info['entity_keys']['bundle'])) {
-      $entity_values[$info['entity_keys']['bundle']] = $bundle_name ?: $this->bundle;
+    $info = \Drupal::entityManager()->getDefinition($this->entityType);
+    if ($bundle_key = $info->getKey('bundle')) {
+      $entity_values[$bundle_key] = $bundle_name ?: $this->bundle;
     }
     $controller = $this->container->get('entity.manager')->getStorageController($this->entityType);
     if (!($controller instanceof FieldableDatabaseStorageController)) {
diff --git a/core/modules/content_translation/tests/Drupal/content_translation/Tests/Menu/ContentTranslationLocalTasksTest.php b/core/modules/content_translation/tests/Drupal/content_translation/Tests/Menu/ContentTranslationLocalTasksTest.php
index e0b164b..d5b66d3 100644
--- a/core/modules/content_translation/tests/Drupal/content_translation/Tests/Menu/ContentTranslationLocalTasksTest.php
+++ b/core/modules/content_translation/tests/Drupal/content_translation/Tests/Menu/ContentTranslationLocalTasksTest.php
@@ -33,17 +33,18 @@ public function setUp() {
     );
     parent::setUp();
 
+    $entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
+    $entity_type->expects($this->any())
+      ->method('getLinkTemplate')
+      ->will($this->returnValueMap(array(
+        array('canonical', 'node.view'),
+        array('drupal:content-translation-overview', 'content_translation.translation_overview_node'),
+      )));
     $content_translation_manager = $this->getMock('Drupal\content_translation\ContentTranslationManagerInterface');
     $content_translation_manager->expects($this->any())
       ->method('getSupportedEntityTypes')
       ->will($this->returnValue(array(
-        'node' => array(
-          'translatable' => TRUE,
-          'links' => array(
-            'canonical' => 'node.view',
-            'drupal:content-translation-overview' => 'content_translation.translation_overview_node',
-          ),
-        ),
+        'node' => $entity_type,
       )));
     \Drupal::getContainer()->set('content_translation.manager', $content_translation_manager);
   }
diff --git a/core/modules/entity/entity.module b/core/modules/entity/entity.module
index e838be5..60b4c78 100644
--- a/core/modules/entity/entity.module
+++ b/core/modules/entity/entity.module
@@ -100,11 +100,11 @@ function entity_menu() {
  */
 function entity_entity_bundle_rename($entity_type, $bundle_old, $bundle_new) {
   // Rename entity displays.
-  $entity_info = entity_get_info('entity_display');
+  $entity_info = \Drupal::entityManager()->getDefinition('entity_display');
   if ($bundle_old !== $bundle_new) {
     $ids = config_get_storage_names_with_prefix('entity.display.' . $entity_type . '.' . $bundle_old);
     foreach ($ids as $id) {
-      $id = ConfigStorageController::getIDFromConfigName($id, $entity_info['config_prefix']);
+      $id = ConfigStorageController::getIDFromConfigName($id, $entity_info->getConfigPrefix());
       $display = entity_load('entity_display', $id);
       $new_id = $entity_type . '.' . $bundle_new . '.' . $display->mode;
       $display->id = $new_id;
@@ -114,11 +114,11 @@ function entity_entity_bundle_rename($entity_type, $bundle_old, $bundle_new) {
   }
 
   // Rename entity form displays.
-  $entity_info = entity_get_info('entity_form_display');
+  $entity_info = \Drupal::entityManager()->getDefinition('entity_form_display');
   if ($bundle_old !== $bundle_new) {
     $ids = config_get_storage_names_with_prefix('entity.form_display.' . $entity_type . '.' . $bundle_old);
     foreach ($ids as $id) {
-      $id = ConfigStorageController::getIDFromConfigName($id, $entity_info['config_prefix']);
+      $id = ConfigStorageController::getIDFromConfigName($id, $entity_info->getConfigPrefix());
       $form_display = entity_load('entity_form_display', $id);
       $new_id = $entity_type . '.' . $bundle_new . '.' . $form_display->mode;
       $form_display->id = $new_id;
@@ -133,18 +133,18 @@ function entity_entity_bundle_rename($entity_type, $bundle_old, $bundle_new) {
  */
 function entity_entity_bundle_delete($entity_type, $bundle) {
   // Remove entity displays of the deleted bundle.
-  $entity_info = entity_get_info('entity_display');
+  $entity_info = \Drupal::entityManager()->getDefinition('entity_display');
   $ids = config_get_storage_names_with_prefix('entity.display.' . $entity_type . '.' . $bundle);
   foreach ($ids as &$id) {
-    $id = ConfigStorageController::getIDFromConfigName($id, $entity_info['config_prefix']);
+    $id = ConfigStorageController::getIDFromConfigName($id, $entity_info->getConfigPrefix());
   }
   entity_delete_multiple('entity_display', $ids);
 
   // Remove entity form displays of the deleted bundle.
-  $entity_info = entity_get_info('entity_form_display');
+  $entity_info = \Drupal::entityManager()->getDefinition('entity_form_display');
   $ids = config_get_storage_names_with_prefix('entity.form_display.' . $entity_type . '.' . $bundle);
   foreach ($ids as &$id) {
-    $id = ConfigStorageController::getIDFromConfigName($id, $entity_info['config_prefix']);
+    $id = ConfigStorageController::getIDFromConfigName($id, $entity_info->getConfigPrefix());
   }
   entity_delete_multiple('entity_form_display', $ids);
 }
@@ -156,7 +156,7 @@ function entity_module_preuninstall($module) {
   // Clean up all entity bundles (including field instances) of every entity
   // type provided by the module that is being uninstalled.
   foreach (\Drupal::entityManager()->getDefinitions() as $entity_type => $entity_info) {
-    if ($entity_info['provider'] == $module) {
+    if ($entity_info->getProvider() == $module) {
       foreach (array_keys(entity_get_bundles($entity_type)) as $bundle) {
         entity_invoke_bundle_hook('delete', $entity_type, $bundle);
       }
diff --git a/core/modules/entity/lib/Drupal/entity/Controller/EntityDisplayModeController.php b/core/modules/entity/lib/Drupal/entity/Controller/EntityDisplayModeController.php
index eb0e641..e0f1d0f 100644
--- a/core/modules/entity/lib/Drupal/entity/Controller/EntityDisplayModeController.php
+++ b/core/modules/entity/lib/Drupal/entity/Controller/EntityDisplayModeController.php
@@ -7,8 +7,8 @@
 
 namespace Drupal\entity\Controller;
 
-use Drupal\Component\Plugin\PluginManagerInterface;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Entity\EntityManagerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -19,17 +19,17 @@ class EntityDisplayModeController implements ContainerInjectionInterface {
   /**
    * The entity manager.
    *
-   * @var \Drupal\Component\Plugin\PluginManagerInterface
+   * @var \Drupal\Core\Entity\EntityManagerInterface
    */
   protected $entityManager;
 
   /**
    * Constructs a new EntityDisplayModeFormBase.
    *
-   * @param \Drupal\Component\Plugin\PluginManagerInterface $entity_manager
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager.
    */
-  public function __construct(PluginManagerInterface $entity_manager) {
+  public function __construct(EntityManagerInterface $entity_manager) {
     $this->entityManager = $entity_manager;
   }
 
@@ -51,9 +51,9 @@ public static function create(ContainerInterface $container) {
   public function viewModeTypeSelection() {
     $entity_types = array();
     foreach ($this->entityManager->getDefinitions() as $entity_type => $entity_info) {
-      if ($entity_info['fieldable'] && isset($entity_info['controllers']['view_builder'])) {
+      if ($entity_info->isFieldable() && $entity_info->hasController('view_builder')) {
         $entity_types[$entity_type] = array(
-          'title' => $entity_info['label'],
+          'title' => $entity_info->getLabel(),
           'href' => 'admin/structure/display-modes/view/add/' . $entity_type,
           'localized_options' => array(),
         );
@@ -74,9 +74,9 @@ public function viewModeTypeSelection() {
   public function formModeTypeSelection() {
     $entity_types = array();
     foreach ($this->entityManager->getDefinitions() as $entity_type => $entity_info) {
-      if ($entity_info['fieldable'] && isset($entity_info['controllers']['form'])) {
+      if ($entity_info->isFieldable() && $entity_info->hasController('form')) {
         $entity_types[$entity_type] = array(
-          'title' => $entity_info['label'],
+          'title' => $entity_info->getLabel(),
           'href' => 'admin/structure/display-modes/form/add/' . $entity_type,
           'localized_options' => array(),
         );
diff --git a/core/modules/entity/lib/Drupal/entity/EntityDisplayModeListController.php b/core/modules/entity/lib/Drupal/entity/EntityDisplayModeListController.php
index 835c1c6..9f20734 100644
--- a/core/modules/entity/lib/Drupal/entity/EntityDisplayModeListController.php
+++ b/core/modules/entity/lib/Drupal/entity/EntityDisplayModeListController.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Config\Entity\ConfigEntityListController;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -21,26 +22,24 @@ class EntityDisplayModeListController extends ConfigEntityListController {
   /**
    * The entity info for all entity types.
    *
-   * @var array
+   * @var \Drupal\Core\Entity\EntityTypeInterface[]
    */
   protected $entityInfoComplete;
 
   /**
    * Constructs a new EntityListController object.
    *
-   * @param string $entity_type
-   *   The type of entity to be listed.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    * @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage
    *   The entity storage controller class.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    *   The module handler to invoke hooks on.
-   * @param array $entity_info_complete
+   * @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_info_complete
    *   The entity info for all entity types.
    */
-  public function __construct($entity_type, array $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler, array $entity_info_complete) {
-    parent::__construct($entity_type, $entity_info, $storage, $module_handler);
+  public function __construct(EntityTypeInterface $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler, array $entity_info_complete) {
+    parent::__construct($entity_info, $storage, $module_handler);
 
     $this->entityInfoComplete = $entity_info_complete;
   }
@@ -48,12 +47,11 @@ public function __construct($entity_type, array $entity_info, EntityStorageContr
   /**
    * {@inheritdoc}
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
     $entity_manager = $container->get('entity.manager');
     return new static(
-      $entity_type,
       $entity_info,
-      $entity_manager->getStorageController($entity_type),
+      $entity_manager->getStorageController($entity_info->getId()),
       $container->get('module_handler'),
       $entity_manager->getDefinitions()
     );
@@ -97,12 +95,12 @@ public function render() {
       }
 
       // Filter entities
-      if ($this->entityInfoComplete[$entity_type]['fieldable'] && !$this->isValidEntity($entity_type)) {
+      if ($this->entityInfoComplete[$entity_type]->isFieldable() && !$this->isValidEntity($entity_type)) {
         continue;
       }
 
       $table = array(
-        '#prefix' => '<h2>' . $this->entityInfoComplete[$entity_type]['label'] . '</h2>',
+        '#prefix' => '<h2>' . $this->entityInfoComplete[$entity_type]->getLabel() . '</h2>',
         '#type' => 'table',
         '#header' => $this->buildHeader(),
         '#rows' => array(),
@@ -123,7 +121,7 @@ public function render() {
         'data' => array(
           '#type' => 'link',
           '#href' => "admin/structure/display-modes/$short_type/add/$entity_type",
-          '#title' => t('Add new %label @entity-type', array('%label' => $this->entityInfoComplete[$entity_type]['label'], '@entity-type' => strtolower($this->entityInfo['label']))),
+          '#title' => t('Add new %label @entity-type', array('%label' => $this->entityInfoComplete[$entity_type]->getLabel(), '@entity-type' => $this->entityInfo->getLowercaseLabel())),
           '#options' => array(
             'html' => TRUE,
           ),
@@ -146,7 +144,7 @@ public function render() {
    *   doesn't has the correct controller.
    */
   protected function isValidEntity($entity_type) {
-    return isset($this->entityInfoComplete[$entity_type]['controllers']['view_builder']);
+    return $this->entityInfoComplete[$entity_type]->hasController('view_builder');
   }
 
 }
diff --git a/core/modules/entity/lib/Drupal/entity/EntityFormModeListController.php b/core/modules/entity/lib/Drupal/entity/EntityFormModeListController.php
index 79417ff..0ae68f1 100644
--- a/core/modules/entity/lib/Drupal/entity/EntityFormModeListController.php
+++ b/core/modules/entity/lib/Drupal/entity/EntityFormModeListController.php
@@ -23,7 +23,7 @@ class EntityFormModeListController extends EntityDisplayModeListController {
    *   doesn't has the correct controller.
    */
   protected function isValidEntity($entity_type) {
-    return isset($this->entityInfoComplete[$entity_type]['controllers']['form']);
+    return $this->entityInfoComplete[$entity_type]->hasController('form');
   }
 
 }
diff --git a/core/modules/entity/lib/Drupal/entity/Form/EntityDisplayModeAddForm.php b/core/modules/entity/lib/Drupal/entity/Form/EntityDisplayModeAddForm.php
index 2088273..0d27cec 100644
--- a/core/modules/entity/lib/Drupal/entity/Form/EntityDisplayModeAddForm.php
+++ b/core/modules/entity/lib/Drupal/entity/Form/EntityDisplayModeAddForm.php
@@ -26,7 +26,7 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL)
     $this->entityType = $entity_type;
     $form = parent::buildForm($form, $form_state);
     $definition = $this->entityManager->getDefinition($this->entityType);
-    $form['#title'] = $this->t('Add new %label @entity-type', array('%label' => $definition['label'], '@entity-type' => strtolower($this->entityInfo['label'])));
+    $form['#title'] = $this->t('Add new %label @entity-type', array('%label' => $definition->getLabel(), '@entity-type' => $this->entityInfo->getLowercaseLabel()));
     return $form;
   }
 
@@ -44,7 +44,7 @@ public function validate(array $form, array &$form_state) {
    */
   protected function prepareEntity() {
     $definition = $this->entityManager->getDefinition($this->entityType);
-    if (!$definition['fieldable'] || !isset($definition['controllers']['view_builder'])) {
+    if (!$definition->isFieldable() || !$definition->hasController('view_builder')) {
       throw new NotFoundHttpException();
     }
 
diff --git a/core/modules/entity/lib/Drupal/entity/Form/EntityDisplayModeDeleteForm.php b/core/modules/entity/lib/Drupal/entity/Form/EntityDisplayModeDeleteForm.php
index beee9d7..d1bdd51 100644
--- a/core/modules/entity/lib/Drupal/entity/Form/EntityDisplayModeDeleteForm.php
+++ b/core/modules/entity/lib/Drupal/entity/Form/EntityDisplayModeDeleteForm.php
@@ -28,7 +28,7 @@ public function getCancelRoute() {
    */
   public function getQuestion() {
     $entity_info = $this->entity->entityInfo();
-    return t('Are you sure you want to delete the %label @entity-type?', array('%label' => $this->entity->label(), '@entity-type' => strtolower($entity_info['label'])));
+    return t('Are you sure you want to delete the %label @entity-type?', array('%label' => $this->entity->label(), '@entity-type' => $entity_info->getLowercaseLabel()));
   }
 
   /**
@@ -36,7 +36,7 @@ public function getQuestion() {
    */
   public function getDescription() {
     $entity_info = $this->entity->entityInfo();
-    return t('Deleting a @entity-type will cause any output still requesting to use that @entity-type to use the default display settings.', array('@entity-type' => strtolower($entity_info['label'])));
+    return t('Deleting a @entity-type will cause any output still requesting to use that @entity-type to use the default display settings.', array('@entity-type' => $entity_info->getLowercaseLabel()));
   }
 
   /**
@@ -53,7 +53,7 @@ public function submit(array $form, array &$form_state) {
     parent::submit($form, $form_state);
 
     $entity_info = $this->entity->entityInfo();
-    drupal_set_message(t('Deleted the %label @entity-type.', array('%label' => $this->entity->label(), '@entity-type' => strtolower($entity_info['label']))));
+    drupal_set_message(t('Deleted the %label @entity-type.', array('%label' => $this->entity->label(), '@entity-type' => $entity_info->getLowercaseLabel())));
     $this->entity->delete();
     entity_info_cache_clear();
     $form_state['redirect_route']['route_name'] = 'entity.' . $this->entity->entityType() . '_list';
diff --git a/core/modules/entity/lib/Drupal/entity/Form/EntityDisplayModeFormBase.php b/core/modules/entity/lib/Drupal/entity/Form/EntityDisplayModeFormBase.php
index dc96922..2f41ab5 100644
--- a/core/modules/entity/lib/Drupal/entity/Form/EntityDisplayModeFormBase.php
+++ b/core/modules/entity/lib/Drupal/entity/Form/EntityDisplayModeFormBase.php
@@ -27,7 +27,7 @@
   /**
    * The entity type definition.
    *
-   * @var array
+   * @var \Drupal\Core\Entity\EntityTypeInterface
    */
   protected $entityInfo;
 
@@ -119,7 +119,7 @@ public function exists($entity_id, array $element, array $form_state) {
    * {@inheritdoc}
    */
   public function save(array $form, array &$form_state) {
-    drupal_set_message(t('Saved the %label @entity-type.', array('%label' => $this->entity->label(), '@entity-type' => strtolower($this->entityInfo['label']))));
+    drupal_set_message(t('Saved the %label @entity-type.', array('%label' => $this->entity->label(), '@entity-type' => $this->entityInfo->getLowercaseLabel())));
     $this->entity->save();
     entity_info_cache_clear();
     $form_state['redirect_route']['route_name'] = 'entity.' . $this->entity->entityType() . '_list';
diff --git a/core/modules/entity/lib/Drupal/entity/Form/EntityFormModeAddForm.php b/core/modules/entity/lib/Drupal/entity/Form/EntityFormModeAddForm.php
index 84432e4..ce27c0f 100644
--- a/core/modules/entity/lib/Drupal/entity/Form/EntityFormModeAddForm.php
+++ b/core/modules/entity/lib/Drupal/entity/Form/EntityFormModeAddForm.php
@@ -19,7 +19,7 @@ class EntityFormModeAddForm extends EntityDisplayModeAddForm {
    */
   protected function prepareEntity() {
     $definition = $this->entityManager->getDefinition($this->entityType);
-    if (!$definition['fieldable'] || !isset($definition['controllers']['form'])) {
+    if (!$definition->isFieldable() || !$definition->hasController('form')) {
       throw new NotFoundHttpException();
     }
 
diff --git a/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php
index 5fe5f89..bc4e144 100644
--- a/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php
+++ b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php
@@ -14,7 +14,7 @@
  */
 class EntityDisplayTest extends DrupalUnitTestBase {
 
-  public static $modules = array('entity', 'field', 'entity_test');
+  public static $modules = array('entity', 'field', 'entity_test', 'user');
 
   public static function getInfo() {
     return array(
diff --git a/core/modules/entity/lib/Drupal/entity/Tests/EntityFormDisplayTest.php b/core/modules/entity/lib/Drupal/entity/Tests/EntityFormDisplayTest.php
index a364b3f..5ee2a89 100644
--- a/core/modules/entity/lib/Drupal/entity/Tests/EntityFormDisplayTest.php
+++ b/core/modules/entity/lib/Drupal/entity/Tests/EntityFormDisplayTest.php
@@ -14,7 +14,7 @@
  */
 class EntityFormDisplayTest extends DrupalUnitTestBase {
 
-  public static $modules = array('entity', 'field', 'entity_test');
+  public static $modules = array('entity', 'field', 'entity_test', 'user');
 
   public static function getInfo() {
     return array(
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/ConfigurableEntityReferenceItem.php b/core/modules/entity_reference/lib/Drupal/entity_reference/ConfigurableEntityReferenceItem.php
index 533610d..0f708c3 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/ConfigurableEntityReferenceItem.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/ConfigurableEntityReferenceItem.php
@@ -53,7 +53,7 @@ public function getPropertyDefinitions() {
       // Only add the revision ID property if the target entity type supports
       // revisions.
       $target_type_info = \Drupal::entityManager()->getDefinition($target_type);
-      if (!empty($target_type_info['entity_keys']['revision']) && !empty($target_type_info['revision_table'])) {
+      if ($target_type_info->hasKey('revision') && $target_type_info->getRevisionTable()) {
         static::$propertyDefinitions[$key]['revision_id'] = DataDefinition::create('integer')
           ->setLabel(t('Revision ID'))
           ->setConstraints(array('Range' => array('min' => 0)));
@@ -70,7 +70,7 @@ public static function schema(FieldInterface $field) {
     $target_type = $field->getSetting('target_type');
     $target_type_info = \Drupal::entityManager()->getDefinition($target_type);
 
-    if (is_subclass_of($target_type_info['class'], '\Drupal\Core\Entity\ContentEntityInterface')) {
+    if ($target_type_info->isSubclassOf('\Drupal\Core\Entity\ContentEntityInterface')) {
       $columns = array(
         'target_id' => array(
           'description' => 'The ID of the target entity.',
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/Derivative/SelectionBase.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/Derivative/SelectionBase.php
index a1d7991..3e41599 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/Derivative/SelectionBase.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/Derivative/SelectionBase.php
@@ -24,10 +24,10 @@ public function getDerivativeDefinitions(array $base_plugin_definition) {
       'taxonomy_term',
       'user'
     );
-    foreach (entity_get_info() as $entity_type => $info) {
+    foreach (\Drupal::entityManager()->getDefinitions() as $entity_type => $info) {
       if (!in_array($entity_type, $supported_entities)) {
         $this->derivatives[$entity_type] = $base_plugin_definition;
-        $this->derivatives[$entity_type]['label'] = t('@entity_type selection', array('@entity_type' => $info['label']));
+        $this->derivatives[$entity_type]['label'] = t('@entity_type selection', array('@entity_type' => $info->getLabel()));
       }
     }
     return parent::getDerivativeDefinitions($base_plugin_definition);
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/Field/FieldWidget/AutocompleteWidgetBase.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/Field/FieldWidget/AutocompleteWidgetBase.php
index 301ee9a..4621c5c 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/Field/FieldWidget/AutocompleteWidgetBase.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/Field/FieldWidget/AutocompleteWidgetBase.php
@@ -169,8 +169,8 @@ protected function createNewEntity($label, $uid) {
     }
 
     $entity_info = $entity_manager->getDefinition($target_type);
-    $bundle_key = $entity_info['entity_keys']['bundle'];
-    $label_key = $entity_info['entity_keys']['label'];
+    $bundle_key = $entity_info->getKey('bundle');
+    $label_key = $entity_info->getKey('label');
 
     return $entity_manager->getStorageController($target_type)->create(array(
       $label_key => $label,
@@ -201,7 +201,7 @@ protected function getSelectionHandlerSetting($setting_name) {
   protected function isContentReferenced() {
     $target_type = $this->getFieldSetting('target_type');
     $target_type_info = \Drupal::entityManager()->getDefinition($target_type);
-    return is_subclass_of($target_type_info['class'], '\Drupal\Core\Entity\ContentEntityInterface');
+    return $target_type_info->isSubclassOf('\Drupal\Core\Entity\ContentEntityInterface');
   }
 
 }
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/entity_reference/selection/SelectionBase.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/entity_reference/selection/SelectionBase.php
index abecaa3..0986c17 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/entity_reference/selection/SelectionBase.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/entity_reference/selection/SelectionBase.php
@@ -67,7 +67,7 @@ public static function settingsForm(FieldDefinitionInterface $field_definition)
       'auto_create' => FALSE,
     );
 
-    if (!empty($entity_info['entity_keys']['bundle'])) {
+    if ($entity_info->hasKey('bundle')) {
       $bundle_options = array();
       foreach ($bundles as $bundle_name => $bundle_info) {
         $bundle_options[$bundle_name] = $bundle_info['label'];
@@ -101,10 +101,10 @@ public static function settingsForm(FieldDefinitionInterface $field_definition)
     }
 
     $target_type_info = \Drupal::entityManager()->getDefinition($target_type);
-    if (is_subclass_of($target_type_info['class'], '\Drupal\Core\Entity\ContentEntityInterface')) {
+    if ($target_type_info->isSubclassOf('\Drupal\Core\Entity\ContentEntityInterface')) {
       // @todo Use Entity::getPropertyDefinitions() when all entity types are
       // converted to the new Field API.
-      $fields = drupal_map_assoc(drupal_schema_fields_sql($entity_info['base_table']));
+      $fields = drupal_map_assoc(drupal_schema_fields_sql($entity_info->getBaseTable()));
       foreach (field_info_instances($target_type) as $bundle_instances) {
         foreach ($bundle_instances as $instance_name => $instance) {
           foreach ($instance->getField()->getColumns() as $column_name => $column_info) {
@@ -197,10 +197,10 @@ public function validateReferenceableEntities(array $ids) {
     $result = array();
     if ($ids) {
       $target_type = $this->fieldDefinition->getSetting('target_type');
-      $entity_info = entity_get_info($target_type);
+      $entity_info = \Drupal::entityManager()->getDefinition($target_type);
       $query = $this->buildEntityQuery();
       $result = $query
-        ->condition($entity_info['entity_keys']['id'], $ids, 'IN')
+        ->condition($entity_info->getKey('id'), $ids, 'IN')
         ->execute();
     }
 
@@ -258,15 +258,15 @@ public function validateAutocompleteInput($input, &$element, &$form_state, $form
   public function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') {
     $target_type = $this->fieldDefinition->getSetting('target_type');
     $handler_settings = $this->fieldDefinition->getSetting('handler_settings');
-    $entity_info = entity_get_info($target_type);
+    $entity_info = \Drupal::entityManager()->getDefinition($target_type);
 
     $query = \Drupal::entityQuery($target_type);
     if (!empty($handler_settings['target_bundles'])) {
-      $query->condition($entity_info['entity_keys']['bundle'], $handler_settings['target_bundles'], 'IN');
+      $query->condition($entity_info->getKey('bundle'), $handler_settings['target_bundles'], 'IN');
     }
 
-    if (isset($match) && isset($entity_info['entity_keys']['label'])) {
-      $query->condition($entity_info['entity_keys']['label'], $match, $match_operator);
+    if (isset($match) && $label_key = $entity_info->getKey('label')) {
+      $query->condition($label_key, $match, $match_operator);
     }
 
     // Add entity-access tag.
diff --git a/core/modules/field/field.attach.inc b/core/modules/field/field.attach.inc
index 50b3cb2..75ee6a2 100644
--- a/core/modules/field/field.attach.inc
+++ b/core/modules/field/field.attach.inc
@@ -36,9 +36,11 @@
  *
  * The Field API reads the bundle name for a given entity from a particular
  * property of the entity object, and hook_entity_info_alter() defines which
- * property to use. For instance, node_entity_info_alter() specifies:
+ * property to use. For instance, \Drupal\node\Entity\Node specifies:
  * @code
- *   $info['entity_keys']['bundle'] = 'type'
+ *   entity_keys = {
+ *     "bundle" = "type"
+ *   }
  * @endcode
  * This indicates that for a particular node object, the bundle name can be
  * found in $node->type. This property can be omitted if the entity type only
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index 8b80c56..b2e8c76 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -488,9 +488,9 @@ function field_extract_bundle($entity_type, $bundle) {
     return $bundle;
   }
 
-  $info = entity_get_info($entity_type);
-  if (is_object($bundle) && isset($info['bundle_keys']['bundle']) && isset($bundle->{$info['bundle_keys']['bundle']})) {
-    return $bundle->{$info['bundle_keys']['bundle']};
+  $info = \Drupal::entityManager()->getDefinition($entity_type);
+  if (is_object($bundle) && ($bundle_key = $info->getBundleKey('bundle')) && isset($bundle->{$bundle_key})) {
+    return $bundle->{$bundle_key};
   }
 }
 
@@ -700,15 +700,15 @@ function theme_field($variables) {
  */
 function _field_create_entity_from_ids($ids) {
   $id_properties = array();
-  $info = entity_get_info($ids->entity_type);
-  if (isset($info['entity_keys']['id'])) {
-    $id_properties[$info['entity_keys']['id']] = $ids->entity_id;
+  $info = \Drupal::entityManager()->getDefinition($ids->entity_type);
+  if ($id_key = $info->getKey('id')) {
+    $id_properties[$id_key] = $ids->entity_id;
   }
-  if (!empty($info['entity_keys']['revision']) && isset($ids->revision_id)) {
-    $id_properties[$info['entity_keys']['revision']] = $ids->revision_id;
+  if (isset($ids->revision_id) && $revision_key = $info->getKey('revision')) {
+    $id_properties[$revision_key] = $ids->revision_id;
   }
-  if (!empty($info['entity_keys']['bundle']) && isset($ids->bundle)) {
-    $id_properties[$info['entity_keys']['bundle']] = $ids->bundle;
+  if (isset($ids->bundle) && $bundle_key = $info->getKey('bundle')) {
+    $id_properties[$bundle_key] = $ids->bundle;
   }
   return entity_create($ids->entity_type, $id_properties);
 }
diff --git a/core/modules/field/field.purge.inc b/core/modules/field/field.purge.inc
index 7e1e90a..95af3a7 100644
--- a/core/modules/field/field.purge.inc
+++ b/core/modules/field/field.purge.inc
@@ -74,7 +74,7 @@ function field_purge_batch($batch_size) {
   // because that function does not return deleted instances.
   $instances = entity_load_multiple_by_properties('field_instance', array('deleted' => TRUE, 'include_deleted' => TRUE));
   $factory = \Drupal::service('entity.query');
-  $info = entity_get_info();
+  $info = \Drupal::entityManager()->getDefinitions();
   foreach ($instances as $instance) {
     $entity_type = $instance->entity_type;
 
@@ -100,8 +100,8 @@ function field_purge_batch($batch_size) {
       ->condition('id:' . $instance->getField()->uuid() . '.deleted', 1)
       ->range(0, $batch_size);
     // If there's no bundle key, all results will have the same bundle.
-    if (!empty($info[$entity_type]['entity_keys']['bundle'])) {
-      $query->condition($info[$entity_type]['entity_keys']['bundle'], $ids->bundle);
+    if ($bundle_key = $info[$entity_type]->getKey('bundle')) {
+      $query->condition($bundle_key, $ids->bundle);
     }
     $results = $query->execute();
     if ($results) {
diff --git a/core/modules/field/field.views.inc b/core/modules/field/field.views.inc
index 7def00e..e9e65c4 100644
--- a/core/modules/field/field.views.inc
+++ b/core/modules/field/field.views.inc
@@ -132,14 +132,13 @@ function field_views_field_default_views_data(FieldInterface $field) {
   $entity_manager = \Drupal::entityManager();
   $entity_type = $field->entity_type;
   $entity_info = $entity_manager->getDefinition($entity_type);
-  if (!isset($entity_info['base_table'])) {
+  if (!$entity_table = $entity_info->getBaseTable()) {
     return $data;
   }
-  $entity_table = $entity_info['base_table'];
   $entity_tables = array($entity_table => $entity_type);
-  $supports_revisions = !empty($entity_info['entity_keys']['revision']) && !empty($entity_info['revision_table']);
+  $entity_revision_table = $entity_info->getRevisionTable();
+  $supports_revisions = $entity_info->hasKey('revision') && $entity_revision_table;
   if ($supports_revisions) {
-    $entity_revision_table = $entity_info['revision_table'];
     $entity_tables[$entity_revision_table] = $entity_type;
   }
 
@@ -160,7 +159,7 @@ function field_views_field_default_views_data(FieldInterface $field) {
   // Build the relationships between the field table and the entity tables.
   $table_alias = $field_tables[EntityStorageControllerInterface::FIELD_LOAD_CURRENT]['alias'];
   $data[$table_alias]['table']['join'][$entity_table] = array(
-    'left_field' => $entity_info['entity_keys']['id'],
+    'left_field' => $entity_info->getKey('id'),
     'field' => 'entity_id',
     'extra' => array(
       array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
@@ -169,7 +168,7 @@ function field_views_field_default_views_data(FieldInterface $field) {
   if ($supports_revisions) {
     $table_alias = $field_tables[EntityStorageControllerInterface::FIELD_LOAD_REVISION]['alias'];
     $data[$table_alias]['table']['join'][$entity_revision_table] = array(
-      'left_field' => $entity_info['entity_keys']['revision'],
+      'left_field' => $entity_info->getKey('revision'),
       'field' => 'revision_id',
       'extra' => array(
         array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
@@ -177,7 +176,7 @@ function field_views_field_default_views_data(FieldInterface $field) {
     );
   }
 
-  $group_name = $entity_info['label'];
+  $group_name = $entity_info->getLabel();
   // Get the list of bundles the field appears in.
   $bundles_names = $field->getBundles();
   // Build the list of additional fields to add to queries.
diff --git a/core/modules/field/lib/Drupal/field/Entity/Field.php b/core/modules/field/lib/Drupal/field/Entity/Field.php
index e1f89e2..098d83f 100644
--- a/core/modules/field/lib/Drupal/field/Entity/Field.php
+++ b/core/modules/field/lib/Drupal/field/Entity/Field.php
@@ -326,7 +326,7 @@ protected function preSaveNew(EntityStorageControllerInterface $storage_controll
     // collisions with existing entity properties, but some is better than
     // none.
     foreach ($entity_manager->getDefinitions() as $type => $info) {
-      if (in_array($this->name, $info['entity_keys'])) {
+      if (in_array($this->name, $info->getKeys())) {
         throw new FieldException(format_string('Attempt to create field %name which is reserved by entity type %type.', array('%name' => $this->name, '%type' => $type)));
       }
     }
@@ -654,7 +654,7 @@ public function hasData() {
       $factory = \Drupal::service('entity.query');
       // Entity Query throws an exception if there is no base table.
       $entity_info = \Drupal::entityManager()->getDefinition($this->entity_type);
-      if (!isset($entity_info['base_table'])) {
+      if (!$entity_info->getBaseTable()) {
         return FALSE;
       }
       $query = $factory->get($this->entity_type);
diff --git a/core/modules/field/lib/Drupal/field/FieldInfo.php b/core/modules/field/lib/Drupal/field/FieldInfo.php
index 482b040..4df7ab8 100644
--- a/core/modules/field/lib/Drupal/field/FieldInfo.php
+++ b/core/modules/field/lib/Drupal/field/FieldInfo.php
@@ -437,7 +437,7 @@ public function getBundleInstances($entity_type, $bundle) {
     $fields = array();
 
     // Do not return anything for unknown entity types.
-    if (entity_get_info($entity_type) && !empty($field_map[$entity_type])) {
+    if (\Drupal::entityManager()->getDefinition($entity_type) && !empty($field_map[$entity_type])) {
 
       // Collect names of fields and instances involved in the bundle, using the
       // field map. The field map is already filtered to non-deleted fields and
diff --git a/core/modules/field/lib/Drupal/field/FieldInstanceStorageController.php b/core/modules/field/lib/Drupal/field/FieldInstanceStorageController.php
index 3b3e397..ef25a51 100644
--- a/core/modules/field/lib/Drupal/field/FieldInstanceStorageController.php
+++ b/core/modules/field/lib/Drupal/field/FieldInstanceStorageController.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Config\Config;
 use Drupal\Core\Config\Entity\ConfigStorageController;
 use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Entity\Query\QueryFactory;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Drupal\Core\Config\ConfigFactory;
@@ -52,10 +53,8 @@ class FieldInstanceStorageController extends ConfigStorageController {
   /**
    * Constructs a FieldInstanceStorageController object.
    *
-   * @param string $entity_type
-   *   The entity type for which the instance is created.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    * @param \Drupal\Core\Config\ConfigFactory $config_factory
    *   The config factory service.
    * @param \Drupal\Core\Config\StorageInterface $config_storage
@@ -71,8 +70,8 @@ class FieldInstanceStorageController extends ConfigStorageController {
    * @param \Drupal\Core\KeyValueStore\StateInterface $state
    *   The state key value store.
    */
-  public function __construct($entity_type, array $entity_info, ConfigFactory $config_factory, StorageInterface $config_storage, QueryFactory $entity_query_factory, UuidInterface $uuid_service, EntityManagerInterface $entity_manager, ModuleHandler $module_handler, StateInterface $state) {
-    parent::__construct($entity_type, $entity_info, $config_factory, $config_storage, $entity_query_factory, $uuid_service);
+  public function __construct(EntityTypeInterface $entity_info, ConfigFactory $config_factory, StorageInterface $config_storage, QueryFactory $entity_query_factory, UuidInterface $uuid_service, EntityManagerInterface $entity_manager, ModuleHandler $module_handler, StateInterface $state) {
+    parent::__construct($entity_info, $config_factory, $config_storage, $entity_query_factory, $uuid_service);
     $this->entityManager = $entity_manager;
     $this->moduleHandler = $module_handler;
     $this->state = $state;
@@ -81,9 +80,8 @@ public function __construct($entity_type, array $entity_info, ConfigFactory $con
   /**
    * {@inheritdoc}
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
     return new static(
-      $entity_type,
       $entity_info,
       $container->get('config.factory'),
       $container->get('config.storage'),
diff --git a/core/modules/field/lib/Drupal/field/FieldStorageController.php b/core/modules/field/lib/Drupal/field/FieldStorageController.php
index eac89c2..a1f9716 100644
--- a/core/modules/field/lib/Drupal/field/FieldStorageController.php
+++ b/core/modules/field/lib/Drupal/field/FieldStorageController.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Config\Config;
 use Drupal\Core\Config\Entity\ConfigStorageController;
 use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Entity\Query\QueryFactory;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Drupal\Core\Config\ConfigFactory;
@@ -47,10 +48,8 @@ class FieldStorageController extends ConfigStorageController {
   /**
    * Constructs a FieldStorageController object.
    *
-   * @param string $entity_type
-   *   The entity type for which the instance is created.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    * @param \Drupal\Core\Config\ConfigFactory $config_factory
    *   The config factory service.
    * @param \Drupal\Core\Config\StorageInterface $config_storage
@@ -66,8 +65,8 @@ class FieldStorageController extends ConfigStorageController {
    * @param \Drupal\Core\KeyValueStore\StateInterface $state
    *   The state key value store.
    */
-  public function __construct($entity_type, array $entity_info, ConfigFactory $config_factory, StorageInterface $config_storage, QueryFactory $entity_query_factory, UuidInterface $uuid_service, EntityManagerInterface $entity_manager, ModuleHandler $module_handler, StateInterface $state) {
-    parent::__construct($entity_type, $entity_info, $config_factory, $config_storage, $entity_query_factory, $uuid_service);
+  public function __construct(EntityTypeInterface $entity_info, ConfigFactory $config_factory, StorageInterface $config_storage, QueryFactory $entity_query_factory, UuidInterface $uuid_service, EntityManagerInterface $entity_manager, ModuleHandler $module_handler, StateInterface $state) {
+    parent::__construct($entity_info, $config_factory, $config_storage, $entity_query_factory, $uuid_service);
     $this->entityManager = $entity_manager;
     $this->moduleHandler = $module_handler;
     $this->state = $state;
@@ -76,9 +75,8 @@ public function __construct($entity_type, array $entity_info, ConfigFactory $con
   /**
    * {@inheritdoc}
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
     return new static(
-      $entity_type,
       $entity_info,
       $container->get('config.factory'),
       $container->get('config.storage'),
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
index b1b9346..03fc549 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
@@ -70,10 +70,10 @@ function testFieldInfo() {
     $instance = entity_create('field_instance', $instance_definition);
     $instance->save();
 
-    $info = entity_get_info('entity_test');
+    $info = \Drupal::entityManager()->getDefinition('entity_test');
     $instances = field_info_instances('entity_test', $instance->bundle);
     $this->assertEqual(count($instances), 1, format_string('One instance shows up in info when attached to a bundle on a @label.', array(
-      '@label' => $info['label']
+      '@label' => $info->getLabel(),
     )));
     $this->assertTrue($instance_definition < $instances[$instance->getName()], 'Instance appears in info correctly');
 
diff --git a/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php b/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php
index b30625b..7509fbd 100644
--- a/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php
@@ -118,8 +118,8 @@ function setUp() {
   function testTranslatableFieldSaveLoad() {
     // Enable field translations for nodes.
     field_test_entity_info_translatable('node', TRUE);
-    $entity_info = entity_get_info('node');
-    $this->assertTrue(count($entity_info['translatable']), 'Nodes are translatable.');
+    $entity_info = \Drupal::entityManager()->getDefinition('node');
+    $this->assertTrue($entity_info->isTranslatable(), 'Nodes are translatable.');
 
     // Prepare the field translations.
     $entity_type = 'entity_test';
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 a642b85..af6482d 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
@@ -9,6 +9,7 @@
  * Implements hook_entity_info().
  */
 function field_test_entity_info() {
+  /** @var $entity_info \Drupal\Core\Entity\EntityTypeInterface[] */
   // If requested, clear the field cache while this is being called. See
   // Drupal\field\Tests\FieldInfoTest::testFieldInfoCache().
   if (\Drupal::state()->get('field_test.clear_info_cache_in_hook_entity_info')) {
@@ -20,8 +21,9 @@ function field_test_entity_info() {
  * Implements hook_entity_info_alter().
  */
 function field_test_entity_info_alter(&$entity_info) {
+  /** @var $entity_info \Drupal\Core\Entity\EntityTypeInterface[] */
   foreach (field_test_entity_info_translatable() as $entity_type => $translatable) {
-    $entity_info[$entity_type]['translatable'] = $translatable;
+    $entity_info[$entity_type]->set('translatable', $translatable);
   }
 }
 
diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module
index bc50a76..cdcc9be 100644
--- a/core/modules/field_ui/field_ui.module
+++ b/core/modules/field_ui/field_ui.module
@@ -71,19 +71,19 @@ function field_ui_menu() {
 function field_ui_permission() {
   $permissions = array();
 
-  foreach (entity_get_info() as $entity_type => $entity_info) {
-    if ($entity_info['fieldable']) {
+  foreach (\Drupal::entityManager()->getDefinitions() as $entity_type => $entity_info) {
+    if ($entity_info->isFieldable()) {
       // Create a permission for each fieldable entity to manage
       // the fields and the display.
       $permissions['administer ' . $entity_type . ' fields'] = array(
-        'title' => t('%entity_label: Administer fields', array('%entity_label' => $entity_info['label'])),
+        'title' => t('%entity_label: Administer fields', array('%entity_label' => $entity_info->getLabel())),
         'restrict access' => TRUE,
       );
       $permissions['administer ' . $entity_type . ' form display'] = array(
-        'title' => t('%entity_label: Administer form display', array('%entity_label' => $entity_info['label']))
+        'title' => t('%entity_label: Administer form display', array('%entity_label' => $entity_info->getLabel()))
       );
       $permissions['administer ' . $entity_type . ' display'] = array(
-        'title' => t('%entity_label: Administer display', array('%entity_label' => $entity_info['label']))
+        'title' => t('%entity_label: Administer display', array('%entity_label' => $entity_info->getLabel()))
       );
     }
   }
@@ -118,8 +118,9 @@ function field_ui_element_info() {
  * Implements hook_entity_info().
  */
 function field_ui_entity_info(&$entity_info) {
-  $entity_info['field_instance']['controllers']['form']['delete'] = 'Drupal\field_ui\Form\FieldDeleteForm';
-  $entity_info['field_entity']['controllers']['list'] = 'Drupal\field_ui\FieldListController';
+  /** @var $entity_info \Drupal\Core\Entity\EntityTypeInterface[] */
+  $entity_info['field_instance']->setForm('delete', 'Drupal\field_ui\Form\FieldDeleteForm');
+  $entity_info['field_entity']->setList('Drupal\field_ui\FieldListController');
 }
 
 /**
@@ -167,8 +168,7 @@ function field_ui_entity_operation_alter(array &$operations, EntityInterface $en
   $info = $entity->entityInfo();
   // Add manage fields and display links if this entity type is the bundle
   // of another.
-  if (!empty($info['bundle_of'])) {
-    $bundle_of = $info['bundle_of'];
+  if ($bundle_of = $info->getBundleOf()) {
     $uri = $entity->uri();
     if (user_access('administer '. $bundle_of . ' fields')) {
       $operations['manage-fields'] = array(
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Access/FormModeAccessCheck.php b/core/modules/field_ui/lib/Drupal/field_ui/Access/FormModeAccessCheck.php
index 10552da..30e1689 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/Access/FormModeAccessCheck.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/Access/FormModeAccessCheck.php
@@ -51,7 +51,7 @@ public function access(Route $route, Request $request, AccountInterface $account
 
       if (!($bundle = $request->attributes->get('bundle'))) {
         $entity_info = $this->entityManager->getDefinition($entity_type);
-        $bundle = $request->attributes->get('_raw_variables')->get($entity_info['bundle_entity_type']);
+        $bundle = $request->attributes->get('_raw_variables')->get($entity_info->getBundleEntityType());
       }
 
       $visibility = FALSE;
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Access/ViewModeAccessCheck.php b/core/modules/field_ui/lib/Drupal/field_ui/Access/ViewModeAccessCheck.php
index e5fb8d5..41da9de 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/Access/ViewModeAccessCheck.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/Access/ViewModeAccessCheck.php
@@ -51,7 +51,7 @@ public function access(Route $route, Request $request, AccountInterface $account
 
       if (!($bundle = $request->attributes->get('bundle'))) {
         $entity_info = $this->entityManager->getDefinition($entity_type);
-        $bundle = $request->attributes->get('_raw_variables')->get($entity_info['bundle_entity_type']);
+        $bundle = $request->attributes->get('_raw_variables')->get($entity_info->getBundleEntityType());
       }
 
       $visibility = FALSE;
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverviewBase.php b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverviewBase.php
index 0858119..91eeddf 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverviewBase.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverviewBase.php
@@ -741,7 +741,7 @@ protected function getDisplays() {
     $load_ids = array();
     $display_entity_type = $this->getDisplayType();
     $entity_info = $this->entityManager->getDefinition($display_entity_type);
-    $config_prefix = $entity_info['config_prefix'];
+    $config_prefix = $entity_info->getConfigPrefix();
     $ids = config_get_storage_names_with_prefix($config_prefix . '.' . $this->entity_type . '.' . $this->bundle);
     foreach ($ids as $id) {
       $config_id = str_replace($config_prefix . '.', '', $id);
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/FieldListController.php b/core/modules/field_ui/lib/Drupal/field_ui/FieldListController.php
index 16ecfe9..8a4d40c 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/FieldListController.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/FieldListController.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Config\Entity\ConfigEntityListController;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Field\FieldTypePluginManager;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -50,21 +51,17 @@ class FieldListController extends ConfigEntityListController {
   /**
    * Constructs a new EntityListController object.
    *
-   * @param string $entity_type
-   *   The type of entity to be listed.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    *   The module handler to invoke hooks on.
-   * @param \Drupal\field\FieldInfo $field_info
-   *   The field info service.
    * @param \Drupal\Core\Field\FieldTypePluginManager $field_type_manager
    *   The 'field type' plugin manager.
    */
-  public function __construct($entity_type, array $entity_info, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, FieldTypePluginManager $field_type_manager) {
-    parent::__construct($entity_type, $entity_info, $entity_manager->getStorageController($entity_type), $module_handler);
+  public function __construct(EntityTypeInterface $entity_info, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, FieldTypePluginManager $field_type_manager) {
+    parent::__construct($entity_info, $entity_manager->getStorageController($entity_info->getId()), $module_handler);
 
     $this->entityManager = $entity_manager;
     $this->bundles = entity_get_bundles();
@@ -75,9 +72,8 @@ public function __construct($entity_type, array $entity_info, EntityManagerInter
   /**
    * {@inheritdoc}
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
     return new static(
-      $entity_type,
       $entity_info,
       $container->get('entity.manager'),
       $container->get('module_handler'),
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceEditForm.php b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceEditForm.php
index 4a81159..3d02cd5 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceEditForm.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceEditForm.php
@@ -212,7 +212,7 @@ public function delete(array &$form, array &$form_state) {
     $form_state['redirect_route'] = array(
       'route_name' => 'field_ui.delete_' . $this->instance->entity_type,
       'route_parameters' => array(
-        $entity_info['bundle_entity_type'] => $this->instance->bundle,
+        $entity_info->getBundleEntityType() => $this->instance->bundle,
         'field_instance' => $this->instance->id(),
       ),
       'options' => array(
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/OverviewBase.php b/core/modules/field_ui/lib/Drupal/field_ui/OverviewBase.php
index 272d807..61674d4 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/OverviewBase.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/OverviewBase.php
@@ -77,7 +77,7 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL,
     if (!isset($form_state['bundle'])) {
       if (!$bundle) {
         $entity_info = $this->entityManager->getDefinition($entity_type);
-        $bundle = $this->getRequest()->attributes->get('_raw_variables')->get($entity_info['bundle_entity_type']);
+        $bundle = $this->getRequest()->attributes->get('_raw_variables')->get($entity_info->getBundleEntityType());
       }
       $form_state['bundle'] = $bundle;
     }
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Plugin/Derivative/FieldUiLocalTask.php b/core/modules/field_ui/lib/Drupal/field_ui/Plugin/Derivative/FieldUiLocalTask.php
index 9a7c208..7d9e362 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/Plugin/Derivative/FieldUiLocalTask.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/Plugin/Derivative/FieldUiLocalTask.php
@@ -74,7 +74,7 @@ public function getDerivativeDefinitions(array $base_plugin_definition) {
     $this->derivatives = array();
 
     foreach ($this->entityManager->getDefinitions() as $entity_type => $entity_info) {
-      if ($entity_info['fieldable'] && isset($entity_info['links']['admin-form'])) {
+      if ($entity_info->isFieldable() && $entity_info->hasLinkTemplate('admin-form')) {
         $this->derivatives["overview_$entity_type"] = array(
           'route_name' => "field_ui.overview_$entity_type",
           'weight' => 1,
@@ -179,8 +179,8 @@ public function getDerivativeDefinitions(array $base_plugin_definition) {
    */
   public function alterLocalTasks(&$local_tasks) {
     foreach ($this->entityManager->getDefinitions() as $entity_type => $entity_info) {
-      if ($entity_info['fieldable'] && isset($entity_info['links']['admin-form'])) {
-        if ($parent_task = $this->getPluginIdFromRoute($entity_info['links']['admin-form'], $local_tasks)) {
+      if ($entity_info->isFieldable() && $entity_info->hasLinkTemplate('admin-form')) {
+        if ($parent_task = $this->getPluginIdFromRoute($entity_info->getLinkTemplate('admin-form'), $local_tasks)) {
           $local_tasks["field_ui.fields:overview_$entity_type"]['tab_root_id'] = $parent_task;
           $local_tasks["field_ui.fields:form_display_overview_$entity_type"]['tab_root_id'] = $parent_task;
           $local_tasks["field_ui.fields:display_overview_$entity_type"]['tab_root_id'] = $parent_task;
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Routing/RouteSubscriber.php b/core/modules/field_ui/lib/Drupal/field_ui/Routing/RouteSubscriber.php
index 1a20aee..b78a88f 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/Routing/RouteSubscriber.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/Routing/RouteSubscriber.php
@@ -41,9 +41,9 @@ public function __construct(EntityManagerInterface $manager) {
   protected function alterRoutes(RouteCollection $collection, $provider) {
     foreach ($this->manager->getDefinitions() as $entity_type => $entity_info) {
       $defaults = array();
-      if ($entity_info['fieldable'] && isset($entity_info['links']['admin-form'])) {
+      if ($entity_info->isFieldable() && $entity_info->hasLinkTemplate('admin-form')) {
         // Try to get the route from the current collection.
-        if (!$entity_route = $collection->get($entity_info['links']['admin-form'])) {
+        if (!$entity_route = $collection->get($entity_info->getLinkTemplate('admin-form'))) {
           continue;
         }
         $path = $entity_route->getPath();
@@ -74,7 +74,7 @@ protected function alterRoutes(RouteCollection $collection, $provider) {
 
         // If the entity type has no bundles, use the entity type.
         $defaults['entity_type'] = $entity_type;
-        if (empty($entity_info['entity_keys']['bundle'])) {
+        if (!$entity_info->hasKey('bundle')) {
           $defaults['bundle'] = $entity_type;
         }
         $route = new Route(
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index 4041886..10830d1 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -1852,11 +1852,11 @@ function file_get_file_references(File $file, $field = NULL, $age = EntityStorag
     $usage_list = \Drupal::service('file.usage')->listUsage($file);
     $file_usage_list = isset($usage_list['file']) ? $usage_list['file'] : array();
     foreach ($file_usage_list as $entity_type => $entity_ids) {
-      $entity_info = entity_get_info($entity_type);
+      $entity_info = \Drupal::entityManager()->getDefinition($entity_type);
       // The usage table contains usage of every revision. If we are looking
       // for every revision or the entity does not support revisions then
       // every usage is already a match.
-      $match_entity_type = $age == EntityStorageControllerInterface::FIELD_LOAD_REVISION || !isset($entity_info['entity_keys']['revision']);
+      $match_entity_type = $age == EntityStorageControllerInterface::FIELD_LOAD_REVISION || !$entity_info->hasKey('revision');
       $entities = entity_load_multiple($entity_type, array_keys($entity_ids));
       foreach ($entities as $entity) {
         $bundle = $entity->bundle();
diff --git a/core/modules/file/file.views.inc b/core/modules/file/file.views.inc
index 729ee4b..f78ec87 100644
--- a/core/modules/file/file.views.inc
+++ b/core/modules/file/file.views.inc
@@ -488,22 +488,22 @@ function file_field_views_data(FieldInterface $field) {
  */
 function file_field_views_data_views_data_alter(array &$data, FieldInterface $field) {
   $entity_type = $field->entity_type;
-  $entity_info = entity_get_info($entity_type);
+  $entity_info = \Drupal::entityManager()->getDefinition($entity_type);
   $field_name = $field->getName();
   $pseudo_field_name = 'reverse_' . $field_name . '_' . $entity_type;
 
   list($label) = field_views_field_label($entity_type, $field_name);
 
   $data['file_managed'][$pseudo_field_name]['relationship'] = array(
-    'title' => t('@entity using @field', array('@entity' => $entity_info['label'], '@field' => $label)),
-    'help' => t('Relate each @entity with a @field set to the file.', array('@entity' => $entity_info['label'], '@field' => $label)),
+    'title' => t('@entity using @field', array('@entity' => $entity_info->getLabel(), '@field' => $label)),
+    'help' => t('Relate each @entity with a @field set to the file.', array('@entity' => $entity_info->getLabel(), '@field' => $label)),
     'id' => 'entity_reverse',
     'field_name' => $field_name,
     'entity_type' => $entity_type,
     'field table' => FieldableDatabaseStorageController::_fieldTableName($field),
     'field field' => $field_name . '_target_id',
-    'base' => $entity_info['base_table'],
-    'base field' => $entity_info['entity_keys']['id'],
+    'base' => $entity_info->getBaseTable(),
+    'base field' => $entity_info->getKey('id'),
     'label' => t('!field_name', array('!field_name' => $field_name)),
     'join_extra' => array(
       0 => array(
diff --git a/core/modules/file/lib/Drupal/file/FileStorageController.php b/core/modules/file/lib/Drupal/file/FileStorageController.php
index 9dbe289..15cf036 100644
--- a/core/modules/file/lib/Drupal/file/FileStorageController.php
+++ b/core/modules/file/lib/Drupal/file/FileStorageController.php
@@ -18,7 +18,7 @@ class FileStorageController extends FieldableDatabaseStorageController implement
    * {@inheritdoc}
    */
   public function spaceUsed($uid = NULL, $status = FILE_STATUS_PERMANENT) {
-    $query = $this->database->select($this->entityInfo['base_table'], 'f')
+    $query = $this->database->select($this->entityInfo->getBaseTable(), 'f')
       ->condition('f.status', $status);
     $query->addExpression('SUM(f.filesize)', 'filesize');
     if (isset($uid)) {
@@ -33,7 +33,7 @@ public function spaceUsed($uid = NULL, $status = FILE_STATUS_PERMANENT) {
   public function retrieveTemporaryFiles() {
     // Use separate placeholders for the status to avoid a bug in some versions
     // of PHP. See http://drupal.org/node/352956.
-    return $this->database->query('SELECT fid FROM {' . $this->entityInfo['base_table'] . '} WHERE status <> :permanent AND timestamp < :timestamp', array(
+    return $this->database->query('SELECT fid FROM {' . $this->entityInfo->getBaseTable() . '} WHERE status <> :permanent AND timestamp < :timestamp', array(
       ':permanent' => FILE_STATUS_PERMANENT,
       ':timestamp' => REQUEST_TIME - DRUPAL_MAXIMUM_TEMP_FILE_AGE
     ));
diff --git a/core/modules/filter/lib/Drupal/filter/FilterFormatListController.php b/core/modules/filter/lib/Drupal/filter/FilterFormatListController.php
index cd94f86..b338572 100644
--- a/core/modules/filter/lib/Drupal/filter/FilterFormatListController.php
+++ b/core/modules/filter/lib/Drupal/filter/FilterFormatListController.php
@@ -13,6 +13,7 @@
 use Drupal\Core\Entity\EntityControllerInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -36,10 +37,8 @@ class FilterFormatListController extends DraggableListController implements Enti
   /**
    * Constructs a new FilterFormatListController.
    *
-   * @param string $entity_type
-   *   The type of entity to be listed.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    * @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage
    *   The entity storage controller class.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
@@ -47,20 +46,23 @@ class FilterFormatListController extends DraggableListController implements Enti
    * @param \Drupal\Core\Config\ConfigFactory $config_factory
    *   The config factory.
    */
-  public function __construct($entity_type, array $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler, ConfigFactory $config_factory) {
-    parent::__construct($entity_type, $entity_info, $storage, $module_handler);
+  public function __construct(EntityTypeInterface $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler, ConfigFactory $config_factory) {
+    parent::__construct($entity_info, $storage, $module_handler);
 
     $this->configFactory = $config_factory;
   }
 
   /**
    * {@inheritdoc}
+   * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *
+   * @return static
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
     return new static(
-      $entity_type,
       $entity_info,
-      $container->get('entity.manager')->getStorageController($entity_type),
+      $container->get('entity.manager')->getStorageController($entity_info->getId()),
       $container->get('module_handler'),
       $container->get('config.factory')
     );
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index 95ebe76..9b405b2 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -194,10 +194,12 @@ function forum_menu_local_tasks(&$data, $route_name) {
 /**
  * Implements hook_entity_info().
  */
-function forum_entity_info(&$info) {
+function forum_entity_info(&$entity_info) {
+  /** @var $entity_info \Drupal\Core\Entity\EntityTypeInterface[] */
   // Register forum specific form controllers.
-  $info['taxonomy_term']['controllers']['form']['forum'] = 'Drupal\forum\Form\ForumFormController';
-  $info['taxonomy_term']['controllers']['form']['container'] = 'Drupal\forum\Form\ContainerFormController';
+  $entity_info['taxonomy_term']
+    ->setForm('forum', 'Drupal\forum\Form\ForumFormController')
+    ->setForm('container', 'Drupal\forum\Form\ContainerFormController');
 }
 
 /**
diff --git a/core/modules/image/image.views.inc b/core/modules/image/image.views.inc
index 0cd2c6e..083b507 100644
--- a/core/modules/image/image.views.inc
+++ b/core/modules/image/image.views.inc
@@ -39,21 +39,21 @@ function image_field_views_data(FieldInterface $field) {
 function image_field_views_data_views_data_alter(array &$data, FieldInterface $field) {
   $entity_type = $field->entity_type;
   $field_name = $field->getName();
-  $entity_info = entity_get_info($entity_type);
+  $entity_info = \Drupal::entityManager()->getDefinition($entity_type);
   $pseudo_field_name = 'reverse_' . $field_name . '_' . $entity_type;
 
   list($label) = field_views_field_label($entity_type, $field_name);
 
   $data['file_managed'][$pseudo_field_name]['relationship'] = array(
-    'title' => t('@entity using @field', array('@entity' => $entity_info['label'], '@field' => $label)),
-    'help' => t('Relate each @entity with a @field set to the image.', array('@entity' => $entity_info['label'], '@field' => $label)),
+    'title' => t('@entity using @field', array('@entity' => $entity_info->getLabel(), '@field' => $label)),
+    'help' => t('Relate each @entity with a @field set to the image.', array('@entity' => $entity_info->getLabel(), '@field' => $label)),
     'id' => 'entity_reverse',
     'field_name' => $field_name,
     'entity_type' => $entity_type,
     'field table' => FieldableDatabaseStorageController::_fieldTableName($field),
     'field field' => $field_name . '_target_id',
-    'base' => $entity_info['base_table'],
-    'base field' => $entity_info['entity_keys']['id'],
+    'base' => $entity_info->getBaseTable(),
+    'base field' => $entity_info->getKey('id'),
     'label' => t('!field_name', array('!field_name' => $field_name)),
     'join_extra' => array(
       0 => array(
diff --git a/core/modules/image/lib/Drupal/image/ImageStyleListController.php b/core/modules/image/lib/Drupal/image/ImageStyleListController.php
index 3c4f3e7..f88afbf 100644
--- a/core/modules/image/lib/Drupal/image/ImageStyleListController.php
+++ b/core/modules/image/lib/Drupal/image/ImageStyleListController.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Entity\EntityControllerInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Routing\UrlGeneratorInterface;
 use Drupal\Core\StringTranslation\Translator\TranslatorInterface;
@@ -31,10 +32,8 @@ class ImageStyleListController extends ConfigEntityListController implements Ent
   /**
    * Constructs a new ImageStyleListController object.
    *
-   * @param string $entity_type
-   *   The type of entity to be listed.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    * @param \Drupal\Core\Entity\EntityStorageControllerInterface $image_style_storage
    *   The image style entity storage controller class.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
@@ -42,19 +41,18 @@ class ImageStyleListController extends ConfigEntityListController implements Ent
    * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator
    *   The URL generator.
    */
-  public function __construct($entity_type, array $entity_info, EntityStorageControllerInterface $image_style_storage, ModuleHandlerInterface $module_handler, UrlGeneratorInterface $url_generator) {
-    parent::__construct($entity_type, $entity_info, $image_style_storage, $module_handler);
+  public function __construct(EntityTypeInterface $entity_info, EntityStorageControllerInterface $image_style_storage, ModuleHandlerInterface $module_handler, UrlGeneratorInterface $url_generator) {
+    parent::__construct($entity_info, $image_style_storage, $module_handler);
     $this->urlGenerator = $url_generator;
   }
 
   /**
    * {@inheritdoc}
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
     return new static(
-      $entity_type,
       $entity_info,
-      $container->get('entity.manager')->getStorageController($entity_type),
+      $container->get('entity.manager')->getStorageController($entity_info->getId()),
       $container->get('module_handler'),
       $container->get('url_generator'),
       $container->get('string_translation')
diff --git a/core/modules/language/language.admin.inc b/core/modules/language/language.admin.inc
index f02d7bd..2c8e7af 100644
--- a/core/modules/language/language.admin.inc
+++ b/core/modules/language/language.admin.inc
@@ -362,12 +362,12 @@ function language_content_settings_page() {
  * @ingroup forms
  */
 function language_content_settings_form(array $form, array $form_state, array $supported) {
-  $entity_info = entity_get_info();
+  $entity_info = \Drupal::entityManager()->getDefinitions();
   $labels = array();
   $default = array();
 
   foreach ($supported as $entity_type) {
-    $labels[$entity_type] = isset($entity_info[$entity_type]['label']) ? $entity_info[$entity_type]['label'] : $entity_type;
+    $labels[$entity_type] = $entity_info[$entity_type]->getLabel() ?: $entity_type;
     $default[$entity_type] = FALSE;
 
     // Check whether we have any custom setting.
@@ -407,7 +407,7 @@ function language_content_settings_form(array $form, array $form_state, array $s
       '#type' => 'container',
       '#entity_type' => $entity_type,
       '#theme' => 'language_content_settings_table',
-      '#bundle_label' => isset($info['bundle_label']) ? $info['bundle_label'] : $label,
+      '#bundle_label' => $info->getBundleLabel() ?: $label,
       '#states' => array(
         'visible' => array(
           ':input[name="entity_types[' . $entity_type . ']"]' => array('checked' => TRUE),
diff --git a/core/modules/language/language.module b/core/modules/language/language.module
index 60f5c64..ddd0cd2 100644
--- a/core/modules/language/language.module
+++ b/core/modules/language/language.module
@@ -132,8 +132,8 @@ function language_theme() {
  */
 function language_entity_supported() {
   $supported = array();
-  foreach (entity_get_info() as $entity_type => $info) {
-    if (!empty($info['translatable'])) {
+  foreach (\Drupal::entityManager()->getDefinitions() as $entity_type => $info) {
+    if ($info->isTranslatable()) {
       $supported[$entity_type] = $entity_type;
     }
   }
diff --git a/core/modules/language/lib/Drupal/language/Form/ContentLanguageSettingsForm.php b/core/modules/language/lib/Drupal/language/Form/ContentLanguageSettingsForm.php
index a253854..6cd8514 100644
--- a/core/modules/language/lib/Drupal/language/Form/ContentLanguageSettingsForm.php
+++ b/core/modules/language/lib/Drupal/language/Form/ContentLanguageSettingsForm.php
@@ -61,7 +61,7 @@ public static function create(ContainerInterface $container) {
   protected function entitySupported() {
     $supported = array();
     foreach ($this->entityManager->getDefinitions() as $entity_type => $info) {
-      if (!empty($info['translatable'])) {
+      if ($info->isTranslatable()) {
         $supported[$entity_type] = $entity_type;
       }
     }
@@ -86,7 +86,7 @@ public function buildForm(array $form, array &$form_state) {
     $bundles = entity_get_bundles();
     $language_configuration = array();
     foreach ($this->entitySupported() as $entity_type) {
-      $labels[$entity_type] = isset($entity_info[$entity_type]['label']) ? $entity_info[$entity_type]['label'] : $entity_type;
+      $labels[$entity_type] = $entity_info[$entity_type]->getLabel() ?: $entity_type;
       $default[$entity_type] = FALSE;
 
       // Check whether we have any custom setting.
@@ -127,7 +127,7 @@ public function buildForm(array $form, array &$form_state) {
         '#type' => 'container',
         '#entity_type' => $entity_type,
         '#theme' => 'language_content_settings_table',
-        '#bundle_label' => isset($info['bundle_label']) ? $info['bundle_label'] : $label,
+        '#bundle_label' => $info->getBundleLabel() ?: $label,
         '#states' => array(
           'visible' => array(
             ':input[name="entity_types[' . $entity_type . ']"]' => array('checked' => TRUE),
diff --git a/core/modules/language/lib/Drupal/language/LanguageListController.php b/core/modules/language/lib/Drupal/language/LanguageListController.php
index 35724b7..9d65146 100644
--- a/core/modules/language/lib/Drupal/language/LanguageListController.php
+++ b/core/modules/language/lib/Drupal/language/LanguageListController.php
@@ -27,7 +27,7 @@ public function load() {
 
     // Sort the entities using the entity class's sort() method.
     // See \Drupal\Core\Config\Entity\ConfigEntityBase::sort().
-    uasort($entities, array($this->entityInfo['class'], 'sort'));
+    uasort($entities, array($this->entityInfo->getClass(), 'sort'));
     return $entities;
   }
 
diff --git a/core/modules/locale/lib/Drupal/locale/ParamConverter/LocaleAdminPathConfigEntityConverter.php b/core/modules/locale/lib/Drupal/locale/ParamConverter/LocaleAdminPathConfigEntityConverter.php
index 5061be2..1be24be 100644
--- a/core/modules/locale/lib/Drupal/locale/ParamConverter/LocaleAdminPathConfigEntityConverter.php
+++ b/core/modules/locale/lib/Drupal/locale/ParamConverter/LocaleAdminPathConfigEntityConverter.php
@@ -53,7 +53,7 @@ public function applies($definition, $name, Route $route) {
       // out whether the current entity is a ConfigEntity.
       $entity_type = substr($definition['type'], strlen('entity:'));
       $info = $this->entityManager->getDefinition($entity_type);
-      if (is_subclass_of($info['class'], '\Drupal\Core\Config\Entity\ConfigEntityInterface')) {
+      if ($info->isSubclassOf('\Drupal\Core\Config\Entity\ConfigEntityInterface')) {
         // path_is_admin() needs the path without the leading slash.
         $path = ltrim($route->getPath(), '/');
         return path_is_admin($path);
diff --git a/core/modules/menu/menu.module b/core/modules/menu/menu.module
index b82a8b4..0e5ea66 100644
--- a/core/modules/menu/menu.module
+++ b/core/modules/menu/menu.module
@@ -96,16 +96,17 @@ function menu_menu() {
  * Implements hook_entity_info().
  */
 function menu_entity_info(&$entity_info) {
-  $entity_info['menu']['controllers']['list'] = 'Drupal\menu\MenuListController';
-  $entity_info['menu']['links']['edit-form'] = 'menu.menu_edit';
-  $entity_info['menu']['controllers']['form'] = array(
-    'add' => 'Drupal\menu\MenuFormController',
-    'edit' => 'Drupal\menu\MenuFormController',
-    'delete' => 'Drupal\menu\Form\MenuDeleteForm',
-  );
-
-  $entity_info['menu_link']['controllers']['form']['delete'] = 'Drupal\menu\Form\MenuLinkDeleteForm';
-  $entity_info['menu_link']['controllers']['form']['reset'] = 'Drupal\menu\Form\MenuLinkResetForm';
+  /** @var $entity_info \Drupal\Core\Entity\EntityTypeInterface[] */
+  $entity_info['menu']
+    ->setForm('add', 'Drupal\menu\MenuFormController')
+    ->setForm('edit', 'Drupal\menu\MenuFormController')
+    ->setForm('delete', 'Drupal\menu\Form\MenuDeleteForm')
+    ->setList('Drupal\menu\MenuListController')
+    ->setLinkTemplate('edit-form', 'menu.menu_edit');
+
+  $entity_info['menu_link']
+    ->setForm('delete', 'Drupal\menu\Form\MenuLinkDeleteForm')
+    ->setForm('reset', 'Drupal\menu\Form\MenuLinkResetForm');
 }
 
 /**
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/Entity/MenuLink.php b/core/modules/menu_link/lib/Drupal/menu_link/Entity/MenuLink.php
index 64340e0..638f47f 100644
--- a/core/modules/menu_link/lib/Drupal/menu_link/Entity/MenuLink.php
+++ b/core/modules/menu_link/lib/Drupal/menu_link/Entity/MenuLink.php
@@ -289,8 +289,7 @@ public function setNewRevision($value = TRUE) {
    * {@inheritdoc}
    */
   public function isNewRevision() {
-    $info = $this->entityInfo();
-    return $this->newRevision || (!empty($info['entity_keys']['revision']) && !$this->getRevisionId());
+    return $this->newRevision || ($this->entityInfo()->hasKey('revision') && !$this->getRevisionId());
   }
 
   /**
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php
index 9744718..0e95f42 100644
--- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php
+++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php
@@ -12,6 +12,7 @@
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageException;
 use Drupal\Core\Database\Connection;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\field\FieldInfo;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Cmf\Component\Routing\RouteProviderInterface;
@@ -48,10 +49,8 @@ class MenuLinkStorageController extends DatabaseStorageController implements Men
   /**
    * Overrides DatabaseStorageController::__construct().
    *
-   * @param string $entity_type
-   *   The entity type for which the instance is created.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    * @param \Drupal\Core\Database\Connection $database
    *   The database connection to be used.
    * @param \Drupal\Component\Uuid\UuidInterface $uuid_service
@@ -59,8 +58,8 @@ class MenuLinkStorageController extends DatabaseStorageController implements Men
    * @param \Symfony\Cmf\Component\Routing\RouteProviderInterface $route_provider
    *   The route provider service.
    */
-  public function __construct($entity_type, array $entity_info, Connection $database, UuidInterface $uuid_service, RouteProviderInterface $route_provider) {
-    parent::__construct($entity_type, $entity_info, $database, $uuid_service);
+  public function __construct(EntityTypeInterface $entity_info, Connection $database, UuidInterface $uuid_service, RouteProviderInterface $route_provider) {
+    parent::__construct($entity_info, $database, $uuid_service);
 
     $this->routeProvider = $route_provider;
 
@@ -84,9 +83,8 @@ public function create(array $values) {
   /**
    * {@inheritdoc}
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
     return new static(
-      $entity_type,
       $entity_info,
       $container->get('database'),
       $container->get('uuid'),
@@ -123,7 +121,7 @@ public function save(EntityInterface $entity) {
       }
 
       if ($entity->isNew()) {
-        $entity->mlid = $this->database->insert($this->entityInfo['base_table'])->fields(array('menu_name' => 'tools'))->execute();
+        $entity->mlid = $this->database->insert($this->entityInfo->getBaseTable())->fields(array('menu_name' => 'tools'))->execute();
         $entity->enforceIsNew();
       }
 
@@ -139,7 +137,7 @@ public function save(EntityInterface $entity) {
       // $entity may have additional keys left over from building a router entry.
       // The intersect removes the extra keys, allowing a meaningful comparison.
       if ($entity->isNew() || (array_intersect_key(get_object_vars($entity), get_object_vars($entity->original)) != get_object_vars($entity->original))) {
-        $return = drupal_write_record($this->entityInfo['base_table'], $entity, $this->idKey);
+        $return = drupal_write_record($this->entityInfo->getBaseTable(), $entity, $this->idKey);
 
         if ($return) {
           if (!$entity->isNew()) {
@@ -201,11 +199,11 @@ public function loadUpdatedCustomized(array $router_paths) {
       );
     $query_result = $query->execute();
 
-    if (!empty($this->entityInfo['class'])) {
+    if ($class = $this->entityInfo->getClass()) {
       // We provide the necessary arguments for PDO to create objects of the
       // specified entity class.
       // @see \Drupal\Core\Entity\EntityInterface::__construct()
-      $query_result->setFetchMode(\PDO::FETCH_CLASS, $this->entityInfo['class'], array(array(), $this->entityType));
+      $query_result->setFetchMode(\PDO::FETCH_CLASS, $class, array(array(), $this->entityType));
     }
 
     return $query_result->fetchAllAssoc($this->idKey);
@@ -281,7 +279,7 @@ public function findChildrenRelativeDepth(EntityInterface $entity) {
    * {@inheritdoc}
    */
   public function moveChildren(EntityInterface $entity) {
-    $query = $this->database->update($this->entityInfo['base_table']);
+    $query = $this->database->update($this->entityInfo->getBaseTable());
 
     $query->fields(array('menu_name' => $entity->menu_name));
 
diff --git a/core/modules/node/lib/Drupal/node/NodeAccessController.php b/core/modules/node/lib/Drupal/node/NodeAccessController.php
index 944b411..a069854 100644
--- a/core/modules/node/lib/Drupal/node/NodeAccessController.php
+++ b/core/modules/node/lib/Drupal/node/NodeAccessController.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Database\Connection;
 use Drupal\Core\Database\Query\SelectInterface;
 use Drupal\Core\Entity\EntityControllerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Entity\EntityAccessController;
 use Drupal\Core\Entity\EntityInterface;
@@ -33,24 +34,21 @@ class NodeAccessController extends EntityAccessController implements NodeAccessC
   /**
    * Constructs a NodeAccessController object.
    *
-   * @param string $entity_type
-   *   The entity type of the access controller instance.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    * @param \Drupal\node\NodeGrantDatabaseStorageInterface $grant_storage
    *   The node grant storage.
    */
-  public function __construct($entity_type, array $entity_info, NodeGrantDatabaseStorageInterface $grant_storage) {
-    parent::__construct($entity_type, $entity_info);
+  public function __construct(EntityTypeInterface $entity_info, NodeGrantDatabaseStorageInterface $grant_storage) {
+    parent::__construct($entity_info);
     $this->grantStorage = $grant_storage;
   }
 
   /**
    * {@inheritdoc}
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
     return new static(
-      $entity_type,
       $entity_info,
       $container->get('node.grant_storage')
     );
diff --git a/core/modules/node/lib/Drupal/node/NodeListController.php b/core/modules/node/lib/Drupal/node/NodeListController.php
index f7770d5..6f54472 100644
--- a/core/modules/node/lib/Drupal/node/NodeListController.php
+++ b/core/modules/node/lib/Drupal/node/NodeListController.php
@@ -12,6 +12,7 @@
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityListController;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Language\Language;
 use Drupal\Core\StringTranslation\TranslationInterface;
@@ -32,21 +33,17 @@ class NodeListController extends EntityListController {
   /**
    * Constructs a new NodeListController object.
    *
-   * @param string $entity_type
-   *   The type of entity to be listed.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    * @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage
    *   The entity storage controller class.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    *   The module handler to invoke hooks on.
    * @param \Drupal\Core\Datetime\Date $date_service
    *   The date service.
-   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
-   *   The string translation service.
    */
-  public function __construct($entity_type, array $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler, Date $date_service) {
-    parent::__construct($entity_type, $entity_info, $storage, $module_handler);
+  public function __construct(EntityTypeInterface $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler, Date $date_service) {
+    parent::__construct($entity_info, $storage, $module_handler);
 
     $this->dateService = $date_service;
   }
@@ -54,11 +51,10 @@ public function __construct($entity_type, array $entity_info, EntityStorageContr
   /**
    * {@inheritdoc}
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
     return new static(
-      $entity_type,
       $entity_info,
-      $container->get('entity.manager')->getStorageController($entity_type),
+      $container->get('entity.manager')->getStorageController($entity_info->getId()),
       $container->get('module_handler'),
       $container->get('date')
     );
diff --git a/core/modules/node/lib/Drupal/node/NodeTypeListController.php b/core/modules/node/lib/Drupal/node/NodeTypeListController.php
index 0cb406f..4f2c117 100644
--- a/core/modules/node/lib/Drupal/node/NodeTypeListController.php
+++ b/core/modules/node/lib/Drupal/node/NodeTypeListController.php
@@ -8,6 +8,7 @@
 
 use Drupal\Core\Config\Entity\ConfigEntityListController;
 use Drupal\Core\Entity\EntityControllerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
@@ -31,10 +32,8 @@ class NodeTypeListController extends ConfigEntityListController implements Entit
   /**
    * Constructs a NodeTypeFormController object.
    *
-   * @param string $entity_type
-   *   The type of entity to be listed.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    * @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage
    *   The entity storage controller class.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
@@ -42,18 +41,18 @@ class NodeTypeListController extends ConfigEntityListController implements Entit
    * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator
    *   The url generator service.
    */
-  public function __construct($entity_type, array $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler, UrlGeneratorInterface $url_generator) {
-    parent::__construct($entity_type, $entity_info, $storage, $module_handler);
+  public function __construct(EntityTypeInterface $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler, UrlGeneratorInterface $url_generator) {
+    parent::__construct($entity_info, $storage, $module_handler);
     $this->urlGenerator = $url_generator;
   }
+
   /**
    * {@inheritdoc}
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
     return new static(
-      $entity_type,
       $entity_info,
-      $container->get('entity.manager')->getStorageController($entity_type),
+      $container->get('entity.manager')->getStorageController($entity_info->getId()),
       $container->get('module_handler'),
       $container->get('url_generator')
     );
diff --git a/core/modules/node/lib/Drupal/node/Tests/Config/NodeImportChangeTest.php b/core/modules/node/lib/Drupal/node/Tests/Config/NodeImportChangeTest.php
index d50d6ce..bdd2b92 100644
--- a/core/modules/node/lib/Drupal/node/Tests/Config/NodeImportChangeTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/Config/NodeImportChangeTest.php
@@ -19,7 +19,7 @@ class NodeImportChangeTest extends DrupalUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('node', 'entity', 'field', 'text', 'system', 'node_test_config');
+  public static $modules = array('node', 'entity', 'field', 'text', 'system', 'node_test_config', 'user');
 
   /**
    * Set the default field storage backend for fields created during tests.
diff --git a/core/modules/node/lib/Drupal/node/Tests/Config/NodeImportCreateTest.php b/core/modules/node/lib/Drupal/node/Tests/Config/NodeImportCreateTest.php
index 764d96c..c6d4b7a 100644
--- a/core/modules/node/lib/Drupal/node/Tests/Config/NodeImportCreateTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/Config/NodeImportCreateTest.php
@@ -19,7 +19,7 @@ class NodeImportCreateTest extends DrupalUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('node', 'entity', 'field', 'text', 'system');
+  public static $modules = array('node', 'entity', 'field', 'text', 'system', 'user');
 
   /**
    * Set the default field storage backend for fields created during tests.
@@ -28,6 +28,7 @@ public function setUp() {
     parent::setUp();
 
     $this->installSchema('system', array('config_snapshot'));
+    $this->installSchema('user', array('users'));
 
     // Set default storage backend.
     $this->installConfig(array('field'));
diff --git a/core/modules/node/tests/modules/node_test/node_test.module b/core/modules/node/tests/modules/node_test/node_test.module
index d999462..27de982 100644
--- a/core/modules/node/tests/modules/node_test/node_test.module
+++ b/core/modules/node/tests/modules/node_test/node_test.module
@@ -170,7 +170,8 @@ function node_test_node_insert(EntityInterface $node) {
  * Implements hook_entity_info_alter().
  */
 function node_test_entity_info_alter(&$entity_info) {
+  /** @var $entity_info \Drupal\Core\Entity\EntityTypeInterface[] */
   if (\Drupal::state()->get('node_test.storage_controller')) {
-    $entity_info['node']['class'] = 'Drupal\node_test\NodeTest';
+    $entity_info['node']->setClass('Drupal\node_test\NodeTest');
   }
 }
diff --git a/core/modules/rest/lib/Drupal/rest/LinkManager/TypeLinkManager.php b/core/modules/rest/lib/Drupal/rest/LinkManager/TypeLinkManager.php
index 6802282..7e41aee 100644
--- a/core/modules/rest/lib/Drupal/rest/LinkManager/TypeLinkManager.php
+++ b/core/modules/rest/lib/Drupal/rest/LinkManager/TypeLinkManager.php
@@ -80,13 +80,11 @@ protected function writeCache() {
 
     // Type URIs correspond to bundles. Iterate through the bundles to get the
     // URI and data for them.
-    $entity_info = entity_get_info();
+    $entity_info = \Drupal::entityManager()->getDefinitions();
     foreach (entity_get_bundles() as $entity_type => $bundles) {
-      $entity_type_info = $entity_info[$entity_type];
-      $reflection = new \ReflectionClass($entity_type_info['class']);
       // Only content entities are supported currently.
       // @todo Consider supporting config entities.
-      if ($reflection->implementsInterface('\Drupal\Core\Config\Entity\ConfigEntityInterface')) {
+      if ($entity_info[$entity_type]->isSubclassOf('\Drupal\Core\Config\Entity\ConfigEntityInterface')) {
         continue;
       }
       foreach ($bundles as $bundle => $bundle_info) {
diff --git a/core/modules/rest/lib/Drupal/rest/Plugin/Derivative/EntityDerivative.php b/core/modules/rest/lib/Drupal/rest/Plugin/Derivative/EntityDerivative.php
index ed09ce3..3723287 100644
--- a/core/modules/rest/lib/Drupal/rest/Plugin/Derivative/EntityDerivative.php
+++ b/core/modules/rest/lib/Drupal/rest/Plugin/Derivative/EntityDerivative.php
@@ -71,8 +71,8 @@ public function getDerivativeDefinitions(array $base_plugin_definition) {
         $this->derivatives[$entity_type] = array(
           'id' => 'entity:' . $entity_type,
           'entity_type' => $entity_type,
-          'serialization_class' => $entity_info['class'],
-          'label' => $entity_info['label'],
+          'serialization_class' => $entity_info->getClass(),
+          'label' => $entity_info->getLabel(),
         );
         $this->derivatives[$entity_type] += $base_plugin_definition;
       }
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Form/SetCustomize.php b/core/modules/shortcut/lib/Drupal/shortcut/Form/SetCustomize.php
index 80e9a01..12a789c 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/Form/SetCustomize.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Form/SetCustomize.php
@@ -8,7 +8,7 @@
 namespace Drupal\shortcut\Form;
 
 use Drupal\Core\Entity\EntityFormController;
-use Drupal\Core\Entity\EntityManager;
+use Drupal\Core\Entity\EntityManagerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -26,10 +26,10 @@ class SetCustomize extends EntityFormController {
   /**
    * Constructs a SetCustomize object.
    *
-   * @param \Drupal\Core\Entity\EntityManager $entity_manager
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager.
    */
-  public function __construct(EntityManager $entity_manager) {
+  public function __construct(EntityManagerInterface $entity_manager) {
     $this->storageController = $entity_manager->getStorageController('shortcut');
   }
 
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutAccessController.php b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutAccessController.php
index 4dd0aea..79683b8 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutAccessController.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutAccessController.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Entity\EntityAccessController;
 use Drupal\Core\Entity\EntityControllerInterface;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Session\AccountInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -28,24 +29,21 @@ class ShortcutAccessController extends EntityAccessController implements EntityC
   /**
    * Constructs a ShortcutAccessController object.
    *
-   * @param string $entity_type
-   *   The entity type of the access controller instance.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    * @param \Drupal\shortcut\ShortcutSetStorageController $shortcut_set_storage
    *   The shortcut_set storage controller.
    */
-  public function __construct($entity_type, array $entity_info, ShortcutSetStorageController $shortcut_set_storage) {
-    parent::__construct($entity_type, $entity_info);
+  public function __construct(EntityTypeInterface $entity_info, ShortcutSetStorageController $shortcut_set_storage) {
+    parent::__construct($entity_info);
     $this->shortcutSetStorage = $shortcut_set_storage;
   }
 
   /**
    * {@inheritdoc}
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
     return new static(
-      $entity_type,
       $entity_info,
       $container->get('entity.manager')->getStorageController('shortcut_set')
     );
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php b/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php
index 66cc333..bd229ff 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php
@@ -200,30 +200,31 @@ function testInstallConfig() {
   function testEnableModulesFixedList() {
     // Install system module.
     $this->container->get('module_handler')->install(array('system'));
+    $entity_manager = \Drupal::entityManager();
 
     // entity_test is loaded via $modules; its entity type should exist.
     $this->assertEqual($this->container->get('module_handler')->moduleExists('entity_test'), TRUE);
-    $this->assertTrue(TRUE == entity_get_info('entity_test'));
+    $this->assertTrue(TRUE == $entity_manager->getDefinition('entity_test'));
 
     // Load some additional modules; entity_test should still exist.
     $this->enableModules(array('entity', 'field', 'text', 'entity_test'));
     $this->assertEqual($this->container->get('module_handler')->moduleExists('entity_test'), TRUE);
-    $this->assertTrue(TRUE == entity_get_info('entity_test'));
+    $this->assertTrue(TRUE == $entity_manager->getDefinition('entity_test'));
 
     // Install some other modules; entity_test should still exist.
     $this->container->get('module_handler')->install(array('field', 'field_test'), FALSE);
     $this->assertEqual($this->container->get('module_handler')->moduleExists('entity_test'), TRUE);
-    $this->assertTrue(TRUE == entity_get_info('entity_test'));
+    $this->assertTrue(TRUE == $entity_manager->getDefinition('entity_test'));
 
     // Uninstall one of those modules; entity_test should still exist.
     $this->container->get('module_handler')->uninstall(array('field_test'));
     $this->assertEqual($this->container->get('module_handler')->moduleExists('entity_test'), TRUE);
-    $this->assertTrue(TRUE == entity_get_info('entity_test'));
+    $this->assertTrue(TRUE == $entity_manager->getDefinition('entity_test'));
 
     // Set the weight of a module; entity_test should still exist.
     module_set_weight('entity', -1);
     $this->assertEqual($this->container->get('module_handler')->moduleExists('entity_test'), TRUE);
-    $this->assertTrue(TRUE == entity_get_info('entity_test'));
+    $this->assertTrue(TRUE == $entity_manager->getDefinition('entity_test'));
 
     // Reactivate the previously uninstalled module.
     $this->enableModules(array('field_test'));
diff --git a/core/modules/system/entity.api.php b/core/modules/system/entity.api.php
index 5b880cd..5ed89cc 100644
--- a/core/modules/system/entity.api.php
+++ b/core/modules/system/entity.api.php
@@ -98,19 +98,19 @@ function hook_ENTITY_TYPE_create_access(\Drupal\Core\Session\AccountInterface $a
  *
  * Modules may implement this hook to add information to defined entity types.
  *
- * @param array $entity_info
+ * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
  *   An associative array of all entity type definitions, keyed by the entity
  *   type name. Passed by reference.
  *
  * @see \Drupal\Core\Entity\Entity
  * @see \Drupal\Core\Entity\EntityManagerInterface
- * @see entity_get_info()
+ * @see \Drupal\Core\Entity\EntityTypeInterface
  */
 function hook_entity_info(&$entity_info) {
+  /** @var $entity_info \Drupal\Core\Entity\EntityTypeInterface[] */
   // 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']['controllers']['form']['default'].
-  $entity_info['node']['controllers']['form']['mymodule_foo'] = 'Drupal\mymodule\NodeFooFormController';
+  // node form. To override the default node form, use hook_entity_info_alter().
+  $entity_info['node']->setForm('mymodule_foo', 'Drupal\mymodule\NodeFooFormController');
 }
 
 /**
@@ -230,18 +230,19 @@ function hook_entity_bundle_delete($entity_type, $bundle) {
  * Do not use this hook to add information to entity types. Use
  * hook_entity_info() for that instead.
  *
- * @param array $entity_info
+ * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
  *   An associative array of all entity type definitions, keyed by the entity
  *   type name. Passed by reference.
  *
  * @see \Drupal\Core\Entity\Entity
  * @see \Drupal\Core\Entity\EntityManagerInterface
- * @see entity_get_info()
+ * @see \Drupal\Core\Entity\EntityTypeInterface
  */
 function hook_entity_info_alter(&$entity_info) {
+  /** @var $entity_info \Drupal\Core\Entity\EntityTypeInterface[] */
   // Set the controller class for nodes to an alternate implementation of the
   // Drupal\Core\Entity\EntityStorageControllerInterface interface.
-  $entity_info['node']['controllers']['storage'] = 'Drupal\mymodule\MyCustomNodeStorageController';
+  $entity_info['node']->setController('storage', 'Drupal\mymodule\MyCustomNodeStorageController');
 }
 
 /**
diff --git a/core/modules/system/lib/Drupal/system/DateFormatListController.php b/core/modules/system/lib/Drupal/system/DateFormatListController.php
index d22642b..a0e09ab 100644
--- a/core/modules/system/lib/Drupal/system/DateFormatListController.php
+++ b/core/modules/system/lib/Drupal/system/DateFormatListController.php
@@ -12,6 +12,7 @@
 use Drupal\Core\Datetime\Date;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -30,10 +31,8 @@ class DateFormatListController extends ConfigEntityListController {
   /**
    * Constructs a new DateFormatListController object.
    *
-   * @param string $entity_type
-   *   The type of entity to be listed.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    * @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage
    *   The entity storage controller class.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
@@ -41,8 +40,8 @@ class DateFormatListController extends ConfigEntityListController {
    * @param \Drupal\Core\Datetime\Date $date_service
    *   The date service.
    */
-  public function __construct($entity_type, array $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler, Date $date_service) {
-    parent::__construct($entity_type, $entity_info, $storage, $module_handler);
+  public function __construct(EntityTypeInterface $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler, Date $date_service) {
+    parent::__construct($entity_info, $storage, $module_handler);
 
     $this->dateService = $date_service;
   }
@@ -50,11 +49,10 @@ public function __construct($entity_type, array $entity_info, EntityStorageContr
   /**
    * {@inheritdoc}
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
     return new static(
-      $entity_type,
       $entity_info,
-      $container->get('entity.manager')->getStorageController($entity_type),
+      $container->get('entity.manager')->getStorageController($entity_info->getId()),
       $container->get('module_handler'),
       $container->get('date')
     );
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiInfoTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiInfoTest.php
index 4e45336..cb5ba0c 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiInfoTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiInfoTest.php
@@ -27,21 +27,21 @@ public static function getInfo() {
    */
   function testEntityInfoChanges() {
     \Drupal::moduleHandler()->install(array('entity_cache_test'));
-    $entity_info = entity_get_info();
+    $entity_info = \Drupal::entityManager()->getDefinitions();
     $this->assertTrue(isset($entity_info['entity_cache_test']), 'Test entity type found.');
 
     // Change the label of the test entity type and make sure changes appear
     // after flushing caches.
     \Drupal::state()->set('entity_cache_test.label', 'New label.');
-    $info = entity_get_info('entity_cache_test');
-    $this->assertEqual($info['label'], 'Entity Cache Test', 'Original label appears in cached entity info.');
+    $info = \Drupal::entityManager()->getDefinition('entity_cache_test');
+    $this->assertEqual($info->getLabel(), 'Entity Cache Test', 'Original label appears in cached entity info.');
     $this->resetAll();
-    $info = entity_get_info('entity_cache_test');
-    $this->assertEqual($info['label'], 'New label.', 'New label appears in entity info.');
+    $info = \Drupal::entityManager()->getDefinition('entity_cache_test');
+    $this->assertEqual($info->getLabel(), 'New label.', 'New label appears in entity info.');
 
     // Uninstall the providing module and make sure the entity type is gone.
     module_uninstall(array('entity_cache_test', 'entity_cache_test_dependency'));
-    $entity_info = entity_get_info();
+    $entity_info = \Drupal::entityManager()->getDefinitions();
     $this->assertFalse(isset($entity_info['entity_cache_test']), 'Entity type of the providing module is gone.');
   }
 
@@ -53,7 +53,7 @@ function testEntityInfoChanges() {
   function testEntityInfoCacheWatchdog() {
     \Drupal::moduleHandler()->install(array('entity_cache_test'));
     $info = \Drupal::state()->get('entity_cache_test');
-    $this->assertEqual($info['label'], 'Entity Cache Test', 'Entity info label is correct.');
-    $this->assertEqual($info['controllers']['storage'], 'Drupal\Core\Entity\DatabaseStorageController', 'Entity controller class info is correct.');
+    $this->assertEqual($info->getLabel(), 'Entity Cache Test', 'Entity info label is correct.');
+    $this->assertEqual($info->getController('storage'), 'Drupal\Core\Entity\DatabaseStorageController', 'Entity controller class info is correct.');
   }
 }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php
index 41cab33..a9f0105 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php
@@ -94,12 +94,12 @@ protected function assertCRUD($entity_type, UserInterface $user1) {
 
     // Verify that all data got deleted.
     $definition = \Drupal::entityManager()->getDefinition($entity_type);
-    $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $definition['base_table'] . '}')->fetchField(), 'Base table was emptied');
-    if (isset($definition['data_table'])) {
-      $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $definition['data_table'] . '}')->fetchField(), 'Data table was emptied');
+    $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $definition->getBaseTable() . '}')->fetchField(), 'Base table was emptied');
+    if ($data_table = $definition->getDataTable()) {
+      $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $data_table . '}')->fetchField(), 'Data table was emptied');
     }
-    if (isset($definition['revision_table'])) {
-      $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $definition['revision_table'] . '}')->fetchField(), 'Data table was emptied');
+    if ($revision_table = $definition->getRevisionTable()) {
+      $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $revision_table . '}')->fetchField(), 'Data table was emptied');
     }
   }
 
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 91117b8..9b28815 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
@@ -18,7 +18,7 @@
  */
 function entity_cache_test_watchdog($log_entry) {
   if ($log_entry['type'] == 'system' && $log_entry['message'] == '%module module installed.') {
-    $info = entity_get_info('entity_cache_test');
+    $info = \Drupal::entityManager()->getDefinition('entity_cache_test');
     // Store the information in a system variable to analyze it later in the
     // test case.
     \Drupal::state()->set('entity_cache_test', $info);
diff --git a/core/modules/system/tests/modules/entity_cache_test_dependency/entity_cache_test_dependency.module b/core/modules/system/tests/modules/entity_cache_test_dependency/entity_cache_test_dependency.module
index 004f531..61dc196 100644
--- a/core/modules/system/tests/modules/entity_cache_test_dependency/entity_cache_test_dependency.module
+++ b/core/modules/system/tests/modules/entity_cache_test_dependency/entity_cache_test_dependency.module
@@ -8,6 +8,7 @@
 /**
  * Implements hook_entity_info_alter().
  */
-function entity_cache_test_dependency_entity_info_alter(&$info) {
-  $info['entity_cache_test']['label'] = \Drupal::state()->get('entity_cache_test.label') ?: 'Entity Cache Test';
+function entity_cache_test_dependency_entity_info_alter(&$entity_info) {
+  /** @var $entity_info \Drupal\Core\Entity\EntityTypeInterface[] */
+  $entity_info['entity_cache_test']->set('label', \Drupal::state()->get('entity_cache_test.label') ?: 'Entity Cache Test');
 }
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.module b/core/modules/system/tests/modules/entity_test/entity_test.module
index d16c503..d552d1d 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.module
+++ b/core/modules/system/tests/modules/entity_test/entity_test.module
@@ -60,11 +60,14 @@ function entity_test_entity_types($filter = NULL) {
 /**
  * Implements hook_entity_info_alter().
  */
-function entity_test_entity_info_alter(&$info) {
+function entity_test_entity_info_alter(&$entity_info) {
+  /** @var $entity_info \Drupal\Core\Entity\EntityTypeInterface[] */
   foreach (entity_test_entity_types() as $entity_type) {
     // Optionally specify a translation handler for testing translations.
     if (\Drupal::state()->get('entity_test.translation')) {
-      $info[$entity_type]['translation'][$entity_type] = TRUE;
+      $translation = $entity_info[$entity_type]->get('translation');
+      $translation[$entity_type] = TRUE;
+      $entity_info[$entity_type]->set('translation', $translation);
     }
   }
 }
@@ -142,9 +145,9 @@ function entity_test_delete_bundle($bundle, $entity_type = 'entity_test') {
  */
 function entity_test_entity_bundle_info() {
   $bundles = array();
-  $entity_info = entity_get_info();
+  $entity_info = \Drupal::entityManager()->getDefinitions();
   foreach ($entity_info as $entity_type => $info) {
-    if ($info['provider'] == 'entity_test') {
+    if ($info->getProvider() == 'entity_test') {
       $bundles[$entity_type] = \Drupal::state()->get($entity_type . '.bundles') ?: array($entity_type => array('label' => 'Entity Test Bundle'));
     }
   }
@@ -155,9 +158,9 @@ function entity_test_entity_bundle_info() {
  * Implements hook_entity_view_mode_info_alter().
  */
 function entity_test_entity_view_mode_info_alter(&$view_modes) {
-  $entity_info = entity_get_info();
+  $entity_info = \Drupal::entityManager()->getDefinitions();
   foreach ($entity_info as $entity_type => $info) {
-    if ($entity_info[$entity_type]['provider'] == 'entity_test' && !isset($view_modes[$entity_type])) {
+    if ($entity_info[$entity_type]->getProvider() == 'entity_test' && !isset($view_modes[$entity_type])) {
       $view_modes[$entity_type] = array(
         'full' => array(
           'label' => t('Full object'),
@@ -178,9 +181,9 @@ function entity_test_entity_view_mode_info_alter(&$view_modes) {
  * Implements hook_entity_form_mode_info_alter().
  */
 function entity_test_entity_form_mode_info_alter(&$form_modes) {
-  $entity_info = entity_get_info();
+  $entity_info = \Drupal::entityManager()->getDefinitions();
   foreach ($entity_info as $entity_type => $info) {
-    if ($entity_info[$entity_type]['provider'] == 'entity_test') {
+    if ($entity_info[$entity_type]->getProvider() == 'entity_test') {
       $form_modes[$entity_type] = array(
         'compact' => array(
           'label' => t('Compact version'),
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php
index 6ca66a4..1193cd5 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php
@@ -112,7 +112,7 @@ public function label($langcode = NULL) {
     if (!isset($langcode)) {
       $langcode = $this->activeLangcode;
     }
-    if (isset($info['entity_keys']['label']) && $info['entity_keys']['label'] == 'name') {
+    if ($info->getKey('laebl') == 'name') {
       return $this->getTranslation($langcode)->name->value;
     }
     else {
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestFormController.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestFormController.php
index 7fe2812..89fe5ac 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestFormController.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestFormController.php
@@ -50,7 +50,7 @@ public function form(array $form, array &$form_state) {
 
     // @todo: Is there a better way to check if an entity type is revisionable?
     $entity_info = $entity->entityInfo();
-    if (!empty($entity_info['entity_keys']['revision']) && !$entity->isNew()) {
+    if ($entity_info->hasKey('revision') && !$entity->isNew()) {
       $form['revision'] = array(
         '#type' => 'checkbox',
         '#title' => t('Create new revision'),
diff --git a/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/Annotation/PluginExample.php b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/Annotation/PluginExample.php
index f830152..155e25d 100644
--- a/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/Annotation/PluginExample.php
+++ b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/Annotation/PluginExample.php
@@ -7,14 +7,14 @@
 
 namespace Drupal\plugin_test\Plugin\Annotation;
 
-use Drupal\Component\Annotation\AnnotationInterface;
+use Drupal\Component\Annotation\AnnotationBase;
 
 /**
  * Defines a custom Plugin annotation.
  *
  * @Annotation
  */
-class PluginExample implements AnnotationInterface {
+class PluginExample extends AnnotationBase {
 
   /**
    * The plugin ID.
@@ -37,6 +37,8 @@ public function get() {
     return array(
       'id' => $this->id,
       'custom' => $this->custom,
+      'class' => $this->class,
+      'provider' => $this->provider,
     );
   }
 
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/views/argument_validator/TermName.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/views/argument_validator/TermName.php
index 56475f9..bbddc4b 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/views/argument_validator/TermName.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/views/argument_validator/TermName.php
@@ -9,7 +9,7 @@
 
 use Drupal\views\ViewExecutable;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
-use Drupal\Core\Entity\EntityManager;
+use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\views\Plugin\views\argument_validator\Entity;
 
 /**
@@ -33,7 +33,7 @@ class TermName extends Entity {
   /**
    * {@inheritdoc}
    */
-  public function __construct(array $configuration, $plugin_id, array $plugin_definition, EntityManager $entity_manager) {
+  public function __construct(array $configuration, $plugin_id, array $plugin_definition, EntityManagerInterface $entity_manager) {
     parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_manager);
     // Not handling exploding term names.
     $this->multipleCapable = FALSE;
diff --git a/core/modules/taxonomy/taxonomy.views.inc b/core/modules/taxonomy/taxonomy.views.inc
index 4c4666b..d114050 100644
--- a/core/modules/taxonomy/taxonomy.views.inc
+++ b/core/modules/taxonomy/taxonomy.views.inc
@@ -437,21 +437,21 @@ function taxonomy_field_views_data(FieldInterface $field) {
 function taxonomy_field_views_data_views_data_alter(array &$data, FieldInterface $field) {
   $field_name = $field->getName();
   $entity_type = $field->entity_type;
-  $entity_info = entity_get_info($entity_type);
+  $entity_info = \Drupal::entityManager()->getDefinition($entity_type);
   $pseudo_field_name = 'reverse_' . $field_name . '_' . $entity_type;
 
   list($label) = field_views_field_label($entity_type, $field_name);
 
   $data['taxonomy_term_data'][$pseudo_field_name]['relationship'] = array(
-    'title' => t('@entity using @field', array('@entity' => $entity_info['label'], '@field' => $label)),
-    'help' => t('Relate each @entity with a @field set to the term.', array('@entity' => $entity_info['label'], '@field' => $label)),
+    'title' => t('@entity using @field', array('@entity' => $entity_info->getLabel(), '@field' => $label)),
+    'help' => t('Relate each @entity with a @field set to the term.', array('@entity' => $entity_info->getLabel(), '@field' => $label)),
     'id' => 'entity_reverse',
     'field_name' => $field_name,
     'entity_type' => $entity_type,
     'field table' => FieldableDatabaseStorageController::_fieldTableName($field),
     'field field' => $field_name . '_target_id',
-    'base' => $entity_info['base_table'],
-    'base field' => $entity_info['entity_keys']['id'],
+    'base' => $entity_info->getBaseTable(),
+    'base field' => $entity_info->getKey('id'),
     'label' => t('!field_name', array('!field_name' => $field_name)),
     'join_extra' => array(
       0 => array(
diff --git a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php
index 38d184e..2acba8d 100644
--- a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php
+++ b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php
@@ -106,8 +106,8 @@ function setUp() {
    *   The created entity object.
    */
   protected function createEntity($values = array()) {
-    $info = entity_get_info($this->entity_type);
-    $bundle_key = $info['entity_keys']['bundle'];
+    $info = \Drupal::entityManager()->getDefinition($this->entity_type);
+    $bundle_key = $info->getKey('bundle');
     $entity = entity_create($this->entity_type, $values + array(
       $bundle_key => $this->bundle,
     ));
diff --git a/core/modules/user/lib/Drupal/user/Controller/UserListController.php b/core/modules/user/lib/Drupal/user/Controller/UserListController.php
index 7f8da1d..fcb2844 100644
--- a/core/modules/user/lib/Drupal/user/Controller/UserListController.php
+++ b/core/modules/user/lib/Drupal/user/Controller/UserListController.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityListController;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Entity\Query\QueryFactory;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -32,10 +33,8 @@ class UserListController extends EntityListController implements EntityControlle
   /**
    * Constructs a new UserListController object.
    *
-   * @param string $entity_type
-   *   The type of entity to be listed.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    * @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage
    *   The entity storage controller class.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
@@ -43,19 +42,18 @@ class UserListController extends EntityListController implements EntityControlle
    * @param \Drupal\Core\Entity\Query\QueryFactory $query_factory
    *   The entity query factory.
    */
-  public function __construct($entity_type, array $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler, QueryFactory $query_factory) {
-    parent::__construct($entity_type, $entity_info, $storage, $module_handler);
+  public function __construct(EntityTypeInterface $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler, QueryFactory $query_factory) {
+    parent::__construct($entity_info, $storage, $module_handler);
     $this->queryFactory = $query_factory;
   }
 
   /**
    * {@inheritdoc}
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
     return new static(
-      $entity_type,
       $entity_info,
-      $container->get('entity.manager')->getStorageController($entity_type),
+      $container->get('entity.manager')->getStorageController($entity_info->getId()),
       $container->get('module_handler'),
       $container->get('entity.query')
     );
diff --git a/core/modules/user/lib/Drupal/user/Theme/AdminNegotiator.php b/core/modules/user/lib/Drupal/user/Theme/AdminNegotiator.php
index a12e157..faaeb40 100644
--- a/core/modules/user/lib/Drupal/user/Theme/AdminNegotiator.php
+++ b/core/modules/user/lib/Drupal/user/Theme/AdminNegotiator.php
@@ -8,7 +8,7 @@
 namespace Drupal\user\Theme;
 
 use Drupal\Core\Config\ConfigFactory;
-use Drupal\Core\Entity\EntityManager;
+use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Theme\ThemeNegotiatorInterface;
 use Symfony\Component\HttpFoundation\Request;
@@ -35,7 +35,7 @@ class AdminNegotiator implements ThemeNegotiatorInterface {
   /**
    * The entity manager.
    *
-   * @var \Drupal\Core\Entity\EntityManager
+   * @var \Drupal\Core\Entity\EntityManagerInterface
    */
   protected $entityManager;
 
@@ -46,10 +46,10 @@ class AdminNegotiator implements ThemeNegotiatorInterface {
    *   The current user.
    * @param \Drupal\Core\Config\ConfigFactory $config_factory
    *   The config factory.
-   * @param \Drupal\Core\Entity\EntityManager $entity_manager
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager.
    */
-  public function __construct(AccountInterface $user, ConfigFactory $config_factory, EntityManager $entity_manager) {
+  public function __construct(AccountInterface $user, ConfigFactory $config_factory, EntityManagerInterface $entity_manager) {
     $this->user = $user;
     $this->configFactory = $config_factory;
     $this->entityManager = $entity_manager;
diff --git a/core/modules/user/lib/Drupal/user/Theme/UserNegotiator.php b/core/modules/user/lib/Drupal/user/Theme/UserNegotiator.php
index f601acf..6160ed7 100644
--- a/core/modules/user/lib/Drupal/user/Theme/UserNegotiator.php
+++ b/core/modules/user/lib/Drupal/user/Theme/UserNegotiator.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\user\Theme;
 
-use Drupal\Core\Entity\EntityManager;
+use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Theme\ActiveTheme;
 use Drupal\Core\Theme\ThemeNegotiatorInterface;
@@ -35,12 +35,12 @@ class UserNegotiator implements  ThemeNegotiatorInterface {
   /**
    * Constructs a UserNegotiator object.
    *
-   * @param \Drupal\Core\Entity\EntityManager $entity_manager
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager
    * @param \Drupal\Core\Session\AccountInterface $current_user
    *   The current user.
    */
-  public function __construct(EntityManager $entity_manager, AccountInterface $current_user) {
+  public function __construct(EntityManagerInterface $entity_manager, AccountInterface $current_user) {
     $this->userStorageController = $entity_manager->getStorageController('user');
     $this->currentUser = $current_user;
   }
diff --git a/core/modules/user/lib/Drupal/user/UserStorageController.php b/core/modules/user/lib/Drupal/user/UserStorageController.php
index 23b2d27..69cfdba 100644
--- a/core/modules/user/lib/Drupal/user/UserStorageController.php
+++ b/core/modules/user/lib/Drupal/user/UserStorageController.php
@@ -9,6 +9,7 @@
 
 use Drupal\Component\Uuid\UuidInterface;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Password\PasswordInterface;
 use Drupal\Core\Database\Connection;
 use Drupal\field\FieldInfo;
@@ -41,10 +42,8 @@ class UserStorageController extends FieldableDatabaseStorageController implement
   /**
    * Constructs a new UserStorageController object.
    *
-   * @param string $entity_type
-   *  The entity type for which the instance is created.
-   * @param array $entity_info
-   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   The entity info for the entity type.
    * @param \Drupal\Core\Database\Connection $database
    *   The database connection to be used.
    * @param \Drupal\field\FieldInfo $field_info
@@ -56,8 +55,8 @@ class UserStorageController extends FieldableDatabaseStorageController implement
    * @param \Drupal\user\UserDataInterface $user_data
    *   The user data service.
    */
-  public function __construct($entity_type, $entity_info, Connection $database, FieldInfo $field_info, UuidInterface $uuid_service, PasswordInterface $password, UserDataInterface $user_data) {
-    parent::__construct($entity_type, $entity_info, $database, $field_info, $uuid_service);
+  public function __construct(EntityTypeInterface $entity_info, Connection $database, FieldInfo $field_info, UuidInterface $uuid_service, PasswordInterface $password, UserDataInterface $user_data) {
+    parent::__construct($entity_info, $database, $field_info, $uuid_service);
 
     $this->password = $password;
     $this->userData = $user_data;
@@ -66,9 +65,8 @@ public function __construct($entity_type, $entity_info, Connection $database, Fi
   /**
    * {@inheritdoc}
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
     return new static(
-      $entity_type,
       $entity_info,
       $container->get('database'),
       $container->get('field.info'),
diff --git a/core/modules/user/tests/Drupal/user/Tests/Views/Argument/RolesRidTest.php b/core/modules/user/tests/Drupal/user/Tests/Views/Argument/RolesRidTest.php
index a546d73..054d25a 100644
--- a/core/modules/user/tests/Drupal/user/Tests/Views/Argument/RolesRidTest.php
+++ b/core/modules/user/tests/Drupal/user/Tests/Views/Argument/RolesRidTest.php
@@ -20,20 +20,6 @@
  */
 class RolesRidTest extends UnitTestCase {
 
-  /**
-   * Entity info used by the test.
-   *
-   * @var array
-   */
-  public static $entityInfo = array(
-    'entity_keys' => array(
-      'id' => 'id',
-      'label' => 'label',
-    ),
-    'config_prefix' => 'user.role',
-    'class' => 'Drupal\user\Entity\Role',
-  );
-
   public static function getInfo() {
     return array(
       'name' => 'User: Roles Rid Argument',
@@ -67,12 +53,17 @@ public function testTitleQuery() {
         array(array('test_rid_1', 'test_rid_2'), array('test_rid_1' => $role1, 'test_rid_2' => $role2)),
       )));
 
-    $entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
+    $entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
+    $entity_type->expects($this->any())
+      ->method('getKey')
+      ->with('label')
+      ->will($this->returnValue('label'));
 
+    $entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
     $entity_manager->expects($this->any())
       ->method('getDefinition')
       ->with($this->equalTo('user_role'))
-      ->will($this->returnValue(static::$entityInfo));
+      ->will($this->returnValue($entity_type));
 
     $entity_manager
       ->expects($this->once())
diff --git a/core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsEntityArgumentValidator.php b/core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsEntityArgumentValidator.php
index 5d77a9b..b5f026d 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsEntityArgumentValidator.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsEntityArgumentValidator.php
@@ -9,7 +9,7 @@
 
 use Drupal\Component\Plugin\Derivative\DerivativeBase;
 use Drupal\Core\Plugin\Discovery\ContainerDerivativeInterface;
-use Drupal\Core\Entity\EntityManager;
+use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\StringTranslation\TranslationInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -31,7 +31,7 @@ class ViewsEntityArgumentValidator extends DerivativeBase implements ContainerDe
   /**
    * The entity manager.
    *
-   * @var \Drupal\Core\Entity\EntityManager
+   * @var \Drupal\Core\Entity\EntityManagerInterface
    */
   protected $entityManager;
 
@@ -54,12 +54,12 @@ class ViewsEntityArgumentValidator extends DerivativeBase implements ContainerDe
    *
    * @param string $base_plugin_id
    *   The base plugin ID.
-   * @param \Drupal\Core\Entity\EntityManager $entity_manager
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager.
    * @param \Drupal\Core\StringTranslation\TranslationInterface $translation_manager
    *   The string translation.
    */
-  public function __construct($base_plugin_id, EntityManager $entity_manager, TranslationInterface $translation_manager) {
+  public function __construct($base_plugin_id, EntityManagerInterface $entity_manager, TranslationInterface $translation_manager) {
     $this->basePluginId = $base_plugin_id;
     $this->entityManager = $entity_manager;
     $this->translationManager = $translation_manager;
@@ -86,8 +86,8 @@ public function getDerivativeDefinitions(array $base_plugin_definition) {
       $this->derivatives[$entity_type] = array(
         'id' => 'entity:' . $entity_type,
         'provider' => 'views',
-        'title' => $entity_info['label'],
-        'help' => $this->t('Validate @label', array('@label' => $entity_info['label'])),
+        'title' => $entity_info->getLabel(),
+        'help' => $this->t('Validate @label', array('@label' => $entity_info->getLabel())),
         'entity_type' => $entity_type,
         'class' => $base_plugin_definition['class'],
       );
diff --git a/core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsEntityRow.php b/core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsEntityRow.php
index 7f11cae..5058364 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsEntityRow.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsEntityRow.php
@@ -93,13 +93,13 @@ public function getDerivativeDefinition($derivative_id, array $base_plugin_defin
   public function getDerivativeDefinitions(array $base_plugin_definition) {
     foreach ($this->entityManager->getDefinitions() as $entity_type => $entity_info) {
       // Just add support for entity types which have a views integration.
-      if (isset($entity_info['base_table']) && $this->viewsData->get($entity_info['base_table']) && $this->entityManager->hasController($entity_type, 'view_builder')) {
+      if (($base_table = $entity_info->getBaseTable()) && $this->viewsData->get($base_table) && $this->entityManager->hasController($entity_type, 'view_builder')) {
         $this->derivatives[$entity_type] = array(
           'id' => 'entity:' . $entity_type,
           'provider' => 'views',
-          'title' => $entity_info['label'],
-          'help' => t('Display the @label', array('@label' => $entity_info['label'])),
-          'base' => array($entity_info['base_table']),
+          'title' => $entity_info->getLabel(),
+          'help' => t('Display the @label', array('@label' => $entity_info->getLabel())),
+          'base' => array($base_table),
           'entity_type' => $entity_type,
           'display_types' => array('normal'),
           'class' => $base_plugin_definition['class'],
diff --git a/core/modules/views/lib/Drupal/views/Plugin/entity_reference/selection/ViewsSelection.php b/core/modules/views/lib/Drupal/views/Plugin/entity_reference/selection/ViewsSelection.php
index 3cc2d36..7fef5b2 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/entity_reference/selection/ViewsSelection.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/entity_reference/selection/ViewsSelection.php
@@ -66,7 +66,7 @@ public static function settingsForm(FieldDefinitionInterface $field_definition)
     $options = array();
     foreach ($displays as $data) {
       list($view, $display_id) = $data;
-      if ($view->storage->get('base_table') == $entity_info['base_table']) {
+      if ($view->storage->get('base_table') == $entity_info->getBaseTable()) {
         $name = $view->storage->get('id');
         $display = $view->storage->get('display');
         $options[$name . ':' . $display_id] = $name . ' - ' . $display[$display_id]['display_title'];
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/argument_validator/Entity.php b/core/modules/views/lib/Drupal/views/Plugin/views/argument_validator/Entity.php
index 6fdea49..1d1d958 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/argument_validator/Entity.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/argument_validator/Entity.php
@@ -8,7 +8,7 @@
 namespace Drupal\views\Plugin\views\argument_validator;
 
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Entity\EntityManager;
+use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\views\Plugin\views\argument\ArgumentPluginBase;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -27,7 +27,7 @@ class Entity extends ArgumentValidatorPluginBase {
   /**
    * The entity manager.
    *
-   * @var \Drupal\Core\Entity\EntityManager
+   * @var \Drupal\Core\Entity\EntityManagerInterface
    */
   protected $entityManager;
 
@@ -45,10 +45,10 @@ class Entity extends ArgumentValidatorPluginBase {
    *   The plugin_id for the plugin instance.
    * @param array $plugin_definition
    *   The plugin implementation definition.
-   * @param \Drupal\Core\Entity\EntityManager $entity_manager
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager.
    */
-  public function __construct(array $configuration, $plugin_id, array $plugin_definition, EntityManager $entity_manager) {
+  public function __construct(array $configuration, $plugin_id, array $plugin_definition, EntityManagerInterface $entity_manager) {
     parent::__construct($configuration, $plugin_id, $plugin_definition);
     $this->entityManager = $entity_manager;
     $this->multipleCapable = TRUE;
@@ -90,8 +90,8 @@ public function buildOptionsForm(&$form, &$form_state) {
     // Derivative IDs are all entity:entity_type. Sanitized for js.
     // The ID is converted back on submission.
     $sanitized_id = ArgumentPluginBase::encodeValidatorId($this->definition['id']);
-    $entity_definitions = $this->entityManager->getDefinitions();
-    $bundle_type = $entity_definitions[$entity_type]['entity_keys']['bundle'];
+    $entity_info = $this->entityManager->getDefinition($entity_type);
+    $bundle_type = $entity_info->getKey('bundle');
 
     // If the entity has bundles, allow option to restrict to bundle(s).
     if ($bundle_type) {
@@ -100,8 +100,8 @@ public function buildOptionsForm(&$form, &$form_state) {
       foreach ($bundles as $bundle_id => $bundle_info) {
         $bundle_options[$bundle_id] = $bundle_info['label'];
       }
-      $bundles_title = empty($entity_definitions[$entity_type]['bundle_label']) ? t('Bundles') : $entity_definitions[$entity_type]['bundle_label'];
-      if (in_array('Drupal\Core\Entity\ContentEntityInterface', class_implements($entity_definitions[$entity_type]['class']))) {
+      $bundles_title = $entity_info->getBundleLabel() ?: $this->t('Bundles');
+      if ($entity_info->isSubclassOf('Drupal\Core\Entity\ContentEntityInterface')) {
         $fields = $this->entityManager->getFieldDefinitions($entity_type);
       }
       $bundle_name = (empty($fields) || empty($fields[$bundle_type]['label'])) ? t('bundles') : $fields[$bundle_type]['label'];
@@ -117,7 +117,7 @@ public function buildOptionsForm(&$form, &$form_state) {
     // Offer the option to filter by access to the entity in the argument.
     $form['access'] = array(
       '#type' => 'checkbox',
-      '#title' => t('Validate user has access to the %name', array('%name' => $entity_definitions[$entity_type]['label'])),
+      '#title' => t('Validate user has access to the %name', array('%name' => $entity_info->getLabel())),
       '#default_value' => $this->options['access'],
     );
     $form['operation'] = array(
@@ -138,8 +138,8 @@ public function buildOptionsForm(&$form, &$form_state) {
         '#type' => 'radios',
         '#title' => t('Multiple arguments'),
         '#options' => array(
-          0 => t('Single ID', array('%type' => $entity_definitions[$entity_type]['label'])),
-          1 => t('One or more IDs separated by , or +', array('%type' => $entity_definitions[$entity_type]['label'])),
+          0 => t('Single ID', array('%type' => $entity_info->getLabel())),
+          1 => t('One or more IDs separated by , or +', array('%type' => $entity_info->getLabel())),
         ),
         '#default_value' => (string) $this->options['multiple'],
       );
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
index 2862d10..7c643cf 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
@@ -1609,8 +1609,8 @@ public function buildOptionsForm(&$form, &$form_state) {
 
         $translatable_entity_tables = array();
         foreach (\Drupal::entityManager()->getDefinitions() as $entity_info) {
-          if (isset($entity_info['base_table']) && !empty($entity_info['translatable'])) {
-            $translatable_entity_tables[] = $entity_info['base_table'];
+          if ($entity_info->isTranslatable() && $base_table = $entity_info->getBaseTable()) {
+            $translatable_entity_tables[] = $base_table;
           }
         }
 
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/filter/Bundle.php b/core/modules/views/lib/Drupal/views/Plugin/views/filter/Bundle.php
index 402e722..bc4b201 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/filter/Bundle.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/filter/Bundle.php
@@ -29,7 +29,7 @@ class Bundle extends InOperator {
   /**
    * The entity info for the entity type.
    *
-   * @var array
+   * @var \Drupal\Core\Entity\EntityTypeInterface
    */
   protected $entityInfo;
 
@@ -40,8 +40,8 @@ public function init(ViewExecutable $view, DisplayPluginBase $display, array &$o
     parent::init($view, $display, $options);
 
     $this->entityType = $this->getEntityType();
-    $this->entityInfo = entity_get_info($this->entityType);
-    $this->real_field = $this->entityInfo['entity_keys']['bundle'];
+    $this->entityInfo = \Drupal::entityManager()->getDefinition($this->entityType);
+    $this->real_field = $this->entityInfo->getKey('bundle');
   }
 
   /**
@@ -50,7 +50,7 @@ public function init(ViewExecutable $view, DisplayPluginBase $display, array &$o
   public function getValueOptions() {
     if (!isset($this->value_options)) {
       $types = entity_get_bundles($this->entityType);
-      $this->value_title = t('@entity types', array('@entity' => $this->entityInfo['label']));
+      $this->value_title = t('@entity types', array('@entity' => $this->entityInfo->getLabel()));
 
       $options = array();
       foreach ($types as $type => $info) {
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php b/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php
index 265a025..424ac86 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php
@@ -1257,7 +1257,7 @@ public function query($get_count = FALSE) {
 
       foreach ($entity_information as $entity_type => $info) {
         $entity_info = \Drupal::entityManager()->getDefinition($entity_type);
-        $base_field = empty($table['revision']) ? $entity_info['entity_keys']['id'] : $entity_info['entity_keys']['revision'];
+        $base_field = empty($table['revision']) ? $entity_info->getKey('id') : $entity_info->getKey('revision');
         $this->addField($info['alias'], $base_field, '', $params);
       }
     }
@@ -1494,7 +1494,7 @@ public function getEntityTableInfo() {
     // Determine which of the tables are revision tables.
     foreach ($entity_tables as $table_alias => $table) {
       $info = \Drupal::entityManager()->getDefinition($table['entity_type']);
-      if (isset($info['revision table']) && $info['revision table'] == $table['base']) {
+      if ($info->getRevisionTable() == $table['base']) {
         $entity_tables[$table_alias]['revision'] = TRUE;
       }
     }
@@ -1520,7 +1520,7 @@ function loadEntities(&$results) {
     $ids_by_type = array();
     foreach ($entity_information as $entity_type => $info) {
       $entity_info = \Drupal::entityManager()->getDefinition($entity_type);
-      $id_key = empty($table['revision']) ? $entity_info['entity_keys']['id'] : $entity_info['entity_keys']['revision'];
+      $id_key = empty($table['revision']) ? $entity_info->getKey('id') : $entity_info->getKey('revision');
       $id_alias = $this->getFieldAlias($info['alias'], $id_key);
 
       foreach ($results as $index => $result) {
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/row/EntityRow.php b/core/modules/views/lib/Drupal/views/Plugin/views/row/EntityRow.php
index 31d9e17..f7d9510 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/row/EntityRow.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/row/EntityRow.php
@@ -46,7 +46,7 @@ class EntityRow extends RowPluginBase {
   /**
    * Contains the entity info of the entity type of this row plugin instance.
    *
-   * @see entity_get_info
+   * @var \Drupal\Core\Entity\EntityTypeInterface
    */
   protected $entityInfo;
 
@@ -77,8 +77,8 @@ public function init(ViewExecutable $view, DisplayPluginBase $display, array &$o
 
     $this->entityType = $this->definition['entity_type'];
     $this->entityInfo = $this->entityManager->getDefinition($this->entityType);
-    $this->base_table = $this->entityInfo['base_table'];
-    $this->base_field = $this->entityInfo['entity_keys']['id'];
+    $this->base_table = $this->entityInfo->getBaseTable();
+    $this->base_field = $this->entityInfo->getKey('id');
   }
 
   /**
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 debb036..8de866c 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
@@ -43,9 +43,9 @@
   /**
    * Contains the information from entity_get_info of the $entity_type.
    *
-   * @var array
+   * @var \Drupal\Core\Entity\EntityTypeInterface
    */
-  protected $entity_info = array();
+  protected $entity_info;
 
   /**
    * An array of validated view objects, keyed by a hash.
@@ -118,9 +118,9 @@ public function __construct(array $configuration, $plugin_id, array $plugin_defi
 
     $this->base_table = $this->definition['base_table'];
 
-    $entities = entity_get_info();
+    $entities = \Drupal::entityManager()->getDefinitions();
     foreach ($entities as $entity_type => $entity_info) {
-      if (isset($entity_info['base_table']) && $this->base_table == $entity_info['base_table']) {
+      if ($this->base_table == $entity_info->getBaseTable()) {
         $this->entity_info = $entity_info;
         $this->entity_type = $entity_type;
       }
@@ -563,7 +563,7 @@ protected function buildFilters(&$form, &$form_state) {
 
     $bundles = entity_get_bundles($this->entity_type);
     // If the current base table support bundles and has more than one (like user).
-    if (isset($this->entity_info['bundle_keys']) && !empty($bundles)) {
+    if (!empty($bundles) && $this->entity_info && $this->entity_info->getBundleKeys()) {
       // Get all bundles and their human readable names.
       $options = array('all' => t('All'));
       foreach ($bundles as $type => $bundle) {
@@ -837,7 +837,7 @@ protected function defaultDisplayFiltersUser(array $form, array &$form_state) {
     $filters = array();
 
     if (!empty($form_state['values']['show']['type']) && $form_state['values']['show']['type'] != 'all') {
-      $bundle_key = $this->entity_info['bundle_keys']['bundle'];
+      $bundle_key = $this->entity_info->getBundleKey('bundle');
       // Figure out the table where $bundle_key lives. It may not be the same as
       // the base table for the view; the taxonomy vocabulary machine_name, for
       // example, is stored in taxonomy_vocabulary, not taxonomy_term_data.
diff --git a/core/modules/views/lib/Drupal/views/Tests/Entity/FilterEntityBundleTest.php b/core/modules/views/lib/Drupal/views/Tests/Entity/FilterEntityBundleTest.php
index e41a960..db00221 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Entity/FilterEntityBundleTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Entity/FilterEntityBundleTest.php
@@ -29,13 +29,6 @@ class FilterEntityBundleTest extends ViewTestBase {
   public static $modules = array('node');
 
   /**
-   * Entity info data.
-   *
-   * @var array
-   */
-  protected $entityInfo;
-
-  /**
    * Entity bundle data.
    *
    * @var array
diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/AreaEntityTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/AreaEntityTest.php
index e8f70b5..528bcda 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Handler/AreaEntityTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Handler/AreaEntityTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\views\Tests\Handler;
 
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\views\Tests\ViewTestBase;
 use Drupal\views\Views;
 
@@ -52,8 +53,8 @@ public function testEntityAreaData() {
     $data = $this->container->get('views.views_data')->get('views');
     $entity_info = $this->container->get('entity.manager')->getDefinitions();
 
-    $expected_entities = array_filter($entity_info, function($info) {
-      return !empty($info['controllers']['view_builder']);
+    $expected_entities = array_filter($entity_info, function (EntityTypeInterface $info) {
+      return $info->hasController('view_builder');
     });
 
     // Test that all expected entity types have data.
@@ -63,8 +64,8 @@ public function testEntityAreaData() {
       $this->assertEqual($entity, $data['entity_' . $entity]['area']['entity_type'], format_string('Correct entity_type set for @entity', array('@entity' => $entity)));
     }
 
-    $expected_entities = array_filter($entity_info, function($info) {
-      return empty($info['controllers']['view_builder']);
+    $expected_entities = array_filter($entity_info, function (EntityTypeInterface $info) {
+      return !$info->hasController('view_builder');
     });
 
     // Test that no configuration entity types have data.
diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewStorageTest.php b/core/modules/views/lib/Drupal/views/Tests/ViewStorageTest.php
index f9beb79..70df481 100644
--- a/core/modules/views/lib/Drupal/views/Tests/ViewStorageTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/ViewStorageTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\views\Tests;
 
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\views\ViewStorageController;
 use Drupal\views\Entity\View;
 use Drupal\views\Plugin\views\display\Page;
@@ -72,11 +73,11 @@ public static function getInfo() {
    */
   function testConfigurationEntityCRUD() {
     // Get the configuration entity information and controller.
-    $this->info = entity_get_info('view');
+    $this->info = \Drupal::entityManager()->getDefinition('view');
     $this->controller = $this->container->get('entity.manager')->getStorageController('view');
 
     // Confirm that an info array has been returned.
-    $this->assertTrue(!empty($this->info) && is_array($this->info), 'The View info array is loaded.');
+    $this->assertTrue($this->info instanceof EntityTypeInterface, 'The View info array is loaded.');
 
     // Confirm we have the correct controller class.
     $this->assertTrue($this->controller instanceof ViewStorageController, 'The correct controller is loaded.');
diff --git a/core/modules/views/tests/Drupal/views/Tests/Plugin/argument_validator/EntityTest.php b/core/modules/views/tests/Drupal/views/Tests/Plugin/argument_validator/EntityTest.php
index 15d44f6..c6a9104 100644
--- a/core/modules/views/tests/Drupal/views/Tests/Plugin/argument_validator/EntityTest.php
+++ b/core/modules/views/tests/Drupal/views/Tests/Plugin/argument_validator/EntityTest.php
@@ -37,7 +37,7 @@ class EntityTest extends UnitTestCase {
   /**
    * The entity manager.
    *
-   * @var \PHPUnit_Framework_MockObject_MockObject|\Drupal\Core\Entity\EntityManager
+   * @var \PHPUnit_Framework_MockObject_MockObject|\Drupal\Core\Entity\EntityManagerInterface
    */
   protected $entityManager;
 
@@ -62,9 +62,7 @@ public static function getInfo() {
   protected function setUp() {
     parent::setUp();
 
-    $this->entityManager = $this->getMockBuilder('Drupal\Core\Entity\EntityManager')
-      ->disableOriginalConstructor()
-      ->getMock();
+    $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
 
     $mock_entity = $this->getMockBuilder('Drupal\Core\Entity\Entity')
       ->disableOriginalConstructor()
diff --git a/core/modules/views/views.views.inc b/core/modules/views/views.views.inc
index edf844a..02e41cb 100644
--- a/core/modules/views/views.views.inc
+++ b/core/modules/views/views.views.inc
@@ -113,10 +113,10 @@ function views_views_data() {
   );
 
   // Registers an entity area handler per entity type.
-  foreach (entity_get_info() as $entity_type => $entity_info) {
+  foreach (\Drupal::entityManager()->getDefinitions() as $entity_type => $entity_info) {
     // Exclude entity types, which cannot be rendered.
-    if (!empty($entity_info['controllers']['view_builder'])) {
-      $label = $entity_info['label'];
+    if ($entity_info->hasController('view_builder')) {
+      $label = $entity_info->getLabel();
       $data['views']['entity_' . $entity_type] = array(
         'title' => t('Rendered entity - @label', array('@label' => $label)),
         'help' => t('Displays a rendered @label entity in an area.', array('@label' => $label)),
diff --git a/core/modules/views_ui/lib/Drupal/views_ui/ViewListController.php b/core/modules/views_ui/lib/Drupal/views_ui/ViewListController.php
index ed9d983..3b2c57b 100644
--- a/core/modules/views_ui/lib/Drupal/views_ui/ViewListController.php
+++ b/core/modules/views_ui/lib/Drupal/views_ui/ViewListController.php
@@ -13,6 +13,7 @@
 use Drupal\Core\Config\Entity\ConfigEntityListController;
 use Drupal\Core\Entity\EntityControllerInterface;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -31,10 +32,9 @@ class ViewListController extends ConfigEntityListController implements EntityCon
   /**
    * {@inheritdoc}
    */
-  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
     return new static(
-      $entity_type,
-      $container->get('entity.manager')->getStorageController($entity_type),
+      $container->get('entity.manager')->getStorageController($entity_info->getId()),
       $entity_info,
       $container->get('plugin.manager.views.display'),
       $container->get('module_handler')
@@ -44,19 +44,17 @@ public static function createInstance(ContainerInterface $container, $entity_typ
   /**
    * Constructs a new EntityListController object.
    *
-   * @param string $entity_type.
-   *   The type of entity to be listed.
    * @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage.
    *   The entity storage controller class.
-   * @param array $entity_info
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
    *   An array of entity info for this entity type.
    * @param \Drupal\Component\Plugin\PluginManagerInterface $display_manager
    *   The views display plugin manager to use.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    *   The module handler.
    */
-  public function __construct($entity_type, EntityStorageControllerInterface $storage, $entity_info, PluginManagerInterface $display_manager, ModuleHandlerInterface $module_handler) {
-    parent::__construct($entity_type, $entity_info, $storage, $module_handler);
+  public function __construct(EntityStorageControllerInterface $storage, EntityTypeInterface $entity_info, PluginManagerInterface $display_manager, ModuleHandlerInterface $module_handler) {
+    parent::__construct($entity_info, $storage, $module_handler);
 
     $this->displayManager = $display_manager;
   }
diff --git a/core/modules/views_ui/tests/Drupal/views_ui/Tests/ViewListControllerTest.php b/core/modules/views_ui/tests/Drupal/views_ui/Tests/ViewListControllerTest.php
index e170fc8..d058288 100644
--- a/core/modules/views_ui/tests/Drupal/views_ui/Tests/ViewListControllerTest.php
+++ b/core/modules/views_ui/tests/Drupal/views_ui/Tests/ViewListControllerTest.php
@@ -31,7 +31,6 @@ public function testBuildRowEntityList() {
     $storage_controller = $this->getMockBuilder('Drupal\views\ViewStorageController')
       ->disableOriginalConstructor()
       ->getMock();
-    $entity_info = array();
     $display_manager = $this->getMockBuilder('\Drupal\views\Plugin\ViewsPluginManager')
       ->disableOriginalConstructor()
       ->getMock();
@@ -121,13 +120,12 @@ public function testBuildRowEntityList() {
     $container->set('string_translation', $this->getStringTranslationStub());
     \Drupal::setContainer($container);
 
-    $module_handler = $this->getMockBuilder('Drupal\Core\Extension\ModuleHandler')
-      ->disableOriginalConstructor()
-      ->getMock();
+    $module_handler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
 
     // Setup a view list controller with a mocked buildOperations method,
     // because t() is called on there.
-    $view_list_controller = $this->getMock('Drupal\views_ui\ViewListController', array('buildOperations'), array('view', $storage_controller, $entity_info, $display_manager, $module_handler));
+    $entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
+    $view_list_controller = $this->getMock('Drupal\views_ui\ViewListController', array('buildOperations'), array($storage_controller, $entity_type, $display_manager, $module_handler));
     $view_list_controller->expects($this->any())
       ->method('buildOperations')
       ->will($this->returnValue(array()));
diff --git a/core/modules/views_ui/views_ui.module b/core/modules/views_ui/views_ui.module
index c175caa..fadeace 100644
--- a/core/modules/views_ui/views_ui.module
+++ b/core/modules/views_ui/views_ui.module
@@ -39,18 +39,16 @@ function views_ui_menu() {
  * Implements hook_entity_info().
  */
 function views_ui_entity_info(&$entity_info) {
-  $entity_info['view']['controllers'] += array(
-    'list' => 'Drupal\views_ui\ViewListController',
-    'form' => array(
-      'edit' => 'Drupal\views_ui\ViewEditFormController',
-      'add' => 'Drupal\views_ui\ViewAddFormController',
-      'preview' => 'Drupal\views_ui\ViewPreviewFormController',
-      'clone' => 'Drupal\views_ui\ViewCloneFormController',
-      'delete' => 'Drupal\views_ui\ViewDeleteFormController',
-      'break_lock' => 'Drupal\views_ui\Form\BreakLockForm',
-    ),
-  );
-  $entity_info['view']['links']['edit-form'] = 'views_ui.edit';
+  /** @var $entity_info \Drupal\Core\Entity\EntityTypeInterface[] */
+  $entity_info['view']
+    ->setForm('edit', 'Drupal\views_ui\ViewEditFormController')
+    ->setForm('add', 'Drupal\views_ui\ViewAddFormController')
+    ->setForm('preview', 'Drupal\views_ui\ViewPreviewFormController')
+    ->setForm('clone', 'Drupal\views_ui\ViewCloneFormController')
+    ->setForm('delete', 'Drupal\views_ui\ViewDeleteFormController')
+    ->setForm('break_lock', 'Drupal\views_ui\Form\BreakLockForm')
+    ->setList('Drupal\views_ui\ViewListController')
+    ->setLinkTemplate('edit-form', 'views_ui.edit');
 }
 
 /**
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityListControllerTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityListControllerTest.php
index c8b07d6..6db04b5 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityListControllerTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityListControllerTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\Tests\Core\Entity;
 
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\entity_test\EntityTestListController;
 use Drupal\Tests\UnitTestCase;
 
 /**
@@ -21,7 +23,7 @@ class EntityListControllerTest extends UnitTestCase {
   /**
    * The entity used to construct the EntityListController.
    *
-   * @var \Drupal\user\Entity\Role
+   * @var \Drupal\user\RoleInterface|\PHPUnit_Framework_MockObject_MockObject
    */
   protected $role;
 
@@ -41,49 +43,16 @@ public static function getInfo() {
   }
 
   /**
-   * Entity info used by the test.
-   *
-   * @var array
-   *
-   * @see entity_get_info()
-   */
-  public static $entityInfo = array(
-    'entity_keys' => array(
-      'id' => 'id',
-      'label' => 'label',
-    ),
-    'config_prefix' => 'user.role',
-    'class' => 'Drupal\user\Entity\Role',
-  );
-
-
-  /**
    * {@inheritdoc}
    */
   protected function setUp() {
     parent::setUp();
 
-    $this->role = $this
-      ->getMockBuilder('Drupal\user\Entity\Role')
-      ->setConstructorArgs(array('entityInfo' => static::$entityInfo, 'user_role'))
-      ->getMock();
-
-    // Creates a stub role storage controller and replace the buildOperations()
-    // method with an empty version, because buildOperations() relies on hooks.
-    $role_storage_controller = $this->getMockBuilder('Drupal\user\RoleStorageController')
-      ->disableOriginalConstructor()
-      ->getMock();
-
-    $module_handler = $this->getMockBuilder('Drupal\Core\Extension\ModuleHandler')
-      ->disableOriginalConstructor()
-      ->getMock();
-
-    $this->entityListController = $this->getMock('Drupal\entity_test\EntityTestListController', array('buildOperations'), array('user_role', static::$entityInfo, $role_storage_controller, $module_handler));
-
-    $this->entityListController->expects($this->any())
-      ->method('buildOperations')
-      ->will($this->returnValue(array()));
-
+    $this->role = $this->getMock('Drupal\user\RoleInterface');
+    $role_storage_controller = $this->getMock('Drupal\user\RoleStorageControllerInterface');
+    $module_handler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
+    $entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
+    $this->entityListController = new TestEntityListController($entity_type, $role_storage_controller, $module_handler);
   }
 
   /**
@@ -146,3 +115,8 @@ public function providerTestBuildRow() {
 
 }
 
+class TestEntityListController extends EntityTestListController {
+  public function buildOperations(EntityInterface $entity) {
+    return array();
+  }
+}
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php
new file mode 100644
index 0000000..8624838
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php
@@ -0,0 +1,88 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Entity\EntityTypeTest.
+ */
+
+namespace Drupal\Tests\Core\Entity;
+
+use Drupal\Core\Entity\EntityType;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests the \Drupal\Core\Entity\EntityType class.
+ *
+ * @group Drupal
+ * @group Entity
+ */
+class EntityTypeTest extends UnitTestCase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Entity type test',
+      'description' => 'Unit test entity type info.',
+      'group' => 'Entity',
+    );
+  }
+
+  /**
+   * Sets up an EntityType object for a given set of values.
+   *
+   * @param array $definition
+   *   An array of values to use for the EntityType.
+   *
+   * @return \Drupal\Core\Entity\EntityTypeInterface
+   */
+  protected function setUpEntityType($definition) {
+    return new EntityType($definition);
+  }
+
+  /**
+   * Tests the getKeys() method.
+   *
+   * @dataProvider providerTestGetKeys
+   */
+  public function testGetKeys($entity_keys, $expected) {
+    $entity_type = $this->setUpEntityType(array('entity_keys' => $entity_keys));
+    $this->assertSame($expected, $entity_type->getKeys());
+  }
+
+  /**
+   * Tests the getKey() method.
+   *
+   * @dataProvider providerTestGetKeys
+   */
+  public function testGetKey($entity_keys, $expected) {
+    $entity_type = $this->setUpEntityType(array('entity_keys' => $entity_keys));
+    $this->assertSame($expected['bundle'], $entity_type->getKey('bundle'));
+    $this->assertSame(FALSE, $entity_type->getKey('bananas'));
+  }
+
+  /**
+   * Tests the hasKey() method.
+   *
+   * @dataProvider providerTestGetKeys
+   */
+  public function testHasKey($entity_keys, $expected) {
+    $entity_type = $this->setUpEntityType(array('entity_keys' => $entity_keys));
+    $this->assertSame(!empty($expected['bundle']), $entity_type->hasKey('bundle'));
+    $this->assertSame(!empty($expected['id']), $entity_type->hasKey('id'));
+    $this->assertSame(FALSE, $entity_type->hasKey('bananas'));
+  }
+
+  /**
+   * Provides test data.
+   */
+  public function providerTestGetKeys() {
+    return array(
+      array(array(), array('revision' => '', 'bundle' => '')),
+      array(array('id' => 'id'), array('id' => 'id', 'revision' => '', 'bundle' => '')),
+      array(array('bundle' => 'bundle'), array('bundle' => 'bundle', 'revision' => '')),
+    );
+  }
+
+}
