diff --git a/file_entity.module b/file_entity.module
index 77ec6ea..05c827d 100644
--- a/file_entity.module
+++ b/file_entity.module
@@ -1135,8 +1135,6 @@ function file_entity_ctools_plugin_api($owner, $api) {
  *
  * Stream wrappers that are considered private should implement a 'private'
  * flag equal to TRUE in hook_stream_wrappers().
- *
- * @todo Unify core's hook_file_download() as a 'download' op of file_entity_access().
  */
 
 /**
@@ -1148,6 +1146,7 @@ function file_entity_ctools_plugin_api($owner, $api) {
  *   - "update"
  *   - "delete"
  *   - "create"
+ *   - "download"
  * @param $file
  *   The file object on which the operation is to be performed, or file type
  *   (e.g. 'image') for "create" operation.
@@ -1161,7 +1160,7 @@ function file_entity_ctools_plugin_api($owner, $api) {
 function file_entity_access($op, $file = NULL, $account = NULL) {
   $rights = &drupal_static(__FUNCTION__, array());
 
-  if (!$file && !in_array($op, array('view', 'update', 'delete', 'create'), TRUE)) {
+  if (!$file && !in_array($op, array('view', 'update', 'delete', 'create', 'download'), TRUE)) {
     // If there was no file to check against, and the $op was not one of the
     // supported ones, we return access denied.
     return FALSE;
@@ -1232,7 +1231,6 @@ function file_entity_access($op, $file = NULL, $account = NULL) {
  * Implements hook_file_entity_access().
  */
 function file_entity_file_entity_access($op, $file, $account) {
-  $grants = array();
 
   // If the file URI is invalid, deny access.
   if (is_object($file) && !file_valid_uri($file->uri)) {
@@ -1245,35 +1243,46 @@ function file_entity_file_entity_access($op, $file, $account) {
     }
   }
 
+  $is_own_file = is_object($file) && ($account->uid == $file->uid);
+
   if ($op == 'update') {
-    if (user_access('edit any files', $account) || (is_object($file) && user_access('edit own files', $account) && ($account->uid == $file->uid))) {
+    if (user_access('edit any files', $account) || ($is_own_file && user_access('edit own files', $account))) {
       return FILE_ENTITY_ACCESS_ALLOW;
     }
   }
 
   if ($op == 'delete') {
-    if (user_access('delete any files', $account) || (is_object($file) && user_access('delete own files', $account) && ($account->uid == $file->uid))) {
+     if (user_access('delete any files', $account) || ($is_own_file && user_access('delete own files', $account))) {
       return FILE_ENTITY_ACCESS_ALLOW;
     }
   }
 
-  if ($op == 'view' && is_object($file) && file_uri_scheme($file->uri) == 'private') {
-    // When viewing private files, we can only invoke hook_file_download()
-    // if the $account user objet matches the current user.
-    if ($GLOBALS['user']->uid == $account->uid) {
-      foreach (module_implements('file_download') as $module) {
-        $access = module_invoke($module, 'file_download', $file->uri);
-        if ($access === -1) {
-          return FILE_ENTITY_ACCESS_DENY;
-        }
-        elseif (!empty($access)) {
-          $grants[] = $access;
-        }
-      }
-    }
+  return FILE_ENTITY_ACCESS_IGNORE;
+}
+
+/**
+ * Implements hook_file_download().
+ */
+function file_entity_file_download($uri) {
+
+  // Get the File entity from $uri.
+  $file = file_load_multiple(array(), array('uri' => $uri));
+  $file = reset($file);
+
+  // We didn't find any managed record in the database. File entity module will
+  // not make any decision in this case. E.g. image derivatives will fall here.
+  if (!is_object($file) || empty($file->fid)) {
+    return NULL;
   }
 
-  return !empty($grants) ? FILE_ENTITY_ACCESS_ALLOW : FILE_ENTITY_ACCESS_IGNORE;
+  // If user can view file details, he's able also to download the file.
+  if (file_entity_access('download', $file)) {
+    return array(
+      'Content-Type' => $file->filemime,
+      'Content-Length' => $file->filesize,
+    );
+  }
+  return -1;
 }
 
 /**
diff --git a/tests/file_entity.test b/tests/file_entity.test
index 94f49db..fc60e9e 100644
--- a/tests/file_entity.test
+++ b/tests/file_entity.test
@@ -537,4 +537,91 @@ class FileEntityAccessTestCase extends FileEntityTestHelper {
     $this->drupalGet("file/{$file->fid}/delete");
     $this->assertResponse(200, 'Users with access can access the file add page');
   }
+
+  /**
+   * Test download access.
+   */
+  function testFileEntityPrivateDownloadAccess() {
+    foreach ($this->getPrivateDownloadAccessCases() as $case) {
+      // Create users and login only if non-anonymous.
+      $authenticated_user = !is_null($case['permissions']);
+      if ($authenticated_user) {
+        $account = $this->drupalCreateUser($case['permissions']);
+        $this->drupalLogin($account);
+      }
+
+      // Create private, permanent files owned by this user only he's an owner.
+      if (!empty($case['owner'])) {
+        $file = next($this->files['text']);
+        $file->status = FILE_STATUS_PERMANENT;
+        $file->uid = $account->uid;
+        file_save($file);
+        $file = file_move($file, 'private://');
+
+        // Check if the physical file is there.
+        $arguments = array('%name' => $file->filename, '%username' => $account->name, '%uri' => $file->uri);
+        $this->assertTrue(is_file($file->uri), format_string('File %name owned by %username successfully created at %uri.', $arguments));
+        $url = file_create_url($file->uri);
+        $message_file_info = ' ' . format_string('File %uri was checked.', array('%uri' => $file->uri));
+      }
+
+      // Try to download the file.
+      $this->drupalGet($url);
+      $this->assertResponse($case['expect'], $case['message'] . $message_file_info);
+
+      // Logout authenticated users.
+      if ($authenticated_user) {
+        $this->drupalLogout();
+      }
+    }
+  }
+
+  /**
+   * Helper for testFileEntityPrivateDownloadAccess() test.
+   *
+   * Defines several cases for accesing private files.
+   *
+   * @return array
+   *   Array of associative arrays, each one having the next keys:
+   *   - "message" string with the assertion message.
+   *   - "permissions" array of permissions or NULL for anonymous user.
+   *   - "expect" expected HTTP response code.
+   *   - "owner" Optional boolean indicating if the user is a file owner.
+   */
+  protected function getPrivateDownloadAccessCases() {
+    return array(
+      array(
+        'message' => "Owner not granted with 'view own private files' cannot download his files.",
+        'permissions' => array(),
+        'expect' => 403,
+        'owner' => TRUE,
+      ),
+      array(
+        'message' => "Owner granted with 'view own private files' can download his files.",
+        'permissions' => array('view own private files'),
+        'expect' => 200,
+        'owner' => TRUE,
+      ),
+      array(
+        'message' => "Anonymous cannot download private files.",
+        'permissions' => NULL,
+        'expect' => 403,
+      ),
+      array(
+        'message' => "Regular user cannot download other's private files.",
+        'permissions' => array(),
+        'expect' => 403,
+      ),
+      array(
+        'message' => "User able to see everyone's public file details cannot download private files.",
+        'permissions' => array('view files'),
+        'expect' => 403,
+      ),
+      array(
+        'message' => "Superuser can download everyone's files.",
+        'permissions' => array('bypass file access'),
+        'expect' => 200,
+      ),
+    );
+  }
 }
