Create local_card_migration_manager class to manage local card migration(CL#2).

The local_card_migration_manager is the manager to control the local card migration flow, which is similar to the credit_card_save_manager class. The FormDataImporter will trigger the migration when user submit a form using a local card or a server card.

If the user is signed-in and sync, and have [more than one local card when use a local card] or [at least one valid local card when use server card], migration will start to fetch legal docs. When successfully fetch the legal docs, it will pop-up first round window to ask user whether to migrate local card. If user agree, it will pop-up second window with all valid local card for upload. If user agree on the second window, we will sequentially upload valid local cards.

In this CL, we mainly solve the triggering logic for the migration. The logic is mainly in the FormDataImporter and the ShouldOfferLocalCreditCardMigration() in LocalCardMigrationManager. If the migration requirements are met, we will fetch legal docs from the server. If we successfully fetch the legal docs, we will trigger the first round pop-up window to the user. We also add experimental flag in this CL to enable/disable the migration.

For the unittest, we use a variable in the test class as an observer and it is True when successfully fetch the legal docs. We check this variable to test whether the migration is triggered or not. And we also create new constructor in the test_autofill_manager and test_form_data_importer for the new local_card_migration_manager for testing.

The triggered two-round pop-ups windows together with their callback functions will be included in the following CLs.

Bug: 852904
Change-Id: Iadd17b4e36e12bc9ef17075d0703b796d37ed334
Reviewed-on: https://chromium-review.googlesource.com/1111569
Commit-Queue: Sujie Zhu <sujiezhu@google.com>
Reviewed-by: Sebastien Seguin-Gagnon <sebsg@chromium.org>
Reviewed-by: Jared Saul <jsaul@google.com>
Cr-Commit-Position: refs/heads/master@{#575715}
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 1481c10..7fcc95a 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2923,6 +2923,11 @@
      flag_descriptions::kEnableAutofillCreditCardAblationExperimentDescription,
      kOsAll,
      FEATURE_VALUE_TYPE(autofill::kAutofillCreditCardAblationExperiment)},
+    {"enable-autofill-credit-card-local-card-migration",
+     flag_descriptions::kEnableAutofillCreditCardLocalCardMigrationName,
+     flag_descriptions::kEnableAutofillCreditCardLocalCardMigrationDescription,
+     kOsDesktop,
+     FEATURE_VALUE_TYPE(autofill::kAutofillCreditCardLocalCardMigration)},
     {"enable-autofill-credit-card-upload-editable-cardholder-name",
      flag_descriptions::
          kEnableAutofillCreditCardUploadEditableCardholderNameName,
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 25b1b0d..81b81d3 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -365,6 +365,11 @@
 const char kEnableAutofillCreditCardLastUsedDateDisplayDescription[] =
     "If enabled, display the last used date of a credit card in autofill.";
 
+const char kEnableAutofillCreditCardLocalCardMigrationName[] =
+    "Enable migrating local cards to Google Pay";
+const char kEnableAutofillCreditCardLocalCardMigrationDescription[] =
+    "If enabled, prompt migration of locally-saved credit cards to Google Pay.";
+
 const char kEnableAutofillCreditCardUploadEditableCardholderNameName[] =
     "Make cardholder name editable in dialog during credit card upload";
 const char kEnableAutofillCreditCardUploadEditableCardholderNameDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 51d7b92..0468d41 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -252,6 +252,9 @@
 extern const char kEnableAutofillCreditCardLastUsedDateDisplayName[];
 extern const char kEnableAutofillCreditCardLastUsedDateDisplayDescription[];
 
+extern const char kEnableAutofillCreditCardLocalCardMigrationName[];
+extern const char kEnableAutofillCreditCardLocalCardMigrationDescription[];
+
 extern const char kEnableAutofillCreditCardUploadEditableCardholderNameName[];
 extern const char
     kEnableAutofillCreditCardUploadEditableCardholderNameDescription[];
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index 96bac8e..9753eec 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -111,6 +111,8 @@
     "form_types.h",
     "legal_message_line.cc",
     "legal_message_line.h",
+    "local_card_migration_manager.cc",
+    "local_card_migration_manager.h",
     "name_field.cc",
     "name_field.h",
     "password_generator.cc",
@@ -321,6 +323,8 @@
     "test_form_data_importer.h",
     "test_form_structure.cc",
     "test_form_structure.h",
+    "test_local_card_migration_manager.cc",
+    "test_local_card_migration_manager.h",
     "test_personal_data_manager.cc",
     "test_personal_data_manager.h",
     "test_region_data_loader.cc",
@@ -433,6 +437,7 @@
     "form_field_unittest.cc",
     "form_structure_unittest.cc",
     "legal_message_line_unittest.cc",
+    "local_card_migration_manager_unittest.cc",
     "name_field_unittest.cc",
     "password_generator_fips181_unittest.cc",
     "password_generator_unittest.cc",
diff --git a/components/autofill/core/browser/autofill_experiments.cc b/components/autofill/core/browser/autofill_experiments.cc
index cdd5dd1..35548d3 100644
--- a/components/autofill/core/browser/autofill_experiments.cc
+++ b/components/autofill/core/browser/autofill_experiments.cc
@@ -36,6 +36,8 @@
     "AutofillScanCardholderName", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kAutofillCreditCardAblationExperiment{
     "AutofillCreditCardAblationExperiment", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kAutofillCreditCardLocalCardMigration{
+    "AutofillCreditCardLocalCardMigration", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kAutofillDeleteDisusedAddresses{
     "AutofillDeleteDisusedAddresses", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kAutofillDeleteDisusedCreditCards{
@@ -102,6 +104,10 @@
 #endif
 }
 
+bool IsAutofillCreditCardLocalCardMigrationExperimentEnabled() {
+  return base::FeatureList::IsEnabled(kAutofillCreditCardLocalCardMigration);
+}
+
 bool OfferStoreUnmaskedCards() {
 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
   // The checkbox can be forced on with a flag, but by default we don't store
diff --git a/components/autofill/core/browser/autofill_experiments.h b/components/autofill/core/browser/autofill_experiments.h
index fcec572..7264f45 100644
--- a/components/autofill/core/browser/autofill_experiments.h
+++ b/components/autofill/core/browser/autofill_experiments.h
@@ -27,6 +27,7 @@
 extern const base::Feature kAutofillCreditCardAssist;
 extern const base::Feature kAutofillScanCardholderName;
 extern const base::Feature kAutofillCreditCardAblationExperiment;
+extern const base::Feature kAutofillCreditCardLocalCardMigration;
 extern const base::Feature kAutofillDeleteDisusedAddresses;
 extern const base::Feature kAutofillDeleteDisusedCreditCards;
 extern const base::Feature kAutofillExpandedPopupViews;
@@ -77,6 +78,10 @@
                                const syncer::SyncService* sync_service,
                                const std::string& user_email);
 
+// Returns whether Autofill credit card local card migration experiment is
+// enabled.
+bool IsAutofillCreditCardLocalCardMigrationExperimentEnabled();
+
 // For testing purposes; not to be launched.  When enabled, Chrome Upstream
 // always requests that the user enters/confirms cardholder name in the
 // offer-to-save dialog, regardless of if it was present or if the user is a
diff --git a/components/autofill/core/browser/autofill_test_utils.cc b/components/autofill/core/browser/autofill_test_utils.cc
index fcb9fa9..0140cc5 100644
--- a/components/autofill/core/browser/autofill_test_utils.cc
+++ b/components/autofill/core/browser/autofill_test_utils.cc
@@ -10,6 +10,7 @@
 #include "base/rand_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
 #include "build/build_config.h"
 #include "components/autofill/core/browser/autofill_external_delegate.h"
 #include "components/autofill/core/browser/autofill_manager.h"
@@ -193,6 +194,54 @@
   form->fields.push_back(field);
 }
 
+void CreateTestCreditCardFormData(FormData* form,
+                                  bool is_https,
+                                  bool use_month_type,
+                                  bool split_names) {
+  form->name = ASCIIToUTF16("MyForm");
+  if (is_https) {
+    form->origin = GURL("https://myform.com/form.html");
+    form->action = GURL("https://myform.com/submit.html");
+    form->main_frame_origin =
+        url::Origin::Create(GURL("https://myform_root.com/form.html"));
+  } else {
+    form->origin = GURL("http://myform.com/form.html");
+    form->action = GURL("http://myform.com/submit.html");
+    form->main_frame_origin =
+        url::Origin::Create(GURL("http://myform_root.com/form.html"));
+  }
+
+  FormFieldData field;
+  if (split_names) {
+    test::CreateTestFormField("First Name on Card", "firstnameoncard", "",
+                              "text", &field);
+    field.autocomplete_attribute = "cc-given-name";
+    form->fields.push_back(field);
+    test::CreateTestFormField("Last Name on Card", "lastnameoncard", "", "text",
+                              &field);
+    field.autocomplete_attribute = "cc-family-name";
+    form->fields.push_back(field);
+    field.autocomplete_attribute = "";
+  } else {
+    test::CreateTestFormField("Name on Card", "nameoncard", "", "text", &field);
+    form->fields.push_back(field);
+  }
+  test::CreateTestFormField("Card Number", "cardnumber", "", "text", &field);
+  form->fields.push_back(field);
+  if (use_month_type) {
+    test::CreateTestFormField("Expiration Date", "ccmonth", "", "month",
+                              &field);
+    form->fields.push_back(field);
+  } else {
+    test::CreateTestFormField("Expiration Date", "ccmonth", "", "text", &field);
+    form->fields.push_back(field);
+    test::CreateTestFormField("", "ccyear", "", "text", &field);
+    form->fields.push_back(field);
+  }
+  test::CreateTestFormField("CVC", "cvc", "", "text", &field);
+  form->fields.push_back(field);
+}
+
 inline void check_and_set(
     FormGroup* profile, ServerFieldType type, const char* value) {
   if (value)
@@ -515,5 +564,16 @@
       internal::GetObfuscatedStringForCardDigits(base::ASCIIToUTF16(str)));
 }
 
+std::string NextYear() {
+  base::Time::Exploded now;
+  base::Time::Now().LocalExplode(&now);
+  return std::to_string(now.year + 1);
+}
+std::string LastYear() {
+  base::Time::Exploded now;
+  base::Time::Now().LocalExplode(&now);
+  return std::to_string(now.year - 1);
+}
+
 }  // namespace test
 }  // namespace autofill
diff --git a/components/autofill/core/browser/autofill_test_utils.h b/components/autofill/core/browser/autofill_test_utils.h
index 80af947..38077c0 100644
--- a/components/autofill/core/browser/autofill_test_utils.h
+++ b/components/autofill/core/browser/autofill_test_utils.h
@@ -72,6 +72,14 @@
 // form, including name and email, but no address-related fields.
 void CreateTestPersonalInformationFormData(FormData* form);
 
+// Populates |form| with data corresponding to a simple credit card form.
+// Note that this actually appends fields to the form data, which can be
+// useful for building up more complex test forms.
+void CreateTestCreditCardFormData(FormData* form,
+                                  bool is_https,
+                                  bool use_month_type,
+                                  bool split_names = false);
+
 // Returns a full profile with valid info according to rules for Canada.
 AutofillProfile GetFullValidProfileForCanada();
 
@@ -208,6 +216,9 @@
 
 std::string ObfuscatedCardDigitsAsUTF8(const std::string& str);
 
+std::string NextYear();
+std::string LastYear();
+
 }  // namespace test
 }  // namespace autofill
 
diff --git a/components/autofill/core/browser/credit_card_save_manager.cc b/components/autofill/core/browser/credit_card_save_manager.cc
index 6103a33..9c9cb83 100644
--- a/components/autofill/core/browser/credit_card_save_manager.cc
+++ b/components/autofill/core/browser/credit_card_save_manager.cc
@@ -101,6 +101,10 @@
     const FormStructure& submitted_form,
     const CreditCard& card,
     const bool uploading_local_card) {
+  // Abort the uploading if |payments_client_| is nullptr.
+  if (!payments_client_)
+    return;
+  payments_client_->SetSaveDelegate(this);
   upload_request_ = payments::PaymentsClient::UploadRequestDetails();
   upload_request_.card = card;
   uploading_local_card_ = uploading_local_card;
diff --git a/components/autofill/core/browser/form_data_importer.cc b/components/autofill/core/browser/form_data_importer.cc
index 43420f2..3dacd2d 100644
--- a/components/autofill/core/browser/form_data_importer.cc
+++ b/components/autofill/core/browser/form_data_importer.cc
@@ -25,7 +25,6 @@
 #include "components/autofill/core/browser/autofill_profile.h"
 #include "components/autofill/core/browser/autofill_type.h"
 #include "components/autofill/core/browser/credit_card.h"
-#include "components/autofill/core/browser/credit_card_save_manager.h"
 #include "components/autofill/core/browser/form_structure.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/phone_number.h"
@@ -102,6 +101,12 @@
                                                   payments_client,
                                                   app_locale,
                                                   personal_data_manager)),
+      local_card_migration_manager_(
+          std::make_unique<LocalCardMigrationManager>(client,
+                                                      payments_client,
+                                                      app_locale,
+                                                      personal_data_manager)),
+
       personal_data_manager_(personal_data_manager),
       app_locale_(app_locale) {}
 
@@ -111,28 +116,58 @@
                                       bool profile_autofill_enabled,
                                       bool credit_card_autofill_enabled) {
   std::unique_ptr<CreditCard> imported_credit_card;
-  if (!ImportFormData(submitted_form, profile_autofill_enabled,
-                      credit_card_autofill_enabled,
-                      credit_card_save_manager_->IsCreditCardUploadEnabled(),
-                      &imported_credit_card))
-    return;
 
-  // No card available to offer save or upload.
+  bool is_credit_card_upstream_enabled =
+      credit_card_save_manager_->IsCreditCardUploadEnabled();
+  // ImportFormData will set the |imported_credit_card_record_type_|. If the
+  // imported card is invalid or already a server card, or if
+  // |credit_card_save_manager_| does not allow uploading,
+  // |imported_credit_card| will be nullptr.
+  ImportFormData(submitted_form, profile_autofill_enabled,
+                 credit_card_autofill_enabled,
+                 /*should_return_local_card=*/is_credit_card_upstream_enabled,
+                 &imported_credit_card);
+  // If no card was successfully imported from the form, return.
+  if (imported_credit_card_record_type_ ==
+      ImportedCreditCardRecordType::NO_CARD) {
+    return;
+  }
+  // A credit card was successfully imported, but it's possible it is already a
+  // local or server card. First, check to see if we should offer local card
+  // migration in this case, as local cards could go either way.
+  if (local_card_migration_manager_ &&
+      local_card_migration_manager_->ShouldOfferLocalCardMigration(
+          imported_credit_card_record_type_)) {
+    local_card_migration_manager_->AttemptToOfferLocalCardMigration();
+    return;
+  }
+
+  // Local card migration will not be offered. If we do not have a new card to
+  // save (or a local card to upload save), return.
   if (!imported_credit_card)
     return;
 
-  if (!credit_card_save_manager_->IsCreditCardUploadEnabled()) {
-    // Offer local save.
-    credit_card_save_manager_->OfferCardLocalSave(*imported_credit_card);
-  } else {
-    // Attempt to offer upload save. Because we pass IsCreditCardUploadEnabled()
-    // to ImportFormData, this block can be reached on observing either a new
-    // card or one already stored locally and whose |TypeAndLastFourDigits| do
-    // not match a masked server card. We can offer to upload either kind.
+  // We have a card to save; decide what type of save flow to display.
+  if (is_credit_card_upstream_enabled) {
+    // Attempt to offer upload save. Because we pass
+    // |credit_card_upstream_enabled| to ImportFormData, this block can be
+    // reached on observing either a new card or one already stored locally
+    // which doesn't match an existing server card. If Google Payments declines
+    // allowing upload, |credit_card_save_manager_| is tasked with deciding if
+    // we should fall back to local save or not.
+    DCHECK(imported_credit_card_record_type_ ==
+               ImportedCreditCardRecordType::LOCAL_CARD ||
+           imported_credit_card_record_type_ ==
+               ImportedCreditCardRecordType::NEW_CARD);
     credit_card_save_manager_->AttemptToOfferCardUploadSave(
         submitted_form, *imported_credit_card,
-        imported_credit_card_record_type_ ==
+        /*uploading_local_card=*/imported_credit_card_record_type_ ==
             ImportedCreditCardRecordType::LOCAL_CARD);
+  } else {
+    // If upload save is not allowed, new cards should be saved locally.
+    DCHECK(imported_credit_card_record_type_ ==
+           ImportedCreditCardRecordType::NEW_CARD);
+    credit_card_save_manager_->OfferCardLocalSave(*imported_credit_card);
   }
 }
 
@@ -401,7 +436,6 @@
       return false;
     }
   }
-
   *imported_credit_card = std::make_unique<CreditCard>(candidate_credit_card);
   return true;
 }
diff --git a/components/autofill/core/browser/form_data_importer.h b/components/autofill/core/browser/form_data_importer.h
index a47d19a..596fd06 100644
--- a/components/autofill/core/browser/form_data_importer.h
+++ b/components/autofill/core/browser/form_data_importer.h
@@ -15,6 +15,7 @@
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/credit_card_save_manager.h"
 #include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/browser/local_card_migration_manager.h"
 #include "components/autofill/core/browser/payments/payments_client.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 
@@ -67,6 +68,12 @@
     credit_card_save_manager_ = std::move(credit_card_save_manager);
   }
 
+  // Exposed for testing.
+  void set_local_card_migration_manager(
+      std::unique_ptr<LocalCardMigrationManager> local_card_migration_manager) {
+    local_card_migration_manager_ = std::move(local_card_migration_manager);
+  }
+
  private:
   // Scans the given |form| for importable Autofill data. If the form includes
   // sufficient address data for a new profile, it is immediately imported. If
@@ -115,6 +122,9 @@
   // Responsible for managing credit card save flows (local or upload).
   std::unique_ptr<CreditCardSaveManager> credit_card_save_manager_;
 
+  // Responsible for migrating locally saved credit cards to Google Pay.
+  std::unique_ptr<LocalCardMigrationManager> local_card_migration_manager_;
+
   // The personal data manager, used to save and load personal data to/from the
   // web database.  This is overridden by the AutofillManagerTest.
   // Weak reference.
diff --git a/components/autofill/core/browser/local_card_migration_manager.cc b/components/autofill/core/browser/local_card_migration_manager.cc
new file mode 100644
index 0000000..0040b57
--- /dev/null
+++ b/components/autofill/core/browser/local_card_migration_manager.cc
@@ -0,0 +1,170 @@
+// Copyright 2018 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/autofill/core/browser/local_card_migration_manager.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "base/bind.h"
+#include "components/autofill/core/browser/autofill_client.h"
+#include "components/autofill/core/browser/autofill_experiments.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/form_data_importer.h"
+#include "components/autofill/core/browser/payments/payments_client.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "services/identity/public/cpp/identity_manager.h"
+
+namespace autofill {
+
+LocalCardMigrationManager::LocalCardMigrationManager(
+    AutofillClient* client,
+    payments::PaymentsClient* payments_client,
+    const std::string& app_locale,
+    PersonalDataManager* personal_data_manager)
+    : client_(client),
+      payments_client_(payments_client),
+      app_locale_(app_locale),
+      personal_data_manager_(personal_data_manager) {
+  if (payments_client_)
+    payments_client_->SetSaveDelegate(this);
+}
+
+LocalCardMigrationManager::~LocalCardMigrationManager() {}
+
+bool LocalCardMigrationManager::ShouldOfferLocalCardMigration(
+    int imported_credit_card_record_type) {
+  // Must be an existing card. New cards always get Upstream or local save.
+  if (imported_credit_card_record_type !=
+          FormDataImporter::ImportedCreditCardRecordType::LOCAL_CARD &&
+      imported_credit_card_record_type !=
+          FormDataImporter::ImportedCreditCardRecordType::SERVER_CARD) {
+    return false;
+  }
+
+  if (!IsCreditCardMigrationEnabled())
+    return false;
+
+  std::vector<CreditCard*> local_credit_cards =
+      personal_data_manager_->GetLocalCreditCards();
+
+  // Empty previous state.
+  migratable_credit_cards_.clear();
+
+  // Initialize the local credit card list and queue for showing and uploading.
+  for (CreditCard* card : local_credit_cards) {
+    // If the card is valid (has a valid card number, expiration date, and is
+    // not expired) and is not a server card, add it to the list of migratable
+    // cards.
+    if (card->IsValid() && !IsServerCard(card))
+      migratable_credit_cards_.push_back(*card);
+  }
+
+  // If the form was submitted with a local card, only offer migration instead
+  // of Upstream if there are other local cards to migrate as well. If the form
+  // was submitted with a server card, offer migration if ANY local cards can be
+  // migrated.
+  return (imported_credit_card_record_type ==
+              FormDataImporter::ImportedCreditCardRecordType::LOCAL_CARD &&
+          migratable_credit_cards_.size() > 1) ||
+         (imported_credit_card_record_type ==
+              FormDataImporter::ImportedCreditCardRecordType::SERVER_CARD &&
+          !migratable_credit_cards_.empty());
+}
+
+void LocalCardMigrationManager::AttemptToOfferLocalCardMigration() {
+  // Abort the migration if |payments_client_| is nullptr.
+  if (!payments_client_)
+    return;
+  payments_client_->SetSaveDelegate(this);
+  upload_request_ = payments::PaymentsClient::UploadRequestDetails();
+
+  // Payments server determines which version of the legal message to show based
+  // on the existence of this experiment flag.
+  if (IsAutofillUpstreamUpdatePromptExplanationExperimentEnabled()) {
+    upload_request_.active_experiments.push_back(
+        kAutofillUpstreamUpdatePromptExplanation.name);
+  }
+
+  // Don't send pan_first_six, as potentially migrating multiple local cards at
+  // once will negate its usefulness.
+  payments_client_->GetUploadDetails(
+      upload_request_.profiles, GetDetectedValues(),
+      /*pan_first_six=*/std::string(), upload_request_.active_experiments,
+      app_locale_);
+}
+
+bool LocalCardMigrationManager::IsCreditCardMigrationEnabled() {
+  // Confirm that the user is signed in, syncing, and the proper experiment
+  // flags are enabled.
+  bool migration_experiment_enabled =
+      IsAutofillCreditCardLocalCardMigrationExperimentEnabled();
+  bool credit_card_upload_enabled = ::autofill::IsCreditCardUploadEnabled(
+      client_->GetPrefs(), client_->GetSyncService(),
+      client_->GetIdentityManager()->GetPrimaryAccountInfo().email);
+  bool has_google_payments_account =
+      (static_cast<int64_t>(payments_client_->GetPrefService()->GetDouble(
+           prefs::kAutofillBillingCustomerNumber)) != 0);
+  return migration_experiment_enabled && credit_card_upload_enabled &&
+         has_google_payments_account;
+}
+
+void LocalCardMigrationManager::OnDidGetUploadDetails(
+    AutofillClient::PaymentsRpcResult result,
+    const base::string16& context_token,
+    std::unique_ptr<base::DictionaryValue> legal_message) {
+  if (result == AutofillClient::SUCCESS) {
+    upload_request_.context_token = context_token;
+    legal_message_ = std::move(legal_message);
+    // If we successfully received the legal docs, trigger the offer-to-migrate
+    // dialog.
+    // TODO(crbug.com/852904): Show intermediate migration prompt here! Relies
+    // on CL/1117929 first.
+  }
+}
+
+// TODO(crbug.com/852904): Starts the upload of the next local card if one
+// exists.
+void LocalCardMigrationManager::OnDidUploadCard(
+    AutofillClient::PaymentsRpcResult result,
+    const std::string& server_id) {}
+
+int LocalCardMigrationManager::GetDetectedValues() const {
+  int detected_values = 0;
+
+  // If all cards to be migrated have a cardholder name, include it in the
+  // detected values.
+  bool all_cards_have_cardholder_name = true;
+  for (CreditCard card : migratable_credit_cards_) {
+    all_cards_have_cardholder_name &=
+        !card.GetInfo(AutofillType(CREDIT_CARD_NAME_FULL), app_locale_).empty();
+  }
+  if (all_cards_have_cardholder_name)
+    detected_values |= CreditCardSaveManager::DetectedValue::CARDHOLDER_NAME;
+
+  // Local card migration should ONLY be offered when the user already has a
+  // Google Payments account.
+  DCHECK(static_cast<int64_t>(payments_client_->GetPrefService()->GetDouble(
+             prefs::kAutofillBillingCustomerNumber)) != 0);
+  detected_values |=
+      CreditCardSaveManager::DetectedValue::HAS_GOOGLE_PAYMENTS_ACCOUNT;
+
+  return detected_values;
+}
+
+bool LocalCardMigrationManager::IsServerCard(CreditCard* local_card) const {
+  std::vector<CreditCard*> server_credit_cards =
+      personal_data_manager_->GetServerCreditCards();
+  // Check whether the current card is already uploaded.
+  for (const CreditCard* server_card : server_credit_cards) {
+    if (local_card->HasSameNumberAs(*server_card))
+      return true;
+  }
+  return false;
+}
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/local_card_migration_manager.h b/components/autofill/core/browser/local_card_migration_manager.h
new file mode 100644
index 0000000..62802c8
--- /dev/null
+++ b/components/autofill/core/browser/local_card_migration_manager.h
@@ -0,0 +1,96 @@
+// Copyright 2018 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.
+
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_LOCAL_CARD_MIGRATION_MANAGER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_LOCAL_CARD_MIGRATION_MANAGER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/strings/string16.h"
+#include "components/autofill/core/browser/autofill_client.h"
+#include "components/autofill/core/browser/payments/payments_client.h"
+
+namespace autofill {
+
+class CreditCard;
+class PersonalDataManager;
+
+// Manages logic for determining whether migration of locally saved credit cards
+// to Google Payments is available as well as multiple local card uploading.
+// Owned by FormDataImporter.
+class LocalCardMigrationManager : public payments::PaymentsClientSaveDelegate {
+ public:
+  // The parameters should outlive the LocalCardMigrationManager.
+  LocalCardMigrationManager(AutofillClient* client,
+                            payments::PaymentsClient* payments_client,
+                            const std::string& app_locale,
+                            PersonalDataManager* personal_data_manager);
+  virtual ~LocalCardMigrationManager();
+
+  // Returns true if all of the conditions for allowing local credit card
+  // migration are satisfied. Initializes the local card list for upload.
+  bool ShouldOfferLocalCardMigration(int imported_credit_card_record_type);
+
+  // Called from FormDataImporter when all migration requirements are met.
+  // Fetches legal documents and triggers the OnDidGetUploadDetails callback.
+  void AttemptToOfferLocalCardMigration();
+
+  // Check that the user is signed in, syncing, and the proper experiment
+  // flags are enabled. Override in the test class.
+  virtual bool IsCreditCardMigrationEnabled();
+
+  // Determines what detected_values metadata to send (generally, cardholder
+  // name if it exists on all cards, and existence of Payments customer).
+  int GetDetectedValues() const;
+
+ protected:
+  // payments::PaymentsClientSaveDelegate:
+  // Callback after successfully getting the legal documents. On success,
+  // displays the offer-to-migrate dialog, which the user can accept or not.
+  void OnDidGetUploadDetails(
+      AutofillClient::PaymentsRpcResult result,
+      const base::string16& context_token,
+      std::unique_ptr<base::DictionaryValue> legal_message) override;
+
+  // payments::PaymentsClientSaveDelegate:
+  // Callback after a local card was uploaded. Starts the upload of the next
+  // local card if one exists.
+  // Exposed for testing.
+  void OnDidUploadCard(AutofillClient::PaymentsRpcResult result,
+                       const std::string& server_id) override;
+
+  // Check whether a local card is already a server card.
+  bool IsServerCard(CreditCard* local_card) const;
+
+  AutofillClient* const client_;
+
+  // Handles Payments service requests.
+  // Owned by AutofillManager.
+  payments::PaymentsClient* payments_client_;
+
+ private:
+  std::unique_ptr<base::DictionaryValue> legal_message_;
+
+  std::string app_locale_;
+
+  // The personal data manager, used to save and load personal data to/from the
+  // web database.  This is overridden by the AutofillManagerTest.
+  // Weak reference.
+  // May be NULL.  NULL indicates OTR.
+  PersonalDataManager* personal_data_manager_;
+
+  // Collected information about a pending upload request.
+  payments::PaymentsClient::UploadRequestDetails upload_request_;
+
+  // The local credit cards to be uploaded.
+  std::vector<CreditCard> migratable_credit_cards_;
+
+  DISALLOW_COPY_AND_ASSIGN(LocalCardMigrationManager);
+};
+
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_LOCAL_CARD_MIGRATION_MANAGER_H_
diff --git a/components/autofill/core/browser/local_card_migration_manager_unittest.cc b/components/autofill/core/browser/local_card_migration_manager_unittest.cc
new file mode 100644
index 0000000..57ac443
--- /dev/null
+++ b/components/autofill/core/browser/local_card_migration_manager_unittest.cc
@@ -0,0 +1,603 @@
+// Copyright 2018 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/autofill/core/browser/local_card_migration_manager.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <list>
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/guid.h"
+#include "base/metrics/metrics_hashes.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "components/autofill/core/browser/autofill_experiments.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/payments/test_payments_client.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/browser/test_autofill_client.h"
+#include "components/autofill/core/browser/test_autofill_clock.h"
+#include "components/autofill/core/browser/test_autofill_driver.h"
+#include "components/autofill/core/browser/test_autofill_manager.h"
+#include "components/autofill/core/browser/test_credit_card_save_manager.h"
+#include "components/autofill/core/browser/test_local_card_migration_manager.h"
+#include "components/autofill/core/browser/test_personal_data_manager.h"
+#include "components/autofill/core/browser/test_sync_service.h"
+#include "components/autofill/core/browser/validation.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
+#include "components/autofill/core/common/autofill_clock.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/prefs/pref_service.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "net/url_request/url_request_test_util.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using base::ASCIIToUTF16;
+using testing::_;
+
+namespace autofill {
+
+class LocalCardMigrationManagerTest : public testing::Test {
+ public:
+  void SetUp() override {
+    autofill_client_.SetPrefs(test::PrefServiceForTesting());
+    personal_data_.set_database(autofill_client_.GetDatabase());
+    personal_data_.SetPrefService(autofill_client_.GetPrefs());
+    personal_data_.SetSyncServiceForTest(&sync_service_);
+    autofill_driver_.reset(new TestAutofillDriver());
+    request_context_ = new net::TestURLRequestContextGetter(
+        base::ThreadTaskRunnerHandle::Get());
+    autofill_driver_->SetURLRequestContext(request_context_.get());
+    payments_client_ = new payments::TestPaymentsClient(
+        autofill_driver_->GetURLLoaderFactory(), autofill_client_.GetPrefs(),
+        autofill_client_.GetIdentityManager(),
+        /*unmask_delegate=*/nullptr,
+        /*save_delegate=*/nullptr);
+    credit_card_save_manager_ =
+        new TestCreditCardSaveManager(autofill_driver_.get(), &autofill_client_,
+                                      payments_client_, &personal_data_);
+    local_card_migration_manager_ = new TestLocalCardMigrationManager(
+        autofill_driver_.get(), &autofill_client_, payments_client_,
+        &personal_data_);
+    autofill_manager_.reset(new TestAutofillManager(
+        autofill_driver_.get(), &autofill_client_, &personal_data_,
+        std::unique_ptr<CreditCardSaveManager>(credit_card_save_manager_),
+        payments_client_,
+        std::unique_ptr<LocalCardMigrationManager>(
+            local_card_migration_manager_)));
+    autofill_manager_->SetExpectedObservedSubmission(true);
+  }
+
+  void TearDown() override {
+    // Order of destruction is important as AutofillManager relies on
+    // PersonalDataManager to be around when it gets destroyed.
+    autofill_manager_.reset();
+    autofill_driver_.reset();
+
+    // Remove the AutofillWebDataService so TestPersonalDataManager does not
+    // need to care about removing self as an observer in destruction.
+    personal_data_.set_database(scoped_refptr<AutofillWebDataService>(nullptr));
+    personal_data_.SetPrefService(nullptr);
+    personal_data_.ClearCreditCards();
+
+    request_context_ = nullptr;
+  }
+
+  void EnableAutofillCreditCardLocalCardMigrationExperiment() {
+    scoped_feature_list_.InitAndEnableFeature(
+        kAutofillCreditCardLocalCardMigration);
+  }
+
+  void DisableAutofillCreditCardLocalCardMigrationExperiment() {
+    scoped_feature_list_.InitAndDisableFeature(
+        kAutofillCreditCardLocalCardMigration);
+  }
+
+  void FormsSeen(const std::vector<FormData>& forms) {
+    autofill_manager_->OnFormsSeen(forms, base::TimeTicks());
+  }
+
+  void FormSubmitted(const FormData& form) {
+    autofill_manager_->OnFormSubmitted(
+        form, false, SubmissionSource::FORM_SUBMISSION, base::TimeTicks::Now());
+  }
+
+  void EditCreditCardFrom(FormData& credit_card_form,
+                          const char* name_on_card,
+                          const char* card_number,
+                          const char* expiration_month,
+                          const char* expiration_year,
+                          const char* cvc) {
+    DCHECK(credit_card_form.fields.size() >= 5);
+    credit_card_form.fields[0].value = ASCIIToUTF16(name_on_card);
+    credit_card_form.fields[1].value = ASCIIToUTF16(card_number);
+    credit_card_form.fields[2].value = ASCIIToUTF16(expiration_month);
+    credit_card_form.fields[3].value = ASCIIToUTF16(expiration_year);
+    credit_card_form.fields[4].value = ASCIIToUTF16(cvc);
+  }
+
+  void AddLocalCrediCard(TestPersonalDataManager& personal_data,
+                         const char* name_on_card,
+                         const char* card_number,
+                         const char* expiration_month,
+                         const char* expiration_year,
+                         const std::string& billing_address_id) {
+    CreditCard local_card;
+    test::SetCreditCardInfo(&local_card, name_on_card, card_number,
+                            expiration_month, expiration_year,
+                            billing_address_id);
+    local_card.set_record_type(CreditCard::LOCAL_CARD);
+    personal_data.AddCreditCard(local_card);
+  }
+
+ protected:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  ukm::TestAutoSetUkmRecorder test_ukm_recorder_;
+  TestAutofillClient autofill_client_;
+  std::unique_ptr<TestAutofillDriver> autofill_driver_;
+  std::unique_ptr<TestAutofillManager> autofill_manager_;
+  scoped_refptr<net::TestURLRequestContextGetter> request_context_;
+  TestPersonalDataManager personal_data_;
+  TestSyncService sync_service_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+  // Ends up getting owned (and destroyed) by TestFormDataImporter:
+  TestCreditCardSaveManager* credit_card_save_manager_;
+  TestLocalCardMigrationManager* local_card_migration_manager_;
+  // Ends up getting owned (and destroyed) by TestAutofillManager:
+  payments::TestPaymentsClient* payments_client_;
+};
+
+// Having one local card on file and using it will not trigger migration.
+TEST_F(LocalCardMigrationManagerTest,
+       MigrateCreditCard_UseLocalCardWithOneLocal) {
+  EnableAutofillCreditCardLocalCardMigrationExperiment();
+  personal_data_.ClearCreditCards();
+  personal_data_.ClearProfiles();
+  credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+  // Set the billing_customer_number Priority Preference to designate
+  // existence of a Payments account.
+  autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+                                         12345);
+  // Add a local credit card whose |TypeAndLastFourDigits| matches what we will
+  // enter below.
+  AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111111", "11",
+                    test::NextYear().c_str(), "1");
+
+  // Set up our credit card form data.
+  FormData credit_card_form;
+  test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+  FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+  // Edit the data, and submit.
+  EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11",
+                     test::NextYear().c_str(), "123");
+  FormSubmitted(credit_card_form);
+  EXPECT_FALSE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+}
+
+// Having any number of local cards on file and using a new card will not
+// trigger migration.
+TEST_F(LocalCardMigrationManagerTest,
+       MigrateCreditCard_UseNewCardWithAnyLocal) {
+  EnableAutofillCreditCardLocalCardMigrationExperiment();
+  personal_data_.ClearCreditCards();
+  personal_data_.ClearProfiles();
+  credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+  // Set the billing_customer_number Priority Preference to designate
+  // existence of a Payments account.
+  autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+                                         12345);
+  // Add a local credit card (but it will not match what we will enter below).
+  AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111111", "11",
+                    test::NextYear().c_str(), "1");
+  // Add another local credit card (but it will not match what we will enter
+  // below).
+  AddLocalCrediCard(personal_data_, "Flo Master", "4444333322221111", "11",
+                    test::NextYear().c_str(), "1");
+
+  // Set up our credit card form data.
+  FormData credit_card_form;
+  test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+  FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+  // Edit the data, and submit.
+  EditCreditCardFrom(credit_card_form, "Flo Master", "5555555555554444", "11",
+                     test::NextYear().c_str(), "123");
+  FormSubmitted(credit_card_form);
+  EXPECT_FALSE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+}
+
+// Use one local card with more valid local cards available, will trigger
+// migration.
+TEST_F(LocalCardMigrationManagerTest,
+       MigrateCreditCard_UseLocalCardWithMoreLocal) {
+  EnableAutofillCreditCardLocalCardMigrationExperiment();
+  personal_data_.ClearCreditCards();
+  personal_data_.ClearProfiles();
+  credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+  // Set the billing_customer_number Priority Preference to designate
+  // existence of a Payments account.
+  autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+                                         12345);
+  // Add a local credit card whose |TypeAndLastFourDigits| matches what we will
+  // enter below.
+  AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111111", "11",
+                    test::NextYear().c_str(), "1");
+  // Add another local credit card, so it will trigger migration.
+  AddLocalCrediCard(personal_data_, "Flo Master", "5555555555554444", "11",
+                    test::NextYear().c_str(), "1");
+
+  // Set up our credit card form data.
+  FormData credit_card_form;
+  test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+  FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+  // Edit the data, and submit.
+  EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11",
+                     test::NextYear().c_str(), "123");
+  FormSubmitted(credit_card_form);
+  EXPECT_TRUE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+}
+
+// Using a local card will not trigger migration even if there are other local
+// cards as long as the other local cards are not eligible for migration.
+TEST_F(LocalCardMigrationManagerTest,
+       MigrateCreditCard_UseLocalCardWithInvalidLocal) {
+  EnableAutofillCreditCardLocalCardMigrationExperiment();
+  personal_data_.ClearCreditCards();
+  personal_data_.ClearProfiles();
+  credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+  // Set the billing_customer_number Priority Preference to designate
+  // existence of a Payments account.
+  autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+                                         12345);
+  // Add a local credit card whose |TypeAndLastFourDigits| matches what we will
+  // enter below.
+  AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111111", "11",
+                    test::NextYear().c_str(), "1");
+  // Add other invalid local credit cards(invalid card number or expired), so it
+  // will not trigger migration.
+  AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111112", "11",
+                    test::NextYear().c_str(), "1");
+  AddLocalCrediCard(personal_data_, "Flo Master", "5555555555554444", "11",
+                    test::LastYear().c_str(), "1");
+
+  // Set up our credit card form data.
+  FormData credit_card_form;
+  test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+  FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+  // Edit the data, and submit.
+  EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11",
+                     test::NextYear().c_str(), "123");
+  FormSubmitted(credit_card_form);
+  EXPECT_FALSE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+}
+
+// Using a server card when any number of local cards are eligible for migration
+// will trigger migration.
+TEST_F(LocalCardMigrationManagerTest,
+       MigrateCreditCard_UseServerCardWithOneValidLocal) {
+  EnableAutofillCreditCardLocalCardMigrationExperiment();
+  personal_data_.ClearCreditCards();
+  personal_data_.ClearProfiles();
+  credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+  // Set the billing_customer_number Priority Preference to designate
+  // existence of a Payments account.
+  autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+                                         12345);
+  // Add a masked server credit card whose |TypeAndLastFourDigits| matches what
+  // we will enter below.
+  CreditCard credit_card(CreditCard::MASKED_SERVER_CARD, "a123");
+  test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", "11",
+                          test::NextYear().c_str(), "1");
+  credit_card.SetNetworkForMaskedCard(kVisaCard);
+  personal_data_.AddServerCreditCard(credit_card);
+  // Add one valid local credit card, so it will trigger migration
+  AddLocalCrediCard(personal_data_, "Flo Master", "5555555555554444", "11",
+                    test::NextYear().c_str(), "1");
+
+  // Set up our credit card form data.
+  FormData credit_card_form;
+  test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+  FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+  // Edit the data, and submit.
+  EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11",
+                     test::NextYear().c_str(), "123");
+  FormSubmitted(credit_card_form);
+  EXPECT_TRUE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+}
+
+// Using a server card will not trigger migration even if there are other local
+// cards as long as the other local cards are not eligible for migration.
+TEST_F(LocalCardMigrationManagerTest,
+       MigrateCreditCard_UseServerCardWithNoneValidLocal) {
+  EnableAutofillCreditCardLocalCardMigrationExperiment();
+  personal_data_.ClearCreditCards();
+  personal_data_.ClearProfiles();
+  credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+  // Set the billing_customer_number Priority Preference to designate
+  // existence of a Payments account.
+  autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+                                         12345);
+  // Add a masked credit card whose |TypeAndLastFourDigits| matches what we will
+  // enter below.
+  CreditCard credit_card(CreditCard::MASKED_SERVER_CARD, "a123");
+  test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", "11",
+                          test::NextYear().c_str(), "1");
+  credit_card.SetNetworkForMaskedCard(kVisaCard);
+  personal_data_.AddServerCreditCard(credit_card);
+  // Add other invalid local credit cards(invalid card number or expired), so it
+  // will not trigger migration.
+  AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111112", "11",
+                    test::NextYear().c_str(), "1");
+  AddLocalCrediCard(personal_data_, "Flo Master", "5555555555554444", "11",
+                    test::LastYear().c_str(), "1");
+
+  // Set up our credit card form data.
+  FormData credit_card_form;
+  test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+  FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+  // Edit the data, and submit.
+  EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11",
+                     test::NextYear().c_str(), "123");
+  FormSubmitted(credit_card_form);
+  EXPECT_FALSE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+}
+
+// Use one local card with more valid local cards available but experiment flag
+// is off, will not trigger migration.
+TEST_F(LocalCardMigrationManagerTest, MigrateCreditCard_FeatureNotEnabled) {
+  // Turn off the experiment flag.
+  DisableAutofillCreditCardLocalCardMigrationExperiment();
+  personal_data_.ClearCreditCards();
+  personal_data_.ClearProfiles();
+  credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+  // Set the billing_customer_number Priority Preference to designate
+  // existence of a Payments account.
+  autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+                                         12345);
+  // Add a local credit card whose |TypeAndLastFourDigits| matches what we will
+  // enter below.
+  AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111111", "11",
+                    test::NextYear().c_str(), "1");
+  // Add another local credit card.
+  AddLocalCrediCard(personal_data_, "Flo Master", "5555555555554444", "11",
+                    test::NextYear().c_str(), "1");
+
+  // Set up our credit card form data.
+  FormData credit_card_form;
+  test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+  FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+  // Edit the data, and submit.
+  EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11",
+                     test::NextYear().c_str(), "123");
+  FormSubmitted(credit_card_form);
+  EXPECT_FALSE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+}
+
+// Use one local card with more valid local cards available but billing customer
+// number is blank, will not trigger migration.
+TEST_F(LocalCardMigrationManagerTest, MigrateCreditCard_NoPaymentsAccount) {
+  EnableAutofillCreditCardLocalCardMigrationExperiment();
+  personal_data_.ClearCreditCards();
+  personal_data_.ClearProfiles();
+  credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+  // Add a local credit card whose |TypeAndLastFourDigits| matches what we will
+  // enter below.
+  AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111111", "11",
+                    test::NextYear().c_str(), "1");
+  // Add another local credit card.
+  AddLocalCrediCard(personal_data_, "Flo Master", "5555555555554444", "11",
+                    test::NextYear().c_str(), "1");
+
+  // Set up our credit card form data.
+  FormData credit_card_form;
+  test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+  FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+  // Edit the data, and submit.
+  EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11",
+                     test::NextYear().c_str(), "123");
+  FormSubmitted(credit_card_form);
+  EXPECT_FALSE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+}
+
+// Tests that local cards that match masked server cards do not count as
+// migratable.
+TEST_F(LocalCardMigrationManagerTest,
+       MigrateCreditCard_LocalCardMatchMaskedServerCard) {
+  EnableAutofillCreditCardLocalCardMigrationExperiment();
+  personal_data_.ClearCreditCards();
+  personal_data_.ClearProfiles();
+  credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+  // Set the billing_customer_number Priority Preference to designate
+  // existence of a Payments account.
+  autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+                                         12345);
+  // Add a masked server card whose |TypeAndLastFourDigits| matches a local
+  // card.
+  CreditCard server_card(CreditCard::MASKED_SERVER_CARD, "a123");
+  test::SetCreditCardInfo(&server_card, "Flo Master", "1111", "11",
+                          test::NextYear().c_str(), "1");
+  server_card.SetNetworkForMaskedCard(kVisaCard);
+  personal_data_.AddServerCreditCard(server_card);
+  // Add a local card whose |TypeAndLastFourDigits| matches a masked server
+  // card.
+  AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111111", "11",
+                    test::NextYear().c_str(), "1");
+  // Add another local credit card
+  AddLocalCrediCard(personal_data_, "Flo Master", "5555555555554444", "11",
+                    test::NextYear().c_str(), "1");
+
+  // Set up our credit card form data.
+  FormData credit_card_form;
+  test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+  FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+  // Edit the data, and submit.
+  EditCreditCardFrom(credit_card_form, "Flo Master", "5555555555554444", "11",
+                     test::NextYear().c_str(), "123");
+  FormSubmitted(credit_card_form);
+  EXPECT_FALSE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+}
+
+// Tests that local cards that match full server cards do not count as
+// migratable.
+TEST_F(LocalCardMigrationManagerTest,
+       MigrateCreditCard_LocalCardMatchFullServerCard) {
+  EnableAutofillCreditCardLocalCardMigrationExperiment();
+  personal_data_.ClearCreditCards();
+  personal_data_.ClearProfiles();
+  credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+  // Set the billing_customer_number Priority Preference to designate
+  // existence of a Payments account.
+  autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+                                         12345);
+  // Add a full server card whose number matches a local card.
+  CreditCard server_card(CreditCard::FULL_SERVER_CARD, "a123");
+  test::SetCreditCardInfo(&server_card, "Flo Master", "4111111111111111", "11",
+                          test::NextYear().c_str(), "1");
+  personal_data_.AddServerCreditCard(server_card);
+  // Add a local credit card whose number matches a full server card.
+  AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111111", "11",
+                    test::NextYear().c_str(), "1");
+  // Add another local credit card
+  AddLocalCrediCard(personal_data_, "Flo Master", "5555555555554444", "11",
+                    test::NextYear().c_str(), "1");
+
+  // Set up our credit card form data.
+  FormData credit_card_form;
+  test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+  FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+  // Edit the data, and submit.
+  EditCreditCardFrom(credit_card_form, "Flo Master", "5555555555554444", "11",
+                     test::NextYear().c_str(), "123");
+  FormSubmitted(credit_card_form);
+  EXPECT_FALSE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+}
+
+// GetDetectedValues() should includes cardholder name if all cards have it.
+TEST_F(LocalCardMigrationManagerTest, GetDetectedValues_AllWithCardHolderName) {
+  EnableAutofillCreditCardLocalCardMigrationExperiment();
+  personal_data_.ClearCreditCards();
+  personal_data_.ClearProfiles();
+  credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+  // Set the billing_customer_number Priority Preference to designate
+  // existence of a Payments account.
+  autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+                                         12345);
+  // Add a local credit card whose |TypeAndLastFourDigits| matches what we will
+  // enter below.
+  AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111111", "11",
+                    test::NextYear().c_str(), "1");
+  // Add another local credit card with a different cardholder name.
+  AddLocalCrediCard(personal_data_, "John Smith", "5555555555554444", "11",
+                    test::NextYear().c_str(), "1");
+
+  // Set up our credit card form data.
+  FormData credit_card_form;
+  test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+  FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+  // Edit the data, and submit.
+  EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11",
+                     test::NextYear().c_str(), "123");
+  FormSubmitted(credit_card_form);
+  EXPECT_TRUE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+  EXPECT_TRUE(local_card_migration_manager_->GetDetectedValues() &
+              CreditCardSaveManager::DetectedValue::CARDHOLDER_NAME);
+}
+
+// GetDetectedValues() should not include cardholder name if not all cards have
+// a cardholder name.
+TEST_F(LocalCardMigrationManagerTest,
+       GetDetectedValues_OneCardWithoutCardHolderName) {
+  EnableAutofillCreditCardLocalCardMigrationExperiment();
+  personal_data_.ClearCreditCards();
+  personal_data_.ClearProfiles();
+  credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+  // Set the billing_customer_number Priority Preference to designate
+  // existence of a Payments account.
+  autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+                                         12345);
+  // Add a local credit card whose |TypeAndLastFourDigits| matches what we will
+  // enter below.
+  AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111111", "11",
+                    test::NextYear().c_str(), "1");
+  // Add another local credit card without card holder name.
+  AddLocalCrediCard(personal_data_, "", "5555555555554444", "11",
+                    test::NextYear().c_str(), "1");
+  // Set up our credit card form data.
+  FormData credit_card_form;
+  test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+  FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+  // Edit the data, and submit.
+  EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11",
+                     test::NextYear().c_str(), "123");
+  FormSubmitted(credit_card_form);
+  EXPECT_TRUE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+  EXPECT_FALSE(local_card_migration_manager_->GetDetectedValues() &
+               CreditCardSaveManager::DetectedValue::CARDHOLDER_NAME);
+}
+
+// GetDetectedValues() should include the existence of a Google Payments
+// account.
+TEST_F(LocalCardMigrationManagerTest,
+       GetDetectedValues_IncludeGooglePaymentsAccount) {
+  EnableAutofillCreditCardLocalCardMigrationExperiment();
+  personal_data_.ClearCreditCards();
+  personal_data_.ClearProfiles();
+  credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+  // Set the billing_customer_number Priority Preference to designate
+  // existence of a Payments account.
+  autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+                                         12345);
+  // Add a local credit card whose |TypeAndLastFourDigits| matches what we will
+  // enter below.
+  AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111111", "11",
+                    test::NextYear().c_str(), "1");
+  // Add another local credit card
+  AddLocalCrediCard(personal_data_, "Flo Master", "5555555555554444", "11",
+                    test::NextYear().c_str(), "1");
+
+  // Set up our credit card form data.
+  FormData credit_card_form;
+  test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+  FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+  // Edit the data, and submit.
+  EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11",
+                     test::NextYear().c_str(), "123");
+  FormSubmitted(credit_card_form);
+  EXPECT_TRUE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+  EXPECT_TRUE(
+      local_card_migration_manager_->GetDetectedValues() &
+      CreditCardSaveManager::DetectedValue::HAS_GOOGLE_PAYMENTS_ACCOUNT);
+}
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/test_autofill_manager.cc b/components/autofill/core/browser/test_autofill_manager.cc
index 53b9d61..dfbe4a7 100644
--- a/components/autofill/core/browser/test_autofill_manager.cc
+++ b/components/autofill/core/browser/test_autofill_manager.cc
@@ -35,7 +35,8 @@
     AutofillClient* client,
     TestPersonalDataManager* personal_data,
     std::unique_ptr<CreditCardSaveManager> credit_card_save_manager,
-    payments::TestPaymentsClient* payments_client)
+    payments::TestPaymentsClient* payments_client,
+    std::unique_ptr<LocalCardMigrationManager> local_card_migration_manager)
     : AutofillManager(driver, client, personal_data),
       personal_data_(personal_data),
       test_form_data_importer_(
@@ -43,7 +44,8 @@
                                    payments_client,
                                    std::move(credit_card_save_manager),
                                    personal_data,
-                                   "en-US")),
+                                   "en-US",
+                                   std::move(local_card_migration_manager))),
       client_(client) {
   set_payments_client(payments_client);
   set_form_data_importer(test_form_data_importer_);
diff --git a/components/autofill/core/browser/test_autofill_manager.h b/components/autofill/core/browser/test_autofill_manager.h
index 5a365d4..29a4ba8b2 100644
--- a/components/autofill/core/browser/test_autofill_manager.h
+++ b/components/autofill/core/browser/test_autofill_manager.h
@@ -38,13 +38,15 @@
   TestAutofillManager(AutofillDriver* driver,
                       AutofillClient* client,
                       TestPersonalDataManager* personal_data);
-  // Called by CreditCardSaveManagerTest.
+  // Called by CreditCardSaveManagerTest and LocalCardMigrationManagerTest.
   TestAutofillManager(
       AutofillDriver* driver,
       AutofillClient* client,
       TestPersonalDataManager* personal_data,
       std::unique_ptr<CreditCardSaveManager> credit_card_save_manager,
-      payments::TestPaymentsClient* payments_client);
+      payments::TestPaymentsClient* payments_client,
+      std::unique_ptr<LocalCardMigrationManager> local_card_migration_manager =
+          nullptr);
   ~TestAutofillManager() override;
 
   // AutofillManager overrides.
diff --git a/components/autofill/core/browser/test_form_data_importer.cc b/components/autofill/core/browser/test_form_data_importer.cc
index cd6943e..297f620 100644
--- a/components/autofill/core/browser/test_form_data_importer.cc
+++ b/components/autofill/core/browser/test_form_data_importer.cc
@@ -11,12 +11,14 @@
     payments::PaymentsClient* payments_client,
     std::unique_ptr<CreditCardSaveManager> credit_card_save_manager,
     PersonalDataManager* personal_data_manager,
-    const std::string& app_locale)
+    const std::string& app_locale,
+    std::unique_ptr<LocalCardMigrationManager> local_card_migration_manager)
     : FormDataImporter(client,
                        payments_client,
                        personal_data_manager,
                        app_locale) {
   set_credit_card_save_manager(std::move(credit_card_save_manager));
+  set_local_card_migration_manager(std::move(local_card_migration_manager));
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/test_form_data_importer.h b/components/autofill/core/browser/test_form_data_importer.h
index c84887d..0125a8c 100644
--- a/components/autofill/core/browser/test_form_data_importer.h
+++ b/components/autofill/core/browser/test_form_data_importer.h
@@ -19,7 +19,9 @@
       payments::PaymentsClient* payments_client,
       std::unique_ptr<CreditCardSaveManager> credit_card_save_manager,
       PersonalDataManager* personal_data_manager,
-      const std::string& app_locale);
+      const std::string& app_locale,
+      std::unique_ptr<LocalCardMigrationManager> local_card_migration_manager =
+          nullptr);
 };
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/test_local_card_migration_manager.cc b/components/autofill/core/browser/test_local_card_migration_manager.cc
new file mode 100644
index 0000000..9ef4888
--- /dev/null
+++ b/components/autofill/core/browser/test_local_card_migration_manager.cc
@@ -0,0 +1,54 @@
+// Copyright 2018 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/autofill/core/browser/test_local_card_migration_manager.h"
+
+#include "components/autofill/core/browser/autofill_experiments.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/autofill/core/browser/payments/test_payments_client.h"
+#include "services/identity/public/cpp/identity_manager.h"
+
+namespace autofill {
+
+TestLocalCardMigrationManager::TestLocalCardMigrationManager(
+    AutofillDriver* driver,
+    AutofillClient* client,
+    payments::TestPaymentsClient* payments_client,
+    PersonalDataManager* personal_data_manager)
+    : LocalCardMigrationManager(client,
+                                payments_client,
+                                "en-US",
+                                personal_data_manager),
+      test_payments_client_(payments_client) {
+  if (test_payments_client_)
+    test_payments_client_->SetSaveDelegate(this);
+}
+
+TestLocalCardMigrationManager::~TestLocalCardMigrationManager() {}
+
+bool TestLocalCardMigrationManager::IsCreditCardMigrationEnabled() {
+  bool migration_experiment_enabled =
+      IsAutofillCreditCardLocalCardMigrationExperimentEnabled();
+  bool has_google_payments_account =
+      (static_cast<int64_t>(payments_client_->GetPrefService()->GetDouble(
+           prefs::kAutofillBillingCustomerNumber)) != 0);
+  return migration_experiment_enabled && has_google_payments_account;
+}
+
+bool TestLocalCardMigrationManager::LocalCardMigrationWasTriggered() {
+  return local_card_migration_was_triggered_;
+}
+
+void TestLocalCardMigrationManager::OnDidGetUploadDetails(
+    AutofillClient::PaymentsRpcResult result,
+    const base::string16& context_token,
+    std::unique_ptr<base::DictionaryValue> legal_message) {
+  if (result == AutofillClient::SUCCESS) {
+    local_card_migration_was_triggered_ = true;
+    LocalCardMigrationManager::OnDidGetUploadDetails(result, context_token,
+                                                     std::move(legal_message));
+  }
+}
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/test_local_card_migration_manager.h b/components/autofill/core/browser/test_local_card_migration_manager.h
new file mode 100644
index 0000000..07041a9d
--- /dev/null
+++ b/components/autofill/core/browser/test_local_card_migration_manager.h
@@ -0,0 +1,52 @@
+// Copyright 2018 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.
+
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_LOCAL_CARD_MIGRATION_MANAGER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_LOCAL_CARD_MIGRATION_MANAGER_H_
+
+#include <string>
+
+#include "components/autofill/core/browser/local_card_migration_manager.h"
+
+namespace autofill {
+
+namespace payments {
+class TestPaymentsClient;
+}  // namespace payments
+
+class AutofillClient;
+class AutofillDriver;
+class PersonalDataManager;
+
+class TestLocalCardMigrationManager : public LocalCardMigrationManager {
+ public:
+  TestLocalCardMigrationManager(AutofillDriver* driver,
+                                AutofillClient* client,
+                                payments::TestPaymentsClient* payments_client,
+                                PersonalDataManager* personal_data_manager);
+  ~TestLocalCardMigrationManager() override;
+
+  // Override the base function. Checks the existnece of billing customer number
+  // and the experiment flag, but unlike the real class, does not check if the
+  // user is signed in/syncing.
+  bool IsCreditCardMigrationEnabled() override;
+
+  // Returns whether the first round migration pop-up window was triggered.
+  bool LocalCardMigrationWasTriggered();
+
+ private:
+  void OnDidGetUploadDetails(
+      AutofillClient::PaymentsRpcResult result,
+      const base::string16& context_token,
+      std::unique_ptr<base::DictionaryValue> legal_message) override;
+
+  payments::TestPaymentsClient* test_payments_client_;  // Weak reference.
+  bool local_card_migration_was_triggered_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(TestLocalCardMigrationManager);
+};
+
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_LOCAL_CARD_MIGRATION_MANAGER_H_
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 27221c5..cb97cd6 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -27604,6 +27604,8 @@
   <int value="-1648216169" label="NewOmniboxAnswerTypes:disabled"/>
   <int value="-1638815914" label="enable-experimental-productivity-features"/>
   <int value="-1634878515" label="ChromeHomeModernLayout:enabled"/>
+  <int value="-1634490190"
+      label="AutofillCreditCardLocalCardMigration:disabled"/>
   <int value="-1634154256" label="ZeroSuggestRedirectToChrome:enabled"/>
   <int value="-1633586675" label="TabModalJsDialog:enabled"/>
   <int value="-1631329950" label="ssl-version-max"/>
@@ -28965,6 +28967,7 @@
   <int value="1214219155" label="gamepad-polling-interval"/>
   <int value="1214455758" label="VideoRotateToFullscreen:disabled"/>
   <int value="1215531732" label="OmniboxUIExperiments:disabled"/>
+  <int value="1215768255" label="AutofillCreditCardLocalCardMigration:enabled"/>
   <int value="1217907443" label="spurious-power-button-keyboard-accel"/>
   <int value="1219317631" label="ContextualSuggestionsAboveArticles:disabled"/>
   <int value="1219628795" label="PrintScaling:disabled"/>