Merged LogoService and LogoTracker.

LogoTracker is now an implementation detail of LogoService.
In order to simplify things further,
its implementation was moved inside LogoService.

Bug: 761829
Change-Id: I33e5ab909e3f66aec31546bb1833069f973216c9
Reviewed-on: https://chromium-review.googlesource.com/c/1349511
Reviewed-by: Ramin Halavati <rhalavati@chromium.org>
Reviewed-by: Marc Treib <treib@chromium.org>
Commit-Queue: Yeol Park <peary2@gmail.com>
Cr-Commit-Position: refs/heads/master@{#614651}
diff --git a/components/search_provider_logos/BUILD.gn b/components/search_provider_logos/BUILD.gn
index 18619792..44688b7f 100644
--- a/components/search_provider_logos/BUILD.gn
+++ b/components/search_provider_logos/BUILD.gn
@@ -18,7 +18,6 @@
     "logo_service.h",
     "logo_service_impl.cc",
     "logo_service_impl.h",
-    "logo_tracker.cc",
     "logo_tracker.h",
     "switches.cc",
     "switches.h",
diff --git a/components/search_provider_logos/fixed_logo_api.h b/components/search_provider_logos/fixed_logo_api.h
index 7801357..4d625d0 100644
--- a/components/search_provider_logos/fixed_logo_api.h
+++ b/components/search_provider_logos/fixed_logo_api.h
@@ -16,11 +16,12 @@
 
 struct EncodedLogo;
 
-// Implements AppendFingerprintToLogoURL, defined in logo_tracker.h,
+// Implements AppendQueryparamsToLogoURL, defined in logo_common.h,
 // for static logos.
 GURL UseFixedLogoUrl(const GURL& logo_url, const std::string& fingerprint);
 
-// Implements ParseLogoResponse, defined in logo_tracker.h, for static logos.
+// Implements ParseLogoResponse, defined in logo_common.h,
+// for static logos.
 std::unique_ptr<EncodedLogo> ParseFixedLogoResponse(
     std::unique_ptr<std::string> response,
     base::Time response_time,
diff --git a/components/search_provider_logos/logo_service_impl.cc b/components/search_provider_logos/logo_service_impl.cc
index 692bf78..b7dc7df2 100644
--- a/components/search_provider_logos/logo_service_impl.cc
+++ b/components/search_provider_logos/logo_service_impl.cc
@@ -4,11 +4,17 @@
 
 #include "components/search_provider_logos/logo_service_impl.h"
 
+#include <algorithm>
+#include <utility>
+
 #include "base/command_line.h"
 #include "base/feature_list.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/task/post_task.h"
+#include "base/task_runner_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/default_clock.h"
 #include "build/build_config.h"
@@ -22,13 +28,60 @@
 #include "components/search_provider_logos/logo_tracker.h"
 #include "components/search_provider_logos/switches.h"
 #include "components/signin/core/browser/gaia_cookie_manager_service.h"
+#include "net/http/http_status_code.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
-
-using search_provider_logos::LogoTracker;
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "ui/gfx/image/image.h"
 
 namespace search_provider_logos {
 namespace {
 
+const int64_t kMaxDownloadBytes = 1024 * 1024;
+const int kDecodeLogoTimeoutSeconds = 30;
+
+// Implements a callback for image_fetcher::ImageDecoder. If Run() is called on
+// a callback returned by GetCallback() within 30 seconds, forwards the decoded
+// image to the wrapped callback. If not, sends an empty image to the wrapped
+// callback instead. Either way, deletes the object and prevents further calls.
+//
+// TODO(sfiera): find a more idiomatic way of setting a deadline on the
+// callback. This is implemented as a self-deleting object in part because it
+// needed to when it used to be a delegate and in part because I couldn't figure
+// out a better way, now that it isn't.
+class ImageDecodedHandlerWithTimeout {
+ public:
+  static base::RepeatingCallback<void(const gfx::Image&)> Wrap(
+      const base::RepeatingCallback<void(const SkBitmap&)>&
+          image_decoded_callback) {
+    auto* handler = new ImageDecodedHandlerWithTimeout(image_decoded_callback);
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(&ImageDecodedHandlerWithTimeout::OnImageDecoded,
+                       handler->weak_ptr_factory_.GetWeakPtr(), gfx::Image()),
+        base::TimeDelta::FromSeconds(kDecodeLogoTimeoutSeconds));
+    return base::BindRepeating(&ImageDecodedHandlerWithTimeout::OnImageDecoded,
+                               handler->weak_ptr_factory_.GetWeakPtr());
+  }
+
+ private:
+  explicit ImageDecodedHandlerWithTimeout(
+      const base::RepeatingCallback<void(const SkBitmap&)>&
+          image_decoded_callback)
+      : image_decoded_callback_(image_decoded_callback),
+        weak_ptr_factory_(this) {}
+
+  void OnImageDecoded(const gfx::Image& decoded_image) {
+    image_decoded_callback_.Run(decoded_image.AsBitmap());
+    delete this;
+  }
+
+  base::RepeatingCallback<void(const SkBitmap&)> image_decoded_callback_;
+  base::WeakPtrFactory<ImageDecodedHandlerWithTimeout> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ImageDecodedHandlerWithTimeout);
+};
+
 void ObserverOnLogoAvailable(LogoObserver* observer,
                              bool from_cache,
                              LogoCallbackReason type,
@@ -72,6 +125,57 @@
   }
 }
 
+// Returns whether the metadata for the cached logo indicates that the logo is
+// OK to show, i.e. it's not expired or it's allowed to be shown temporarily
+// after expiration.
+bool IsLogoOkToShow(const LogoMetadata& metadata, base::Time now) {
+  base::TimeDelta offset =
+      base::TimeDelta::FromMilliseconds(kMaxTimeToLiveMS * 3 / 2);
+  base::Time distant_past = now - offset;
+  // Sanity check so logos aren't accidentally cached forever.
+  if (metadata.expiration_time < distant_past) {
+    return false;
+  }
+  return metadata.can_show_after_expiration || metadata.expiration_time >= now;
+}
+
+// Reads the logo from the cache and returns it. Returns NULL if the cache is
+// empty, corrupt, expired, or doesn't apply to the current logo URL.
+std::unique_ptr<EncodedLogo> GetLogoFromCacheOnFileThread(LogoCache* logo_cache,
+                                                          const GURL& logo_url,
+                                                          base::Time now) {
+  const LogoMetadata* metadata = logo_cache->GetCachedLogoMetadata();
+  if (!metadata)
+    return nullptr;
+
+  if (metadata->source_url != logo_url || !IsLogoOkToShow(*metadata, now)) {
+    logo_cache->SetCachedLogo(nullptr);
+    return nullptr;
+  }
+
+  return logo_cache->GetCachedLogo();
+}
+
+void NotifyAndClear(std::vector<EncodedLogoCallback>* encoded_callbacks,
+                    std::vector<LogoCallback>* decoded_callbacks,
+                    LogoCallbackReason type,
+                    const EncodedLogo* encoded_logo,
+                    const Logo* decoded_logo) {
+  auto opt_encoded_logo =
+      encoded_logo ? base::Optional<EncodedLogo>(*encoded_logo) : base::nullopt;
+  for (EncodedLogoCallback& callback : *encoded_callbacks) {
+    std::move(callback).Run(type, opt_encoded_logo);
+  }
+  encoded_callbacks->clear();
+
+  auto opt_decoded_logo =
+      decoded_logo ? base::Optional<Logo>(*decoded_logo) : base::nullopt;
+  for (LogoCallback& callback : *decoded_callbacks) {
+    std::move(callback).Run(type, opt_decoded_logo);
+  }
+  decoded_callbacks->clear();
+}
+
 }  // namespace
 
 class LogoServiceImpl::SigninObserver
@@ -120,7 +224,15 @@
       signin_observer_(std::make_unique<SigninObserver>(
           cookie_service,
           base::BindRepeating(&LogoServiceImpl::SigninStatusChanged,
-                              base::Unretained(this)))) {}
+                              base::Unretained(this)))),
+      is_idle_(true),
+      is_cached_logo_valid_(false),
+      cache_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
+          {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
+           base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
+      logo_cache_(new LogoCache(cache_directory_),
+                  base::OnTaskRunnerDeleter(cache_task_runner_)),
+      weak_ptr_factory_(this) {}
 
 LogoServiceImpl::~LogoServiceImpl() = default;
 
@@ -128,6 +240,7 @@
   // The GaiaCookieManagerService may be destroyed at any point after Shutdown,
   // so make sure we drop any references to it.
   signin_observer_.reset();
+  ReturnToIdle(kDownloadOutcomeNotTracked);
 }
 
 void LogoServiceImpl::GetLogo(search_provider_logos::LogoObserver* observer) {
@@ -191,59 +304,402 @@
     return;
   }
 
-  InitializeLogoTrackerIfNecessary();
+  if (!clock_) {
+    clock_ = base::DefaultClock::GetInstance();
+  }
 
   const bool use_fixed_logo = !doodle_url.is_valid();
   if (use_fixed_logo) {
-    logo_tracker_->SetServerAPI(
-        logo_url, base::Bind(&search_provider_logos::ParseFixedLogoResponse),
-        base::Bind(&search_provider_logos::UseFixedLogoUrl));
+    SetServerAPI(
+        logo_url,
+        base::BindRepeating(&search_provider_logos::ParseFixedLogoResponse),
+        base::BindRepeating(&search_provider_logos::UseFixedLogoUrl));
   } else {
     // We encode the type of doodle (regular or gray) in the URL so that the
     // logo cache gets cleared when that value changes.
     GURL prefilled_url = AppendPreliminaryParamsToDoodleURL(
         want_gray_logo_getter_.Run(), doodle_url);
-    logo_tracker_->SetServerAPI(
+    SetServerAPI(
         prefilled_url,
         base::Bind(&search_provider_logos::ParseDoodleLogoResponse, base_url),
         base::Bind(&search_provider_logos::AppendFingerprintParamToDoodleURL));
   }
 
-  logo_tracker_->GetLogo(std::move(callbacks));
+  DCHECK(!logo_url_.is_empty());
+  DCHECK(callbacks.on_cached_decoded_logo_available ||
+         callbacks.on_cached_encoded_logo_available ||
+         callbacks.on_fresh_decoded_logo_available ||
+         callbacks.on_fresh_encoded_logo_available);
+
+  if (callbacks.on_cached_encoded_logo_available) {
+    on_cached_encoded_logo_.push_back(
+        std::move(callbacks.on_cached_encoded_logo_available));
+  }
+  if (callbacks.on_cached_decoded_logo_available) {
+    on_cached_decoded_logo_.push_back(
+        std::move(callbacks.on_cached_decoded_logo_available));
+  }
+  if (callbacks.on_fresh_encoded_logo_available) {
+    on_fresh_encoded_logo_.push_back(
+        std::move(callbacks.on_fresh_encoded_logo_available));
+  }
+  if (callbacks.on_fresh_decoded_logo_available) {
+    on_fresh_decoded_logo_.push_back(
+        std::move(callbacks.on_fresh_decoded_logo_available));
+  }
+
+  if (is_idle_) {
+    is_idle_ = false;
+
+    base::PostTaskAndReplyWithResult(
+        cache_task_runner_.get(), FROM_HERE,
+        base::BindRepeating(&GetLogoFromCacheOnFileThread,
+                            base::Unretained(logo_cache_.get()), logo_url_,
+                            clock_->Now()),
+        base::BindRepeating(&LogoServiceImpl::OnCachedLogoRead,
+                            weak_ptr_factory_.GetWeakPtr()));
+  } else if (is_cached_logo_valid_) {
+    NotifyAndClear(&on_cached_encoded_logo_, &on_cached_decoded_logo_,
+                   LogoCallbackReason::DETERMINED, cached_encoded_logo_.get(),
+                   cached_logo_.get());
+  }
 }
 
 void LogoServiceImpl::SetLogoCacheForTests(std::unique_ptr<LogoCache> cache) {
-  logo_cache_for_test_ = std::move(cache);
+  // |logo_cache_| has a custom deleter, which makes the two unique_ptrs
+  // be different types. so one can't be moved on top of the other.
+  logo_cache_.reset(std::move(cache).release());
 }
 
 void LogoServiceImpl::SetClockForTests(base::Clock* clock) {
-  clock_for_test_ = clock;
+  clock_ = clock;
 }
 
-void LogoServiceImpl::InitializeLogoTrackerIfNecessary() {
-  if (logo_tracker_) {
+void LogoServiceImpl::SetServerAPI(
+    const GURL& logo_url,
+    const ParseLogoResponse& parse_logo_response_func,
+    const AppendQueryparamsToLogoURL& append_queryparams_func) {
+  if (logo_url == logo_url_)
+    return;
+
+  ReturnToIdle(kDownloadOutcomeNotTracked);
+
+  logo_url_ = logo_url;
+  parse_logo_response_func_ = parse_logo_response_func;
+  append_queryparams_func_ = append_queryparams_func;
+}
+
+void LogoServiceImpl::ClearCachedLogo() {
+  // First cancel any fetch that might be ongoing.
+  ReturnToIdle(kDownloadOutcomeNotTracked);
+  // Then clear any cached logo.
+  SetCachedLogo(nullptr);
+}
+
+void LogoServiceImpl::ReturnToIdle(int outcome) {
+  if (outcome != kDownloadOutcomeNotTracked) {
+    UMA_HISTOGRAM_ENUMERATION("NewTabPage.LogoDownloadOutcome",
+                              static_cast<LogoDownloadOutcome>(outcome),
+                              DOWNLOAD_OUTCOME_COUNT);
+  }
+
+  // Cancel the current asynchronous operation, if any.
+  loader_.reset();
+  weak_ptr_factory_.InvalidateWeakPtrs();
+
+  // Reset state.
+  is_idle_ = true;
+  cached_logo_.reset();
+  cached_encoded_logo_.reset();
+  is_cached_logo_valid_ = false;
+
+  // Clear callbacks.
+  NotifyAndClear(&on_cached_encoded_logo_, &on_cached_decoded_logo_,
+                 LogoCallbackReason::CANCELED, nullptr, nullptr);
+  NotifyAndClear(&on_fresh_encoded_logo_, &on_fresh_decoded_logo_,
+                 LogoCallbackReason::CANCELED, nullptr, nullptr);
+}
+
+void LogoServiceImpl::OnCachedLogoRead(
+    std::unique_ptr<EncodedLogo> cached_logo) {
+  DCHECK(!is_idle_);
+
+  if (cached_logo) {
+    // Store the value of logo->encoded_image for use below. This ensures that
+    // logo->encoded_image is evaulated before base::Passed(&logo), which sets
+    // logo to NULL.
+    scoped_refptr<base::RefCountedString> encoded_image =
+        cached_logo->encoded_image;
+    image_decoder_->DecodeImage(
+        encoded_image->data(), gfx::Size(),  // No particular size desired.
+        ImageDecodedHandlerWithTimeout::Wrap(base::BindRepeating(
+            &LogoServiceImpl::OnCachedLogoAvailable,
+            weak_ptr_factory_.GetWeakPtr(), base::Passed(&cached_logo))));
+  } else {
+    OnCachedLogoAvailable({}, SkBitmap());
+  }
+}
+
+void LogoServiceImpl::SetCachedLogo(std::unique_ptr<EncodedLogo> logo) {
+  cache_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&LogoCache::SetCachedLogo,
+                                base::Unretained(logo_cache_.get()),
+                                base::Owned(logo.release())));
+}
+
+void LogoServiceImpl::SetCachedMetadata(const LogoMetadata& metadata) {
+  cache_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&LogoCache::UpdateCachedLogoMetadata,
+                                base::Unretained(logo_cache_.get()), metadata));
+}
+
+void LogoServiceImpl::OnCachedLogoAvailable(
+    std::unique_ptr<EncodedLogo> encoded_logo,
+    const SkBitmap& image) {
+  DCHECK(!is_idle_);
+
+  if (!image.isNull()) {
+    cached_logo_.reset(new Logo());
+    cached_logo_->metadata = encoded_logo->metadata;
+    cached_logo_->image = image;
+    cached_encoded_logo_ = std::move(encoded_logo);
+  }
+  is_cached_logo_valid_ = true;
+  NotifyAndClear(&on_cached_encoded_logo_, &on_cached_decoded_logo_,
+                 LogoCallbackReason::DETERMINED, cached_encoded_logo_.get(),
+                 cached_logo_.get());
+  FetchLogo();
+}
+
+void LogoServiceImpl::FetchLogo() {
+  DCHECK(!loader_);
+  DCHECK(!is_idle_);
+
+  std::string fingerprint;
+  if (cached_logo_ && !cached_logo_->metadata.fingerprint.empty() &&
+      cached_logo_->metadata.expiration_time >= clock_->Now()) {
+    fingerprint = cached_logo_->metadata.fingerprint;
+  }
+  GURL url = append_queryparams_func_.Run(logo_url_, fingerprint);
+  net::NetworkTrafficAnnotationTag traffic_annotation =
+      net::DefineNetworkTrafficAnnotation("logo_service", R"(
+        semantics {
+          sender: "Logo Service"
+          description:
+            "Provides the logo image (aka Doodle) if Google is your configured "
+            "search provider."
+          trigger: "Displaying the new tab page on iOS or Android."
+          data:
+            "Logo ID, and the user's Google cookies to show for example "
+            "birthday doodles at appropriate times."
+          destination: OTHER
+        }
+        policy {
+          cookies_allowed: YES
+          cookies_store: "user"
+          setting:
+            "Choosing a non-Google search engine in Chromium settings under "
+            "'Search Engine' will disable this feature."
+          policy_exception_justification:
+            "Not implemented, considered not useful as it does not upload any"
+            "data and just downloads a logo image."
+        })");
+  auto request = std::make_unique<network::ResourceRequest>();
+  request->url = url;
+  // TODO(https://crbug.com/808498) re-add data use measurement once
+  // SimpleURLLoader supports it:
+  // data_use_measurement::DataUseUserData::SEARCH_PROVIDER_LOGOS
+  loader_ =
+      network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
+  loader_->DownloadToString(
+      url_loader_factory_.get(),
+      base::BindOnce(&LogoServiceImpl::OnURLLoadComplete,
+                     base::Unretained(this), loader_.get()),
+      kMaxDownloadBytes);
+  logo_download_start_time_ = base::TimeTicks::Now();
+}
+
+void LogoServiceImpl::OnFreshLogoParsed(bool* parsing_failed,
+                                        bool from_http_cache,
+                                        std::unique_ptr<EncodedLogo> logo) {
+  DCHECK(!is_idle_);
+
+  if (logo)
+    logo->metadata.source_url = logo_url_;
+
+  if (!logo || !logo->encoded_image) {
+    OnFreshLogoAvailable(std::move(logo), /*download_failed=*/false,
+                         *parsing_failed, from_http_cache, SkBitmap());
+  } else {
+    // Store the value of logo->encoded_image for use below. This ensures that
+    // logo->encoded_image is evaulated before base::Passed(&logo), which sets
+    // logo to NULL.
+    scoped_refptr<base::RefCountedString> encoded_image = logo->encoded_image;
+    image_decoder_->DecodeImage(
+        encoded_image->data(), gfx::Size(),  // No particular size desired.
+        ImageDecodedHandlerWithTimeout::Wrap(base::BindRepeating(
+            &LogoServiceImpl::OnFreshLogoAvailable,
+            weak_ptr_factory_.GetWeakPtr(), base::Passed(&logo),
+            /*download_failed=*/false, *parsing_failed, from_http_cache)));
+  }
+}
+
+void LogoServiceImpl::OnFreshLogoAvailable(
+    std::unique_ptr<EncodedLogo> encoded_logo,
+    bool download_failed,
+    bool parsing_failed,
+    bool from_http_cache,
+    const SkBitmap& image) {
+  DCHECK(!is_idle_);
+
+  LogoDownloadOutcome download_outcome = DOWNLOAD_OUTCOME_COUNT;
+  std::unique_ptr<Logo> logo;
+
+  if (download_failed) {
+    download_outcome = DOWNLOAD_OUTCOME_DOWNLOAD_FAILED;
+  } else if (encoded_logo && !encoded_logo->encoded_image && cached_logo_ &&
+             !encoded_logo->metadata.fingerprint.empty() &&
+             encoded_logo->metadata.fingerprint ==
+                 cached_logo_->metadata.fingerprint) {
+    // The cached logo was revalidated, i.e. its fingerprint was verified.
+    // mime_type isn't sent when revalidating, so copy it from the cached logo.
+    encoded_logo->metadata.mime_type = cached_logo_->metadata.mime_type;
+    SetCachedMetadata(encoded_logo->metadata);
+    download_outcome = DOWNLOAD_OUTCOME_LOGO_REVALIDATED;
+  } else if (encoded_logo && image.isNull()) {
+    // Image decoding failed. Do nothing.
+    download_outcome = DOWNLOAD_OUTCOME_DECODING_FAILED;
+  } else {
+    // Check if the server returned a valid, non-empty response.
+    if (encoded_logo) {
+      UMA_HISTOGRAM_BOOLEAN("NewTabPage.LogoImageDownloaded", from_http_cache);
+
+      DCHECK(!image.isNull());
+      logo.reset(new Logo());
+      logo->metadata = encoded_logo->metadata;
+      logo->image = image;
+    }
+
+    if (logo) {
+      download_outcome = DOWNLOAD_OUTCOME_NEW_LOGO_SUCCESS;
+    } else {
+      if (parsing_failed)
+        download_outcome = DOWNLOAD_OUTCOME_PARSING_FAILED;
+      else
+        download_outcome = DOWNLOAD_OUTCOME_NO_LOGO_TODAY;
+    }
+  }
+
+  LogoCallbackReason callback_type = LogoCallbackReason::FAILED;
+  switch (download_outcome) {
+    case DOWNLOAD_OUTCOME_NEW_LOGO_SUCCESS:
+      DCHECK(encoded_logo);
+      DCHECK(logo);
+      callback_type = LogoCallbackReason::DETERMINED;
+      break;
+
+    case DOWNLOAD_OUTCOME_PARSING_FAILED:
+    case DOWNLOAD_OUTCOME_NO_LOGO_TODAY:
+      // Clear the cached logo if it was non-null. Otherwise, report this as a
+      // revalidation of "no logo".
+      DCHECK(!encoded_logo);
+      DCHECK(!logo);
+      if (cached_logo_) {
+        callback_type = LogoCallbackReason::DETERMINED;
+      } else {
+        callback_type = LogoCallbackReason::REVALIDATED;
+      }
+      break;
+
+    case DOWNLOAD_OUTCOME_DOWNLOAD_FAILED:
+      // In the download failed, don't notify the callback at all, since the
+      // callback should continue to use the cached logo.
+      DCHECK(!encoded_logo);
+      DCHECK(!logo);
+      callback_type = LogoCallbackReason::FAILED;
+      break;
+
+    case DOWNLOAD_OUTCOME_DECODING_FAILED:
+      DCHECK(encoded_logo);
+      DCHECK(!logo);
+      encoded_logo.reset();
+      callback_type = LogoCallbackReason::FAILED;
+      break;
+
+    case DOWNLOAD_OUTCOME_LOGO_REVALIDATED:
+      // In the server reported that the cached logo is still current, don't
+      // notify the callback at all, since the callback should continue to use
+      // the cached logo.
+      DCHECK(encoded_logo);
+      DCHECK(!logo);
+      callback_type = LogoCallbackReason::REVALIDATED;
+      break;
+
+    case DOWNLOAD_OUTCOME_COUNT:
+      NOTREACHED();
+      return;
+  }
+
+  NotifyAndClear(&on_fresh_encoded_logo_, &on_fresh_decoded_logo_,
+                 callback_type, encoded_logo.get(), logo.get());
+
+  switch (callback_type) {
+    case LogoCallbackReason::DETERMINED:
+      SetCachedLogo(std::move(encoded_logo));
+      break;
+
+    default:
+      break;
+  }
+
+  ReturnToIdle(download_outcome);
+}
+
+void LogoServiceImpl::OnURLLoadComplete(const network::SimpleURLLoader* source,
+                                        std::unique_ptr<std::string> body) {
+  DCHECK(!is_idle_);
+  std::unique_ptr<network::SimpleURLLoader> cleanup_loader(loader_.release());
+
+  if (source->NetError() != net::OK) {
+    OnFreshLogoAvailable({}, /*download_failed=*/true, false, false,
+                         SkBitmap());
     return;
   }
 
-  std::unique_ptr<LogoCache> logo_cache = std::move(logo_cache_for_test_);
-  if (!logo_cache) {
-    logo_cache = std::make_unique<LogoCache>(cache_directory_);
+  if (!source->ResponseInfo() || !source->ResponseInfo()->headers ||
+      source->ResponseInfo()->headers->response_code() != net::HTTP_OK) {
+    OnFreshLogoAvailable({}, /*download_failed=*/true, false, false,
+                         SkBitmap());
+    return;
   }
 
-  base::Clock* clock = clock_for_test_;
-  if (!clock) {
-    clock = base::DefaultClock::GetInstance();
-  }
+  UMA_HISTOGRAM_TIMES("NewTabPage.LogoDownloadTime",
+                      base::TimeTicks::Now() - logo_download_start_time_);
 
-  logo_tracker_ = std::make_unique<LogoTracker>(url_loader_factory_,
-                                                std::move(image_decoder_),
-                                                std::move(logo_cache), clock);
+  std::unique_ptr<std::string> response =
+      body ? std::move(body) : std::make_unique<std::string>();
+  base::Time response_time = clock_->Now();
+
+  bool from_http_cache = !source->ResponseInfo()->network_accessed;
+
+  bool* parsing_failed = new bool(false);
+  base::PostTaskWithTraitsAndReplyWithResult(
+      FROM_HERE,
+      {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
+       base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+      base::BindOnce(parse_logo_response_func_, std::move(response),
+                     response_time, parsing_failed),
+      base::BindOnce(&LogoServiceImpl::OnFreshLogoParsed,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     base::Owned(parsing_failed), from_http_cache));
 }
 
 void LogoServiceImpl::SigninStatusChanged() {
   // Clear any cached logo, since it may be personalized (e.g. birthday Doodle).
-  InitializeLogoTrackerIfNecessary();
-  logo_tracker_->ClearCachedLogo();
+  if (!clock_) {
+    clock_ = base::DefaultClock::GetInstance();
+  }
+  ClearCachedLogo();
 }
 
 }  // namespace search_provider_logos
diff --git a/components/search_provider_logos/logo_service_impl.h b/components/search_provider_logos/logo_service_impl.h
index 16bf118..276d72f23 100644
--- a/components/search_provider_logos/logo_service_impl.h
+++ b/components/search_provider_logos/logo_service_impl.h
@@ -5,10 +5,18 @@
 #ifndef COMPONENTS_SEARCH_PROVIDER_LOGOS_ANDROID_LOGO_SERVICE_IMPL_H_
 #define COMPONENTS_SEARCH_PROVIDER_LOGOS_ANDROID_LOGO_SERVICE_IMPL_H_
 
+#include <memory>
+#include <string>
+#include <vector>
+
 #include "base/callback.h"
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "base/time/clock.h"
+#include "base/time/time.h"
 #include "components/search_provider_logos/logo_common.h"
 #include "components/search_provider_logos/logo_service.h"
 
@@ -24,13 +32,13 @@
 }  // namespace image_fetcher
 
 namespace network {
+class SimpleURLLoader;
 class SharedURLLoaderFactory;
 }  // namespace network
 
 namespace search_provider_logos {
 
 class LogoCache;
-class LogoTracker;
 class LogoObserver;
 
 class LogoServiceImpl : public LogoService {
@@ -48,9 +56,28 @@
   // KeyedService implementation.
   void Shutdown() override;
 
-  // LogoService implementation.
-  void GetLogo(LogoCallbacks callbacks) override;
+  // Defines the server API for downloading and parsing the logo. This must be
+  // called at least once before calling GetLogo().
+  //
+  // |logo_url| is the URL from which the logo will be downloaded. If |logo_url|
+  // is different than the current logo URL, any pending callbacks will be run
+  // with LogoCallbackReason::CANCELED.
+  //
+  // |parse_logo_response_func| is a callback that will be used to parse the
+  // server's response into a EncodedLogo object. |append_queryparams_func| is a
+  // callback that will return the URL from which to download the logo.
+  void SetServerAPI(const GURL& logo_url,
+                    const ParseLogoResponse& parse_logo_response_func,
+                    const AppendQueryparamsToLogoURL& append_queryparams_func);
+
+  // Retrieves the current search provider's logo from the local cache and/or
+  // over the network, and registers the callbacks to be called when the cached
+  // and/or fresh logos are available.
+  //
+  // At least one callback must be non-null. All non-null callbacks will be
+  // invoked exactly once.
   void GetLogo(LogoObserver* observer) override;
+  void GetLogo(LogoCallbacks callbacks) override;
 
   // Overrides the cache used to store logos.
   void SetLogoCacheForTests(std::unique_ptr<LogoCache> cache);
@@ -59,12 +86,70 @@
   void SetClockForTests(base::Clock* clock);
 
  private:
+  // These values must stay in sync with the NewTabPageLogoDownloadOutcome enum
+  // in histograms.xml. And any addtion should be treated as append-only!
+  // Animated doodle is not covered by this enum.
+  enum LogoDownloadOutcome {
+    DOWNLOAD_OUTCOME_NEW_LOGO_SUCCESS,
+    DOWNLOAD_OUTCOME_NO_LOGO_TODAY,
+    DOWNLOAD_OUTCOME_DOWNLOAD_FAILED,
+    DOWNLOAD_OUTCOME_PARSING_FAILED,
+    DOWNLOAD_OUTCOME_DECODING_FAILED,
+    DOWNLOAD_OUTCOME_LOGO_REVALIDATED,
+    DOWNLOAD_OUTCOME_COUNT,
+  };
+
+  const int kDownloadOutcomeNotTracked = -1;
+
   class SigninObserver;
 
-  void InitializeLogoTrackerIfNecessary();
-
   void SigninStatusChanged();
 
+  // Clear any cached logo we might have. Useful on sign-out to get rid of
+  // (potentially) personalized data.
+  void ClearCachedLogo();
+
+  // Cancels the current asynchronous operation, if any, and resets all member
+  // variables that change as the logo is fetched. This method also records UMA
+  // histograms for for the given LogoDownloadOutcome.
+  void ReturnToIdle(int outcome);
+
+  // Called when the cached logo has been read from the cache. |cached_logo|
+  // will be NULL if there wasn't a valid, up-to-date logo in the cache.
+  void OnCachedLogoRead(std::unique_ptr<EncodedLogo> cached_logo);
+
+  // Called when the cached logo has been decoded into an SkBitmap. |image| will
+  // be NULL if decoding failed.
+  void OnCachedLogoAvailable(std::unique_ptr<EncodedLogo> encoded_logo,
+                             const SkBitmap& image);
+
+  // Stores |logo| in the cache.
+  void SetCachedLogo(std::unique_ptr<EncodedLogo> logo);
+
+  // Updates the metadata for the logo already stored in the cache.
+  void SetCachedMetadata(const LogoMetadata& metadata);
+
+  // Starts fetching the current logo over the network.
+  void FetchLogo();
+
+  // Called when the logo has been downloaded and parsed. |logo| will be NULL
+  // if the server's response was invalid.
+  void OnFreshLogoParsed(bool* parsing_failed,
+                         bool from_http_cache,
+                         std::unique_ptr<EncodedLogo> logo);
+
+  // Called when the fresh logo has been decoded into an SkBitmap. |image| will
+  // be NULL if decoding failed.
+  void OnFreshLogoAvailable(std::unique_ptr<EncodedLogo> logo,
+                            bool download_failed,
+                            bool parsing_failed,
+                            bool from_http_cache,
+                            const SkBitmap& image);
+
+  // Invoked by |loader|.
+  void OnURLLoadComplete(const network::SimpleURLLoader* source,
+                         std::unique_ptr<std::string> body);
+
   // Constructor arguments.
   const base::FilePath cache_directory_;
   TemplateURLService* const template_url_service_;
@@ -74,19 +159,58 @@
   // optimized for gray backgrounds or not.
   base::RepeatingCallback<bool()> want_gray_logo_getter_;
 
-  // logo_tracker_ takes ownership if/when it is initialized.
   std::unique_ptr<image_fetcher::ImageDecoder> image_decoder_;
 
   std::unique_ptr<SigninObserver> signin_observer_;
 
-  // For testing.
-  base::Clock* clock_for_test_ = nullptr;
+  // The URL from which the logo is fetched.
+  GURL logo_url_;
 
-  // For testing. logo_tracker_ takes ownership if/when it is initialized.
-  std::unique_ptr<LogoCache> logo_cache_for_test_;
+  // The function used to parse the logo response from the server.
+  ParseLogoResponse parse_logo_response_func_;
 
-  // Lazily initialized on first call to GetLogo().
-  std::unique_ptr<search_provider_logos::LogoTracker> logo_tracker_;
+  // The function used to include the cached logo's fingerprint and call to
+  // action request in the logo URL.
+  AppendQueryparamsToLogoURL append_queryparams_func_;
+
+  // False if an asynchronous task is currently running.
+  bool is_idle_;
+
+  // The logo that's been read from the cache, or NULL if the cache is empty.
+  // Meaningful only if is_cached_logo_valid_ is true; NULL otherwise.
+  std::unique_ptr<Logo> cached_logo_;
+  std::unique_ptr<EncodedLogo> cached_encoded_logo_;
+
+  // Whether the value of |cached_logo_| reflects the actual cached logo.
+  // This will be false if the logo hasn't been read from the cache yet.
+  // |cached_logo_| may be NULL even if |is_cached_logo_valid_| is true, if no
+  // logo is cached.
+  bool is_cached_logo_valid_;
+
+  // The timestamp for the last time a logo was stated to be downloaded.
+  base::TimeTicks logo_download_start_time_;
+
+  // The SimpleURLLoader currently fetching the logo. NULL when not loading.
+  std::unique_ptr<network::SimpleURLLoader> loader_;
+
+  // Lists of callbacks to be invoked when logos are available. All should be
+  // empty when the state is IDLE.
+  std::vector<LogoCallback> on_cached_decoded_logo_;
+  std::vector<EncodedLogoCallback> on_cached_encoded_logo_;
+  std::vector<LogoCallback> on_fresh_decoded_logo_;
+  std::vector<EncodedLogoCallback> on_fresh_encoded_logo_;
+
+  // The SequencedTaskRunner on which the cache lives.
+  scoped_refptr<base::SequencedTaskRunner> cache_task_runner_;
+
+  // The cache used to persist the logo on disk. Used only on a background
+  // SequencedTaskRunner.
+  std::unique_ptr<LogoCache, base::OnTaskRunnerDeleter> logo_cache_;
+
+  // Clock used to determine current time. Can be overridden in tests.
+  base::Clock* clock_ = nullptr;
+
+  base::WeakPtrFactory<LogoServiceImpl> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(LogoServiceImpl);
 };
diff --git a/components/search_provider_logos/logo_tracker.cc b/components/search_provider_logos/logo_tracker.cc
deleted file mode 100644
index bb2f5bf..0000000
--- a/components/search_provider_logos/logo_tracker.cc
+++ /dev/null
@@ -1,506 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/search_provider_logos/logo_tracker.h"
-
-#include <algorithm>
-#include <utility>
-
-#include "base/bind_helpers.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/task/post_task.h"
-#include "base/task_runner_util.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/clock.h"
-#include "components/data_use_measurement/core/data_use_user_data.h"
-#include "components/image_fetcher/core/image_decoder.h"
-#include "net/http/http_response_headers.h"
-#include "net/http/http_status_code.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "services/network/public/cpp/resource_request.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/simple_url_loader.h"
-#include "ui/gfx/geometry/size.h"
-#include "ui/gfx/image/image.h"
-
-namespace search_provider_logos {
-
-namespace {
-
-const int64_t kMaxDownloadBytes = 1024 * 1024;
-const int kDecodeLogoTimeoutSeconds = 30;
-
-// Implements a callback for image_fetcher::ImageDecoder. If Run() is called on
-// a callback returned by GetCallback() within 30 seconds, forwards the decoded
-// image to the wrapped callback. If not, sends an empty image to the wrapped
-// callback instead. Either way, deletes the object and prevents further calls.
-//
-// TODO(sfiera): find a more idiomatic way of setting a deadline on the
-// callback. This is implemented as a self-deleting object in part because it
-// needed to when it used to be a delegate and in part because I couldn't figure
-// out a better way, now that it isn't.
-class ImageDecodedHandlerWithTimeout {
- public:
-  static base::Callback<void(const gfx::Image&)> Wrap(
-      const base::Callback<void(const SkBitmap&)>& image_decoded_callback) {
-    auto* handler = new ImageDecodedHandlerWithTimeout(image_decoded_callback);
-    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-        FROM_HERE,
-        base::BindOnce(&ImageDecodedHandlerWithTimeout::OnImageDecoded,
-                       handler->weak_ptr_factory_.GetWeakPtr(), gfx::Image()),
-        base::TimeDelta::FromSeconds(kDecodeLogoTimeoutSeconds));
-    return base::Bind(&ImageDecodedHandlerWithTimeout::OnImageDecoded,
-                      handler->weak_ptr_factory_.GetWeakPtr());
-  }
-
- private:
-  explicit ImageDecodedHandlerWithTimeout(
-      const base::Callback<void(const SkBitmap&)>& image_decoded_callback)
-      : image_decoded_callback_(image_decoded_callback),
-        weak_ptr_factory_(this) {}
-
-  void OnImageDecoded(const gfx::Image& decoded_image) {
-    image_decoded_callback_.Run(decoded_image.AsBitmap());
-    delete this;
-  }
-
-  base::Callback<void(const SkBitmap&)> image_decoded_callback_;
-  base::WeakPtrFactory<ImageDecodedHandlerWithTimeout> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(ImageDecodedHandlerWithTimeout);
-};
-
-// Returns whether the metadata for the cached logo indicates that the logo is
-// OK to show, i.e. it's not expired or it's allowed to be shown temporarily
-// after expiration.
-bool IsLogoOkToShow(const LogoMetadata& metadata, base::Time now) {
-  base::TimeDelta offset =
-      base::TimeDelta::FromMilliseconds(kMaxTimeToLiveMS * 3 / 2);
-  base::Time distant_past = now - offset;
-  // Sanity check so logos aren't accidentally cached forever.
-  if (metadata.expiration_time < distant_past) {
-    return false;
-  }
-  return metadata.can_show_after_expiration || metadata.expiration_time >= now;
-}
-
-// Reads the logo from the cache and returns it. Returns NULL if the cache is
-// empty, corrupt, expired, or doesn't apply to the current logo URL.
-std::unique_ptr<EncodedLogo> GetLogoFromCacheOnFileThread(LogoCache* logo_cache,
-                                                          const GURL& logo_url,
-                                                          base::Time now) {
-  const LogoMetadata* metadata = logo_cache->GetCachedLogoMetadata();
-  if (!metadata)
-    return nullptr;
-
-  if (metadata->source_url != logo_url || !IsLogoOkToShow(*metadata, now)) {
-    logo_cache->SetCachedLogo(nullptr);
-    return nullptr;
-  }
-
-  return logo_cache->GetCachedLogo();
-}
-
-void NotifyAndClear(std::vector<EncodedLogoCallback>* encoded_callbacks,
-                    std::vector<LogoCallback>* decoded_callbacks,
-                    LogoCallbackReason type,
-                    const EncodedLogo* encoded_logo,
-                    const Logo* decoded_logo) {
-  auto opt_encoded_logo =
-      encoded_logo ? base::Optional<EncodedLogo>(*encoded_logo) : base::nullopt;
-  for (EncodedLogoCallback& callback : *encoded_callbacks) {
-    std::move(callback).Run(type, opt_encoded_logo);
-  }
-  encoded_callbacks->clear();
-
-  auto opt_decoded_logo =
-      decoded_logo ? base::Optional<Logo>(*decoded_logo) : base::nullopt;
-  for (LogoCallback& callback : *decoded_callbacks) {
-    std::move(callback).Run(type, opt_decoded_logo);
-  }
-  decoded_callbacks->clear();
-}
-
-}  // namespace
-
-LogoTracker::LogoTracker(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    std::unique_ptr<image_fetcher::ImageDecoder> image_decoder,
-    std::unique_ptr<LogoCache> logo_cache,
-    base::Clock* clock)
-    : is_idle_(true),
-      is_cached_logo_valid_(false),
-      image_decoder_(std::move(image_decoder)),
-      cache_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
-          {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
-           base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
-      logo_cache_(logo_cache.release(),
-                  base::OnTaskRunnerDeleter(cache_task_runner_)),
-      clock_(clock),
-      url_loader_factory_(std::move(url_loader_factory)),
-      weak_ptr_factory_(this) {}
-
-LogoTracker::~LogoTracker() {
-  ReturnToIdle(kDownloadOutcomeNotTracked);
-}
-
-void LogoTracker::SetServerAPI(
-    const GURL& logo_url,
-    const ParseLogoResponse& parse_logo_response_func,
-    const AppendQueryparamsToLogoURL& append_queryparams_func) {
-  if (logo_url == logo_url_)
-    return;
-
-  ReturnToIdle(kDownloadOutcomeNotTracked);
-
-  logo_url_ = logo_url;
-  parse_logo_response_func_ = parse_logo_response_func;
-  append_queryparams_func_ = append_queryparams_func;
-}
-
-void LogoTracker::GetLogo(LogoCallbacks callbacks) {
-  DCHECK(!logo_url_.is_empty());
-  DCHECK(callbacks.on_cached_decoded_logo_available ||
-         callbacks.on_cached_encoded_logo_available ||
-         callbacks.on_fresh_decoded_logo_available ||
-         callbacks.on_fresh_encoded_logo_available);
-
-  if (callbacks.on_cached_encoded_logo_available) {
-    on_cached_encoded_logo_.push_back(
-        std::move(callbacks.on_cached_encoded_logo_available));
-  }
-  if (callbacks.on_cached_decoded_logo_available) {
-    on_cached_decoded_logo_.push_back(
-        std::move(callbacks.on_cached_decoded_logo_available));
-  }
-  if (callbacks.on_fresh_encoded_logo_available) {
-    on_fresh_encoded_logo_.push_back(
-        std::move(callbacks.on_fresh_encoded_logo_available));
-  }
-  if (callbacks.on_fresh_decoded_logo_available) {
-    on_fresh_decoded_logo_.push_back(
-        std::move(callbacks.on_fresh_decoded_logo_available));
-  }
-
-  if (is_idle_) {
-    is_idle_ = false;
-    base::PostTaskAndReplyWithResult(
-        cache_task_runner_.get(), FROM_HERE,
-        base::Bind(&GetLogoFromCacheOnFileThread,
-                   base::Unretained(logo_cache_.get()), logo_url_,
-                   clock_->Now()),
-        base::Bind(&LogoTracker::OnCachedLogoRead,
-                   weak_ptr_factory_.GetWeakPtr()));
-  } else if (is_cached_logo_valid_) {
-    NotifyAndClear(&on_cached_encoded_logo_, &on_cached_decoded_logo_,
-                   LogoCallbackReason::DETERMINED, cached_encoded_logo_.get(),
-                   cached_logo_.get());
-  }
-}
-
-void LogoTracker::ClearCachedLogo() {
-  // First cancel any fetch that might be ongoing.
-  ReturnToIdle(kDownloadOutcomeNotTracked);
-  // Then clear any cached logo.
-  SetCachedLogo(nullptr);
-}
-
-void LogoTracker::ReturnToIdle(int outcome) {
-  if (outcome != kDownloadOutcomeNotTracked) {
-    UMA_HISTOGRAM_ENUMERATION("NewTabPage.LogoDownloadOutcome",
-                              static_cast<LogoDownloadOutcome>(outcome),
-                              DOWNLOAD_OUTCOME_COUNT);
-  }
-  // Cancel the current asynchronous operation, if any.
-  loader_.reset();
-  weak_ptr_factory_.InvalidateWeakPtrs();
-
-  // Reset state.
-  is_idle_ = true;
-  cached_logo_.reset();
-  cached_encoded_logo_.reset();
-  is_cached_logo_valid_ = false;
-
-  // Clear callbacks.
-  NotifyAndClear(&on_cached_encoded_logo_, &on_cached_decoded_logo_,
-                 LogoCallbackReason::CANCELED, nullptr, nullptr);
-  NotifyAndClear(&on_fresh_encoded_logo_, &on_fresh_decoded_logo_,
-                 LogoCallbackReason::CANCELED, nullptr, nullptr);
-}
-
-void LogoTracker::OnCachedLogoRead(std::unique_ptr<EncodedLogo> cached_logo) {
-  DCHECK(!is_idle_);
-
-  if (cached_logo) {
-    // Store the value of logo->encoded_image for use below. This ensures that
-    // logo->encoded_image is evaulated before base::Passed(&logo), which sets
-    // logo to NULL.
-    scoped_refptr<base::RefCountedString> encoded_image =
-        cached_logo->encoded_image;
-    image_decoder_->DecodeImage(
-        encoded_image->data(), gfx::Size(),  // No particular size desired.
-        ImageDecodedHandlerWithTimeout::Wrap(base::Bind(
-            &LogoTracker::OnCachedLogoAvailable, weak_ptr_factory_.GetWeakPtr(),
-            base::Passed(&cached_logo))));
-  } else {
-    OnCachedLogoAvailable({}, SkBitmap());
-  }
-}
-
-void LogoTracker::OnCachedLogoAvailable(
-    std::unique_ptr<EncodedLogo> encoded_logo,
-    const SkBitmap& image) {
-  DCHECK(!is_idle_);
-
-  if (!image.isNull()) {
-    cached_logo_.reset(new Logo());
-    cached_logo_->metadata = encoded_logo->metadata;
-    cached_logo_->image = image;
-    cached_encoded_logo_ = std::move(encoded_logo);
-  }
-  is_cached_logo_valid_ = true;
-  NotifyAndClear(&on_cached_encoded_logo_, &on_cached_decoded_logo_,
-                 LogoCallbackReason::DETERMINED, cached_encoded_logo_.get(),
-                 cached_logo_.get());
-  FetchLogo();
-}
-
-void LogoTracker::SetCachedLogo(std::unique_ptr<EncodedLogo> logo) {
-  cache_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&LogoCache::SetCachedLogo,
-                                base::Unretained(logo_cache_.get()),
-                                base::Owned(logo.release())));
-}
-
-void LogoTracker::SetCachedMetadata(const LogoMetadata& metadata) {
-  cache_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&LogoCache::UpdateCachedLogoMetadata,
-                                base::Unretained(logo_cache_.get()), metadata));
-}
-
-void LogoTracker::FetchLogo() {
-  DCHECK(!loader_);
-  DCHECK(!is_idle_);
-
-  std::string fingerprint;
-  if (cached_logo_ && !cached_logo_->metadata.fingerprint.empty() &&
-      cached_logo_->metadata.expiration_time >= clock_->Now()) {
-    fingerprint = cached_logo_->metadata.fingerprint;
-  }
-  GURL url = append_queryparams_func_.Run(logo_url_, fingerprint);
-
-  net::NetworkTrafficAnnotationTag traffic_annotation =
-      net::DefineNetworkTrafficAnnotation("logo_tracker", R"(
-        semantics {
-          sender: "Logo Tracker"
-          description:
-            "Provides the logo image (aka Doodle) if Google is your configured "
-            "search provider."
-          trigger: "Displaying the new tab page on iOS or Android."
-          data:
-            "Logo ID, and the user's Google cookies to show for example "
-            "birthday doodles at appropriate times."
-          destination: OTHER
-        }
-        policy {
-          cookies_allowed: YES
-          cookies_store: "user"
-          setting:
-            "Choosing a non-Google search engine in Chromium settings under "
-            "'Search Engine' will disable this feature."
-          policy_exception_justification:
-            "Not implemented, considered not useful as it does not upload any"
-            "data and just downloads a logo image."
-        })");
-  auto request = std::make_unique<network::ResourceRequest>();
-  request->url = url;
-  // TODO(https://crbug.com/808498) re-add data use measurement once
-  // SimpleURLLoader supports it:
-  // data_use_measurement::DataUseUserData::SEARCH_PROVIDER_LOGOS
-  loader_ =
-      network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
-  loader_->DownloadToString(
-      url_loader_factory_.get(),
-      base::BindOnce(&LogoTracker::OnURLLoadComplete, base::Unretained(this),
-                     loader_.get()),
-      kMaxDownloadBytes);
-  logo_download_start_time_ = base::TimeTicks::Now();
-}
-
-void LogoTracker::OnFreshLogoParsed(bool* parsing_failed,
-                                    bool from_http_cache,
-                                    std::unique_ptr<EncodedLogo> logo) {
-  DCHECK(!is_idle_);
-
-  if (logo)
-    logo->metadata.source_url = logo_url_;
-
-  if (!logo || !logo->encoded_image) {
-    OnFreshLogoAvailable(std::move(logo), /*download_failed=*/false,
-                         *parsing_failed, from_http_cache, SkBitmap());
-  } else {
-    // Store the value of logo->encoded_image for use below. This ensures that
-    // logo->encoded_image is evaulated before base::Passed(&logo), which sets
-    // logo to NULL.
-    scoped_refptr<base::RefCountedString> encoded_image = logo->encoded_image;
-    image_decoder_->DecodeImage(
-        encoded_image->data(), gfx::Size(),  // No particular size desired.
-        ImageDecodedHandlerWithTimeout::Wrap(base::Bind(
-            &LogoTracker::OnFreshLogoAvailable, weak_ptr_factory_.GetWeakPtr(),
-            base::Passed(&logo), /*download_failed=*/false, *parsing_failed,
-            from_http_cache)));
-  }
-}
-
-void LogoTracker::OnFreshLogoAvailable(
-    std::unique_ptr<EncodedLogo> encoded_logo,
-    bool download_failed,
-    bool parsing_failed,
-    bool from_http_cache,
-    const SkBitmap& image) {
-  DCHECK(!is_idle_);
-
-  LogoDownloadOutcome download_outcome = DOWNLOAD_OUTCOME_COUNT;
-  std::unique_ptr<Logo> logo;
-
-  if (download_failed) {
-    download_outcome = DOWNLOAD_OUTCOME_DOWNLOAD_FAILED;
-  } else if (encoded_logo && !encoded_logo->encoded_image && cached_logo_ &&
-             !encoded_logo->metadata.fingerprint.empty() &&
-             encoded_logo->metadata.fingerprint ==
-                 cached_logo_->metadata.fingerprint) {
-    // The cached logo was revalidated, i.e. its fingerprint was verified.
-    // mime_type isn't sent when revalidating, so copy it from the cached logo.
-    encoded_logo->metadata.mime_type = cached_logo_->metadata.mime_type;
-    SetCachedMetadata(encoded_logo->metadata);
-    download_outcome = DOWNLOAD_OUTCOME_LOGO_REVALIDATED;
-  } else if (encoded_logo && image.isNull()) {
-    // Image decoding failed. Do nothing.
-    download_outcome = DOWNLOAD_OUTCOME_DECODING_FAILED;
-  } else {
-    // Check if the server returned a valid, non-empty response.
-    if (encoded_logo) {
-      UMA_HISTOGRAM_BOOLEAN("NewTabPage.LogoImageDownloaded", from_http_cache);
-
-      DCHECK(!image.isNull());
-      logo.reset(new Logo());
-      logo->metadata = encoded_logo->metadata;
-      logo->image = image;
-    }
-
-    if (logo) {
-      download_outcome = DOWNLOAD_OUTCOME_NEW_LOGO_SUCCESS;
-    } else {
-      if (parsing_failed)
-        download_outcome = DOWNLOAD_OUTCOME_PARSING_FAILED;
-      else
-        download_outcome = DOWNLOAD_OUTCOME_NO_LOGO_TODAY;
-    }
-  }
-
-  LogoCallbackReason callback_type = LogoCallbackReason::FAILED;
-  switch (download_outcome) {
-    case DOWNLOAD_OUTCOME_NEW_LOGO_SUCCESS:
-      DCHECK(encoded_logo);
-      DCHECK(logo);
-      callback_type = LogoCallbackReason::DETERMINED;
-      break;
-
-    case DOWNLOAD_OUTCOME_PARSING_FAILED:
-    case DOWNLOAD_OUTCOME_NO_LOGO_TODAY:
-      // Clear the cached logo if it was non-null. Otherwise, report this as a
-      // revalidation of "no logo".
-      DCHECK(!encoded_logo);
-      DCHECK(!logo);
-      if (cached_logo_) {
-        callback_type = LogoCallbackReason::DETERMINED;
-      } else {
-        callback_type = LogoCallbackReason::REVALIDATED;
-      }
-      break;
-
-    case DOWNLOAD_OUTCOME_DOWNLOAD_FAILED:
-      // In the download failed, don't notify the callback at all, since the
-      // callback should continue to use the cached logo.
-      DCHECK(!encoded_logo);
-      DCHECK(!logo);
-      callback_type = LogoCallbackReason::FAILED;
-      break;
-
-    case DOWNLOAD_OUTCOME_DECODING_FAILED:
-      DCHECK(encoded_logo);
-      DCHECK(!logo);
-      encoded_logo.reset();
-      callback_type = LogoCallbackReason::FAILED;
-      break;
-
-    case DOWNLOAD_OUTCOME_LOGO_REVALIDATED:
-      // In the server reported that the cached logo is still current, don't
-      // notify the callback at all, since the callback should continue to use
-      // the cached logo.
-      DCHECK(encoded_logo);
-      DCHECK(!logo);
-      callback_type = LogoCallbackReason::REVALIDATED;
-      break;
-
-    case DOWNLOAD_OUTCOME_COUNT:
-      NOTREACHED();
-      return;
-  }
-
-  NotifyAndClear(&on_fresh_encoded_logo_, &on_fresh_decoded_logo_,
-                 callback_type, encoded_logo.get(), logo.get());
-
-  switch (callback_type) {
-    case LogoCallbackReason::DETERMINED:
-      SetCachedLogo(std::move(encoded_logo));
-      break;
-
-    default:
-      break;
-  }
-
-  ReturnToIdle(download_outcome);
-}
-
-void LogoTracker::OnURLLoadComplete(const network::SimpleURLLoader* source,
-                                    std::unique_ptr<std::string> body) {
-  DCHECK(!is_idle_);
-  std::unique_ptr<network::SimpleURLLoader> cleanup_loader(loader_.release());
-
-  if (source->NetError() != net::OK) {
-    OnFreshLogoAvailable({}, /*download_failed=*/true, false, false,
-                         SkBitmap());
-    return;
-  }
-
-  if (!source->ResponseInfo() || !source->ResponseInfo()->headers ||
-      source->ResponseInfo()->headers->response_code() != net::HTTP_OK) {
-    OnFreshLogoAvailable({}, /*download_failed=*/true, false, false,
-                         SkBitmap());
-    return;
-  }
-
-  UMA_HISTOGRAM_TIMES("NewTabPage.LogoDownloadTime",
-                      base::TimeTicks::Now() - logo_download_start_time_);
-
-  std::unique_ptr<std::string> response =
-      body ? std::move(body) : std::make_unique<std::string>();
-  base::Time response_time = clock_->Now();
-
-  bool from_http_cache = !source->ResponseInfo()->network_accessed;
-
-  bool* parsing_failed = new bool(false);
-  base::PostTaskWithTraitsAndReplyWithResult(
-      FROM_HERE,
-      {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
-       base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
-      base::BindOnce(parse_logo_response_func_, std::move(response),
-                     response_time, parsing_failed),
-      base::BindOnce(&LogoTracker::OnFreshLogoParsed,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     base::Owned(parsing_failed), from_http_cache));
-}
-
-}  // namespace search_provider_logos
diff --git a/components/search_provider_logos/logo_tracker.h b/components/search_provider_logos/logo_tracker.h
index 762aa8db..af057c8 100644
--- a/components/search_provider_logos/logo_tracker.h
+++ b/components/search_provider_logos/logo_tracker.h
@@ -5,32 +5,7 @@
 #ifndef COMPONENTS_SEARCH_PROVIDER_LOGOS_LOGO_TRACKER_H_
 #define COMPONENTS_SEARCH_PROVIDER_LOGOS_LOGO_TRACKER_H_
 
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/files/file_path.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/optional.h"
-#include "base/sequenced_task_runner.h"
-#include "base/time/clock.h"
-#include "base/time/time.h"
-#include "components/search_provider_logos/logo_cache.h"
 #include "components/search_provider_logos/logo_common.h"
-#include "url/gurl.h"
-
-namespace image_fetcher {
-class ImageDecoder;
-}
-
-namespace network {
-class SimpleURLLoader;
-class SharedURLLoaderFactory;
-}
 
 namespace search_provider_logos {
 
@@ -55,173 +30,6 @@
   virtual void OnObserverRemoved() = 0;
 };
 
-// This class provides the logo for a search provider. Logos are downloaded from
-// the search provider's logo URL and cached on disk.
-//
-// Call SetServerAPI() at least once to specify how to get the logo from the
-// server. Then call GetLogo() to trigger retrieval of the logo and receive
-// updates once the cached and/or fresh logos are available.
-class LogoTracker {
- public:
-  // Constructs a LogoTracker with the given LogoDelegate. Takes ownership of
-  // |delegate|, which will be deleted at the same time as the LogoTracker.
-  //
-  // |cached_logo_directory| is the directory in which the cached logo and its
-  // metadata should be saved.
-  //
-  // |background_task_runner| is the TaskRunner that should be used to for
-  // CPU-intensive background operations.
-  //
-  // |url_loader_factory| is the SharedURLLoaderFactory used to download
-  // the logo.
-  explicit LogoTracker(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      std::unique_ptr<image_fetcher::ImageDecoder> image_decoder,
-      std::unique_ptr<LogoCache> logo_cache,
-      base::Clock* clock);
-
-  ~LogoTracker();
-
-  // Defines the server API for downloading and parsing the logo. This must be
-  // called at least once before calling GetLogo().
-  //
-  // |logo_url| is the URL from which the logo will be downloaded. If |logo_url|
-  // is different than the current logo URL, any pending callbacks will be run
-  // with LogoCallbackReason::CANCELED.
-  //
-  // |parse_logo_response_func| is a callback that will be used to parse the
-  // server's response into a EncodedLogo object. |append_queryparams_func| is a
-  // callback that will return the URL from which to download the logo.
-  void SetServerAPI(const GURL& logo_url,
-                    const ParseLogoResponse& parse_logo_response_func,
-                    const AppendQueryparamsToLogoURL& append_queryparams_func);
-
-  // Retrieves the current search provider's logo from the local cache and/or
-  // over the network, and registers the callbacks to be called when the cached
-  // and/or fresh logos are available.
-  //
-  // At least one callback must be non-null. All non-null callbacks will be
-  // invoked exactly once.
-  void GetLogo(LogoCallbacks callbacks);
-
-  // Clear any cached logo we might have. Useful on sign-out to get rid of
-  // (potentially) personalized data.
-  void ClearCachedLogo();
-
- private:
-  // These values must stay in sync with the NewTabPageLogoDownloadOutcome enum
-  // in histograms.xml. And any addtion should be treated as append-only!
-  // Animated doodle is not covered by this enum.
-  enum LogoDownloadOutcome {
-      DOWNLOAD_OUTCOME_NEW_LOGO_SUCCESS,
-      DOWNLOAD_OUTCOME_NO_LOGO_TODAY,
-      DOWNLOAD_OUTCOME_DOWNLOAD_FAILED,
-      DOWNLOAD_OUTCOME_PARSING_FAILED,
-      DOWNLOAD_OUTCOME_DECODING_FAILED,
-      DOWNLOAD_OUTCOME_LOGO_REVALIDATED,
-      DOWNLOAD_OUTCOME_COUNT,
-  };
-
-  const int kDownloadOutcomeNotTracked = -1;
-
-  // Cancels the current asynchronous operation, if any, and resets all member
-  // variables that change as the logo is fetched. This method also records UMA
-  // histograms for for the given LogoDownloadOutcome.
-  void ReturnToIdle(int outcome);
-
-  // Called when the cached logo has been read from the cache. |cached_logo|
-  // will be NULL if there wasn't a valid, up-to-date logo in the cache.
-  void OnCachedLogoRead(std::unique_ptr<EncodedLogo> cached_logo);
-
-  // Called when the cached logo has been decoded into an SkBitmap. |image| will
-  // be NULL if decoding failed.
-  void OnCachedLogoAvailable(std::unique_ptr<EncodedLogo> encoded_logo,
-                             const SkBitmap& image);
-
-  // Stores |logo| in the cache.
-  void SetCachedLogo(std::unique_ptr<EncodedLogo> logo);
-
-  // Updates the metadata for the logo already stored in the cache.
-  void SetCachedMetadata(const LogoMetadata& metadata);
-
-  // Starts fetching the current logo over the network.
-  void FetchLogo();
-
-  // Called when the logo has been downloaded and parsed. |logo| will be NULL
-  // if the server's response was invalid.
-  void OnFreshLogoParsed(bool* parsing_failed,
-                         bool from_http_cache,
-                         std::unique_ptr<EncodedLogo> logo);
-
-  // Called when the fresh logo has been decoded into an SkBitmap. |image| will
-  // be NULL if decoding failed.
-  void OnFreshLogoAvailable(std::unique_ptr<EncodedLogo> logo,
-                            bool download_failed,
-                            bool parsing_failed,
-                            bool from_http_cache,
-                            const SkBitmap& image);
-
-  // Invoked by |loader|.
-  void OnURLLoadComplete(const network::SimpleURLLoader* source,
-                         std::unique_ptr<std::string> body);
-
-  // The URL from which the logo is fetched.
-  GURL logo_url_;
-
-  // The function used to parse the logo response from the server.
-  ParseLogoResponse parse_logo_response_func_;
-
-  // The function used to include the cached logo's fingerprint and call to
-  // action request in the logo URL.
-  AppendQueryparamsToLogoURL append_queryparams_func_;
-
-  // False if an asynchronous task is currently running.
-  bool is_idle_;
-
-  // The logo that's been read from the cache, or NULL if the cache is empty.
-  // Meaningful only if is_cached_logo_valid_ is true; NULL otherwise.
-  std::unique_ptr<Logo> cached_logo_;
-  std::unique_ptr<EncodedLogo> cached_encoded_logo_;
-
-  // Whether the value of |cached_logo_| reflects the actual cached logo.
-  // This will be false if the logo hasn't been read from the cache yet.
-  // |cached_logo_| may be NULL even if |is_cached_logo_valid_| is true, if no
-  // logo is cached.
-  bool is_cached_logo_valid_;
-
-  // The timestamp for the last time a logo is stated to be downloaded.
-  base::TimeTicks logo_download_start_time_;
-
-  // The SimpleURLLoader currently fetching the logo. NULL when not loading.
-  std::unique_ptr<network::SimpleURLLoader> loader_;
-
-  // Lists of callbacks to be invoked when logos are available. All should be
-  // empty when the state is IDLE.
-  std::vector<LogoCallback> on_cached_decoded_logo_;
-  std::vector<EncodedLogoCallback> on_cached_encoded_logo_;
-  std::vector<LogoCallback> on_fresh_decoded_logo_;
-  std::vector<EncodedLogoCallback> on_fresh_encoded_logo_;
-
-  const std::unique_ptr<image_fetcher::ImageDecoder> image_decoder_;
-
-  // The SequencedTaskRunner on which the cache lives.
-  scoped_refptr<base::SequencedTaskRunner> cache_task_runner_;
-
-  // The cache used to persist the logo on disk. Used only on a background
-  // SequencedTaskRunner.
-  std::unique_ptr<LogoCache, base::OnTaskRunnerDeleter> logo_cache_;
-
-  // Clock used to determine current time. Can be overridden in tests.
-  base::Clock* clock_;
-
-  // Used for network requests.
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-
-  base::WeakPtrFactory<LogoTracker> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(LogoTracker);
-};
-
 }  // namespace search_provider_logos
 
 #endif  // COMPONENTS_SEARCH_PROVIDER_LOGOS_LOGO_TRACKER_H_
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 98445ce..3f02682 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -36878,7 +36878,7 @@
 <enum name="NewTabPageLogoDownloadOutcome">
   <summary>
     These values are defined in LogoDownloadOutcome enum in
-    components/search_provider_logos/logo_tracker.h.
+    components/search_provider_logos/logo_service_impl.h.
   </summary>
   <int value="0" label="New logo success"/>
   <int value="1" label="No logo today"/>
@@ -51419,7 +51419,7 @@
   <int value="35370363" label="suggestions_service"/>
   <int value="35380758" label="download_manager_resume"/>
   <int value="35565745" label="gaia_auth_list_accounts"/>
-  <int value="36859107" label="logo_tracker"/>
+  <int value="36859107" label="logo_service"/>
   <int value="37249086" label="android_device_manager_socket"/>
   <int value="37531401" label="proxy_script_fetcher"/>
   <int value="38670228" label="download_internals_webui_source"/>
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 473df42..f3c86896 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -142,7 +142,8 @@
  <item id="intranet_redirect_detector" hash_code="21785164" type="0" content_hash_code="62025595" os_list="linux,windows" file_path="chrome/browser/intranet_redirect_detector.cc"/>
  <item id="invalidation_service" hash_code="72354423" type="0" content_hash_code="78425687" os_list="linux,windows" file_path="components/invalidation/impl/gcm_network_channel.cc"/>
  <item id="lib_address_input" hash_code="50816767" type="0" content_hash_code="57977576" os_list="linux,windows" file_path="third_party/libaddressinput/chromium/chrome_metadata_source.cc"/>
- <item id="logo_tracker" hash_code="36859107" type="0" content_hash_code="67588075" os_list="linux,windows" file_path="components/search_provider_logos/logo_tracker.cc"/>
+ <item id="logo_service" hash_code="35473769" type="0" content_hash_code="20271299" os_list="linux,windows" file_path="components/search_provider_logos/logo_service_impl.cc"/>
+ <item id="logo_tracker" hash_code="36859107" type="0" deprecated="2018-12-07" content_hash_code="67588075" file_path=""/>
  <item id="md_downloads_dom_handler" hash_code="65603364" type="0" content_hash_code="134779147" os_list="linux,windows" file_path="chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler.cc"/>
  <item id="metrics_report_ukm" hash_code="727478" type="0" content_hash_code="40919254" os_list="linux,windows" file_path="components/metrics/net/net_metrics_log_uploader.cc"/>
  <item id="metrics_report_uma" hash_code="727528" type="0" content_hash_code="10176197" os_list="linux,windows" file_path="components/metrics/net/net_metrics_log_uploader.cc"/>