diff --git a/.gitignore b/.gitignore
index 124ff7a..b17c78e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -155,7 +155,6 @@
 /chrome/test/data/vr/
 /chrome/test/data/webrtc/resources
 /chrome/test/data/xr/webvr_info/
-/chrome/test/data/xr/webxr_samples/
 /chrome/test/media_router/internal
 /chrome/test/python_tests/
 /chrome/tools/memory
diff --git a/AUTHORS b/AUTHORS
index 8b7f7b1..b9e09ac 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -697,6 +697,7 @@
 Peter Brophy <pbrophy@adobe.com>
 Peter Collingbourne <peter@pcc.me.uk>
 Peter Gal <pgal.u-szeged@partner.samsung.com>
+Peter Griffin <peter.griffin@linaro.org>
 Peter Molnar <pmolnar.u-szeged@partner.samsung.com>
 Peter Snyder <snyderp@gmail.com>
 Peter Wong <peter.wm.wong@gmail.com>
diff --git a/DEPS b/DEPS
index 933c504..1b9e321 100644
--- a/DEPS
+++ b/DEPS
@@ -106,7 +106,7 @@
   'checkout_simplechrome': '(checkout_chromeos and host_os == "linux") and ("{cros_board}" != "")',
   # Surround the board var in quotes so gclient doesn't try parsing the string
   # as an expression.
-  'cros_download_vm': '"{cros_board}" == "amd64-generic"',
+  'cros_download_vm': '("{cros_board}" == "amd64-generic") or ("{cros_board}" == "betty")',
   # Should we build and test for public (ie: full) CrOS images, or private
   # (ie: release) images.
   'use_public_cros_config': 'not checkout_src_internal',
@@ -138,11 +138,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'c51d791ab99228a9163c3313bb61015fb096f03f',
+  'skia_revision': '0dee19bacc00f327dca20cc7e4c0a97bc8082c0e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '5b40be11f04192c14b6b15fc273012b25e64725c',
+  'v8_revision': 'bffe9927669b41ad07e1a14bba243c4d4599e9c2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -150,11 +150,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '344ecaa6755dac1da0c1843fc0b69d5ff8207f8d',
+  'angle_revision': 'd9fa0744e82b9f77de54e086ce39f88cbe986b57',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '8458639991d4cf866ea5445550c9b24f84c11146',
+  'swiftshader_revision': 'e2cb4e0573e39651c863c93a002c6c97ef4f128a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -201,7 +201,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '1d6ef8a04840e9e9f8a09b3f4ad591229b769793',
+  'catapult_revision': '37e000347c2f946b6b35a7d9b429f772f11a0b02',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -273,7 +273,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '1c85976abe4e059684375915f955fc36049c8380',
+  'dawn_revision': '820a04b9ce6b4c9b21ad10e0e7a5a5e718b8a6db',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -477,7 +477,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '2a20a6899d0b8c47de26d6a33a12ece09821718a',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'f3f089d8833ef1c33357421779e75054e02c91fa',
       'condition': 'checkout_ios',
   },
 
@@ -832,7 +832,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '181e44c231854a3f201ef0884f6f8462f321ea15',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '6768b27cc897fc6f467fda7380643fb94101eed9',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1350,7 +1350,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '6f0b34abee8dba611c253738d955c59f703c147a',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '58c71db1b344295a059ae72d9fe247b7c1cd1047',
+    Var('webrtc_git') + '/src.git' + '@' + 'c1b36669db55a17ab7049bd2ea68db27653c097c',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1391,7 +1391,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@cbc5d3765f6061c9e3bb9bca73b681e2c7f50f98',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@617f67de972a2672713e8acd9ed1c6c032bac0ec',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index 3721856..813db8e 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -1514,6 +1514,7 @@
     'send_tab_to_self': {
       'filepath': 'components/send_tab_to_self'\
                   '|chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/'\
+                  '|chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/'\
                   '|chrome/browser/android/send_tab_to_self/'\
                   '|chrome/browser/send_tab_to_self/'\
                   '|chrome/browser/ui/send_tab_to_self'\
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index c0f3bdb..5d2b36a 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -1098,22 +1098,6 @@
   android_manifest_for_lint = system_webview_android_manifest
 }
 
-# Generate LocaleConfig.java for android_webview_locale_config_java's
-# compile step works.
-generate_locale_config_srcjar("webview_locale_config") {
-  java_package = "org.chromium.android_webview"
-}
-
-# LocaleConfig.java is excluded from the generated .jar
-# (via. jar_excluded_patterns) and the final version is inserted at the APK
-# level - with the list of pak locales populated by looking at the assets that
-# are listed in the final APK's .build_config.
-android_library("android_webview_locale_config_java") {
-  java_files = [ "java/src/org/chromium/android_webview/AwLocaleConfig.java" ]
-  srcjar_deps = [ ":webview_locale_config" ]
-  jar_excluded_patterns = [ "*/LocaleConfig.class" ]
-}
-
 android_aidl("crash_receiver_aidl") {
   import_include = [ "java/src" ]
   sources = [
diff --git a/android_webview/apk/BUILD.gn b/android_webview/apk/BUILD.gn
index 47e9226..8f5d555 100644
--- a/android_webview/apk/BUILD.gn
+++ b/android_webview/apk/BUILD.gn
@@ -31,9 +31,7 @@
     "java/src/com/android/webview/chromium/WebViewApkApplication.java",
   ]
   deps = [
-    "//android_webview:android_webview_locale_config_java",
     "//base:base_java",
     "//components/embedder_support/android:application_java",
-    "//ui/android:ui_java",
   ]
 }
diff --git a/android_webview/apk/java/src/com/android/webview/chromium/WebViewApkApplication.java b/android_webview/apk/java/src/com/android/webview/chromium/WebViewApkApplication.java
index 22d7b54..303ee89 100644
--- a/android_webview/apk/java/src/com/android/webview/chromium/WebViewApkApplication.java
+++ b/android_webview/apk/java/src/com/android/webview/chromium/WebViewApkApplication.java
@@ -7,12 +7,10 @@
 import android.app.Application;
 import android.content.Context;
 
-import org.chromium.android_webview.AwLocaleConfig;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.components.embedder_support.application.FontPreloadingWorkaround;
-import org.chromium.ui.base.ResourceBundle;
 
 /**
  * Application subclass for SystemWebView and Trichrome.
@@ -52,8 +50,6 @@
             return false;
         }
         LibraryLoader.getInstance().switchCommandLineForWebView();
-        ResourceBundle.setAvailablePakLocales(
-                new String[] {}, AwLocaleConfig.getWebViewSupportedPakLocales());
         nativeInitializePakResources();
         return true;
     }
diff --git a/android_webview/glue/glue.gni b/android_webview/glue/glue.gni
index aba86bd..0581c9b 100644
--- a/android_webview/glue/glue.gni
+++ b/android_webview/glue/glue.gni
@@ -5,9 +5,8 @@
 # This variable shared between 'glue' and 'glue_resource_rewriter' because
 # ResourceRewrite.java need to be generated according 'glue' deps.
 glue_library_deps = [
-  "//android_webview:android_webview_commandline_java",
   "//android_webview:android_webview_java",
-  "//android_webview:android_webview_locale_config_java",
+  "//android_webview:android_webview_commandline_java",
   "//android_webview:android_webview_platform_services_java",
   "//android_webview:system_webview_manifest",
   "//android_webview/support_library/boundary_interfaces:boundary_interface_java",
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
index 2c32e31..1d286e2 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
@@ -25,7 +25,6 @@
 import org.chromium.android_webview.AwContentsStatics;
 import org.chromium.android_webview.AwCookieManager;
 import org.chromium.android_webview.AwFirebaseConfig;
-import org.chromium.android_webview.AwLocaleConfig;
 import org.chromium.android_webview.AwNetworkChangeNotifierRegistrationPolicy;
 import org.chromium.android_webview.AwProxyController;
 import org.chromium.android_webview.AwQuotaManagerBridge;
@@ -54,7 +53,6 @@
 import org.chromium.base.task.TaskTraits;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.net.NetworkChangeNotifier;
-import org.chromium.ui.base.ResourceBundle;
 
 /**
  * Class controlling the Chromium initialization for WebView.
@@ -142,9 +140,6 @@
 
             JNIUtils.setClassLoader(WebViewChromiumAwInit.class.getClassLoader());
 
-            ResourceBundle.setAvailablePakLocales(
-                    new String[] {}, AwLocaleConfig.getWebViewSupportedPakLocales());
-
             // We are rewriting Java resources in the background.
             // NOTE: Any reference to Java resources will cause a crash.
 
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java b/android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java
index b134a9d..4512ab2 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java
@@ -168,6 +168,11 @@
         // thrown by the application callback won't have to be propagated through a native call
         // stack.
         new Handler().post(() -> mClient.onReceivedSslError(callback, sslError));
+
+        // Record UMA on ssl error
+        // Use sparse histogram in case new values are added in future releases
+        RecordHistogram.recordSparseHistogram(
+                "Android.WebView.onReceivedSslError.ErrorCode", sslError.getPrimaryError());
         return true;
     }
 
diff --git a/android_webview/java/src/org/chromium/android_webview/AwLocaleConfig.java b/android_webview/java/src/org/chromium/android_webview/AwLocaleConfig.java
deleted file mode 100644
index a383ef0..0000000
--- a/android_webview/java/src/org/chromium/android_webview/AwLocaleConfig.java
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2019 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.
-
-package org.chromium.android_webview;
-
-/**
- * Simple class that provides access to the array of uncompressed pak locales. See
- * //android_webview/BUILD.gn for more details.
- */
-public final class AwLocaleConfig {
-    private AwLocaleConfig() {}
-
-    public static String[] getWebViewSupportedPakLocales() {
-        return LocaleConfig.UNCOMPRESSED_LOCALES;
-    }
-}
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java
index ade959e..efd9942 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java
@@ -35,7 +35,6 @@
 import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer.OnPageFinishedHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.net.test.util.TestWebServer;
-import org.chromium.ui.base.ResourceBundle;
 
 import java.lang.annotation.Annotation;
 import java.util.Map;
@@ -84,7 +83,6 @@
     }
 
     public void setUp() throws Exception {
-        ResourceBundle.setAvailablePakLocales(new String[] {}, LocaleConfig.UNCOMPRESSED_LOCALES);
         if (needsAwBrowserContextCreated()) {
             createAwBrowserContext();
         }
diff --git a/android_webview/system_webview_apk_tmpl.gni b/android_webview/system_webview_apk_tmpl.gni
index aba533b..4d0bff5 100644
--- a/android_webview/system_webview_apk_tmpl.gni
+++ b/android_webview/system_webview_apk_tmpl.gni
@@ -32,8 +32,6 @@
       "//base:base_java",
     ]
 
-    locale_config_java_packages = [ "org.chromium.android_webview" ]
-
     if (!defined(alternative_android_sdk_dep)) {
       alternative_android_sdk_dep = webview_framework_dep
     }
diff --git a/android_webview/test/BUILD.gn b/android_webview/test/BUILD.gn
index d154d4a..d062602 100644
--- a/android_webview/test/BUILD.gn
+++ b/android_webview/test/BUILD.gn
@@ -73,7 +73,6 @@
     "shell/src/org/chromium/android_webview/test/OnlyRunIn.java",
     "shell/src/org/chromium/android_webview/test/TestContentProvider.java",
   ]
-  locale_config_java_packages = [ "org.chromium.android_webview.test" ]
 
   shared_libraries = [ ":libstandalonelibwebviewchromium" ]
 
diff --git a/android_webview/tools/apk_merger.py b/android_webview/tools/apk_merger.py
index ee44c51..5b95d3d 100755
--- a/android_webview/tools/apk_merger.py
+++ b/android_webview/tools/apk_merger.py
@@ -214,7 +214,7 @@
 
   ignores = ['META-INF', 'AndroidManifest.xml']
   if args.ignore_classes_dex:
-    ignores += ['classes.dex', 'classes2.dex']
+    ignores += ['classes.dex', 'classes2.dex', 'classes3.dex']
   if args.debug:
     # see http://crbug.com/648720
     ignores += ['webview_licenses.notice']
diff --git a/ash/DEPS b/ash/DEPS
index deae74a..10e3040 100644
--- a/ash/DEPS
+++ b/ash/DEPS
@@ -107,6 +107,9 @@
   "ash_service\.*": [
     "+chromeos/cryptohome",
   ],
+  "login_types\.*": [
+    "+chromeos/components/proximity_auth/public/interfaces/auth_type.mojom.h",
+  ],
   "message_center_controller\.*": [
     "+components/arc/common/notifications.mojom.h"
   ],
diff --git a/ash/detachable_base/detachable_base_handler.cc b/ash/detachable_base/detachable_base_handler.cc
index 84f449e..67c29c9 100644
--- a/ash/detachable_base/detachable_base_handler.cc
+++ b/ash/detachable_base/detachable_base_handler.cc
@@ -6,6 +6,7 @@
 
 #include "ash/detachable_base/detachable_base_observer.h"
 #include "ash/public/cpp/ash_pref_names.h"
+#include "ash/public/cpp/session/user_info.h"
 #include "ash/shell.h"
 #include "base/bind.h"
 #include "base/strings/string_number_conversions.h"
@@ -75,7 +76,7 @@
   observers_.RemoveObserver(observer);
 }
 
-void DetachableBaseHandler::RemoveUserData(const mojom::UserInfo& user) {
+void DetachableBaseHandler::RemoveUserData(const UserInfo& user) {
   last_used_devices_.erase(user.account_id);
 
   if (local_state_) {
@@ -94,7 +95,7 @@
 }
 
 bool DetachableBaseHandler::PairedBaseMatchesLastUsedByUser(
-    const mojom::UserInfo& user) const {
+    const UserInfo& user) const {
   if (GetPairingStatus() != DetachableBasePairingStatus::kAuthenticated)
     return false;
 
@@ -112,7 +113,7 @@
 }
 
 bool DetachableBaseHandler::SetPairedBaseAsLastUsedByUser(
-    const mojom::UserInfo& user) {
+    const UserInfo& user) {
   if (GetPairingStatus() != DetachableBasePairingStatus::kAuthenticated)
     return false;
 
@@ -202,8 +203,7 @@
 }
 
 DetachableBaseHandler::DetachableBaseId
-DetachableBaseHandler::GetLastUsedDeviceForUser(
-    const mojom::UserInfo& user) const {
+DetachableBaseHandler::GetLastUsedDeviceForUser(const UserInfo& user) const {
   const auto it = last_used_devices_.find(user.account_id);
   // If the last used device was set within this session, bypass local state.
   if (it != last_used_devices_.end())
diff --git a/ash/detachable_base/detachable_base_handler.h b/ash/detachable_base/detachable_base_handler.h
index ae1f0e5..ad6338d7 100644
--- a/ash/detachable_base/detachable_base_handler.h
+++ b/ash/detachable_base/detachable_base_handler.h
@@ -11,7 +11,6 @@
 
 #include "ash/ash_export.h"
 #include "ash/detachable_base/detachable_base_pairing_status.h"
-#include "ash/public/interfaces/user_info.mojom.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
@@ -19,6 +18,7 @@
 #include "base/scoped_observer.h"
 #include "chromeos/dbus/hammerd/hammerd_client.h"
 #include "chromeos/dbus/power/power_manager_client.h"
+#include "components/account_id/account_id.h"
 
 class PrefRegistrySimple;
 class PrefService;
@@ -26,6 +26,7 @@
 namespace ash {
 
 class DetachableBaseObserver;
+struct UserInfo;
 
 // Keeps track of the state of Chrome OS device's detachable base. It tracks
 // whether:
@@ -58,7 +59,7 @@
   void RemoveObserver(DetachableBaseObserver* observer);
 
   // Removes the detachable base data associated with a user from local state.
-  void RemoveUserData(const mojom::UserInfo& user);
+  void RemoveUserData(const UserInfo& user);
 
   // Gets the detachable base pairing state.
   DetachableBasePairingStatus GetPairingStatus() const;
@@ -67,7 +68,7 @@
   // Returns true only if the base has kAuthenticated pairing state.
   // If this the user has not previously used any detachable bases (i.e. the
   // last used detachable base is empty), this will return true.
-  bool PairedBaseMatchesLastUsedByUser(const mojom::UserInfo& user) const;
+  bool PairedBaseMatchesLastUsedByUser(const UserInfo& user) const;
 
   // Sets the currently paired base as the last used base for the user.
   // If the user is not ephemeral, the last used base information will be
@@ -79,7 +80,7 @@
   // yet initialized. Observers will be notified that pairing status
   // changed when local state is initialized (provided a base is still
   // paired) - setting the last used base can be retried at that point.
-  bool SetPairedBaseAsLastUsedByUser(const mojom::UserInfo& user);
+  bool SetPairedBaseAsLastUsedByUser(const UserInfo& user);
 
   // chromeos::HammerdClient::Observer:
   void BaseFirmwareUpdateNeeded() override;
@@ -111,7 +112,7 @@
 
   // Retrieves the last known base used by a user. Returns an empty string if
   // a detachable base usage was not previously recorded for the user.
-  DetachableBaseId GetLastUsedDeviceForUser(const mojom::UserInfo& user) const;
+  DetachableBaseId GetLastUsedDeviceForUser(const UserInfo& user) const;
 
   // Notifies observers that the detachable base pairing state has changed.
   void NotifyPairingStatusChanged();
diff --git a/ash/detachable_base/detachable_base_handler_unittest.cc b/ash/detachable_base/detachable_base_handler_unittest.cc
index 02602e8..03a2f00 100644
--- a/ash/detachable_base/detachable_base_handler_unittest.cc
+++ b/ash/detachable_base/detachable_base_handler_unittest.cc
@@ -9,7 +9,7 @@
 
 #include "ash/detachable_base/detachable_base_observer.h"
 #include "ash/detachable_base/detachable_base_pairing_status.h"
-#include "ash/public/interfaces/user_info.mojom.h"
+#include "ash/public/cpp/session/user_info.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/test/scoped_task_environment.h"
@@ -29,12 +29,12 @@
   kEphemeral,
 };
 
-mojom::UserInfoPtr CreateUser(const std::string& email,
-                              const std::string& gaia_id,
-                              UserType user_type) {
-  mojom::UserInfoPtr user = mojom::UserInfo::New();
-  user->account_id = AccountId::FromUserEmailGaiaId(email, gaia_id);
-  user->is_ephemeral = user_type == UserType::kEphemeral;
+UserInfo CreateUser(const std::string& email,
+                    const std::string& gaia_id,
+                    UserType user_type) {
+  UserInfo user;
+  user.account_id = AccountId::FromUserEmailGaiaId(email, gaia_id);
+  user.is_ephemeral = user_type == UserType::kEphemeral;
   return user;
 }
 
@@ -131,7 +131,7 @@
 
   std::unique_ptr<DetachableBaseHandler> handler_;
 
-  mojom::UserInfoPtr default_user_;
+  UserInfo default_user_;
 
  private:
   base::test::ScopedTaskEnvironment task_environment_;
@@ -147,7 +147,7 @@
 
   EXPECT_EQ(DetachableBasePairingStatus::kNone, handler_->GetPairingStatus());
   EXPECT_EQ(0, detachable_base_observer_.pairing_status_changed_count());
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
 }
 
 TEST_F(DetachableBaseHandlerTest, TabletModeOnOnStartup) {
@@ -165,7 +165,7 @@
   EXPECT_EQ(DetachableBasePairingStatus::kAuthenticated,
             handler_->GetPairingStatus());
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
-  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
+  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
 }
 
 TEST_F(DetachableBaseHandlerTest, SuccessfullPairing) {
@@ -181,7 +181,7 @@
   // The user should not be notified when they attach a base for the first time,
   // so the first paired base should be reported as kAuthenticated rather than
   // kAuthenticatedNotMatchingLastUsed.
-  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
+  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
   detachable_base_observer_.reset_pairing_status_changed_count();
 
   // Assume the base has been detached when the device switches to tablet mode.
@@ -189,7 +189,7 @@
       chromeos::PowerManagerClient::TabletMode::ON, base::TimeTicks());
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
   EXPECT_EQ(DetachableBasePairingStatus::kNone, handler_->GetPairingStatus());
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
   detachable_base_observer_.reset_pairing_status_changed_count();
 
   // When the device exits tablet mode again, the base should not be reported
@@ -197,13 +197,13 @@
   chromeos::FakePowerManagerClient::Get()->SetTabletMode(
       chromeos::PowerManagerClient::TabletMode::OFF, base::TimeTicks());
   EXPECT_EQ(DetachableBasePairingStatus::kNone, handler_->GetPairingStatus());
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
 
   hammerd_client_->FirePairChallengeSucceededSignal({0x01, 0x02, 0x03, 0x04});
   EXPECT_EQ(DetachableBasePairingStatus::kAuthenticated,
             handler_->GetPairingStatus());
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
-  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
+  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
   detachable_base_observer_.reset_pairing_status_changed_count();
 }
 
@@ -217,7 +217,7 @@
   EXPECT_EQ(DetachableBasePairingStatus::kNotAuthenticated,
             handler_->GetPairingStatus());
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
   detachable_base_observer_.reset_pairing_status_changed_count();
 
   // Assume the base has been detached when the device switches to tablet mode.
@@ -225,7 +225,7 @@
       chromeos::PowerManagerClient::TabletMode::ON, base::TimeTicks());
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
   EXPECT_EQ(DetachableBasePairingStatus::kNone, handler_->GetPairingStatus());
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
   detachable_base_observer_.reset_pairing_status_changed_count();
 }
 
@@ -239,7 +239,7 @@
   EXPECT_EQ(DetachableBasePairingStatus::kInvalidDevice,
             handler_->GetPairingStatus());
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
   detachable_base_observer_.reset_pairing_status_changed_count();
 
   // Assume the base has been detached when the device switches to tablet mode.
@@ -247,7 +247,7 @@
       chromeos::PowerManagerClient::TabletMode::ON, base::TimeTicks());
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
   EXPECT_EQ(DetachableBasePairingStatus::kNone, handler_->GetPairingStatus());
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
   detachable_base_observer_.reset_pairing_status_changed_count();
 }
 
@@ -258,7 +258,7 @@
   // device is not in tablet mode.
   EXPECT_EQ(0, detachable_base_observer_.pairing_status_changed_count());
   EXPECT_EQ(DetachableBasePairingStatus::kNone, handler_->GetPairingStatus());
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
 
   // Run loop so the callback for getting the initial power manager state gets
   // run.
@@ -267,7 +267,7 @@
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
   EXPECT_EQ(DetachableBasePairingStatus::kAuthenticated,
             handler_->GetPairingStatus());
-  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
+  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
   detachable_base_observer_.reset_pairing_status_changed_count();
 }
 
@@ -278,7 +278,7 @@
   // device is not in tablet mode.
   EXPECT_EQ(0, detachable_base_observer_.pairing_status_changed_count());
   EXPECT_EQ(DetachableBasePairingStatus::kNone, handler_->GetPairingStatus());
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
 
   // Run loop so the callback for getting the initial power manager state gets
   // run.
@@ -287,7 +287,7 @@
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
   EXPECT_EQ(DetachableBasePairingStatus::kNotAuthenticated,
             handler_->GetPairingStatus());
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
   detachable_base_observer_.reset_pairing_status_changed_count();
 }
 
@@ -298,7 +298,7 @@
   // device is not in tablet mode.
   EXPECT_EQ(0, detachable_base_observer_.pairing_status_changed_count());
   EXPECT_EQ(DetachableBasePairingStatus::kNone, handler_->GetPairingStatus());
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
 
   // Run loop so the callback for getting the initial power manager state gets
   // run.
@@ -307,7 +307,7 @@
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
   EXPECT_EQ(DetachableBasePairingStatus::kInvalidDevice,
             handler_->GetPairingStatus());
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
   detachable_base_observer_.reset_pairing_status_changed_count();
 }
 
@@ -322,7 +322,7 @@
 
   EXPECT_EQ(0, detachable_base_observer_.pairing_status_changed_count());
   EXPECT_EQ(DetachableBasePairingStatus::kNone, handler_->GetPairingStatus());
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
 }
 
 TEST_F(DetachableBaseHandlerTest, DetachableBaseChangeDetection) {
@@ -336,11 +336,11 @@
   EXPECT_EQ(DetachableBasePairingStatus::kAuthenticated,
             handler_->GetPairingStatus());
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
-  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
+  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
   detachable_base_observer_.reset_pairing_status_changed_count();
 
   // Set the current base as last used by the user.
-  handler_->SetPairedBaseAsLastUsedByUser(*default_user_);
+  handler_->SetPairedBaseAsLastUsedByUser(default_user_);
 
   // Simulate the paired base change.
   ChangePairedBase({0x04, 0x05, 0x06, 0x07});
@@ -348,7 +348,7 @@
   EXPECT_EQ(DetachableBasePairingStatus::kAuthenticated,
             handler_->GetPairingStatus());
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
   detachable_base_observer_.reset_pairing_status_changed_count();
 
   // Switch back to last used base.
@@ -357,7 +357,7 @@
   EXPECT_EQ(DetachableBasePairingStatus::kAuthenticated,
             handler_->GetPairingStatus());
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
-  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
+  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
   detachable_base_observer_.reset_pairing_status_changed_count();
 
   // The last used base should be preserved if the detachable base handler is
@@ -370,7 +370,7 @@
   EXPECT_EQ(DetachableBasePairingStatus::kAuthenticated,
             handler_->GetPairingStatus());
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
   detachable_base_observer_.reset_pairing_status_changed_count();
 }
 
@@ -378,7 +378,7 @@
   // Assume the user_1 has a last used base.
   base::RunLoop().RunUntilIdle();
   hammerd_client_->FirePairChallengeSucceededSignal({0x01, 0x02, 0x03, 0x04});
-  handler_->SetPairedBaseAsLastUsedByUser(*default_user_);
+  handler_->SetPairedBaseAsLastUsedByUser(default_user_);
   detachable_base_observer_.reset_pairing_status_changed_count();
 
   // Restart the handler, so it's initialized with the previously set up prefs
@@ -386,13 +386,13 @@
   RestartHandler();
   base::RunLoop().RunUntilIdle();
 
-  const mojom::UserInfoPtr second_user =
+  const UserInfo second_user =
       CreateUser("user_2@foo.bar", "222222", UserType::kNormal);
 
   EXPECT_EQ(DetachableBasePairingStatus::kNone, handler_->GetPairingStatus());
   EXPECT_EQ(0, detachable_base_observer_.pairing_status_changed_count());
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*second_user));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(second_user));
 
   // Pair a detachable base different than the one used by user_1.
   hammerd_client_->FirePairChallengeSucceededSignal({0x04, 0x05, 0x06, 0x07});
@@ -401,14 +401,14 @@
             handler_->GetPairingStatus());
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
   // The base for user_1 has changed.
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
   // User 2 has not used a detachable base yet - the base should be reported as
   // matching last used base.
-  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(*second_user));
+  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(second_user));
   detachable_base_observer_.reset_pairing_status_changed_count();
 
   // Set the last used detachable base for user 2, and pair the initial base.
-  handler_->SetPairedBaseAsLastUsedByUser(*second_user);
+  handler_->SetPairedBaseAsLastUsedByUser(second_user);
 
   ChangePairedBase({0x01, 0x02, 0x03, 0x04});
 
@@ -416,14 +416,14 @@
   EXPECT_EQ(DetachableBasePairingStatus::kAuthenticated,
             handler_->GetPairingStatus());
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
-  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*second_user));
+  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(second_user));
   detachable_base_observer_.reset_pairing_status_changed_count();
 
   // Set the base for user 2 to the current one.
-  handler_->SetPairedBaseAsLastUsedByUser(*second_user);
+  handler_->SetPairedBaseAsLastUsedByUser(second_user);
 
-  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(*second_user));
+  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(second_user));
 
   // When the base is paired next time, it should be considered authenticated
   // for both users.
@@ -432,8 +432,8 @@
   EXPECT_EQ(DetachableBasePairingStatus::kAuthenticated,
             handler_->GetPairingStatus());
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
-  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
-  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(*second_user));
+  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
+  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(second_user));
   detachable_base_observer_.reset_pairing_status_changed_count();
 }
 
@@ -442,7 +442,7 @@
   base::RunLoop().RunUntilIdle();
 
   hammerd_client_->FirePairChallengeSucceededSignal({0x01, 0x02, 0x03, 0x04});
-  handler_->SetPairedBaseAsLastUsedByUser(*default_user_);
+  handler_->SetPairedBaseAsLastUsedByUser(default_user_);
   detachable_base_observer_.reset_pairing_status_changed_count();
 
   // Switch to non-trusted base, and verify it's reported as such regardless
@@ -455,14 +455,14 @@
 
   hammerd_client_->FirePairChallengeFailedSignal();
 
-  const mojom::UserInfoPtr second_user =
+  const UserInfo second_user =
       CreateUser("user_2@foo.bar", "222222", UserType::kNormal);
 
   EXPECT_EQ(DetachableBasePairingStatus::kNotAuthenticated,
             handler_->GetPairingStatus());
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*second_user));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(second_user));
   detachable_base_observer_.reset_pairing_status_changed_count();
 }
 
@@ -471,7 +471,7 @@
   base::RunLoop().RunUntilIdle();
 
   hammerd_client_->FirePairChallengeSucceededSignal({0x01, 0x02, 0x03, 0x04});
-  handler_->SetPairedBaseAsLastUsedByUser(*default_user_);
+  handler_->SetPairedBaseAsLastUsedByUser(default_user_);
   detachable_base_observer_.reset_pairing_status_changed_count();
 
   // Switch to an invalid base, and verify it's reported as such regardless
@@ -484,26 +484,26 @@
 
   hammerd_client_->FireInvalidBaseConnectedSignal();
 
-  const mojom::UserInfoPtr second_user =
+  const UserInfo second_user =
       CreateUser("user_2@foo.bar", "222222", UserType::kNormal);
 
   EXPECT_EQ(DetachableBasePairingStatus::kInvalidDevice,
             handler_->GetPairingStatus());
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*second_user));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(second_user));
   detachable_base_observer_.reset_pairing_status_changed_count();
 }
 
 TEST_F(DetachableBaseHandlerTest, RemoveUserData) {
-  const mojom::UserInfoPtr second_user =
+  const UserInfo second_user =
       CreateUser("user_2@foo.bar", "222222", UserType::kNormal);
 
   // Assume the user_1 has a last used base.
   base::RunLoop().RunUntilIdle();
   hammerd_client_->FirePairChallengeSucceededSignal({0x01, 0x02, 0x03, 0x04});
-  handler_->SetPairedBaseAsLastUsedByUser(*default_user_);
-  handler_->SetPairedBaseAsLastUsedByUser(*second_user);
+  handler_->SetPairedBaseAsLastUsedByUser(default_user_);
+  handler_->SetPairedBaseAsLastUsedByUser(second_user);
   detachable_base_observer_.reset_pairing_status_changed_count();
 
   ChangePairedBase({0x04, 0x05, 0x06});
@@ -511,42 +511,42 @@
   EXPECT_EQ(DetachableBasePairingStatus::kAuthenticated,
             handler_->GetPairingStatus());
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*second_user));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(second_user));
   detachable_base_observer_.reset_pairing_status_changed_count();
 
   // Remove the data for user_2, and verify that the paired base is reported
   // as authenticated when the paired base changes again.
-  handler_->RemoveUserData(*second_user);
+  handler_->RemoveUserData(second_user);
   ChangePairedBase({0x07, 0x08, 0x09});
 
   EXPECT_EQ(DetachableBasePairingStatus::kAuthenticated,
             handler_->GetPairingStatus());
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
-  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(*second_user));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
+  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(second_user));
   detachable_base_observer_.reset_pairing_status_changed_count();
 
   // Verify that paired base will be properly set again for the previously
   // removed user.
-  handler_->SetPairedBaseAsLastUsedByUser(*second_user);
+  handler_->SetPairedBaseAsLastUsedByUser(second_user);
   ChangePairedBase({0x01, 0x02, 0x03, 0x04});
 
   EXPECT_EQ(DetachableBasePairingStatus::kAuthenticated,
             handler_->GetPairingStatus());
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
-  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(*default_user_));
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*second_user));
+  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(default_user_));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(second_user));
   detachable_base_observer_.reset_pairing_status_changed_count();
 }
 
 TEST_F(DetachableBaseHandlerTest, EphemeralUser) {
   base::RunLoop().RunUntilIdle();
 
-  const mojom::UserInfoPtr ephemeral_user =
+  const UserInfo ephemeral_user =
       CreateUser("user_3@foo.bar", "333333", UserType::kEphemeral);
   hammerd_client_->FirePairChallengeSucceededSignal({0x01, 0x02, 0x03, 0x04});
-  handler_->SetPairedBaseAsLastUsedByUser(*ephemeral_user);
+  handler_->SetPairedBaseAsLastUsedByUser(ephemeral_user);
   detachable_base_observer_.reset_pairing_status_changed_count();
 
   ChangePairedBase({0x04, 0x05, 0x06});
@@ -554,7 +554,7 @@
   EXPECT_EQ(DetachableBasePairingStatus::kAuthenticated,
             handler_->GetPairingStatus());
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
-  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(*ephemeral_user));
+  EXPECT_FALSE(handler_->PairedBaseMatchesLastUsedByUser(ephemeral_user));
   detachable_base_observer_.reset_pairing_status_changed_count();
 
   ChangePairedBase({0x01, 0x02, 0x03, 0x04});
@@ -562,7 +562,7 @@
   EXPECT_EQ(DetachableBasePairingStatus::kAuthenticated,
             handler_->GetPairingStatus());
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
-  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(*ephemeral_user));
+  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(ephemeral_user));
   detachable_base_observer_.reset_pairing_status_changed_count();
 
   // Verify that the information about the last used base gets lost if the
@@ -575,7 +575,7 @@
   EXPECT_EQ(DetachableBasePairingStatus::kAuthenticated,
             handler_->GetPairingStatus());
   EXPECT_EQ(1, detachable_base_observer_.pairing_status_changed_count());
-  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(*ephemeral_user));
+  EXPECT_TRUE(handler_->PairedBaseMatchesLastUsedByUser(ephemeral_user));
   detachable_base_observer_.reset_pairing_status_changed_count();
 }
 
diff --git a/ash/detachable_base/detachable_base_notification_controller.cc b/ash/detachable_base/detachable_base_notification_controller.cc
index 54adc9f..47b9a45 100644
--- a/ash/detachable_base/detachable_base_notification_controller.cc
+++ b/ash/detachable_base/detachable_base_notification_controller.cc
@@ -116,14 +116,14 @@
   if (pairing_status == DetachableBasePairingStatus::kNone)
     return;
 
-  const mojom::UserInfoPtr user_info = active_session->user_info.ToMojom();
+  const UserInfo& user_info = active_session->user_info;
   if (pairing_status == DetachableBasePairingStatus::kAuthenticated &&
-      detachable_base_handler_->PairedBaseMatchesLastUsedByUser(*user_info)) {
+      detachable_base_handler_->PairedBaseMatchesLastUsedByUser(user_info)) {
     // Set the current base as last used by the user.
     // PairedBaseMatchesLastUsedByUser returns true if the user has not
     // previously used a base, so make sure the last used base value is actually
     // set.
-    detachable_base_handler_->SetPairedBaseAsLastUsedByUser(*user_info);
+    detachable_base_handler_->SetPairedBaseAsLastUsedByUser(user_info);
     return;
   }
 
@@ -156,7 +156,7 @@
   // At this point the session is unblocked - mark the current base as used by
   // user (as they have just been notified about the base change).
   if (pairing_status == DetachableBasePairingStatus::kAuthenticated)
-    detachable_base_handler_->SetPairedBaseAsLastUsedByUser(*user_info);
+    detachable_base_handler_->SetPairedBaseAsLastUsedByUser(user_info);
 }
 
 void DetachableBaseNotificationController::RemovePairingNotification() {
diff --git a/ash/detachable_base/detachable_base_notification_controller_unittest.cc b/ash/detachable_base/detachable_base_notification_controller_unittest.cc
index 6fd29c1..afd8fa8 100644
--- a/ash/detachable_base/detachable_base_notification_controller_unittest.cc
+++ b/ash/detachable_base/detachable_base_notification_controller_unittest.cc
@@ -7,7 +7,7 @@
 #include <string>
 
 #include "ash/detachable_base/detachable_base_handler.h"
-#include "ash/public/interfaces/user_info.mojom.h"
+#include "ash/public/cpp/session/user_info.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/session/test_session_controller_client.h"
 #include "ash/shell.h"
@@ -21,14 +21,14 @@
 
 namespace ash {
 
-mojom::UserInfoPtr CreateTestUserInfo(const std::string& user_email) {
-  auto user_info = mojom::UserInfo::New();
-  user_info->type = user_manager::USER_TYPE_REGULAR;
-  user_info->account_id = AccountId::FromUserEmail(user_email);
-  user_info->display_name = "Test user";
-  user_info->display_email = user_email;
-  user_info->is_ephemeral = false;
-  user_info->is_new_profile = false;
+UserInfo CreateTestUserInfo(const std::string& user_email) {
+  UserInfo user_info;
+  user_info.type = user_manager::USER_TYPE_REGULAR;
+  user_info.account_id = AccountId::FromUserEmail(user_email);
+  user_info.display_name = "Test user";
+  user_info.display_email = user_email;
+  user_info.is_ephemeral = false;
+  user_info.is_new_profile = false;
   return user_info;
 }
 
@@ -138,11 +138,11 @@
 TEST_F(DetachableBaseNotificationControllerTest,
        NoNotificationIfSessionNotStarted) {
   const char kTestUser[] = "user_1@test.com";
-  mojom::UserInfoPtr test_user_info = CreateTestUserInfo(kTestUser);
+  UserInfo test_user_info = CreateTestUserInfo(kTestUser);
   // Set a detachable base as previously used by the user before log in.
   detachable_base_handler()->PairChallengeSucceeded({0x01, 0x01});
-  EXPECT_TRUE(detachable_base_handler()->SetPairedBaseAsLastUsedByUser(
-      *test_user_info));
+  EXPECT_TRUE(
+      detachable_base_handler()->SetPairedBaseAsLastUsedByUser(test_user_info));
 
   // Set up another detachable base as attached when the user logs in.
   detachable_base_handler()->PairChallengeSucceeded({0x02, 0x02});
@@ -156,11 +156,11 @@
 TEST_F(DetachableBaseNotificationControllerTest,
        NoNotificationOnSessionStartIfBaseMarkedAsLastUsed) {
   const char kTestUser[] = "user_1@test.com";
-  mojom::UserInfoPtr test_user_info = CreateTestUserInfo(kTestUser);
+  UserInfo test_user_info = CreateTestUserInfo(kTestUser);
   // Set a detachable base as previously used by the user before log in.
   detachable_base_handler()->PairChallengeSucceeded({0x01, 0x01});
-  EXPECT_TRUE(detachable_base_handler()->SetPairedBaseAsLastUsedByUser(
-      *test_user_info));
+  EXPECT_TRUE(
+      detachable_base_handler()->SetPairedBaseAsLastUsedByUser(test_user_info));
 
   // Set up another detachable base as attached when the user logs in.
   detachable_base_handler()->PairChallengeSucceeded({0x02, 0x02});
@@ -169,8 +169,8 @@
 
   // Mark the current device as last used by the user, and verify there is no
   // notification when the user logs in.
-  EXPECT_TRUE(detachable_base_handler()->SetPairedBaseAsLastUsedByUser(
-      *test_user_info));
+  EXPECT_TRUE(
+      detachable_base_handler()->SetPairedBaseAsLastUsedByUser(test_user_info));
   SimulateUserLogin(kTestUser);
   EXPECT_FALSE(IsBaseChangedNotificationVisible());
 }
@@ -212,7 +212,7 @@
   EXPECT_FALSE(IsBaseChangedNotificationVisible());
 
   EXPECT_TRUE(detachable_base_handler()->SetPairedBaseAsLastUsedByUser(
-      *session_controller()->GetUserSession(0)->user_info.ToMojom()));
+      session_controller()->GetUserSession(0)->user_info));
 
   UnblockUserSession();
   EXPECT_FALSE(IsBaseChangedNotificationVisible());
diff --git a/ash/login/login_screen_controller.cc b/ash/login/login_screen_controller.cc
index 48bd1e8..169b9c4 100644
--- a/ash/login/login_screen_controller.cc
+++ b/ash/login/login_screen_controller.cc
@@ -306,6 +306,10 @@
   login_screen_client_.FlushForTesting();
 }
 
+LoginScreenModel* LoginScreenController::GetModel() {
+  return DataDispatcher();
+}
+
 void LoginScreenController::SetClient(mojom::LoginScreenClientPtr client) {
   login_screen_client_ = std::move(client);
 }
@@ -354,18 +358,6 @@
   NOTIMPLEMENTED();
 }
 
-void LoginScreenController::ShowUserPodCustomIcon(
-    const AccountId& account_id,
-    mojom::EasyUnlockIconOptionsPtr icon) {
-  DataDispatcher()->ShowEasyUnlockIcon(account_id, icon);
-}
-
-void LoginScreenController::HideUserPodCustomIcon(const AccountId& account_id) {
-  auto icon_options = mojom::EasyUnlockIconOptions::New();
-  icon_options->icon = mojom::EasyUnlockIconId::NONE;
-  DataDispatcher()->ShowEasyUnlockIcon(account_id, icon_options);
-}
-
 void LoginScreenController::SetAuthType(
     const AccountId& account_id,
     proximity_auth::mojom::AuthType auth_type,
@@ -380,13 +372,6 @@
   }
 }
 
-void LoginScreenController::SetUserList(
-    std::vector<mojom::LoginUserInfoPtr> users) {
-  DCHECK(DataDispatcher());
-
-  DataDispatcher()->NotifyUsers(users);
-}
-
 void LoginScreenController::SetPinEnabledForUser(const AccountId& account_id,
                                                  bool is_enabled) {
   // Chrome will update pin pod state every time user tries to authenticate.
@@ -395,12 +380,6 @@
     DataDispatcher()->SetPinEnabledForUser(account_id, is_enabled);
 }
 
-void LoginScreenController::SetFingerprintState(const AccountId& account_id,
-                                                mojom::FingerprintState state) {
-  if (DataDispatcher())
-    DataDispatcher()->SetFingerprintState(account_id, state);
-}
-
 void LoginScreenController::NotifyFingerprintAuthResult(
     const AccountId& account_id,
     bool successful) {
@@ -408,12 +387,6 @@
     DataDispatcher()->NotifyFingerprintAuthResult(account_id, successful);
 }
 
-void LoginScreenController::SetAvatarForUser(const AccountId& account_id,
-                                             mojom::UserAvatarPtr avatar) {
-  for (auto& observer : observers_)
-    observer.SetAvatarForUser(account_id, avatar);
-}
-
 void LoginScreenController::EnableAuthForUser(const AccountId& account_id) {
   if (DataDispatcher())
     DataDispatcher()->EnableAuthForUser(account_id);
@@ -456,27 +429,6 @@
     DataDispatcher()->SetPublicSessionDisplayName(account_id, display_name);
 }
 
-void LoginScreenController::SetPublicSessionLocales(
-    const AccountId& account_id,
-    std::vector<mojom::LocaleItemPtr> locales,
-    const std::string& default_locale,
-    bool show_advanced_view) {
-  if (DataDispatcher()) {
-    DataDispatcher()->SetPublicSessionLocales(
-        account_id, locales, default_locale, show_advanced_view);
-  }
-}
-
-void LoginScreenController::SetPublicSessionKeyboardLayouts(
-    const AccountId& account_id,
-    const std::string& locale,
-    std::vector<mojom::InputMethodItemPtr> keyboard_layouts) {
-  if (DataDispatcher()) {
-    DataDispatcher()->SetPublicSessionKeyboardLayouts(account_id, locale,
-                                                      keyboard_layouts);
-  }
-}
-
 void LoginScreenController::SetPublicSessionShowFullManagementDisclosure(
     bool is_full_management_disclosure_needed) {
   if (DataDispatcher()) {
diff --git a/ash/login/login_screen_controller.h b/ash/login/login_screen_controller.h
index 551e1a9..53468c7da 100644
--- a/ash/login/login_screen_controller.h
+++ b/ash/login/login_screen_controller.h
@@ -10,6 +10,7 @@
 #include "ash/ash_export.h"
 #include "ash/login/login_screen_controller_observer.h"
 #include "ash/public/cpp/kiosk_app_menu.h"
+#include "ash/public/cpp/login_screen.h"
 #include "ash/public/cpp/system_tray_focus_observer.h"
 #include "ash/public/interfaces/login_screen.mojom.h"
 #include "base/macros.h"
@@ -30,6 +31,7 @@
 // This could send requests to LoginScreenClient and also handle requests from
 // LoginScreenClient through mojo.
 class ASH_EXPORT LoginScreenController : public mojom::LoginScreen,
+                                         public LoginScreen,
                                          public KioskAppMenu,
                                          public SystemTrayFocusObserver {
  public:
@@ -111,6 +113,9 @@
     force_fail_auth_for_debug_overlay_ = force_fail;
   }
 
+  // LoginScreen:
+  LoginScreenModel* GetModel() override;
+
   // mojom::LoginScreen:
   void SetClient(mojom::LoginScreenClientPtr client) override;
   void ShowLockScreen(ShowLockScreenCallback on_shown) override;
@@ -122,21 +127,13 @@
   void ShowWarningBanner(const base::string16& message) override;
   void HideWarningBanner() override;
   void ClearErrors() override;
-  void ShowUserPodCustomIcon(const AccountId& account_id,
-                             mojom::EasyUnlockIconOptionsPtr icon) override;
-  void HideUserPodCustomIcon(const AccountId& account_id) override;
   void SetAuthType(const AccountId& account_id,
                    proximity_auth::mojom::AuthType auth_type,
                    const base::string16& initial_value) override;
-  void SetUserList(std::vector<mojom::LoginUserInfoPtr> users) override;
   void SetPinEnabledForUser(const AccountId& account_id,
                             bool is_enabled) override;
-  void SetFingerprintState(const AccountId& account_id,
-                           mojom::FingerprintState state) override;
   void NotifyFingerprintAuthResult(const AccountId& account_id,
                                    bool successful) override;
-  void SetAvatarForUser(const AccountId& account_id,
-                        mojom::UserAvatarPtr avatar) override;
   void EnableAuthForUser(const AccountId& account_id) override;
   void DisableAuthForUser(
       const AccountId& account_id,
@@ -149,14 +146,6 @@
   void IsReadyForPassword(IsReadyForPasswordCallback callback) override;
   void SetPublicSessionDisplayName(const AccountId& account_id,
                                    const std::string& display_name) override;
-  void SetPublicSessionLocales(const AccountId& account_id,
-                               std::vector<mojom::LocaleItemPtr> locales,
-                               const std::string& default_locale,
-                               bool show_advanced_view) override;
-  void SetPublicSessionKeyboardLayouts(
-      const AccountId& account_id,
-      const std::string& locale,
-      std::vector<mojom::InputMethodItemPtr> keyboard_layouts) override;
   void SetPublicSessionShowFullManagementDisclosure(
       bool is_full_management_disclosure_needed) override;
   void ShowKioskAppError(const std::string& message) override;
diff --git a/ash/login/login_screen_controller_observer.cc b/ash/login/login_screen_controller_observer.cc
index 1adca05..3826f07 100644
--- a/ash/login/login_screen_controller_observer.cc
+++ b/ash/login/login_screen_controller_observer.cc
@@ -4,16 +4,10 @@
 
 #include "ash/login/login_screen_controller_observer.h"
 
-#include "ash/login/login_screen_controller.h"
-
 namespace ash {
 
 LoginScreenControllerObserver::~LoginScreenControllerObserver() = default;
 
-void LoginScreenControllerObserver::SetAvatarForUser(
-    const AccountId& account_id,
-    const mojom::UserAvatarPtr& avatar) {}
-
 void LoginScreenControllerObserver::OnFocusLeavingLockScreenApps(bool reverse) {
 }
 
diff --git a/ash/login/login_screen_controller_observer.h b/ash/login/login_screen_controller_observer.h
index 888eb37..92c09a9 100644
--- a/ash/login/login_screen_controller_observer.h
+++ b/ash/login/login_screen_controller_observer.h
@@ -7,9 +7,6 @@
 
 #include "ash/ash_export.h"
 #include "ash/public/interfaces/login_screen.mojom.h"
-#include "ash/public/interfaces/user_info.mojom.h"
-
-class AccountId;
 
 namespace ash {
 
@@ -19,10 +16,6 @@
  public:
   virtual ~LoginScreenControllerObserver();
 
-  // Called when |avatar| for |account_id| has changed.
-  virtual void SetAvatarForUser(const AccountId& account_id,
-                                const mojom::UserAvatarPtr& avatar);
-
   // Called when focus is leaving a lock screen app window due to tabbing.
   // |reverse| - whether the tab order is reversed.
   virtual void OnFocusLeavingLockScreenApps(bool reverse);
diff --git a/ash/login/login_screen_test_api.cc b/ash/login/login_screen_test_api.cc
index 3041669..1372249 100644
--- a/ash/login/login_screen_test_api.cc
+++ b/ash/login/login_screen_test_api.cc
@@ -185,7 +185,7 @@
   // users this API needs to search all user views for the associated user and
   // potentially activate that user so it is showing its password field.
   CHECK_EQ(account_id,
-           auth_test.user_view()->current_user()->basic_user_info->account_id);
+           auth_test.user_view()->current_user().basic_user_info.account_id);
 
   password_test.SubmitPassword(password);
 
diff --git a/ash/login/ui/fake_login_detachable_base_model.cc b/ash/login/ui/fake_login_detachable_base_model.cc
index 6f9d187..6e01c6712 100644
--- a/ash/login/ui/fake_login_detachable_base_model.cc
+++ b/ash/login/ui/fake_login_detachable_base_model.cc
@@ -5,7 +5,7 @@
 #include "ash/login/ui/fake_login_detachable_base_model.h"
 
 #include "ash/login/ui/login_data_dispatcher.h"
-#include "ash/public/interfaces/user_info.mojom.h"
+#include "ash/public/cpp/session/user_info.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash {
@@ -46,7 +46,7 @@
 }
 
 bool FakeLoginDetachableBaseModel::PairedBaseMatchesLastUsedByUser(
-    const mojom::UserInfo& user_info) {
+    const UserInfo& user_info) {
   EXPECT_FALSE(current_authenticated_base_.empty());
 
   std::string last_used = GetLastUsedBase(user_info.account_id);
@@ -54,7 +54,7 @@
 }
 
 bool FakeLoginDetachableBaseModel::SetPairedBaseAsLastUsedByUser(
-    const mojom::UserInfo& user_info) {
+    const UserInfo& user_info) {
   if (current_authenticated_base_.empty())
     return false;
 
diff --git a/ash/login/ui/fake_login_detachable_base_model.h b/ash/login/ui/fake_login_detachable_base_model.h
index bbd87fe..624dee5 100644
--- a/ash/login/ui/fake_login_detachable_base_model.h
+++ b/ash/login/ui/fake_login_detachable_base_model.h
@@ -45,9 +45,8 @@
 
   // LoginDetachableBaseModel:
   DetachableBasePairingStatus GetPairingStatus() override;
-  bool PairedBaseMatchesLastUsedByUser(
-      const mojom::UserInfo& user_info) override;
-  bool SetPairedBaseAsLastUsedByUser(const mojom::UserInfo& user_info) override;
+  bool PairedBaseMatchesLastUsedByUser(const UserInfo& user_info) override;
+  bool SetPairedBaseAsLastUsedByUser(const UserInfo& user_info) override;
 
  private:
   LoginDataDispatcher* data_dispatcher_;
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index 33fc378..9d52fab 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -205,8 +205,8 @@
   return keyboard_window == this_window ? keyboard_controller : nullptr;
 }
 
-bool IsPublicAccountUser(const mojom::LoginUserInfoPtr& user) {
-  return user->basic_user_info->type == user_manager::USER_TYPE_PUBLIC_ACCOUNT;
+bool IsPublicAccountUser(const LoginUserInfo& user) {
+  return user.basic_user_info.type == user_manager::USER_TYPE_PUBLIC_ACCOUNT;
 }
 
 bool IsTabletMode() {
@@ -357,10 +357,10 @@
   return view_->main_view_;
 }
 
-LockContentsView::UserState::UserState(const mojom::LoginUserInfoPtr& user_info)
-    : account_id(user_info->basic_user_info->account_id) {
-  fingerprint_state = user_info->fingerprint_state;
-  if (user_info->auth_type == proximity_auth::mojom::AuthType::ONLINE_SIGN_IN)
+LockContentsView::UserState::UserState(const LoginUserInfo& user_info)
+    : account_id(user_info.basic_user_info.account_id) {
+  fingerprint_state = user_info.fingerprint_state;
+  if (user_info.auth_type == proximity_auth::mojom::AuthType::ONLINE_SIGN_IN)
     force_online_sign_in = true;
 }
 
@@ -603,8 +603,7 @@
   return true;
 }
 
-void LockContentsView::OnUsersChanged(
-    const std::vector<mojom::LoginUserInfoPtr>& users) {
+void LockContentsView::OnUsersChanged(const std::vector<LoginUserInfo>& users) {
   // The debug view will potentially call this method many times. Make sure to
   // invalidate any child references.
   primary_big_view_ = nullptr;
@@ -618,8 +617,8 @@
 
   // Build user state list. Preserve previous state if the user already exists.
   std::vector<UserState> new_users;
-  for (const mojom::LoginUserInfoPtr& user : users) {
-    UserState* old_state = FindStateForUser(user->basic_user_info->account_id);
+  for (const LoginUserInfo& user : users) {
+    UserState* old_state = FindStateForUser(user.basic_user_info.account_id);
     if (old_state)
       new_users.push_back(std::move(*old_state));
     else
@@ -668,6 +667,29 @@
     primary_big_view_->RequestFocus();
 }
 
+void LockContentsView::OnUserAvatarChanged(const AccountId& account_id,
+                                           const UserAvatar& avatar) {
+  auto replace = [&avatar](const LoginUserInfo& user) {
+    auto changed = user;
+    changed.basic_user_info.avatar = avatar;
+    return changed;
+  };
+
+  LoginBigUserView* big =
+      TryToFindBigUser(account_id, false /*require_auth_active*/);
+  if (big) {
+    big->UpdateForUser(replace(big->GetCurrentUser()));
+    return;
+  }
+
+  LoginUserView* user =
+      users_list_ ? users_list_->GetUserView(account_id) : nullptr;
+  if (user) {
+    user->UpdateForUser(replace(user->current_user()), false /*animate*/);
+    return;
+  }
+}
+
 void LockContentsView::OnPinEnabledForUserChanged(const AccountId& user,
                                                   bool enabled) {
   LockContentsView::UserState* state = FindStateForUser(user);
@@ -684,9 +706,8 @@
     LayoutAuth(big_user, nullptr /*opt_to_hide*/, true /*animate*/);
 }
 
-void LockContentsView::OnFingerprintStateChanged(
-    const AccountId& account_id,
-    mojom::FingerprintState state) {
+void LockContentsView::OnFingerprintStateChanged(const AccountId& account_id,
+                                                 FingerprintState state) {
   UserState* user_state = FindStateForUser(account_id);
   if (!user_state)
     return;
@@ -703,7 +724,7 @@
   // location to the target animation position and not the current position.
   bool animate = true;
   if (user_state->fingerprint_state ==
-      mojom::FingerprintState::DISABLED_FROM_TIMEOUT) {
+      FingerprintState::DISABLED_FROM_TIMEOUT) {
     animate = false;
   }
 
@@ -711,7 +732,7 @@
   LayoutAuth(big_view, nullptr /*opt_to_hide*/, animate);
 
   if (user_state->fingerprint_state ==
-      mojom::FingerprintState::DISABLED_FROM_TIMEOUT) {
+      FingerprintState::DISABLED_FROM_TIMEOUT) {
     base::string16 error_text = l10n_util::GetStringUTF16(
         IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_DISABLED_FROM_TIMEOUT);
     auto* label = new views::Label(error_text);
@@ -829,14 +850,13 @@
     LayoutAuth(big_user, nullptr /*opt_to_hide*/, true /*animate*/);
 }
 
-void LockContentsView::OnShowEasyUnlockIcon(
-    const AccountId& user,
-    const mojom::EasyUnlockIconOptionsPtr& icon) {
+void LockContentsView::OnShowEasyUnlockIcon(const AccountId& user,
+                                            const EasyUnlockIconOptions& icon) {
   UserState* state = FindStateForUser(user);
   if (!state)
     return;
 
-  state->easy_unlock_state = icon->Clone();
+  state->easy_unlock_state = icon;
   UpdateEasyUnlockIconForUser(user);
 
   // Show tooltip only if the user is actively showing auth.
@@ -848,9 +868,9 @@
   if (tooltip_bubble_->GetVisible())
     tooltip_bubble_->Hide();
 
-  if (icon->autoshow_tooltip) {
+  if (icon.autoshow_tooltip) {
     tooltip_bubble_->SetAnchorView(big_user->auth_user()->password_view());
-    tooltip_bubble_->SetText(icon->tooltip);
+    tooltip_bubble_->SetText(icon.tooltip);
     tooltip_bubble_->Show();
     tooltip_bubble_->SetVisible(true);
   }
@@ -955,41 +975,39 @@
   if (!user_view || !IsPublicAccountUser(user_view->current_user()))
     return;
 
-  mojom::LoginUserInfoPtr user_info = user_view->current_user()->Clone();
-  user_info->basic_user_info->display_name = display_name;
+  LoginUserInfo user_info = user_view->current_user();
+  user_info.basic_user_info.display_name = display_name;
   user_view->UpdateForUser(user_info, false /*animate*/);
 }
 
 void LockContentsView::OnPublicSessionLocalesChanged(
     const AccountId& account_id,
-    const std::vector<mojom::LocaleItemPtr>& locales,
+    const std::vector<LocaleItem>& locales,
     const std::string& default_locale,
     bool show_advanced_view) {
   LoginUserView* user_view = TryToFindUserView(account_id);
   if (!user_view || !IsPublicAccountUser(user_view->current_user()))
     return;
 
-  mojom::LoginUserInfoPtr user_info = user_view->current_user()->Clone();
-  user_info->public_account_info->available_locales = mojo::Clone(locales);
-  user_info->public_account_info->default_locale = default_locale;
-  user_info->public_account_info->show_advanced_view = show_advanced_view;
+  LoginUserInfo user_info = user_view->current_user();
+  user_info.public_account_info->available_locales = locales;
+  user_info.public_account_info->default_locale = default_locale;
+  user_info.public_account_info->show_advanced_view = show_advanced_view;
   user_view->UpdateForUser(user_info, false /*animate*/);
 }
 
 void LockContentsView::OnPublicSessionKeyboardLayoutsChanged(
     const AccountId& account_id,
     const std::string& locale,
-    const std::vector<mojom::InputMethodItemPtr>& keyboard_layouts) {
+    const std::vector<InputMethodItem>& keyboard_layouts) {
   // Update expanded view because keyboard layouts is user interactive content.
   // I.e. user selects a language locale and the corresponding keyboard layouts
   // will be changed.
   if (expanded_view_->GetVisible() &&
-      expanded_view_->current_user()->basic_user_info->account_id ==
-          account_id) {
-    mojom::LoginUserInfoPtr user_info = expanded_view_->current_user()->Clone();
-    user_info->public_account_info->default_locale = locale;
-    user_info->public_account_info->keyboard_layouts =
-        mojo::Clone(keyboard_layouts);
+      expanded_view_->current_user().basic_user_info.account_id == account_id) {
+    LoginUserInfo user_info = expanded_view_->current_user();
+    user_info.public_account_info->default_locale = locale;
+    user_info.public_account_info->keyboard_layouts = keyboard_layouts;
     expanded_view_->UpdateForUser(user_info);
   }
 
@@ -999,15 +1017,14 @@
     return;
   }
 
-  mojom::LoginUserInfoPtr user_info = user_view->current_user()->Clone();
+  LoginUserInfo user_info = user_view->current_user();
   // Skip updating keyboard layouts if |locale| is not the default locale
   // of the user. I.e. user changed the default locale in the expanded view,
   // and it should be handled by expanded view.
-  if (user_info->public_account_info->default_locale != locale)
+  if (user_info.public_account_info->default_locale != locale)
     return;
 
-  user_info->public_account_info->keyboard_layouts =
-      mojo::Clone(keyboard_layouts);
+  user_info.public_account_info->keyboard_layouts = keyboard_layouts;
   user_view->UpdateForUser(user_info, false /*animate*/);
 }
 
@@ -1027,7 +1044,7 @@
       pairing_status == DetachableBasePairingStatus::kNone ||
       (pairing_status == DetachableBasePairingStatus::kAuthenticated &&
        detachable_base_model_->PairedBaseMatchesLastUsedByUser(
-           *CurrentBigUserView()->GetCurrentUser()->basic_user_info))) {
+           CurrentBigUserView()->GetCurrentUser().basic_user_info))) {
     if (detachable_base_error_bubble_->GetVisible())
       detachable_base_error_bubble_->Hide();
     return;
@@ -1071,29 +1088,6 @@
   Layout();
 }
 
-void LockContentsView::SetAvatarForUser(const AccountId& account_id,
-                                        const mojom::UserAvatarPtr& avatar) {
-  auto replace = [&](const mojom::LoginUserInfoPtr& user) {
-    auto changed = user->Clone();
-    changed->basic_user_info->avatar = avatar->Clone();
-    return changed;
-  };
-
-  LoginBigUserView* big =
-      TryToFindBigUser(account_id, false /*require_auth_active*/);
-  if (big) {
-    big->UpdateForUser(replace(big->GetCurrentUser()));
-    return;
-  }
-
-  LoginUserView* user =
-      users_list_ ? users_list_->GetUserView(account_id) : nullptr;
-  if (user) {
-    user->UpdateForUser(replace(user->current_user()), false /*animate*/);
-    return;
-  }
-}
-
 void LockContentsView::OnFocusLeavingLockScreenApps(bool reverse) {
   if (!reverse || lock_screen_apps_active_)
     FocusNextWidget(reverse);
@@ -1193,7 +1187,7 @@
 }
 
 void LockContentsView::CreateLowDensityLayout(
-    const std::vector<mojom::LoginUserInfoPtr>& users) {
+    const std::vector<LoginUserInfo>& users) {
   DCHECK_LE(users.size(), 2u);
 
   main_view_->AddChildView(primary_big_view_);
@@ -1234,7 +1228,7 @@
 }
 
 void LockContentsView::CreateMediumDensityLayout(
-    const std::vector<mojom::LoginUserInfoPtr>& users) {
+    const std::vector<LoginUserInfo>& users) {
   // Here is a diagram of this layout:
   //
   //    a A x B y b
@@ -1303,7 +1297,7 @@
 }
 
 void LockContentsView::CreateHighDensityLayout(
-    const std::vector<mojom::LoginUserInfoPtr>& users,
+    const std::vector<LoginUserInfo>& users,
     views::BoxLayout* main_layout) {
   // Insert spacing before the auth view.
   auto* fill = new NonAccessibleView();
@@ -1414,7 +1408,7 @@
         detachable_base_model_->GetPairingStatus() ==
             DetachableBasePairingStatus::kAuthenticated) {
       detachable_base_model_->SetPairedBaseAsLastUsedByUser(
-          *CurrentBigUserView()->GetCurrentUser()->basic_user_info);
+          CurrentBigUserView()->GetCurrentUser().basic_user_info);
     }
   } else {
     ++unlock_attempt_;
@@ -1448,7 +1442,7 @@
     DCHECK(view);
     if (view->auth_user()) {
       UserState* state = FindStateForUser(
-          view->auth_user()->current_user()->basic_user_info->account_id);
+          view->auth_user()->current_user().basic_user_info.account_id);
       uint32_t to_update_auth;
       if (state->force_online_sign_in) {
         to_update_auth = LoginAuthUserView::AUTH_ONLINE_SIGN_IN;
@@ -1466,9 +1460,9 @@
           to_update_auth |= LoginAuthUserView::AUTH_PIN;
         if (state->enable_tap_auth)
           to_update_auth |= LoginAuthUserView::AUTH_TAP;
-        if (state->fingerprint_state != mojom::FingerprintState::UNAVAILABLE &&
+        if (state->fingerprint_state != FingerprintState::UNAVAILABLE &&
             state->fingerprint_state !=
-                mojom::FingerprintState::DISABLED_FROM_TIMEOUT) {
+                FingerprintState::DISABLED_FROM_TIMEOUT) {
           to_update_auth |= LoginAuthUserView::AUTH_FINGERPRINT;
         }
 
@@ -1521,9 +1515,8 @@
   DCHECK(users_list_);
   LoginUserView* view = users_list_->user_view_at(user_index);
   DCHECK(view);
-  mojom::LoginUserInfoPtr previous_big_user =
-      primary_big_view_->GetCurrentUser()->Clone();
-  mojom::LoginUserInfoPtr new_big_user = view->current_user()->Clone();
+  LoginUserInfo previous_big_user = primary_big_view_->GetCurrentUser();
+  LoginUserInfo new_big_user = view->current_user();
 
   view->UpdateForUser(previous_big_user, true /*animate*/);
   primary_big_view_->UpdateForUser(new_big_user);
@@ -1543,31 +1536,29 @@
 
   LoginBigUserView* to_remove =
       is_primary ? primary_big_view_ : opt_secondary_big_view_;
-  DCHECK(to_remove->GetCurrentUser()->can_remove);
-  AccountId user = to_remove->GetCurrentUser()->basic_user_info->account_id;
+  DCHECK(to_remove->GetCurrentUser().can_remove);
+  AccountId user = to_remove->GetCurrentUser().basic_user_info.account_id;
 
   // Ask chrome to remove the user.
   Shell::Get()->login_screen_controller()->RemoveUser(user);
 
   // Display the new user list less |user|.
-  std::vector<mojom::LoginUserInfoPtr> new_users;
+  std::vector<LoginUserInfo> new_users;
   if (!is_primary)
-    new_users.push_back(primary_big_view_->GetCurrentUser()->Clone());
+    new_users.push_back(primary_big_view_->GetCurrentUser());
   if (is_primary && opt_secondary_big_view_)
-    new_users.push_back(opt_secondary_big_view_->GetCurrentUser()->Clone());
+    new_users.push_back(opt_secondary_big_view_->GetCurrentUser());
   if (users_list_) {
     for (int i = 0; i < users_list_->user_count(); ++i) {
-      new_users.push_back(
-          users_list_->user_view_at(i)->current_user()->Clone());
+      new_users.push_back(users_list_->user_view_at(i)->current_user());
     }
   }
-  data_dispatcher_->NotifyUsers(new_users);
+  data_dispatcher_->SetUserList(new_users);
 }
 
 void LockContentsView::OnBigUserChanged() {
-  const mojom::LoginUserInfoPtr& big_user =
-      CurrentBigUserView()->GetCurrentUser();
-  const AccountId big_user_account_id = big_user->basic_user_info->account_id;
+  const LoginUserInfo& big_user = CurrentBigUserView()->GetCurrentUser();
+  const AccountId big_user_account_id = big_user.basic_user_info.account_id;
 
   CurrentBigUserView()->RequestFocus();
 
@@ -1586,7 +1577,7 @@
 
   // http://crbug/866790: After Supervised Users are deprecated, remove this.
   if (ash::features::IsSupervisedUserDeprecationNoticeEnabled() &&
-      big_user->basic_user_info->type == user_manager::USER_TYPE_SUPERVISED) {
+      big_user.basic_user_info.type == user_manager::USER_TYPE_SUPERVISED) {
     base::string16 message = l10n_util::GetStringUTF16(
         IDS_ASH_LOGIN_POD_LEGACY_SUPERVISED_EXPIRATION_WARNING);
     // Shows supervised user deprecation message as a persistent error bubble.
@@ -1628,7 +1619,7 @@
 
   // Hide easy unlock icon if there is no data is available.
   if (!state->easy_unlock_state) {
-    big_view->auth_user()->SetEasyUnlockIcon(mojom::EasyUnlockIconId::NONE,
+    big_view->auth_user()->SetEasyUnlockIcon(EasyUnlockIconId::NONE,
                                              base::string16());
     return;
   }
@@ -1662,7 +1653,7 @@
       unlock_attempt_ >= kLoginAttemptsBeforeGaiaDialog) {
     Shell::Get()->login_screen_controller()->ShowGaiaSignin(
         true /*can_close*/,
-        big_view->auth_user()->current_user()->basic_user_info->account_id);
+        big_view->auth_user()->current_user().basic_user_info.account_id);
     return;
   }
 
@@ -1718,28 +1709,26 @@
     return;
 
   UserState* state =
-      FindStateForUser(big_view->GetCurrentUser()->basic_user_info->account_id);
+      FindStateForUser(big_view->GetCurrentUser().basic_user_info.account_id);
   DCHECK(state);
-  mojom::EasyUnlockIconOptionsPtr& easy_unlock_state = state->easy_unlock_state;
-  DCHECK(easy_unlock_state);
+  DCHECK(state->easy_unlock_state);
 
-  if (!easy_unlock_state->tooltip.empty()) {
+  if (!state->easy_unlock_state->tooltip.empty()) {
     tooltip_bubble_->SetAnchorView(big_view->auth_user()->password_view());
-    tooltip_bubble_->SetText(easy_unlock_state->tooltip);
+    tooltip_bubble_->SetText(state->easy_unlock_state->tooltip);
     tooltip_bubble_->Show();
   }
 }
 
 void LockContentsView::OnEasyUnlockIconTapped() {
   UserState* state = FindStateForUser(
-      CurrentBigUserView()->GetCurrentUser()->basic_user_info->account_id);
+      CurrentBigUserView()->GetCurrentUser().basic_user_info.account_id);
   DCHECK(state);
-  mojom::EasyUnlockIconOptionsPtr& easy_unlock_state = state->easy_unlock_state;
-  DCHECK(easy_unlock_state);
+  DCHECK(state->easy_unlock_state);
 
-  if (easy_unlock_state->hardlock_on_click) {
+  if (state->easy_unlock_state->hardlock_on_click) {
     AccountId user =
-        CurrentBigUserView()->GetCurrentUser()->basic_user_info->account_id;
+        CurrentBigUserView()->GetCurrentUser().basic_user_info.account_id;
     Shell::Get()->login_screen_controller()->HardlockPod(user);
     // TODO(jdufault): This should get called as a result of HardlockPod.
     OnTapToUnlockEnabledForUserChanged(user, false /*enabled*/);
@@ -1759,18 +1748,18 @@
   const LoginBigUserView* user = CurrentBigUserView();
   // If the pod should not show an expanded view, tapping on it will launch
   // Public Session immediately.
-  if (!user->GetCurrentUser()->public_account_info->show_expanded_view) {
+  if (!user->GetCurrentUser().public_account_info->show_expanded_view) {
     std::string default_input_method;
     for (const auto& keyboard :
-         user->GetCurrentUser()->public_account_info->keyboard_layouts) {
-      if (keyboard->selected) {
-        default_input_method = keyboard->ime_id;
+         user->GetCurrentUser().public_account_info->keyboard_layouts) {
+      if (keyboard.selected) {
+        default_input_method = keyboard.ime_id;
         break;
       }
     }
     Shell::Get()->login_screen_controller()->LaunchPublicSession(
-        user->GetCurrentUser()->basic_user_info->account_id,
-        user->GetCurrentUser()->public_account_info->default_locale,
+        user->GetCurrentUser().basic_user_info.account_id,
+        user->GetCurrentUser().public_account_info->default_locale,
         default_input_method);
     return;
   }
@@ -1789,7 +1778,7 @@
 }
 
 LoginBigUserView* LockContentsView::AllocateLoginBigUserView(
-    const mojom::LoginUserInfoPtr& user,
+    const LoginUserInfo& user,
     bool is_primary) {
   LoginAuthUserView::Callbacks auth_user_callbacks;
   auth_user_callbacks.on_auth = base::BindRepeating(
@@ -1829,12 +1818,11 @@
 
   // Find auth instance.
   if (primary_big_view_ &&
-      primary_big_view_->GetCurrentUser()->basic_user_info->account_id ==
-          user) {
+      primary_big_view_->GetCurrentUser().basic_user_info.account_id == user) {
     view = primary_big_view_;
   } else if (opt_secondary_big_view_ &&
              opt_secondary_big_view_->GetCurrentUser()
-                     ->basic_user_info->account_id == user) {
+                     .basic_user_info.account_id == user) {
     view = opt_secondary_big_view_;
   }
 
@@ -1857,7 +1845,7 @@
 }
 
 ScrollableUsersListView* LockContentsView::BuildScrollableUsersListView(
-    const std::vector<mojom::LoginUserInfoPtr>& users,
+    const std::vector<LoginUserInfo>& users,
     LoginDisplayStyle display_style) {
   auto* view = new ScrollableUsersListView(
       users,
diff --git a/ash/login/ui/lock_contents_view.h b/ash/login/ui/lock_contents_view.h
index ccde902..157d4ad 100644
--- a/ash/login/ui/lock_contents_view.h
+++ b/ash/login/ui/lock_contents_view.h
@@ -21,6 +21,7 @@
 #include "ash/login/ui/login_error_bubble.h"
 #include "ash/login/ui/login_tooltip_view.h"
 #include "ash/login/ui/non_accessible_view.h"
+#include "ash/public/cpp/login_types.h"
 #include "ash/public/cpp/system_tray_focus_observer.h"
 #include "ash/public/interfaces/login_screen.mojom.h"
 #include "ash/session/session_observer.h"
@@ -136,17 +137,16 @@
   bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
 
   // LoginScreenController::Observer:
-  void SetAvatarForUser(const AccountId& account_id,
-                        const mojom::UserAvatarPtr& avatar) override;
   void OnFocusLeavingLockScreenApps(bool reverse) override;
   void OnOobeDialogStateChanged(mojom::OobeDialogState state) override;
 
   // LoginDataDispatcher::Observer:
-  void OnUsersChanged(
-      const std::vector<mojom::LoginUserInfoPtr>& users) override;
+  void OnUsersChanged(const std::vector<LoginUserInfo>& users) override;
+  void OnUserAvatarChanged(const AccountId& account_id,
+                           const UserAvatar& avatar) override;
   void OnPinEnabledForUserChanged(const AccountId& user, bool enabled) override;
   void OnFingerprintStateChanged(const AccountId& account_id,
-                                 mojom::FingerprintState state) override;
+                                 FingerprintState state) override;
   void OnFingerprintAuthResult(const AccountId& account_id,
                                bool success) override;
   void OnAuthEnabledForUser(const AccountId& user) override;
@@ -157,9 +157,8 @@
   void OnTapToUnlockEnabledForUserChanged(const AccountId& user,
                                           bool enabled) override;
   void OnForceOnlineSignInForUser(const AccountId& user) override;
-  void OnShowEasyUnlockIcon(
-      const AccountId& user,
-      const mojom::EasyUnlockIconOptionsPtr& icon) override;
+  void OnShowEasyUnlockIcon(const AccountId& user,
+                            const EasyUnlockIconOptions& icon) override;
   void OnShowWarningBanner(const base::string16& message) override;
   void OnHideWarningBanner() override;
   void OnSystemInfoChanged(bool show,
@@ -169,15 +168,14 @@
   void OnPublicSessionDisplayNameChanged(
       const AccountId& account_id,
       const std::string& display_name) override;
-  void OnPublicSessionLocalesChanged(
-      const AccountId& account_id,
-      const std::vector<mojom::LocaleItemPtr>& locales,
-      const std::string& default_locale,
-      bool show_advanced_view) override;
+  void OnPublicSessionLocalesChanged(const AccountId& account_id,
+                                     const std::vector<LocaleItem>& locales,
+                                     const std::string& default_locale,
+                                     bool show_advanced_view) override;
   void OnPublicSessionKeyboardLayoutsChanged(
       const AccountId& account_id,
       const std::string& locale,
-      const std::vector<mojom::InputMethodItemPtr>& keyboard_layouts) override;
+      const std::vector<InputMethodItem>& keyboard_layouts) override;
   void OnPublicSessionShowFullManagementDisclosureChanged(
       bool show_full_management_disclosure) override;
   void OnDetachableBasePairingStatusChanged(
@@ -209,7 +207,7 @@
  private:
   class UserState {
    public:
-    explicit UserState(const mojom::LoginUserInfoPtr& user_info);
+    explicit UserState(const LoginUserInfo& user_info);
     UserState(UserState&&);
     ~UserState();
 
@@ -218,8 +216,8 @@
     bool enable_tap_auth = false;
     bool force_online_sign_in = false;
     bool disable_auth = false;
-    mojom::EasyUnlockIconOptionsPtr easy_unlock_state;
-    mojom::FingerprintState fingerprint_state;
+    base::Optional<EasyUnlockIconOptions> easy_unlock_state;
+    FingerprintState fingerprint_state;
 
    private:
     DISALLOW_COPY_AND_ASSIGN(UserState);
@@ -233,15 +231,12 @@
   void FocusNextWidget(bool reverse);
 
   // 1-2 users.
-  void CreateLowDensityLayout(
-      const std::vector<mojom::LoginUserInfoPtr>& users);
+  void CreateLowDensityLayout(const std::vector<LoginUserInfo>& users);
   // 3-6 users.
-  void CreateMediumDensityLayout(
-      const std::vector<mojom::LoginUserInfoPtr>& users);
+  void CreateMediumDensityLayout(const std::vector<LoginUserInfo>& users);
   // 7+ users.
-  void CreateHighDensityLayout(
-      const std::vector<mojom::LoginUserInfoPtr>& users,
-      views::BoxLayout* main_layout);
+  void CreateHighDensityLayout(const std::vector<LoginUserInfo>& users,
+                               views::BoxLayout* main_layout);
 
   // Lay out the entire view. This is called when the view is attached to a
   // widget and when the screen is rotated.
@@ -319,9 +314,8 @@
   void OnPublicAccountTapped(bool is_primary);
 
   // Helper method to allocate a LoginBigUserView instance.
-  LoginBigUserView* AllocateLoginBigUserView(
-      const mojom::LoginUserInfoPtr& user,
-      bool is_primary);
+  LoginBigUserView* AllocateLoginBigUserView(const LoginUserInfo& user,
+                                             bool is_primary);
 
   // Returns the big view for |user| if |user| is one of the active
   // big views. If |require_auth_active| is true then the view must
@@ -334,7 +328,7 @@
 
   // Returns scrollable view with initialized size and rows for all |users|.
   ScrollableUsersListView* BuildScrollableUsersListView(
-      const std::vector<mojom::LoginUserInfoPtr>& users,
+      const std::vector<LoginUserInfo>& users,
       LoginDisplayStyle display_style);
 
   // Change the visibility of child views based on the |style|.
diff --git a/ash/login/ui/lock_contents_view_unittest.cc b/ash/login/ui/lock_contents_view_unittest.cc
index 199632b..d418441 100644
--- a/ash/login/ui/lock_contents_view_unittest.cc
+++ b/ash/login/ui/lock_contents_view_unittest.cc
@@ -115,8 +115,8 @@
       LoginUserView::TestApi user_test_api(users_list.user_views()[i]);
       EXPECT_EQ(expected_style, user_test_api.display_style());
 
-      const mojom::LoginUserInfoPtr& user = users()[i + 1];
-      EXPECT_EQ(base::UTF8ToUTF16(user->basic_user_info->display_name),
+      const LoginUserInfo& user = users()[i + 1];
+      EXPECT_EQ(base::UTF8ToUTF16(user.basic_user_info.display_name),
                 user_test_api.displayed_name());
     }
   }
@@ -443,12 +443,11 @@
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
 
   // Capture user info to validate it did not change during the swap.
-  AccountId primary_user = test_api.primary_big_view()
-                               ->GetCurrentUser()
-                               ->basic_user_info->account_id;
+  AccountId primary_user =
+      test_api.primary_big_view()->GetCurrentUser().basic_user_info.account_id;
   AccountId secondary_user = test_api.opt_secondary_big_view()
                                  ->GetCurrentUser()
-                                 ->basic_user_info->account_id;
+                                 .basic_user_info.account_id;
   EXPECT_NE(primary_user, secondary_user);
 
   // Primary user starts with auth. Secondary user does not have any auth.
@@ -465,12 +464,12 @@
   generator->ClickLeftButton();
 
   // User info is not swapped.
-  EXPECT_EQ(primary_user, test_api.primary_big_view()
-                              ->GetCurrentUser()
-                              ->basic_user_info->account_id);
+  EXPECT_EQ(
+      primary_user,
+      test_api.primary_big_view()->GetCurrentUser().basic_user_info.account_id);
   EXPECT_EQ(secondary_user, test_api.opt_secondary_big_view()
                                 ->GetCurrentUser()
-                                ->basic_user_info->account_id);
+                                .basic_user_info.account_id);
 
   // Active auth user (ie, which user is showing password) is swapped.
   EXPECT_FALSE(test_api.primary_big_view()->IsAuthEnabled());
@@ -494,10 +493,9 @@
 
   for (const LoginUserView* const list_user_view : users_list.user_views()) {
     // Capture user info to validate it did not change during the swap.
-    AccountId auth_id =
-        auth_view->GetCurrentUser()->basic_user_info->account_id;
+    AccountId auth_id = auth_view->GetCurrentUser().basic_user_info.account_id;
     AccountId list_user_id =
-        list_user_view->current_user()->basic_user_info->account_id;
+        list_user_view->current_user().basic_user_info.account_id;
     EXPECT_NE(auth_id, list_user_id);
 
     // Send event to swap users.
@@ -507,15 +505,15 @@
 
     // User info is swapped.
     EXPECT_EQ(list_user_id,
-              auth_view->GetCurrentUser()->basic_user_info->account_id);
+              auth_view->GetCurrentUser().basic_user_info.account_id);
     EXPECT_EQ(auth_id,
-              list_user_view->current_user()->basic_user_info->account_id);
+              list_user_view->current_user().basic_user_info.account_id);
 
     // Validate that every user is still unique.
     std::unordered_set<std::string> emails;
     for (const LoginUserView* const view : users_list.user_views()) {
       std::string email =
-          view->current_user()->basic_user_info->account_id.GetUserEmail();
+          view->current_user().basic_user_info.account_id.GetUserEmail();
       EXPECT_TRUE(emails.insert(email).second);
     }
   }
@@ -730,16 +728,16 @@
 
   // Show an icon with |autoshow_tooltip| is false. Tooltip bubble is not
   // activated.
-  auto icon = mojom::EasyUnlockIconOptions::New();
-  icon->icon = mojom::EasyUnlockIconId::LOCKED;
-  icon->autoshow_tooltip = false;
-  DataDispatcher()->ShowEasyUnlockIcon(users()[0]->basic_user_info->account_id,
+  EasyUnlockIconOptions icon;
+  icon.icon = EasyUnlockIconId::LOCKED;
+  icon.autoshow_tooltip = false;
+  DataDispatcher()->ShowEasyUnlockIcon(users()[0].basic_user_info.account_id,
                                        icon);
   EXPECT_FALSE(test_api.tooltip_bubble()->GetVisible());
 
   // Show icon with |autoshow_tooltip| set to true. Tooltip bubble is shown.
-  icon->autoshow_tooltip = true;
-  DataDispatcher()->ShowEasyUnlockIcon(users()[0]->basic_user_info->account_id,
+  icon.autoshow_tooltip = true;
+  DataDispatcher()->ShowEasyUnlockIcon(users()[0].basic_user_info.account_id,
                                        icon);
   EXPECT_TRUE(test_api.tooltip_bubble()->GetVisible());
 }
@@ -772,18 +770,18 @@
 
   // Enables easy unlock icon for |view|.
   auto enable_icon = [&](LoginBigUserView* view) {
-    auto icon = mojom::EasyUnlockIconOptions::New();
-    icon->icon = mojom::EasyUnlockIconId::LOCKED;
+    EasyUnlockIconOptions icon;
+    icon.icon = EasyUnlockIconId::LOCKED;
     DataDispatcher()->ShowEasyUnlockIcon(
-        view->GetCurrentUser()->basic_user_info->account_id, icon);
+        view->GetCurrentUser().basic_user_info.account_id, icon);
   };
 
   // Disables easy unlock icon for |view|.
   auto disable_icon = [&](LoginBigUserView* view) {
-    auto icon = mojom::EasyUnlockIconOptions::New();
-    icon->icon = mojom::EasyUnlockIconId::NONE;
+    EasyUnlockIconOptions icon;
+    icon.icon = EasyUnlockIconId::NONE;
     DataDispatcher()->ShowEasyUnlockIcon(
-        view->GetCurrentUser()->basic_user_info->account_id, icon);
+        view->GetCurrentUser().basic_user_info.account_id, icon);
   };
 
   // Makes |view| the active auth view so it will can show auth methods.
@@ -850,9 +848,8 @@
   // Password submit runs mojo.
   std::unique_ptr<MockLoginScreenClient> client = BindMockLoginScreenClient();
   client->set_authenticate_user_callback_result(false);
-  EXPECT_CALL(*client,
-              AuthenticateUserWithPasswordOrPin_(
-                  users()[0]->basic_user_info->account_id, _, false, _));
+  EXPECT_CALL(*client, AuthenticateUserWithPasswordOrPin_(
+                           users()[0].basic_user_info.account_id, _, false, _));
 
   // Submit password.
   ui::test::EventGenerator* generator = GetEventGenerator();
@@ -882,9 +879,8 @@
   // Password submit runs mojo.
   std::unique_ptr<MockLoginScreenClient> client = BindMockLoginScreenClient();
   client->set_authenticate_user_callback_result(false);
-  EXPECT_CALL(*client,
-              AuthenticateUserWithPasswordOrPin_(
-                  users()[0]->basic_user_info->account_id, _, false, _));
+  EXPECT_CALL(*client, AuthenticateUserWithPasswordOrPin_(
+                           users()[0].basic_user_info.account_id, _, false, _));
 
   // AuthErrorButton should not be visible yet.
   EXPECT_FALSE(test_api.auth_error_bubble()->GetVisible());
@@ -971,7 +967,7 @@
   EXPECT_CALL(*client,
               ShowGaiaSignin(true /*can_close*/,
                              base::Optional<AccountId>(
-                                 users()[0]->basic_user_info->account_id)))
+                                 users()[0].basic_user_info.account_id)))
       .Times(1);
   submit_password();
   Mock::VerifyAndClearExpectations(client.get());
@@ -989,10 +985,8 @@
       DataDispatcher(), std::move(fake_detachable_base_model));
   SetUserCount(2);
 
-  const AccountId& kFirstUserAccountId =
-      users()[0]->basic_user_info->account_id;
-  const AccountId& kSecondUserAccountId =
-      users()[1]->basic_user_info->account_id;
+  const AccountId& kFirstUserAccountId = users()[0].basic_user_info.account_id;
+  const AccountId& kSecondUserAccountId = users()[1].basic_user_info.account_id;
 
   // Initialize the detachable base state, so the user 1 has previously used
   // detachable base.
@@ -1071,10 +1065,8 @@
       DataDispatcher(), std::move(fake_detachable_base_model));
   SetUserCount(2);
 
-  const AccountId& kFirstUserAccountId =
-      users()[0]->basic_user_info->account_id;
-  const AccountId& kSecondUserAccountId =
-      users()[1]->basic_user_info->account_id;
+  const AccountId& kFirstUserAccountId = users()[0].basic_user_info.account_id;
+  const AccountId& kSecondUserAccountId = users()[1].basic_user_info.account_id;
 
   detachable_base_model->InitLastUsedBases({{kSecondUserAccountId, "5678"}});
 
@@ -1134,7 +1126,7 @@
       DataDispatcher(), std::move(fake_detachable_base_model));
   SetUserCount(1);
 
-  const AccountId& kUserAccountId = users()[0]->basic_user_info->account_id;
+  const AccountId& kUserAccountId = users()[0].basic_user_info.account_id;
 
   // Initialize the detachable base state, as if the user has previously used
   // detachable base.
@@ -1170,7 +1162,7 @@
       DataDispatcher(), std::move(fake_detachable_base_model));
   SetUserCount(1);
 
-  const AccountId& kUserAccountId = users()[0]->basic_user_info->account_id;
+  const AccountId& kUserAccountId = users()[0].basic_user_info.account_id;
 
   // Initialize the detachable base state, as if the user has previously used
   // detachable base.
@@ -1221,7 +1213,7 @@
       DataDispatcher(), std::move(fake_detachable_base_model));
   SetUserCount(1);
 
-  const AccountId& kUserAccountId = users()[0]->basic_user_info->account_id;
+  const AccountId& kUserAccountId = users()[0].basic_user_info.account_id;
 
   // Initialize the detachable base state, as if the user has previously used
   // detachable base.
@@ -1453,12 +1445,11 @@
   LockContentsView::TestApi test_api(contents);
 
   // Capture user info to validate it did not change during the swap.
-  AccountId primary_user = test_api.primary_big_view()
-                               ->GetCurrentUser()
-                               ->basic_user_info->account_id;
+  AccountId primary_user =
+      test_api.primary_big_view()->GetCurrentUser().basic_user_info.account_id;
   AccountId secondary_user = test_api.opt_secondary_big_view()
                                  ->GetCurrentUser()
-                                 ->basic_user_info->account_id;
+                                 .basic_user_info.account_id;
   EXPECT_NE(primary_user, secondary_user);
 
   // Primary user starts with auth. Secondary user does not have any auth.
@@ -1480,12 +1471,12 @@
   generator->ClickLeftButton();
 
   // User info is not swapped.
-  EXPECT_EQ(primary_user, test_api.primary_big_view()
-                              ->GetCurrentUser()
-                              ->basic_user_info->account_id);
+  EXPECT_EQ(
+      primary_user,
+      test_api.primary_big_view()->GetCurrentUser().basic_user_info.account_id);
   EXPECT_EQ(secondary_user, test_api.opt_secondary_big_view()
                                 ->GetCurrentUser()
-                                ->basic_user_info->account_id);
+                                .basic_user_info.account_id);
 
   // Child view of LoginBigUserView stays the same.
   ASSERT_TRUE(test_api.primary_big_view()->public_account());
@@ -1533,7 +1524,7 @@
   };
 
   auto is_public_account = [](const LoginUserView* view) -> bool {
-    return view->current_user()->basic_user_info->type ==
+    return view->current_user().basic_user_info.type ==
            user_manager::USER_TYPE_PUBLIC_ACCOUNT;
   };
 
@@ -1541,9 +1532,9 @@
   // account user).
   EXPECT_TRUE(is_public_account(user_view0));
   AccountId primary_id =
-      primary_big_view->GetCurrentUser()->basic_user_info->account_id;
+      primary_big_view->GetCurrentUser().basic_user_info.account_id;
   AccountId list_user_id =
-      user_view0->current_user()->basic_user_info->account_id;
+      user_view0->current_user().basic_user_info.account_id;
   EXPECT_NE(primary_id, list_user_id);
 
   // Send event to swap users.
@@ -1551,9 +1542,8 @@
 
   // User info is swapped.
   EXPECT_EQ(list_user_id,
-            primary_big_view->GetCurrentUser()->basic_user_info->account_id);
-  EXPECT_EQ(primary_id,
-            user_view0->current_user()->basic_user_info->account_id);
+            primary_big_view->GetCurrentUser().basic_user_info.account_id);
+  EXPECT_EQ(primary_id, user_view0->current_user().basic_user_info.account_id);
 
   // Child view of primary big user stays the same.
   ASSERT_TRUE(primary_big_view->public_account());
@@ -1564,8 +1554,8 @@
   // Case 2: Swap user_view1 (auth user) with primary big user (public account
   // user).
   EXPECT_FALSE(is_public_account(user_view1));
-  primary_id = primary_big_view->GetCurrentUser()->basic_user_info->account_id;
-  list_user_id = user_view1->current_user()->basic_user_info->account_id;
+  primary_id = primary_big_view->GetCurrentUser().basic_user_info.account_id;
+  list_user_id = user_view1->current_user().basic_user_info.account_id;
   EXPECT_NE(primary_id, list_user_id);
 
   // Send event to swap users.
@@ -1573,9 +1563,8 @@
 
   // User info is swapped.
   EXPECT_EQ(list_user_id,
-            primary_big_view->GetCurrentUser()->basic_user_info->account_id);
-  EXPECT_EQ(primary_id,
-            user_view1->current_user()->basic_user_info->account_id);
+            primary_big_view->GetCurrentUser().basic_user_info.account_id);
+  EXPECT_EQ(primary_id, user_view1->current_user().basic_user_info.account_id);
 
   // Primary big user becomes auth user and its child view is rebuilt.
   ASSERT_FALSE(primary_big_view->public_account());
@@ -1585,8 +1574,8 @@
 
   // Case 3: Swap user_view2 (auth user) with primary big user (auth user).
   EXPECT_FALSE(is_public_account(user_view2));
-  primary_id = primary_big_view->GetCurrentUser()->basic_user_info->account_id;
-  list_user_id = user_view2->current_user()->basic_user_info->account_id;
+  primary_id = primary_big_view->GetCurrentUser().basic_user_info.account_id;
+  list_user_id = user_view2->current_user().basic_user_info.account_id;
   EXPECT_NE(primary_id, list_user_id);
 
   // Send event to swap users.
@@ -1594,9 +1583,8 @@
 
   // User info is swapped.
   EXPECT_EQ(list_user_id,
-            primary_big_view->GetCurrentUser()->basic_user_info->account_id);
-  EXPECT_EQ(primary_id,
-            user_view2->current_user()->basic_user_info->account_id);
+            primary_big_view->GetCurrentUser().basic_user_info.account_id);
+  EXPECT_EQ(primary_id, user_view2->current_user().basic_user_info.account_id);
 
   // Child view of primary big user stays the same.
   ASSERT_FALSE(primary_big_view->public_account());
@@ -1607,8 +1595,8 @@
   // Case 4: Swap user_view0 (public account user) with with primary big user
   // (auth user).
   EXPECT_TRUE(is_public_account(user_view0));
-  primary_id = primary_big_view->GetCurrentUser()->basic_user_info->account_id;
-  list_user_id = user_view0->current_user()->basic_user_info->account_id;
+  primary_id = primary_big_view->GetCurrentUser().basic_user_info.account_id;
+  list_user_id = user_view0->current_user().basic_user_info.account_id;
   EXPECT_NE(primary_id, list_user_id);
 
   // Send event to swap users.
@@ -1616,9 +1604,8 @@
 
   // User info is swapped.
   EXPECT_EQ(list_user_id,
-            primary_big_view->GetCurrentUser()->basic_user_info->account_id);
-  EXPECT_EQ(primary_id,
-            user_view0->current_user()->basic_user_info->account_id);
+            primary_big_view->GetCurrentUser().basic_user_info.account_id);
+  EXPECT_EQ(primary_id, user_view0->current_user().basic_user_info.account_id);
 
   // Primary big user becomes public account user and its child view is rebuilt.
   ASSERT_TRUE(primary_big_view->public_account());
@@ -1774,7 +1761,7 @@
 
   LoginBigUserView* primary_big_view = lock_contents.primary_big_view();
   AccountId primary_id =
-      primary_big_view->GetCurrentUser()->basic_user_info->account_id;
+      primary_big_view->GetCurrentUser().basic_user_info.account_id;
 
   // Open the expanded public session view.
   ui::test::EventGenerator* generator = GetEventGenerator();
@@ -1782,7 +1769,7 @@
 
   EXPECT_FALSE(main_view->GetVisible());
   EXPECT_TRUE(expanded_view->GetVisible());
-  EXPECT_EQ(expanded_view->current_user()->basic_user_info->account_id,
+  EXPECT_EQ(expanded_view->current_user().basic_user_info.account_id,
             primary_id);
 
   // Expect LanuchPublicSession mojo call when the submit button is clicked.
@@ -1805,8 +1792,7 @@
   SetUserCount(1);
   SetWidget(CreateWidgetWithContent(contents));
 
-  const AccountId& kFirstUserAccountId =
-      users()[0]->basic_user_info->account_id;
+  const AccountId& kFirstUserAccountId = users()[0].basic_user_info.account_id;
   LockContentsView::TestApi contents_test_api(contents);
   LoginAuthUserView::TestApi auth_test_api(
       contents_test_api.primary_big_view()->auth_user());
@@ -1869,8 +1855,7 @@
   SetUserCount(1);
   SetWidget(CreateWidgetWithContent(contents));
 
-  const AccountId& kFirstUserAccountId =
-      users()[0]->basic_user_info->account_id;
+  const AccountId& kFirstUserAccountId = users()[0].basic_user_info.account_id;
   LockContentsView::TestApi contents_test_api(contents);
   views::View* note_action_button = contents_test_api.note_action();
 
@@ -1911,8 +1896,7 @@
   SetUserCount(1);
   SetWidget(CreateWidgetWithContent(contents));
 
-  const AccountId& kFirstUserAccountId =
-      users()[0]->basic_user_info->account_id;
+  const AccountId& kFirstUserAccountId = users()[0].basic_user_info.account_id;
   LockContentsView::TestApi contents_test_api(contents);
   LoginAuthUserView::TestApi auth_test_api(
       contents_test_api.primary_big_view()->auth_user());
@@ -2150,9 +2134,8 @@
 
   LockContentsView::TestApi test_api(contents);
 
-  AccountId primary_user = test_api.primary_big_view()
-                               ->GetCurrentUser()
-                               ->basic_user_info->account_id;
+  AccountId primary_user =
+      test_api.primary_big_view()->GetCurrentUser().basic_user_info.account_id;
   DataDispatcher()->SetPinEnabledForUser(primary_user, true);
 
   // This user should be identical to the user we enabled PIN for.
@@ -2173,7 +2156,7 @@
   SetUserCount(1);
   SetWidget(CreateWidgetWithContent(lock));
 
-  const AccountId& kUserAccountId = users()[0]->basic_user_info->account_id;
+  const AccountId& kUserAccountId = users()[0].basic_user_info.account_id;
 
   LockContentsView::TestApi test_api(lock);
   ui::test::EventGenerator* generator = GetEventGenerator();
@@ -2220,8 +2203,8 @@
       std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
   AddPublicAccountUsers(1);
   AddUsers(1);
-  users()[1]->can_remove = true;
-  DataDispatcher()->NotifyUsers(users());
+  users()[1].can_remove = true;
+  DataDispatcher()->SetUserList(users());
   SetWidget(CreateWidgetWithContent(lock));
 
   LockContentsView::TestApi test_api(lock);
@@ -2271,8 +2254,8 @@
 
   // Change fingerprint state; backlights remain forced off.
   DataDispatcher()->SetFingerprintState(
-      users()[0]->basic_user_info->account_id,
-      mojom::FingerprintState::DISABLED_FROM_ATTEMPTS);
+      users()[0].basic_user_info.account_id,
+      FingerprintState::DISABLED_FROM_ATTEMPTS);
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(
       Shell::Get()->backlights_forced_off_setter()->backlights_forced_off());
@@ -2303,7 +2286,7 @@
   // Validate a fingerprint authentication attempt resets backlights being
   // forced off.
   DataDispatcher()->NotifyFingerprintAuthResult(
-      users()[0]->basic_user_info->account_id, false /*successful*/);
+      users()[0].basic_user_info.account_id, false /*successful*/);
   base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(
       Shell::Get()->backlights_forced_off_setter()->backlights_forced_off());
@@ -2386,9 +2369,9 @@
   LoginBigUserView* auth_view = lock_contents.primary_big_view();
 
   AccountId auth_view_user =
-      auth_view->GetCurrentUser()->basic_user_info->account_id;
+      auth_view->GetCurrentUser().basic_user_info.account_id;
   AccountId list_user =
-      list_user_view->current_user()->basic_user_info->account_id;
+      list_user_view->current_user().basic_user_info.account_id;
 
   Shell::Get()->login_screen_controller()->NotifyOobeDialogState(
       mojom::OobeDialogState::GAIA_SIGNIN);
@@ -2400,9 +2383,9 @@
 
   // User info is not swapped.
   EXPECT_EQ(auth_view_user,
-            auth_view->GetCurrentUser()->basic_user_info->account_id);
+            auth_view->GetCurrentUser().basic_user_info.account_id);
   EXPECT_EQ(list_user,
-            list_user_view->current_user()->basic_user_info->account_id);
+            list_user_view->current_user().basic_user_info.account_id);
 
   // Hide OOBE dialog.
   Shell::Get()->login_screen_controller()->NotifyOobeDialogState(
@@ -2413,10 +2396,9 @@
   generator->ClickLeftButton();
 
   // User info should be now swapped.
-  EXPECT_EQ(list_user,
-            auth_view->GetCurrentUser()->basic_user_info->account_id);
+  EXPECT_EQ(list_user, auth_view->GetCurrentUser().basic_user_info.account_id);
   EXPECT_EQ(auth_view_user,
-            list_user_view->current_user()->basic_user_info->account_id);
+            list_user_view->current_user().basic_user_info.account_id);
 }
 
 }  // namespace ash
diff --git a/ash/login/ui/lock_debug_view.cc b/ash/login/ui/lock_debug_view.cc
index 6d56df3..f5831f07 100644
--- a/ash/login/ui/lock_debug_view.cc
+++ b/ash/login/ui/lock_debug_view.cc
@@ -19,6 +19,7 @@
 #include "ash/login/ui/non_accessible_view.h"
 #include "ash/login/ui/views_utils.h"
 #include "ash/public/cpp/kiosk_app_menu.h"
+#include "ash/public/cpp/login_types.h"
 #include "ash/shelf/login_shelf_view.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_widget.h"
@@ -28,6 +29,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/optional.h"
 #include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/base/ime/chromeos/ime_keyboard.h"
 #include "ui/views/controls/button/md_text_button.h"
@@ -98,10 +100,10 @@
 
 // Additional state for a user that the debug UI needs to reference.
 struct UserMetadata {
-  explicit UserMetadata(const mojom::UserInfoPtr& user_info)
-      : account_id(user_info->account_id),
-        display_name(user_info->display_name),
-        type(user_info->type) {}
+  explicit UserMetadata(const UserInfo& user_info)
+      : account_id(user_info.account_id),
+        display_name(user_info.display_name),
+        type(user_info.type) {}
 
   AccountId account_id;
   std::string display_name;
@@ -109,9 +111,8 @@
   bool enable_tap_to_unlock = false;
   bool enable_auth = true;
   user_manager::UserType type = user_manager::USER_TYPE_REGULAR;
-  mojom::EasyUnlockIconId easy_unlock_id = mojom::EasyUnlockIconId::NONE;
-  mojom::FingerprintState fingerprint_state =
-      mojom::FingerprintState::UNAVAILABLE;
+  EasyUnlockIconId easy_unlock_id = EasyUnlockIconId::NONE;
+  FingerprintState fingerprint_state = FingerprintState::UNAVAILABLE;
 };
 
 std::string DetachableBasePairingStatusToString(
@@ -130,43 +131,43 @@
 }
 
 // Update the user data based on |type| and |user_index|.
-mojom::LoginUserInfoPtr PopulateUserData(const mojom::LoginUserInfoPtr& user,
-                                         user_manager::UserType type,
-                                         int user_index) {
-  mojom::LoginUserInfoPtr result = user->Clone();
-  result->basic_user_info->type = type;
+LoginUserInfo PopulateUserData(const LoginUserInfo& user,
+                               user_manager::UserType type,
+                               int user_index) {
+  LoginUserInfo result = user;
+  result.basic_user_info.type = type;
 
   bool is_public_account = type == user_manager::USER_TYPE_PUBLIC_ACCOUNT;
   // Set debug user names and email. Useful for the stub user, which does not
   // have a name  and email set.
-  result->basic_user_info->display_name =
+  result.basic_user_info.display_name =
       is_public_account
           ? kDebugPublicAccountNames[user_index %
                                      base::size(kDebugPublicAccountNames)]
           : kDebugUserNames[user_index % base::size(kDebugUserNames)];
-  result->basic_user_info->display_email =
-      result->basic_user_info->account_id.GetUserEmail();
+  result.basic_user_info.display_email =
+      result.basic_user_info.account_id.GetUserEmail();
 
   if (is_public_account) {
-    result->public_account_info = mojom::PublicAccountInfo::New();
-    result->public_account_info->enterprise_domain = kDebugEnterpriseDomain;
-    result->public_account_info->default_locale = kDebugDefaultLocaleCode;
-    result->public_account_info->show_expanded_view = true;
+    result.public_account_info.emplace();
+    result.public_account_info->enterprise_domain = kDebugEnterpriseDomain;
+    result.public_account_info->default_locale = kDebugDefaultLocaleCode;
+    result.public_account_info->show_expanded_view = true;
 
-    std::vector<mojom::LocaleItemPtr> locales;
-    mojom::LocaleItemPtr locale_item = mojom::LocaleItem::New();
-    locale_item->language_code = kDebugDefaultLocaleCode;
-    locale_item->title = kDebugDefaultLocaleTitle;
-    locales.push_back(std::move(locale_item));
-    result->public_account_info->available_locales = std::move(locales);
+    std::vector<LocaleItem> locales;
+    LocaleItem locale_item;
+    locale_item.language_code = kDebugDefaultLocaleCode;
+    locale_item.title = kDebugDefaultLocaleTitle;
+    result.public_account_info->available_locales.push_back(
+        std::move(locale_item));
 
     // Request keyboard layouts for the default locale.
     Shell::Get()
         ->login_screen_controller()
-        ->RequestPublicSessionKeyboardLayouts(
-            result->basic_user_info->account_id, kDebugDefaultLocaleCode);
+        ->RequestPublicSessionKeyboardLayouts(result.basic_user_info.account_id,
+                                              kDebugDefaultLocaleCode);
   } else {
-    result->public_account_info.reset();
+    result.public_account_info.reset();
   }
 
   return result;
@@ -174,9 +175,10 @@
 
 }  // namespace
 
-// Applies a series of user-defined transformations to a |LoginDataDispatcher|
-// instance; this is used for debugging and development. The debug overlay uses
-// this class to change what data is exposed to the UI.
+// Applies a series of user-defined transformations to a
+// |LoginDataDispatcher| instance; this is used for debugging and
+// development. The debug overlay uses this class to change what data is exposed
+// to the UI.
 class LockDebugView::DebugDataDispatcherTransformer
     : public LoginDataDispatcher::Observer {
  public:
@@ -199,7 +201,7 @@
   void SetUserCount(int count) { NotifyUsers(BuildUserList(count)); }
 
   // Create user list.
-  std::vector<mojom::LoginUserInfoPtr> BuildUserList(int count) {
+  std::vector<LoginUserInfo> BuildUserList(int count) {
     DCHECK(!root_users_.empty());
 
     count = std::max(count, 0);
@@ -209,36 +211,36 @@
       debug_users_.erase(debug_users_.begin() + count, debug_users_.end());
 
     // Build |users|, add any new users to |debug_users|.
-    std::vector<mojom::LoginUserInfoPtr> users;
+    std::vector<LoginUserInfo> users;
     for (size_t i = 0; i < size_t{count}; ++i) {
-      users.push_back(root_users_[i % root_users_.size()]->Clone());
+      users.push_back(root_users_[i % root_users_.size()]);
       if (i >= root_users_.size()) {
-        users[i]->basic_user_info->account_id = AccountId::FromUserEmailGaiaId(
-            users[i]->basic_user_info->account_id.GetUserEmail() +
-                std::to_string(i),
-            users[i]->basic_user_info->account_id.GetGaiaId() +
-                std::to_string(i));
+        users[i].basic_user_info.account_id = AccountId::FromUserEmailGaiaId(
+            users[i].basic_user_info.account_id.GetUserEmail() +
+                base::NumberToString(i),
+            users[i].basic_user_info.account_id.GetGaiaId() +
+                base::NumberToString(i));
       }
 
       // Setup user data based on the user type in debug_users_.
       user_manager::UserType type = (i < debug_users_.size())
                                         ? debug_users_[i].type
-                                        : users[i]->basic_user_info->type;
+                                        : users[i].basic_user_info.type;
       users[i] = PopulateUserData(users[i], type, i);
 
       if (i >= debug_users_.size())
-        debug_users_.push_back(UserMetadata(users[i]->basic_user_info));
+        debug_users_.push_back(UserMetadata(users[i].basic_user_info));
     }
 
     return users;
   }
 
-  void NotifyUsers(std::vector<mojom::LoginUserInfoPtr> users) {
+  void NotifyUsers(const std::vector<LoginUserInfo>& users) {
     // User notification resets PIN state.
     for (UserMetadata& user : debug_users_)
       user.enable_pin = false;
 
-    debug_dispatcher_.NotifyUsers(users);
+    debug_dispatcher_.SetUserList(users);
   }
 
   int GetUserCount() const { return debug_users_.size(); }
@@ -278,45 +280,45 @@
     UserMetadata* debug_user = &debug_users_[user_index];
 
     // EasyUnlockIconId state transition.
-    auto get_next_id = [](mojom::EasyUnlockIconId id) {
+    auto get_next_id = [](EasyUnlockIconId id) {
       switch (id) {
-        case mojom::EasyUnlockIconId::NONE:
-          return mojom::EasyUnlockIconId::SPINNER;
-        case mojom::EasyUnlockIconId::SPINNER:
-          return mojom::EasyUnlockIconId::LOCKED;
-        case mojom::EasyUnlockIconId::LOCKED:
-          return mojom::EasyUnlockIconId::LOCKED_TO_BE_ACTIVATED;
-        case mojom::EasyUnlockIconId::LOCKED_TO_BE_ACTIVATED:
-          return mojom::EasyUnlockIconId::LOCKED_WITH_PROXIMITY_HINT;
-        case mojom::EasyUnlockIconId::LOCKED_WITH_PROXIMITY_HINT:
-          return mojom::EasyUnlockIconId::HARDLOCKED;
-        case mojom::EasyUnlockIconId::HARDLOCKED:
-          return mojom::EasyUnlockIconId::UNLOCKED;
-        case mojom::EasyUnlockIconId::UNLOCKED:
-          return mojom::EasyUnlockIconId::NONE;
+        case EasyUnlockIconId::NONE:
+          return EasyUnlockIconId::SPINNER;
+        case EasyUnlockIconId::SPINNER:
+          return EasyUnlockIconId::LOCKED;
+        case EasyUnlockIconId::LOCKED:
+          return EasyUnlockIconId::LOCKED_TO_BE_ACTIVATED;
+        case EasyUnlockIconId::LOCKED_TO_BE_ACTIVATED:
+          return EasyUnlockIconId::LOCKED_WITH_PROXIMITY_HINT;
+        case EasyUnlockIconId::LOCKED_WITH_PROXIMITY_HINT:
+          return EasyUnlockIconId::HARDLOCKED;
+        case EasyUnlockIconId::HARDLOCKED:
+          return EasyUnlockIconId::UNLOCKED;
+        case EasyUnlockIconId::UNLOCKED:
+          return EasyUnlockIconId::NONE;
       }
-      return mojom::EasyUnlockIconId::NONE;
+      return EasyUnlockIconId::NONE;
     };
     debug_user->easy_unlock_id = get_next_id(debug_user->easy_unlock_id);
 
     // Enable/disable click to unlock.
     debug_user->enable_tap_to_unlock =
-        debug_user->easy_unlock_id == mojom::EasyUnlockIconId::UNLOCKED;
+        debug_user->easy_unlock_id == EasyUnlockIconId::UNLOCKED;
 
     // Prepare icon that we will show.
-    auto icon = mojom::EasyUnlockIconOptions::New();
-    icon->icon = debug_user->easy_unlock_id;
-    if (icon->icon == mojom::EasyUnlockIconId::SPINNER) {
-      icon->aria_label = base::ASCIIToUTF16("Icon is spinning");
-    } else if (icon->icon == mojom::EasyUnlockIconId::LOCKED ||
-               icon->icon == mojom::EasyUnlockIconId::LOCKED_TO_BE_ACTIVATED) {
-      icon->autoshow_tooltip = true;
-      icon->tooltip = base::ASCIIToUTF16(
+    EasyUnlockIconOptions icon;
+    icon.icon = debug_user->easy_unlock_id;
+    if (icon.icon == EasyUnlockIconId::SPINNER) {
+      icon.aria_label = base::ASCIIToUTF16("Icon is spinning");
+    } else if (icon.icon == EasyUnlockIconId::LOCKED ||
+               icon.icon == EasyUnlockIconId::LOCKED_TO_BE_ACTIVATED) {
+      icon.autoshow_tooltip = true;
+      icon.tooltip = base::ASCIIToUTF16(
           "This is a long message to trigger overflow. This should show up "
           "automatically. icon_id=" +
-          std::to_string(static_cast<int>(icon->icon)));
+          base::NumberToString(static_cast<int>(icon.icon)));
     } else {
-      icon->tooltip =
+      icon.tooltip =
           base::ASCIIToUTF16("This should not show up automatically.");
     }
 
@@ -331,9 +333,9 @@
     DCHECK(user_index >= 0 && user_index < debug_users_.size());
     UserMetadata* debug_user = &debug_users_[user_index];
 
-    debug_user->fingerprint_state = static_cast<mojom::FingerprintState>(
+    debug_user->fingerprint_state = static_cast<FingerprintState>(
         (static_cast<int>(debug_user->fingerprint_state) + 1) %
-        (static_cast<int>(mojom::FingerprintState::kMaxValue) + 1));
+        (static_cast<int>(FingerprintState::kMaxValue) + 1));
     debug_dispatcher_.SetFingerprintState(debug_user->account_id,
                                           debug_user->fingerprint_state);
   }
@@ -396,10 +398,9 @@
                     ? user_manager::USER_TYPE_PUBLIC_ACCOUNT
                     : user_manager::USER_TYPE_REGULAR;
 
-    std::vector<mojom::LoginUserInfoPtr> users =
-        BuildUserList(debug_users_.size());
+    std::vector<LoginUserInfo> users = BuildUserList(debug_users_.size());
     // Update display name and email in debug users.
-    debug_users_[user_index] = UserMetadata(users[user_index]->basic_user_info);
+    debug_users_[user_index] = UserMetadata(users[user_index].basic_user_info);
     NotifyUsers(std::move(users));
   }
 
@@ -442,12 +443,11 @@
   void HideWarningBanner() { debug_dispatcher_.HideWarningBanner(); }
 
   // LoginDataDispatcher::Observer:
-  void OnUsersChanged(
-      const std::vector<mojom::LoginUserInfoPtr>& users) override {
+  void OnUsersChanged(const std::vector<LoginUserInfo>& users) override {
     // Update root_users_ to new source data.
     root_users_.clear();
     for (auto& user : users)
-      root_users_.push_back(user->Clone());
+      root_users_.push_back(user);
 
     // Rebuild debug users using new source data.
     SetUserCount(root_users_.size());
@@ -480,9 +480,8 @@
     lock_screen_note_state_ = state;
     debug_dispatcher_.SetLockScreenNoteState(state);
   }
-  void OnShowEasyUnlockIcon(
-      const AccountId& user,
-      const mojom::EasyUnlockIconOptionsPtr& icon) override {
+  void OnShowEasyUnlockIcon(const AccountId& user,
+                            const EasyUnlockIconOptions& icon) override {
     debug_dispatcher_.ShowEasyUnlockIcon(user, icon);
   }
   void OnDetachableBasePairingStatusChanged(
@@ -493,7 +492,7 @@
   void OnPublicSessionKeyboardLayoutsChanged(
       const AccountId& account_id,
       const std::string& locale,
-      const std::vector<mojom::InputMethodItemPtr>& keyboard_layouts) override {
+      const std::vector<InputMethodItem>& keyboard_layouts) override {
     debug_dispatcher_.SetPublicSessionKeyboardLayouts(account_id, locale,
                                                       keyboard_layouts);
   }
@@ -512,7 +511,7 @@
   LoginDataDispatcher debug_dispatcher_;
 
   // Original set of users from |root_dispatcher_|.
-  std::vector<mojom::LoginUserInfoPtr> root_users_;
+  std::vector<LoginUserInfo> root_users_;
 
   // Metadata for users that the UI is displaying.
   std::vector<UserMetadata> debug_users_;
@@ -626,8 +625,7 @@
       return DetachableBasePairingStatus::kNone;
     return *pairing_status_;
   }
-  bool PairedBaseMatchesLastUsedByUser(
-      const mojom::UserInfo& user_info) override {
+  bool PairedBaseMatchesLastUsedByUser(const UserInfo& user_info) override {
     if (GetPairingStatus() != DetachableBasePairingStatus::kAuthenticated)
       return false;
 
@@ -635,8 +633,7 @@
       return true;
     return last_used_bases_[user_info.account_id] == base_id_;
   }
-  bool SetPairedBaseAsLastUsedByUser(
-      const mojom::UserInfo& user_info) override {
+  bool SetPairedBaseAsLastUsedByUser(const UserInfo& user_info) override {
     if (GetPairingStatus() != DetachableBasePairingStatus::kAuthenticated)
       return false;
 
diff --git a/ash/login/ui/lock_screen_sanity_unittest.cc b/ash/login/ui/lock_screen_sanity_unittest.cc
index 2b5a58a..b42b5ef 100644
--- a/ash/login/ui/lock_screen_sanity_unittest.cc
+++ b/ash/login/ui/lock_screen_sanity_unittest.cc
@@ -107,9 +107,8 @@
   // Password submit runs mojo.
   std::unique_ptr<MockLoginScreenClient> client = BindMockLoginScreenClient();
   client->set_authenticate_user_callback_result(false);
-  EXPECT_CALL(*client,
-              AuthenticateUserWithPasswordOrPin_(
-                  users()[0]->basic_user_info->account_id, _, false, _));
+  EXPECT_CALL(*client, AuthenticateUserWithPasswordOrPin_(
+                           users()[0].basic_user_info.account_id, _, false, _));
   ui::test::EventGenerator* generator = GetEventGenerator();
   generator->PressKey(ui::KeyboardCode::VKEY_A, 0);
   generator->PressKey(ui::KeyboardCode::VKEY_RETURN, 0);
@@ -362,9 +361,9 @@
 
   // Add two users, the first of which can be removed.
   users().push_back(CreateUser("test1@test"));
-  users()[0]->can_remove = true;
+  users()[0].can_remove = true;
   users().push_back(CreateUser("test2@test"));
-  DataDispatcher()->NotifyUsers(users());
+  DataDispatcher()->SetUserList(users());
 
   std::unique_ptr<views::Widget> widget = CreateWidgetWithContent(contents);
 
@@ -406,7 +405,7 @@
   EXPECT_TRUE(HasFocusInAnyChildView(primary().menu()));
   EXPECT_CALL(*client, OnRemoveUserWarningShown()).Times(1);
   submit();
-  EXPECT_CALL(*client, RemoveUser(users()[0]->basic_user_info->account_id))
+  EXPECT_CALL(*client, RemoveUser(users()[0].basic_user_info.account_id))
       .Times(1);
   submit();
 
@@ -415,8 +414,8 @@
   EXPECT_TRUE(MakeLockContentsViewTestApi(contents)
                   .primary_big_view()
                   ->GetCurrentUser()
-                  ->basic_user_info->account_id ==
-              users()[1]->basic_user_info->account_id);
+                  .basic_user_info.account_id ==
+              users()[1].basic_user_info.account_id);
 }
 
 TEST_F(LockScreenSanityTest, LockScreenKillsPreventsClipboardPaste) {
diff --git a/ash/login/ui/login_auth_user_view.cc b/ash/login/ui/login_auth_user_view.cc
index 9140616..c748fce 100644
--- a/ash/login/ui/login_auth_user_view.cc
+++ b/ash/login/ui/login_auth_user_view.cc
@@ -168,7 +168,7 @@
     SetAutoColorReadabilityEnabled(false);
     SetEnabledColor(login_constants::kAuthMethodsTextColor);
 
-    SetTextBasedOnState(mojom::FingerprintState::AVAILABLE);
+    SetTextBasedOnState(FingerprintState::AVAILABLE);
   }
 
   void SetTextBasedOnAuthAttempt(bool success) {
@@ -180,22 +180,22 @@
                 : IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_ACCESSIBLE_AUTH_FAILED));
   }
 
-  void SetTextBasedOnState(mojom::FingerprintState state) {
+  void SetTextBasedOnState(FingerprintState state) {
     auto get_displayed_id = [&]() {
       switch (state) {
-        case mojom::FingerprintState::UNAVAILABLE:
-        case mojom::FingerprintState::AVAILABLE:
+        case FingerprintState::UNAVAILABLE:
+        case FingerprintState::AVAILABLE:
           return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_AVAILABLE;
-        case mojom::FingerprintState::DISABLED_FROM_ATTEMPTS:
+        case FingerprintState::DISABLED_FROM_ATTEMPTS:
           return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_DISABLED_FROM_ATTEMPTS;
-        case mojom::FingerprintState::DISABLED_FROM_TIMEOUT:
+        case FingerprintState::DISABLED_FROM_TIMEOUT:
           return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_DISABLED_FROM_TIMEOUT;
       }
       NOTREACHED();
     };
 
     auto get_accessible_id = [&]() {
-      if (state == mojom::FingerprintState::DISABLED_FROM_ATTEMPTS)
+      if (state == FingerprintState::DISABLED_FROM_ATTEMPTS)
         return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_ACCESSIBLE_AUTH_DISABLED_FROM_ATTEMPTS;
       return get_displayed_id();
     };
@@ -348,7 +348,7 @@
 
   ~FingerprintView() override = default;
 
-  void SetState(mojom::FingerprintState state) {
+  void SetState(FingerprintState state) {
     if (state_ == state)
       return;
 
@@ -370,7 +370,7 @@
                                             kFingerprintIconSizeDp,
                                             gfx::kGoogleGreenDark500));
     } else {
-      SetIcon(mojom::FingerprintState::DISABLED_FROM_ATTEMPTS);
+      SetIcon(FingerprintState::DISABLED_FROM_ATTEMPTS);
       // base::Unretained is safe because reset_state_ is owned by |this|.
       reset_state_.Start(
           FROM_HERE,
@@ -391,8 +391,8 @@
 
  private:
   void DisplayCurrentState() {
-    SetVisible(state_ != mojom::FingerprintState::UNAVAILABLE &&
-               state_ != mojom::FingerprintState::DISABLED_FROM_TIMEOUT);
+    SetVisible(state_ != FingerprintState::UNAVAILABLE &&
+               state_ != FingerprintState::DISABLED_FROM_TIMEOUT);
     SetIcon(state_);
     label_->SetTextBasedOnState(state_);
   }
@@ -402,15 +402,15 @@
                                      true /*send_native_event*/);
   }
 
-  void SetIcon(mojom::FingerprintState state) {
+  void SetIcon(FingerprintState state) {
     switch (state) {
-      case mojom::FingerprintState::UNAVAILABLE:
-      case mojom::FingerprintState::AVAILABLE:
-      case mojom::FingerprintState::DISABLED_FROM_TIMEOUT:
+      case FingerprintState::UNAVAILABLE:
+      case FingerprintState::AVAILABLE:
+      case FingerprintState::DISABLED_FROM_TIMEOUT:
         icon_->SetImage(gfx::CreateVectorIcon(
             kLockScreenFingerprintIcon, kFingerprintIconSizeDp, SK_ColorWHITE));
         break;
-      case mojom::FingerprintState::DISABLED_FROM_ATTEMPTS:
+      case FingerprintState::DISABLED_FROM_ATTEMPTS:
         icon_->SetAnimationDecoder(
             std::make_unique<HorizontalImageSequenceAnimationDecoder>(
                 *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
@@ -423,15 +423,15 @@
     }
   }
 
-  bool ShouldFireChromeVoxAlert(mojom::FingerprintState state) {
-    return state == mojom::FingerprintState::DISABLED_FROM_ATTEMPTS ||
-           state == mojom::FingerprintState::DISABLED_FROM_TIMEOUT;
+  bool ShouldFireChromeVoxAlert(FingerprintState state) {
+    return state == FingerprintState::DISABLED_FROM_ATTEMPTS ||
+           state == FingerprintState::DISABLED_FROM_TIMEOUT;
   }
 
   FingerprintLabel* label_ = nullptr;
   AnimatedRoundedImageView* icon_ = nullptr;
   base::OneShotTimer reset_state_;
-  mojom::FingerprintState state_ = mojom::FingerprintState::AVAILABLE;
+  FingerprintState state_ = FingerprintState::AVAILABLE;
 
   DISALLOW_COPY_AND_ASSIGN(FingerprintView);
 };
@@ -582,7 +582,7 @@
 
 LoginAuthUserView::Callbacks::~Callbacks() = default;
 
-LoginAuthUserView::LoginAuthUserView(const mojom::LoginUserInfoPtr& user,
+LoginAuthUserView::LoginAuthUserView(const LoginUserInfo& user,
                                      const Callbacks& callbacks)
     : NonAccessibleView(kLoginAuthUserViewClassName),
       on_auth_(callbacks.on_auth),
@@ -593,8 +593,7 @@
   DCHECK(callbacks.on_remove);
   DCHECK(callbacks.on_easy_unlock_icon_hovered);
   DCHECK(callbacks.on_easy_unlock_icon_tapped);
-  DCHECK_NE(user->basic_user_info->type,
-            user_manager::USER_TYPE_PUBLIC_ACCOUNT);
+  DCHECK_NE(user.basic_user_info.type, user_manager::USER_TYPE_PUBLIC_ACCOUNT);
 
   // Build child views.
   user_view_ = new LoginUserView(
@@ -630,7 +629,7 @@
       callbacks.on_easy_unlock_icon_tapped);
 
   online_sign_in_message_ = new views::LabelButton(
-      this, base::UTF8ToUTF16(user->basic_user_info->display_name));
+      this, base::UTF8ToUTF16(user.basic_user_info.display_name));
   DecorateOnlineSignInMessage(online_sign_in_message_);
 
   disabled_auth_message_ = new DisabledAuthMessageView();
@@ -799,7 +798,7 @@
 }
 
 void LoginAuthUserView::SetEasyUnlockIcon(
-    mojom::EasyUnlockIconId id,
+    EasyUnlockIconId id,
     const base::string16& accessibility_label) {
   password_view_->SetEasyUnlockIcon(id, accessibility_label);
 }
@@ -935,15 +934,15 @@
   cached_animation_state_.reset();
 }
 
-void LoginAuthUserView::UpdateForUser(const mojom::LoginUserInfoPtr& user) {
+void LoginAuthUserView::UpdateForUser(const LoginUserInfo& user) {
   user_view_->UpdateForUser(user, true /*animate*/);
   password_view_->UpdateForUser(user);
   password_view_->Clear();
   online_sign_in_message_->SetText(
-      base::UTF8ToUTF16(user->basic_user_info->display_name));
+      base::UTF8ToUTF16(user.basic_user_info.display_name));
 }
 
-void LoginAuthUserView::SetFingerprintState(mojom::FingerprintState state) {
+void LoginAuthUserView::SetFingerprintState(FingerprintState state) {
   fingerprint_view_->SetState(state);
 }
 
@@ -957,7 +956,7 @@
   Layout();
 }
 
-const mojom::LoginUserInfoPtr& LoginAuthUserView::current_user() const {
+const LoginUserInfo& LoginAuthUserView::current_user() const {
   return user_view_->current_user();
 }
 
@@ -1003,13 +1002,13 @@
   // enabled should attempt unlock.
   if (HasAuthMethod(AUTH_TAP) && password.empty()) {
     Shell::Get()->login_screen_controller()->AuthenticateUserWithEasyUnlock(
-        current_user()->basic_user_info->account_id);
+        current_user().basic_user_info.account_id);
     return;
   }
 
   password_view_->SetReadOnly(true);
   Shell::Get()->login_screen_controller()->AuthenticateUserWithPasswordOrPin(
-      current_user()->basic_user_info->account_id, base::UTF16ToUTF8(password),
+      current_user().basic_user_info.account_id, base::UTF16ToUTF8(password),
       can_use_pin_,
       base::BindOnce(&LoginAuthUserView::OnAuthComplete,
                      weak_factory_.GetWeakPtr()));
@@ -1052,7 +1051,7 @@
 void LoginAuthUserView::OnUserViewTap() {
   if (HasAuthMethod(AUTH_TAP)) {
     Shell::Get()->login_screen_controller()->AuthenticateUserWithEasyUnlock(
-        current_user()->basic_user_info->account_id);
+        current_user().basic_user_info.account_id);
   } else if (HasAuthMethod(AUTH_ONLINE_SIGN_IN)) {
     // Tapping anywhere in the user view is the same with tapping the message.
     OnOnlineSignInMessageTap();
@@ -1063,7 +1062,7 @@
 
 void LoginAuthUserView::OnOnlineSignInMessageTap() {
   Shell::Get()->login_screen_controller()->ShowGaiaSignin(
-      true /*can_close*/, current_user()->basic_user_info->account_id);
+      true /*can_close*/, current_user().basic_user_info.account_id);
 }
 
 bool LoginAuthUserView::HasAuthMethod(AuthMethods auth_method) const {
@@ -1075,7 +1074,7 @@
   external_binary_auth_button_->SetEnabled(false);
   external_binary_enrollment_button_->SetEnabled(false);
   Shell::Get()->login_screen_controller()->AuthenticateUserWithExternalBinary(
-      current_user()->basic_user_info->account_id,
+      current_user().basic_user_info.account_id,
       base::BindOnce(&LoginAuthUserView::OnAuthComplete,
                      weak_factory_.GetWeakPtr()));
 }
diff --git a/ash/login/ui/login_auth_user_view.h b/ash/login/ui/login_auth_user_view.h
index 28cf0f1..f952c2d 100644
--- a/ash/login/ui/login_auth_user_view.h
+++ b/ash/login/ui/login_auth_user_view.h
@@ -12,8 +12,8 @@
 #include "ash/login/ui/login_password_view.h"
 #include "ash/login/ui/login_user_view.h"
 #include "ash/login/ui/non_accessible_view.h"
+#include "ash/public/cpp/session/user_info.h"
 #include "ash/public/interfaces/login_screen.mojom.h"
-#include "ash/public/interfaces/user_info.mojom.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "base/scoped_observer.h"
@@ -98,8 +98,7 @@
                                     // message to user.
   };
 
-  LoginAuthUserView(const mojom::LoginUserInfoPtr& user,
-                    const Callbacks& callbacks);
+  LoginAuthUserView(const LoginUserInfo& user, const Callbacks& callbacks);
   ~LoginAuthUserView() override;
 
   // Set the displayed set of auth methods. |auth_methods| contains or-ed
@@ -109,7 +108,7 @@
   AuthMethods auth_methods() const { return auth_methods_; }
 
   // Add an easy unlock icon.
-  void SetEasyUnlockIcon(mojom::EasyUnlockIconId id,
+  void SetEasyUnlockIcon(EasyUnlockIconId id,
                          const base::string16& accessibility_label);
 
   // Captures any metadata about the current view state that will be used for
@@ -120,10 +119,10 @@
   void ApplyAnimationPostLayout();
 
   // Update the displayed name, icon, etc to that of |user|.
-  void UpdateForUser(const mojom::LoginUserInfoPtr& user);
+  void UpdateForUser(const LoginUserInfo& user);
 
   // Update the current fingerprint state.
-  void SetFingerprintState(mojom::FingerprintState state);
+  void SetFingerprintState(FingerprintState state);
 
   // Called to show a fingerprint authentication attempt result.
   void NotifyFingerprintAuthResult(bool success);
@@ -133,7 +132,7 @@
   void SetAuthDisabledMessage(
       const ash::mojom::AuthDisabledDataPtr& auth_disabled_data);
 
-  const mojom::LoginUserInfoPtr& current_user() const;
+  const LoginUserInfo& current_user() const;
 
   LoginPasswordView* password_view() { return password_view_; }
   LoginUserView* user_view() { return user_view_; }
diff --git a/ash/login/ui/login_auth_user_view_unittest.cc b/ash/login/ui/login_auth_user_view_unittest.cc
index 51be2c5..28eef7f 100644
--- a/ash/login/ui/login_auth_user_view_unittest.cc
+++ b/ash/login/ui/login_auth_user_view_unittest.cc
@@ -60,7 +60,7 @@
     view_->SetAuthMethods(auth_methods, can_use_pin);
   }
 
-  mojom::LoginUserInfoPtr user_;
+  LoginUserInfo user_;
   views::View* container_ = nullptr;   // Owned by test widget view hierarchy.
   LoginAuthUserView* view_ = nullptr;  // Owned by test widget view hierarchy.
 
@@ -116,7 +116,7 @@
 
   EXPECT_CALL(*client,
               AuthenticateUserWithEasyUnlock(
-                  user_view->current_user()->basic_user_info->account_id));
+                  user_view->current_user().basic_user_info.account_id));
   SetAuthMethods(LoginAuthUserView::AUTH_PASSWORD |
                  LoginAuthUserView::AUTH_TAP);
   password_view->Clear();
@@ -146,7 +146,7 @@
               ShowGaiaSignin(
                   true /*can_close*/,
                   base::Optional<AccountId>(
-                      user_view->current_user()->basic_user_info->account_id)));
+                      user_view->current_user().basic_user_info.account_id)));
   const ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
                              ui::EventTimeForNow(), 0, 0);
   view_->ButtonPressed(online_sign_in_message, event);
@@ -202,7 +202,7 @@
   EXPECT_CALL(*client, AuthenticateUserWithExternalBinary_(
                            test_auth_user_view.user_view()
                                ->current_user()
-                               ->basic_user_info->account_id,
+                               .basic_user_info.account_id,
                            _));
   power_manager_client()->SetLidState(
       chromeos::PowerManagerClient::LidState::OPEN, base::TimeTicks::Now());
diff --git a/ash/login/ui/login_big_user_view.cc b/ash/login/ui/login_big_user_view.cc
index e2fd1ac..80afdbf2 100644
--- a/ash/login/ui/login_big_user_view.cc
+++ b/ash/login/ui/login_big_user_view.cc
@@ -14,12 +14,12 @@
 
 namespace {
 
-bool IsPublicAccountUser(const mojom::LoginUserInfoPtr& user) {
-  return user->basic_user_info->type == user_manager::USER_TYPE_PUBLIC_ACCOUNT;
+bool IsPublicAccountUser(const LoginUserInfo& user) {
+  return user.basic_user_info.type == user_manager::USER_TYPE_PUBLIC_ACCOUNT;
 }
 
-bool IsChildAccountUser(const mojom::LoginUserInfoPtr& user) {
-  return user->basic_user_info->type == user_manager::USER_TYPE_CHILD;
+bool IsChildAccountUser(const LoginUserInfo& user) {
+  return user.basic_user_info.type == user_manager::USER_TYPE_CHILD;
 }
 
 // Returns true if either a or b have a value, but not both.
@@ -30,7 +30,7 @@
 }  // namespace
 
 LoginBigUserView::LoginBigUserView(
-    const mojom::LoginUserInfoPtr& user,
+    const LoginUserInfo& user,
     const LoginAuthUserView::Callbacks& auth_user_callbacks,
     const LoginPublicAccountUserView::Callbacks& public_account_callbacks,
     const ParentAccessView::Callbacks& parent_access_callbacks)
@@ -52,14 +52,14 @@
 
 LoginBigUserView::~LoginBigUserView() = default;
 
-void LoginBigUserView::CreateChildView(const mojom::LoginUserInfoPtr& user) {
+void LoginBigUserView::CreateChildView(const LoginUserInfo& user) {
   if (IsPublicAccountUser(user))
     CreatePublicAccount(user);
   else
     CreateAuthUser(user);
 }
 
-void LoginBigUserView::UpdateForUser(const mojom::LoginUserInfoPtr& user) {
+void LoginBigUserView::UpdateForUser(const LoginUserInfo& user) {
   // Rebuild child view for the following swap case:
   // 1. Public Account -> Auth User
   // 2. Auth User      -> Public Account
@@ -89,7 +89,7 @@
 
   DCHECK(IsChildAccountUser(auth_user_->current_user()));
   parent_access_ = new ParentAccessView(
-      auth_user_->current_user()->basic_user_info->account_id,
+      auth_user_->current_user().basic_user_info.account_id,
       parent_access_callbacks_);
   RemoveChildView(auth_user_);
   AddChildView(parent_access_);
@@ -112,7 +112,7 @@
   RequestFocus();
 }
 
-const mojom::LoginUserInfoPtr& LoginBigUserView::GetCurrentUser() const {
+const LoginUserInfo& LoginBigUserView::GetCurrentUser() const {
   DCHECK(OnlyOneSet(public_account_, auth_user_));
   if (public_account_) {
     DCHECK(!parent_access_);
@@ -171,7 +171,7 @@
   }
 }
 
-void LoginBigUserView::CreateAuthUser(const mojom::LoginUserInfoPtr& user) {
+void LoginBigUserView::CreateAuthUser(const LoginUserInfo& user) {
   DCHECK(!IsPublicAccountUser(user));
   DCHECK(!auth_user_);
   DCHECK(!parent_access_);
@@ -182,8 +182,7 @@
   AddChildView(auth_user_);
 }
 
-void LoginBigUserView::CreatePublicAccount(
-    const mojom::LoginUserInfoPtr& user) {
+void LoginBigUserView::CreatePublicAccount(const LoginUserInfo& user) {
   DCHECK(IsPublicAccountUser(user));
   DCHECK(!public_account_);
 
diff --git a/ash/login/ui/login_big_user_view.h b/ash/login/ui/login_big_user_view.h
index daeaed1..4ac774f 100644
--- a/ash/login/ui/login_big_user_view.h
+++ b/ash/login/ui/login_big_user_view.h
@@ -11,7 +11,7 @@
 #include "ash/login/ui/login_user_view.h"
 #include "ash/login/ui/non_accessible_view.h"
 #include "ash/login/ui/parent_access_view.h"
-#include "ash/public/interfaces/user_info.mojom.h"
+#include "ash/public/cpp/session/user_info.h"
 #include "ash/wallpaper/wallpaper_controller_observer.h"
 
 namespace ash {
@@ -31,20 +31,20 @@
                                     public WallpaperControllerObserver {
  public:
   LoginBigUserView(
-      const mojom::LoginUserInfoPtr& user,
+      const LoginUserInfo& user,
       const LoginAuthUserView::Callbacks& auth_user_callbacks,
       const LoginPublicAccountUserView::Callbacks& public_account_callbacks,
       const ParentAccessView::Callbacks& parent_access_callbacks);
   ~LoginBigUserView() override;
 
   // Base on the user type, call CreateAuthUser or CreatePublicAccount.
-  void CreateChildView(const mojom::LoginUserInfoPtr& user);
+  void CreateChildView(const LoginUserInfo& user);
 
   // Update the displayed name, icon, etc to that of |user|.
   // It is safe to call it when ParentAccessView is shown.
   // LoginPublicAccountUserView, even if not visible, will be updated and the
   // result will be displayed after ParentAccessView is dismissed.
-  void UpdateForUser(const mojom::LoginUserInfoPtr& user);
+  void UpdateForUser(const LoginUserInfo& user);
 
   // Replaces LoginAuthUserView with ParentAccessView. Does not destroy
   // LoginAuthUserView. Should not be called for LoginBigUserView that contains
@@ -58,7 +58,7 @@
   void HideParentAccessView();
 
   // Safe to call in any state.
-  const mojom::LoginUserInfoPtr& GetCurrentUser() const;
+  const LoginUserInfo& GetCurrentUser() const;
 
   // Safe to call in any state.
   LoginUserView* GetUserView();
@@ -80,12 +80,12 @@
  private:
   // Create LoginAuthUserView and add it as child view.
   // |public_account_| will be deleted if exists to ensure the single child.
-  void CreateAuthUser(const mojom::LoginUserInfoPtr& user);
+  void CreateAuthUser(const LoginUserInfo& user);
 
   // Create LoginPublicAccountUserView and add it as child view.
   // |auth_user_| and |parent_acesss_| will be deleted if exists to ensure the
   // single child.
-  void CreatePublicAccount(const mojom::LoginUserInfoPtr& user);
+  void CreatePublicAccount(const LoginUserInfo& user);
 
   // Either |auth_user_| or |public_account_| must be null.
   LoginPublicAccountUserView* public_account_ = nullptr;
diff --git a/ash/login/ui/login_data_dispatcher.cc b/ash/login/ui/login_data_dispatcher.cc
index 375f0ab..63e5a35 100644
--- a/ash/login/ui/login_data_dispatcher.cc
+++ b/ash/login/ui/login_data_dispatcher.cc
@@ -9,7 +9,11 @@
 LoginDataDispatcher::Observer::~Observer() = default;
 
 void LoginDataDispatcher::Observer::OnUsersChanged(
-    const std::vector<mojom::LoginUserInfoPtr>& users) {}
+    const std::vector<LoginUserInfo>& users) {}
+
+void LoginDataDispatcher::Observer::OnUserAvatarChanged(
+    const AccountId& account_id,
+    const UserAvatar& avatar) {}
 
 void LoginDataDispatcher::Observer::OnPinEnabledForUserChanged(
     const AccountId& user,
@@ -17,7 +21,7 @@
 
 void LoginDataDispatcher::Observer::OnFingerprintStateChanged(
     const AccountId& account_id,
-    mojom::FingerprintState state) {}
+    FingerprintState state) {}
 
 void LoginDataDispatcher::Observer::OnFingerprintAuthResult(
     const AccountId& account_id,
@@ -42,7 +46,7 @@
 
 void LoginDataDispatcher::Observer::OnShowEasyUnlockIcon(
     const AccountId& user,
-    const mojom::EasyUnlockIconOptionsPtr& icon) {}
+    const EasyUnlockIconOptions& icon) {}
 
 void LoginDataDispatcher::Observer::OnShowWarningBanner(
     const base::string16& message) {}
@@ -61,14 +65,14 @@
 
 void LoginDataDispatcher::Observer::OnPublicSessionLocalesChanged(
     const AccountId& account_id,
-    const std::vector<mojom::LocaleItemPtr>& locales,
+    const std::vector<LocaleItem>& locales,
     const std::string& default_locale,
     bool show_advanced_view) {}
 
 void LoginDataDispatcher::Observer::OnPublicSessionKeyboardLayoutsChanged(
     const AccountId& account_id,
     const std::string& locale,
-    const std::vector<mojom::InputMethodItemPtr>& keyboard_layouts) {}
+    const std::vector<InputMethodItem>& keyboard_layouts) {}
 
 void LoginDataDispatcher::Observer::
     OnPublicSessionShowFullManagementDisclosureChanged(
@@ -91,8 +95,7 @@
   observers_.RemoveObserver(observer);
 }
 
-void LoginDataDispatcher::NotifyUsers(
-    const std::vector<mojom::LoginUserInfoPtr>& users) {
+void LoginDataDispatcher::SetUserList(const std::vector<LoginUserInfo>& users) {
   for (auto& observer : observers_)
     observer.OnUsersChanged(users);
 }
@@ -104,11 +107,17 @@
 }
 
 void LoginDataDispatcher::SetFingerprintState(const AccountId& account_id,
-                                              mojom::FingerprintState state) {
+                                              FingerprintState state) {
   for (auto& observer : observers_)
     observer.OnFingerprintStateChanged(account_id, state);
 }
 
+void LoginDataDispatcher::SetAvatarForUser(const AccountId& account_id,
+                                           const UserAvatar& avatar) {
+  for (auto& observer : observers_)
+    observer.OnUserAvatarChanged(account_id, avatar);
+}
+
 void LoginDataDispatcher::NotifyFingerprintAuthResult(
     const AccountId& account_id,
     bool successful) {
@@ -147,7 +156,7 @@
 
 void LoginDataDispatcher::ShowEasyUnlockIcon(
     const AccountId& user,
-    const mojom::EasyUnlockIconOptionsPtr& icon) {
+    const EasyUnlockIconOptions& icon) {
   for (auto& observer : observers_)
     observer.OnShowEasyUnlockIcon(user, icon);
 }
@@ -182,7 +191,7 @@
 
 void LoginDataDispatcher::SetPublicSessionLocales(
     const AccountId& account_id,
-    const std::vector<mojom::LocaleItemPtr>& locales,
+    const std::vector<LocaleItem>& locales,
     const std::string& default_locale,
     bool show_advanced_view) {
   for (auto& observer : observers_) {
@@ -194,7 +203,7 @@
 void LoginDataDispatcher::SetPublicSessionKeyboardLayouts(
     const AccountId& account_id,
     const std::string& locale,
-    const std::vector<mojom::InputMethodItemPtr>& keyboard_layouts) {
+    const std::vector<InputMethodItem>& keyboard_layouts) {
   for (auto& observer : observers_) {
     observer.OnPublicSessionKeyboardLayoutsChanged(account_id, locale,
                                                    keyboard_layouts);
diff --git a/ash/login/ui/login_data_dispatcher.h b/ash/login/ui/login_data_dispatcher.h
index 41dcdc4..f10fec01 100644
--- a/ash/login/ui/login_data_dispatcher.h
+++ b/ash/login/ui/login_data_dispatcher.h
@@ -11,8 +11,8 @@
 
 #include "ash/ash_export.h"
 #include "ash/detachable_base/detachable_base_pairing_status.h"
+#include "ash/public/cpp/login_screen_model.h"
 #include "ash/public/interfaces/login_screen.mojom.h"
-#include "ash/public/interfaces/login_user_info.mojom.h"
 #include "ash/public/interfaces/tray_action.mojom.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
@@ -23,16 +23,16 @@
 // register observers which are then invoked when data is posted to the data
 // dispatcher.
 //
-// This provides access to data notification events only. LoginDataDispatcher is
-// not responsible for owning data (the login embedder should own the data).
-// This type provides a clean interface between the actual view/UI implemenation
-// and the embedder.
+// This provides access to data notification events only.
+// LoginDataDispatcher is not responsible for owning data (the login
+// embedder should own the data). This type provides a clean interface between
+// the actual view/UI implemenation and the embedder.
 //
 // There are various types which provide data to LoginDataDispatcher. For
 // example, the lock screen uses the session manager, whereas the login screen
 // uses the user manager. The debug overlay proxies the original data dispatcher
 // so it can provide fake state from an arbitrary source.
-class ASH_EXPORT LoginDataDispatcher {
+class ASH_EXPORT LoginDataDispatcher : public LoginScreenModel {
  public:
   // Types interested in login state should derive from |Observer| and register
   // themselves on the |LoginDataDispatcher| instance passed to the view
@@ -42,8 +42,11 @@
     virtual ~Observer();
 
     // Called when the displayed set of users has changed.
-    virtual void OnUsersChanged(
-        const std::vector<mojom::LoginUserInfoPtr>& users);
+    virtual void OnUsersChanged(const std::vector<LoginUserInfo>& users);
+
+    // Called when |avatar| for |account_id| has changed.
+    virtual void OnUserAvatarChanged(const AccountId& account_id,
+                                     const UserAvatar& avatar);
 
     // Called when pin should be enabled or disabled for |user|. By default, pin
     // should be disabled.
@@ -52,7 +55,7 @@
 
     // Called when fingerprint unlock state changes for user with |account_id|.
     virtual void OnFingerprintStateChanged(const AccountId& account_id,
-                                           mojom::FingerprintState state);
+                                           FingerprintState state);
 
     // Called after a fingerprint authentication attempt.
     virtual void OnFingerprintAuthResult(const AccountId& account_id,
@@ -80,9 +83,8 @@
     virtual void OnLockScreenNoteStateChanged(mojom::TrayActionState state);
 
     // Called when an easy unlock icon should be displayed.
-    virtual void OnShowEasyUnlockIcon(
-        const AccountId& user,
-        const mojom::EasyUnlockIconOptionsPtr& icon);
+    virtual void OnShowEasyUnlockIcon(const AccountId& user,
+                                      const EasyUnlockIconOptions& icon);
 
     // Called when a warning banner message should be displayed.
     virtual void OnShowWarningBanner(const base::string16& message);
@@ -106,7 +108,7 @@
     // |account_id|.
     virtual void OnPublicSessionLocalesChanged(
         const AccountId& account_id,
-        const std::vector<mojom::LocaleItemPtr>& locales,
+        const std::vector<LocaleItem>& locales,
         const std::string& default_locale,
         bool show_advanced_view);
 
@@ -115,7 +117,7 @@
     virtual void OnPublicSessionKeyboardLayoutsChanged(
         const AccountId& account_id,
         const std::string& locale,
-        const std::vector<mojom::InputMethodItemPtr>& keyboard_layouts);
+        const std::vector<InputMethodItem>& keyboard_layouts);
 
     // Called when conditions for showing full management disclosure message
     // are changed.
@@ -132,15 +134,23 @@
   };
 
   LoginDataDispatcher();
-  ~LoginDataDispatcher();
+  ~LoginDataDispatcher() override;
 
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
-  void NotifyUsers(const std::vector<mojom::LoginUserInfoPtr>& users);
+  // LoginScreenModel:
+  // TODO(estade): for now, LoginScreenModel overrides are mixed with
+  // non-virtual methods. More of the non-virtual methods will become a part of
+  // the LoginScreenModel interface, so ordering is being preserved. When
+  // LoginScreenModel is complete, separate out the methods that aren't
+  // overrides.
+  void SetUserList(const std::vector<LoginUserInfo>& users) override;
   void SetPinEnabledForUser(const AccountId& user, bool enabled);
   void SetFingerprintState(const AccountId& account_id,
-                           mojom::FingerprintState state);
+                           FingerprintState state) override;
+  void SetAvatarForUser(const AccountId& account_id,
+                        const UserAvatar& avatar) override;
   void NotifyFingerprintAuthResult(const AccountId& account_id,
                                    bool successful);
   void EnableAuthForUser(const AccountId& account_id);
@@ -150,7 +160,7 @@
   void SetForceOnlineSignInForUser(const AccountId& user);
   void SetLockScreenNoteState(mojom::TrayActionState state);
   void ShowEasyUnlockIcon(const AccountId& user,
-                          const mojom::EasyUnlockIconOptionsPtr& icon);
+                          const EasyUnlockIconOptions& icon) override;
   void ShowWarningBanner(const base::string16& message);
   void HideWarningBanner();
   void SetSystemInfo(bool show_if_hidden,
@@ -160,13 +170,13 @@
   void SetPublicSessionDisplayName(const AccountId& account_id,
                                    const std::string& display_name);
   void SetPublicSessionLocales(const AccountId& account_id,
-                               const std::vector<mojom::LocaleItemPtr>& locales,
+                               const std::vector<LocaleItem>& locales,
                                const std::string& default_locale,
-                               bool show_advanced_view);
+                               bool show_advanced_view) override;
   void SetPublicSessionKeyboardLayouts(
       const AccountId& account_id,
       const std::string& locale,
-      const std::vector<mojom::InputMethodItemPtr>& keyboard_layouts);
+      const std::vector<InputMethodItem>& keyboard_layouts) override;
   void SetPublicSessionShowFullManagementDisclosure(
       bool show_full_management_disclosure);
   void SetDetachableBasePairingStatus(
diff --git a/ash/login/ui/login_detachable_base_model.cc b/ash/login/ui/login_detachable_base_model.cc
index 935b70c9..95bd89b 100644
--- a/ash/login/ui/login_detachable_base_model.cc
+++ b/ash/login/ui/login_detachable_base_model.cc
@@ -8,7 +8,7 @@
 #include "ash/detachable_base/detachable_base_observer.h"
 #include "ash/detachable_base/detachable_base_pairing_status.h"
 #include "ash/login/ui/login_data_dispatcher.h"
-#include "ash/public/interfaces/user_info.mojom.h"
+#include "ash/public/cpp/session/user_info.h"
 #include "base/macros.h"
 #include "base/scoped_observer.h"
 
@@ -32,12 +32,10 @@
   DetachableBasePairingStatus GetPairingStatus() override {
     return detachable_base_handler_->GetPairingStatus();
   }
-  bool PairedBaseMatchesLastUsedByUser(
-      const mojom::UserInfo& user_info) override {
+  bool PairedBaseMatchesLastUsedByUser(const UserInfo& user_info) override {
     return detachable_base_handler_->PairedBaseMatchesLastUsedByUser(user_info);
   }
-  bool SetPairedBaseAsLastUsedByUser(
-      const mojom::UserInfo& user_info) override {
+  bool SetPairedBaseAsLastUsedByUser(const UserInfo& user_info) override {
     return detachable_base_handler_->SetPairedBaseAsLastUsedByUser(user_info);
   }
 
diff --git a/ash/login/ui/login_detachable_base_model.h b/ash/login/ui/login_detachable_base_model.h
index e21b0807..fcfbac6a 100644
--- a/ash/login/ui/login_detachable_base_model.h
+++ b/ash/login/ui/login_detachable_base_model.h
@@ -14,10 +14,7 @@
 class DetachableBaseHandler;
 enum class DetachableBasePairingStatus;
 class LoginDataDispatcher;
-
-namespace mojom {
-class UserInfo;
-}
+struct UserInfo;
 
 // Wrapper around ash::DetachableBaseHandler used by login UI. Exposed as an
 // interface to ease faking the detachable base state in login UI tests, and in
@@ -40,12 +37,10 @@
 
   // Checks if the currently paired base is different than the last base used by
   // the user.
-  virtual bool PairedBaseMatchesLastUsedByUser(
-      const mojom::UserInfo& user_info) = 0;
+  virtual bool PairedBaseMatchesLastUsedByUser(const UserInfo& user_info) = 0;
 
   // Sets the currently paired base as the last base used by the user.
-  virtual bool SetPairedBaseAsLastUsedByUser(
-      const mojom::UserInfo& user_info) = 0;
+  virtual bool SetPairedBaseAsLastUsedByUser(const UserInfo& user_info) = 0;
 };
 
 }  // namespace ash
diff --git a/ash/login/ui/login_expanded_public_account_view.cc b/ash/login/ui/login_expanded_public_account_view.cc
index e567e88..172d657 100644
--- a/ash/login/ui/login_expanded_public_account_view.cc
+++ b/ash/login/ui/login_expanded_public_account_view.cc
@@ -14,6 +14,7 @@
 #include "ash/login/ui/login_user_view.h"
 #include "ash/login/ui/public_account_warning_dialog.h"
 #include "ash/login/ui/views_utils.h"
+#include "ash/public/cpp/login_types.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -414,7 +415,7 @@
       Layout();
     } else if (sender == submit_button_) {
       Shell::Get()->login_screen_controller()->LaunchPublicSession(
-          current_user_->basic_user_info->account_id,
+          current_user_.basic_user_info.account_id,
           selected_language_item_.value, selected_keyboard_item_.value);
     } else if (sender == language_selection_) {
       DCHECK(language_menu_view_);
@@ -451,22 +452,22 @@
     on_learn_more_tapped_.Run();
   }
 
-  void UpdateForUser(const mojom::LoginUserInfoPtr& user) {
-    DCHECK_EQ(user->basic_user_info->type,
+  void UpdateForUser(const LoginUserInfo& user) {
+    DCHECK_EQ(user.basic_user_info.type,
               user_manager::USER_TYPE_PUBLIC_ACCOUNT);
-    current_user_ = user->Clone();
+    current_user_ = user;
     if (!language_changed_by_user_)
-      selected_language_item_.value = user->public_account_info->default_locale;
+      selected_language_item_.value = user.public_account_info->default_locale;
 
-    PopulateLanguageItems(user->public_account_info->available_locales);
-    PopulateKeyboardItems(user->public_account_info->keyboard_layouts);
+    PopulateLanguageItems(user.public_account_info->available_locales);
+    PopulateKeyboardItems(user.public_account_info->keyboard_layouts);
     language_selection_->SetText(
         base::UTF8ToUTF16(selected_language_item_.title));
     keyboard_selection_->SetText(
         base::UTF8ToUTF16(selected_keyboard_item_.title));
 
     if (!show_advanced_changed_by_user_)
-      show_advanced_view_ = user->public_account_info->show_advanced_view;
+      show_advanced_view_ = user.public_account_info->show_advanced_view;
 
     Layout();
   }
@@ -482,14 +483,14 @@
     language_changed_by_user_ = true;
     selected_language_item_ = item;
     language_selection_->SetText(base::UTF8ToUTF16(item.title));
-    current_user_->public_account_info->default_locale = item.value;
+    current_user_.public_account_info->default_locale = item.value;
 
     // User changed the preferred locale, request to get corresponding keyboard
     // layouts.
     Shell::Get()
         ->login_screen_controller()
         ->RequestPublicSessionKeyboardLayouts(
-            current_user_->basic_user_info->account_id, item.value);
+            current_user_.basic_user_info.account_id, item.value);
   }
 
   void OnKeyboardSelected(LoginMenuView::Item item) {
@@ -497,22 +498,22 @@
     keyboard_selection_->SetText(base::UTF8ToUTF16(item.title));
   }
 
-  void PopulateLanguageItems(const std::vector<mojom::LocaleItemPtr>& locales) {
+  void PopulateLanguageItems(const std::vector<LocaleItem>& locales) {
     language_items_.clear();
     for (const auto& locale : locales) {
       LoginMenuView::Item item;
-      if (locale->group_name) {
-        item.title = locale->group_name.value();
+      if (locale.group_name) {
+        item.title = locale.group_name.value();
         item.is_group = true;
       } else {
-        item.title = locale->title;
-        item.value = locale->language_code;
+        item.title = locale.title;
+        item.value = locale.language_code;
         item.is_group = false;
-        item.selected = selected_language_item_.value == locale->language_code;
+        item.selected = selected_language_item_.value == locale.language_code;
       }
       language_items_.push_back(item);
 
-      if (selected_language_item_.value == locale->language_code)
+      if (selected_language_item_.value == locale.language_code)
         selected_language_item_ = item;
     }
 
@@ -526,17 +527,17 @@
   }
 
   void PopulateKeyboardItems(
-      const std::vector<mojom::InputMethodItemPtr>& keyboard_layouts) {
+      const std::vector<InputMethodItem>& keyboard_layouts) {
     keyboard_items_.clear();
     for (const auto& keyboard : keyboard_layouts) {
       LoginMenuView::Item item;
-      item.title = keyboard->title;
-      item.value = keyboard->ime_id;
+      item.title = keyboard.title;
+      item.value = keyboard.ime_id;
       item.is_group = false;
-      item.selected = keyboard->selected;
+      item.selected = keyboard.selected;
       keyboard_items_.push_back(item);
 
-      if (keyboard->selected)
+      if (keyboard.selected)
         selected_keyboard_item_ = item;
     }
 
@@ -563,7 +564,7 @@
   friend class LoginExpandedPublicAccountView::TestApi;
 
   bool show_advanced_view_ = false;
-  mojom::LoginUserInfoPtr current_user_;
+  LoginUserInfo current_user_;
 
   views::View* labels_view_ = nullptr;
   SelectionButtonView* advanced_view_button_ = nullptr;
@@ -727,14 +728,12 @@
   Hide();
 }
 
-void LoginExpandedPublicAccountView::UpdateForUser(
-    const mojom::LoginUserInfoPtr& user) {
+void LoginExpandedPublicAccountView::UpdateForUser(const LoginUserInfo& user) {
   user_view_->UpdateForUser(user, false /*animate*/);
   right_pane_->UpdateForUser(user);
 }
 
-const mojom::LoginUserInfoPtr& LoginExpandedPublicAccountView::current_user()
-    const {
+const LoginUserInfo& LoginExpandedPublicAccountView::current_user() const {
   return user_view_->current_user();
 }
 
diff --git a/ash/login/ui/login_expanded_public_account_view.h b/ash/login/ui/login_expanded_public_account_view.h
index a044503..dfa6cb4 100644
--- a/ash/login/ui/login_expanded_public_account_view.h
+++ b/ash/login/ui/login_expanded_public_account_view.h
@@ -10,7 +10,6 @@
 #include "ash/ash_export.h"
 #include "ash/login/ui/login_menu_view.h"
 #include "ash/login/ui/non_accessible_view.h"
-#include "ash/public/interfaces/login_user_info.mojom.h"
 #include "ui/events/event_handler.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/styled_label.h"
@@ -22,6 +21,7 @@
 class LoginUserView;
 class RightPaneView;
 class PublicAccountWarningDialog;
+struct LoginUserInfo;
 
 // Implements an expanded view for the public account user to select language
 // and keyboard options.
@@ -56,8 +56,8 @@
   ~LoginExpandedPublicAccountView() override;
 
   void ProcessPressedEvent(const ui::LocatedEvent* event);
-  void UpdateForUser(const mojom::LoginUserInfoPtr& user);
-  const mojom::LoginUserInfoPtr& current_user() const;
+  void UpdateForUser(const LoginUserInfo& user);
+  const LoginUserInfo& current_user() const;
   void Hide();
   void ShowWarningDialog();
   void OnWarningDialogClosed();
diff --git a/ash/login/ui/login_expanded_public_account_view_unittest.cc b/ash/login/ui/login_expanded_public_account_view_unittest.cc
index 3311e5f..3eca940 100644
--- a/ash/login/ui/login_expanded_public_account_view_unittest.cc
+++ b/ash/login/ui/login_expanded_public_account_view_unittest.cc
@@ -3,12 +3,14 @@
 // found in the LICENSE file.
 
 #include "ash/login/ui/login_expanded_public_account_view.h"
+
 #include "ash/login/mock_login_screen_client.h"
 #include "ash/login/ui/arrow_button_view.h"
 #include "ash/login/ui/login_test_base.h"
 #include "ash/login/ui/login_test_utils.h"
 #include "ash/login/ui/public_account_warning_dialog.h"
 #include "ash/login/ui/views_utils.h"
+#include "ash/public/cpp/login_types.h"
 #include "base/bind_helpers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/events/test/event_generator.h"
@@ -64,37 +66,35 @@
 
   // Add two fake language items, the first item is selected by default.
   void SetupLanguageInfo() {
-    std::vector<ash::mojom::LocaleItemPtr> result;
-    ash::mojom::LocaleItemPtr locale_item1 = ash::mojom::LocaleItem::New();
-    locale_item1->language_code = kEnglishLanguageCode;
-    locale_item1->title = kEnglishLanguageName;
+    std::vector<LocaleItem> result;
+    LocaleItem locale_item1;
+    locale_item1.language_code = kEnglishLanguageCode;
+    locale_item1.title = kEnglishLanguageName;
 
-    ash::mojom::LocaleItemPtr locale_item2 = ash::mojom::LocaleItem::New();
-    locale_item2->language_code = kFrenchLanguageCode;
-    locale_item2->title = kFrenchLanguageName;
+    LocaleItem locale_item2;
+    locale_item2.language_code = kFrenchLanguageCode;
+    locale_item2.title = kFrenchLanguageName;
     result.push_back(std::move(locale_item1));
     result.push_back(std::move(locale_item2));
-    user_->public_account_info->available_locales = std::move(result);
-    user_->public_account_info->default_locale = kEnglishLanguageCode;
+    user_.public_account_info->available_locales = std::move(result);
+    user_.public_account_info->default_locale = kEnglishLanguageCode;
   }
 
   // Add two fake keyboard items, the second item is selected by default.
   void SetupKeyboardInfo() {
-    std::vector<ash::mojom::InputMethodItemPtr> result;
-    ash::mojom::InputMethodItemPtr keyboard_item1 =
-        ash::mojom::InputMethodItem::New();
-    keyboard_item1->ime_id = kKeyboardIdForItem1;
-    keyboard_item1->title = kKeyboardNameForItem1;
+    std::vector<InputMethodItem> result;
+    InputMethodItem keyboard_item1;
+    keyboard_item1.ime_id = kKeyboardIdForItem1;
+    keyboard_item1.title = kKeyboardNameForItem1;
 
-    ash::mojom::InputMethodItemPtr keyboard_item2 =
-        ash::mojom::InputMethodItem::New();
-    keyboard_item2->ime_id = kKeyboardIdForItem2;
-    keyboard_item2->title = kKeyboardNameForItem2;
-    keyboard_item2->selected = true;
+    InputMethodItem keyboard_item2;
+    keyboard_item2.ime_id = kKeyboardIdForItem2;
+    keyboard_item2.title = kKeyboardNameForItem2;
+    keyboard_item2.selected = true;
     result.push_back(std::move(keyboard_item1));
     result.push_back(std::move(keyboard_item2));
 
-    user_->public_account_info->keyboard_layouts = std::move(result);
+    user_.public_account_info->keyboard_layouts = std::move(result);
   }
 
   void TapOnView(views::View* tap_target) {
@@ -110,7 +110,7 @@
     }
   }
 
-  mojom::LoginUserInfoPtr user_;
+  LoginUserInfo user_;
 
   // Owned by test widget view hierarchy.
   views::View* container_ = nullptr;
@@ -130,11 +130,11 @@
   EXPECT_EQ(public_account_->height(), kBubbleTotalHeightDp);
 
   LoginExpandedPublicAccountView::TestApi test_api(public_account_);
-  EXPECT_FALSE(user_->public_account_info->show_advanced_view);
+  EXPECT_FALSE(user_.public_account_info->show_advanced_view);
   EXPECT_FALSE(test_api.advanced_view()->GetVisible());
 
   // Toggle show_advanced_view.
-  user_->public_account_info->show_advanced_view = true;
+  user_.public_account_info->show_advanced_view = true;
   public_account_->UpdateForUser(user_);
 
   // Advanced view is shown and the overall size does not change.
@@ -202,7 +202,7 @@
   // Expect LanuchPublicSession mojo call when the submit button is clicked.
   std::unique_ptr<MockLoginScreenClient> client = BindMockLoginScreenClient();
   EXPECT_CALL(*client,
-              LaunchPublicSession(user_->basic_user_info->account_id,
+              LaunchPublicSession(user_.basic_user_info.account_id,
                                   selected_language, selected_keyboard));
 
   // Click on the submit button.
@@ -213,11 +213,11 @@
 // Verifies both language and keyboard menus shows up correctly.
 TEST_P(LoginExpandedPublicAccountViewTest, ShowLanguageAndKeyboardMenu) {
   LoginExpandedPublicAccountView::TestApi test_api(public_account_);
-  EXPECT_FALSE(user_->public_account_info->show_advanced_view);
+  EXPECT_FALSE(user_.public_account_info->show_advanced_view);
   EXPECT_FALSE(test_api.advanced_view()->GetVisible());
 
   // Toggle show_advanced_view.
-  user_->public_account_info->show_advanced_view = true;
+  user_.public_account_info->show_advanced_view = true;
   public_account_->UpdateForUser(user_);
   EXPECT_TRUE(test_api.advanced_view()->GetVisible());
 
@@ -252,7 +252,7 @@
 
 TEST_P(LoginExpandedPublicAccountViewTest, ChangeMenuSelection) {
   LoginExpandedPublicAccountView::TestApi test_api(public_account_);
-  user_->public_account_info->show_advanced_view = true;
+  user_.public_account_info->show_advanced_view = true;
   public_account_->UpdateForUser(user_);
   EXPECT_TRUE(test_api.advanced_view()->GetVisible());
 
@@ -269,7 +269,7 @@
   std::unique_ptr<MockLoginScreenClient> client = BindMockLoginScreenClient();
   EXPECT_CALL(*client,
               RequestPublicSessionKeyboardLayouts(
-                  user_->basic_user_info->account_id, kFrenchLanguageCode));
+                  user_.basic_user_info.account_id, kFrenchLanguageCode));
 
   EXPECT_EQ(test_api.selected_language_item().value, kEnglishLanguageCode);
   LoginMenuView::TestApi language_test_api(test_api.language_menu_view());
diff --git a/ash/login/ui/login_keyboard_test_base.h b/ash/login/ui/login_keyboard_test_base.h
index 94374e6..3f3d1d0 100644
--- a/ash/login/ui/login_keyboard_test_base.h
+++ b/ash/login/ui/login_keyboard_test_base.h
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "ash/login/ui/login_test_base.h"
-#include "ash/public/interfaces/login_user_info.mojom.h"
 #include "ash/test/ash_test_base.h"
 
 namespace ash {
@@ -36,7 +35,7 @@
   void SetUp() override;
 
  private:
-  std::vector<mojom::LoginUserInfoPtr> users_;
+  std::vector<LoginUserInfo> users_;
 
   DISALLOW_COPY_AND_ASSIGN(LoginKeyboardTestBase);
 };
diff --git a/ash/login/ui/login_password_view.cc b/ash/login/ui/login_password_view.cc
index 251ccce..3833e32 100644
--- a/ash/login/ui/login_password_view.cc
+++ b/ash/login/ui/login_password_view.cc
@@ -10,6 +10,7 @@
 #include "ash/login/ui/login_button.h"
 #include "ash/login/ui/non_accessible_view.h"
 #include "ash/public/cpp/login_constants.h"
+#include "ash/public/cpp/login_types.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -124,31 +125,31 @@
   const int num_frames = 0;
 };
 
-// Construct an IconBundle instance for a given mojom::EasyUnlockIconId value.
-IconBundle GetEasyUnlockResources(mojom::EasyUnlockIconId id) {
+// Construct an IconBundle instance for a given EasyUnlockIconId value.
+IconBundle GetEasyUnlockResources(EasyUnlockIconId id) {
   switch (id) {
-    case mojom::EasyUnlockIconId::NONE:
+    case EasyUnlockIconId::NONE:
       break;
-    case mojom::EasyUnlockIconId::HARDLOCKED:
+    case EasyUnlockIconId::HARDLOCKED:
       return IconBundle(IDR_EASY_UNLOCK_HARDLOCKED,
                         IDR_EASY_UNLOCK_HARDLOCKED_HOVER,
                         IDR_EASY_UNLOCK_HARDLOCKED_PRESSED);
-    case mojom::EasyUnlockIconId::LOCKED:
+    case EasyUnlockIconId::LOCKED:
       return IconBundle(IDR_EASY_UNLOCK_LOCKED, IDR_EASY_UNLOCK_LOCKED_HOVER,
                         IDR_EASY_UNLOCK_LOCKED_PRESSED);
-    case mojom::EasyUnlockIconId::LOCKED_TO_BE_ACTIVATED:
+    case EasyUnlockIconId::LOCKED_TO_BE_ACTIVATED:
       return IconBundle(IDR_EASY_UNLOCK_LOCKED_TO_BE_ACTIVATED,
                         IDR_EASY_UNLOCK_LOCKED_TO_BE_ACTIVATED_HOVER,
                         IDR_EASY_UNLOCK_LOCKED_TO_BE_ACTIVATED_PRESSED);
-    case mojom::EasyUnlockIconId::LOCKED_WITH_PROXIMITY_HINT:
+    case EasyUnlockIconId::LOCKED_WITH_PROXIMITY_HINT:
       return IconBundle(IDR_EASY_UNLOCK_LOCKED_WITH_PROXIMITY_HINT,
                         IDR_EASY_UNLOCK_LOCKED_WITH_PROXIMITY_HINT_HOVER,
                         IDR_EASY_UNLOCK_LOCKED_WITH_PROXIMITY_HINT_PRESSED);
-    case mojom::EasyUnlockIconId::UNLOCKED:
+    case EasyUnlockIconId::UNLOCKED:
       return IconBundle(IDR_EASY_UNLOCK_UNLOCKED,
                         IDR_EASY_UNLOCK_UNLOCKED_HOVER,
                         IDR_EASY_UNLOCK_UNLOCKED_PRESSED);
-    case mojom::EasyUnlockIconId::SPINNER:
+    case EasyUnlockIconId::SPINNER:
       return IconBundle(IDR_EASY_UNLOCK_SPINNER,
                         base::TimeDelta::FromSeconds(2), 45 /*num_frames*/);
   }
@@ -186,7 +187,7 @@
                    base::Unretained(this)));
   }
 
-  void SetEasyUnlockIcon(mojom::EasyUnlockIconId icon_id,
+  void SetEasyUnlockIcon(EasyUnlockIconId icon_id,
                          const base::string16& accessibility_label) {
     bool changed_states = icon_id != icon_id_;
     icon_id_ = icon_id;
@@ -245,7 +246,7 @@
     if (!GetWidget() || !GetWidget()->GetRootView())
       return;
 
-    if (icon_id_ == mojom::EasyUnlockIconId::NONE)
+    if (icon_id_ == EasyUnlockIconId::NONE)
       return;
 
     IconBundle resources = GetEasyUnlockResources(icon_id_);
@@ -281,7 +282,7 @@
   }
 
   // Icon we are currently displaying.
-  mojom::EasyUnlockIconId icon_id_ = mojom::EasyUnlockIconId::NONE;
+  EasyUnlockIconId icon_id_ = EasyUnlockIconId::NONE;
 
   // View which renders the icon.
   AnimatedRoundedImageView* icon_;
@@ -445,22 +446,22 @@
 }
 
 void LoginPasswordView::SetEasyUnlockIcon(
-    mojom::EasyUnlockIconId id,
+    EasyUnlockIconId id,
     const base::string16& accessibility_label) {
   // Update icon.
   easy_unlock_icon_->SetEasyUnlockIcon(id, accessibility_label);
 
   // Update icon visiblity.
-  bool has_icon = id != mojom::EasyUnlockIconId::NONE;
+  bool has_icon = id != EasyUnlockIconId::NONE;
   easy_unlock_icon_->SetVisible(has_icon);
   easy_unlock_right_margin_->SetVisible(has_icon);
   password_row_->Layout();
 }
 
-void LoginPasswordView::UpdateForUser(const mojom::LoginUserInfoPtr& user) {
+void LoginPasswordView::UpdateForUser(const LoginUserInfo& user) {
   textfield_->SetAccessibleName(l10n_util::GetStringFUTF16(
       IDS_ASH_LOGIN_POD_PASSWORD_FIELD_ACCESSIBLE_NAME,
-      base::UTF8ToUTF16(user->basic_user_info->display_email)));
+      base::UTF8ToUTF16(user.basic_user_info.display_email)));
 }
 
 void LoginPasswordView::SetFocusEnabledForChildViews(bool enable) {
diff --git a/ash/login/ui/login_password_view.h b/ash/login/ui/login_password_view.h
index d68c5db..0e1d665 100644
--- a/ash/login/ui/login_password_view.h
+++ b/ash/login/ui/login_password_view.h
@@ -8,8 +8,7 @@
 #include "ash/ash_export.h"
 #include "ash/ime/ime_controller.h"
 #include "ash/login/ui/animated_rounded_image_view.h"
-#include "ash/public/interfaces/login_user_info.mojom.h"
-#include "ash/public/interfaces/user_info.mojom.h"
+#include "ash/public/cpp/session/user_info.h"
 #include "base/scoped_observer.h"
 #include "base/strings/string16.h"
 #include "ui/base/ime/chromeos/ime_keyboard.h"
@@ -27,6 +26,8 @@
 
 namespace ash {
 class LoginButton;
+enum class EasyUnlockIconId;
+struct LoginUserInfo;
 
 // Contains a textfield instance with a submit button. The user can type a
 // password into the textfield and hit enter to submit.
@@ -81,11 +82,11 @@
   void SetEnabledOnEmptyPassword(bool enabled);
 
   // Change the active icon for easy unlock.
-  void SetEasyUnlockIcon(mojom::EasyUnlockIconId id,
+  void SetEasyUnlockIcon(EasyUnlockIconId id,
                          const base::string16& accessibility_label);
 
   // Updates accessibility information for |user|.
-  void UpdateForUser(const mojom::LoginUserInfoPtr& user);
+  void UpdateForUser(const LoginUserInfo& user);
 
   // Enable or disable focus on the child elements (ie, password field and
   // submit button).
diff --git a/ash/login/ui/login_password_view_test.cc b/ash/login/ui/login_password_view_test.cc
index 9013928..4fefc4eb 100644
--- a/ash/login/ui/login_password_view_test.cc
+++ b/ash/login/ui/login_password_view_test.cc
@@ -5,6 +5,7 @@
 #include "ash/login/ui/login_password_view.h"
 
 #include "ash/login/ui/login_test_base.h"
+#include "ash/public/cpp/login_types.h"
 #include "ash/shell.h"
 #include "base/bind.h"
 #include "base/strings/utf_string_conversions.h"
@@ -161,7 +162,7 @@
   ui::test::EventGenerator* generator = GetEventGenerator();
 
   // Enable icon.
-  view_->SetEasyUnlockIcon(mojom::EasyUnlockIconId::SPINNER,
+  view_->SetEasyUnlockIcon(EasyUnlockIconId::SPINNER,
                            base::string16() /*accessibility_label*/);
   ASSERT_TRUE(test_api.easy_unlock_icon()->GetVisible());
 
@@ -190,7 +191,7 @@
   ui::test::EventGenerator* generator = GetEventGenerator();
 
   // Enable icon, enable immediate hovering.
-  view_->SetEasyUnlockIcon(mojom::EasyUnlockIconId::SPINNER,
+  view_->SetEasyUnlockIcon(EasyUnlockIconId::SPINNER,
                            base::string16() /*accessibility_label*/);
   test_api.set_immediately_hover_easy_unlock_icon();
   ASSERT_TRUE(test_api.easy_unlock_icon()->GetVisible());
diff --git a/ash/login/ui/login_public_account_user_view.cc b/ash/login/ui/login_public_account_user_view.cc
index 40d1077..5f0a826 100644
--- a/ash/login/ui/login_public_account_user_view.cc
+++ b/ash/login/ui/login_public_account_user_view.cc
@@ -59,13 +59,12 @@
 LoginPublicAccountUserView::Callbacks::~Callbacks() = default;
 
 LoginPublicAccountUserView::LoginPublicAccountUserView(
-    const mojom::LoginUserInfoPtr& user,
+    const LoginUserInfo& user,
     const Callbacks& callbacks)
     : NonAccessibleView(kLoginPublicAccountUserViewClassName),
       on_tap_(callbacks.on_tap),
       on_public_account_tap_(callbacks.on_public_account_tapped) {
-  DCHECK_EQ(user->basic_user_info->type,
-            user_manager::USER_TYPE_PUBLIC_ACCOUNT);
+  DCHECK_EQ(user.basic_user_info.type, user_manager::USER_TYPE_PUBLIC_ACCOUNT);
   DCHECK(callbacks.on_tap);
   DCHECK(callbacks.on_public_account_tapped);
 
@@ -127,13 +126,11 @@
   PreferredSizeChanged();
 }
 
-void LoginPublicAccountUserView::UpdateForUser(
-    const mojom::LoginUserInfoPtr& user) {
+void LoginPublicAccountUserView::UpdateForUser(const LoginUserInfo& user) {
   user_view_->UpdateForUser(user, true /*animate*/);
 }
 
-const mojom::LoginUserInfoPtr& LoginPublicAccountUserView::current_user()
-    const {
+const LoginUserInfo& LoginPublicAccountUserView::current_user() const {
   return user_view_->current_user();
 }
 
diff --git a/ash/login/ui/login_public_account_user_view.h b/ash/login/ui/login_public_account_user_view.h
index 5bd46a4..eac237e 100644
--- a/ash/login/ui/login_public_account_user_view.h
+++ b/ash/login/ui/login_public_account_user_view.h
@@ -8,7 +8,6 @@
 #include "ash/ash_export.h"
 #include "ash/login/ui/login_user_view.h"
 #include "ash/login/ui/non_accessible_view.h"
-#include "ash/public/interfaces/login_user_info.mojom.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/view.h"
 
@@ -47,13 +46,13 @@
     OnPublicAccountTapped on_public_account_tapped;
   };
 
-  LoginPublicAccountUserView(const mojom::LoginUserInfoPtr& user,
+  LoginPublicAccountUserView(const LoginUserInfo& user,
                              const Callbacks& callbacks);
   ~LoginPublicAccountUserView() override;
 
   void SetAuthEnabled(bool enabled, bool animate);
-  void UpdateForUser(const mojom::LoginUserInfoPtr& user);
-  const mojom::LoginUserInfoPtr& current_user() const;
+  void UpdateForUser(const LoginUserInfo& user);
+  const LoginUserInfo& current_user() const;
 
   // views::View:
   gfx::Size CalculatePreferredSize() const override;
diff --git a/ash/login/ui/login_public_account_user_view_unittest.cc b/ash/login/ui/login_public_account_user_view_unittest.cc
index 569e3b28..c1bceac 100644
--- a/ash/login/ui/login_public_account_user_view_unittest.cc
+++ b/ash/login/ui/login_public_account_user_view_unittest.cc
@@ -53,7 +53,7 @@
     SetWidget(CreateWidgetWithContent(container));
   }
 
-  mojom::LoginUserInfoPtr user_;
+  LoginUserInfo user_;
 
   LoginPublicAccountUserView* public_account_view_ = nullptr;
   views::View* focusable_view_ = nullptr;
diff --git a/ash/login/ui/login_test_base.cc b/ash/login/ui/login_test_base.cc
index d0c6da6..9db6a2d 100644
--- a/ash/login/ui/login_test_base.cc
+++ b/ash/login/ui/login_test_base.cc
@@ -109,7 +109,7 @@
 
   users_.erase(users_.begin() + count, users_.end());
   // Notify any listeners that the user count has changed.
-  DataDispatcher()->NotifyUsers(users_);
+  DataDispatcher()->SetUserList(users_);
 }
 
 void LoginTestBase::AddUsers(size_t num_users) {
@@ -120,12 +120,12 @@
   }
 
   // Notify any listeners that the user count has changed.
-  DataDispatcher()->NotifyUsers(users_);
+  DataDispatcher()->SetUserList(users_);
 }
 
 void LoginTestBase::AddUserByEmail(const std::string& email) {
   users_.push_back(CreateUser(email));
-  DataDispatcher()->NotifyUsers(users_);
+  DataDispatcher()->SetUserList(users_);
 }
 
 void LoginTestBase::AddPublicAccountUsers(size_t num_public_accounts) {
@@ -136,7 +136,7 @@
   }
 
   // Notify any listeners that the user count has changed.
-  DataDispatcher()->NotifyUsers(users_);
+  DataDispatcher()->SetUserList(users_);
 }
 
 void LoginTestBase::AddChildUsers(size_t num_users) {
@@ -147,7 +147,7 @@
   }
 
   // Notify any listeners that the user count has changed.
-  DataDispatcher()->NotifyUsers(users_);
+  DataDispatcher()->SetUserList(users_);
 }
 
 LoginDataDispatcher* LoginTestBase::DataDispatcher() {
diff --git a/ash/login/ui/login_test_base.h b/ash/login/ui/login_test_base.h
index fb4a811..d634a2a 100644
--- a/ash/login/ui/login_test_base.h
+++ b/ash/login/ui/login_test_base.h
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "ash/login/ui/login_data_dispatcher.h"
-#include "ash/public/interfaces/login_user_info.mojom.h"
 #include "ash/test/ash_test_base.h"
 #include "base/macros.h"
 
@@ -64,9 +63,9 @@
   // Changes the active number of users. Fires an event on |DataDispatcher()|.
   void AddChildUsers(size_t num_users);
 
-  std::vector<mojom::LoginUserInfoPtr>& users() { return users_; }
+  std::vector<LoginUserInfo>& users() { return users_; }
 
-  const std::vector<mojom::LoginUserInfoPtr>& users() const { return users_; }
+  const std::vector<LoginUserInfo>& users() const { return users_; }
 
   // If the LockScreen is instantiated, returns its data dispatcher. Otherwise,
   // returns a standalone instance.
@@ -81,7 +80,7 @@
   // The widget created using |ShowWidgetWithContent|.
   std::unique_ptr<views::Widget> widget_;
 
-  std::vector<mojom::LoginUserInfoPtr> users_;
+  std::vector<LoginUserInfo> users_;
 
   LoginDataDispatcher data_dispatcher_;
 
diff --git a/ash/login/ui/login_test_utils.cc b/ash/login/ui/login_test_utils.cc
index b31892d..eff688d 100644
--- a/ash/login/ui/login_test_utils.cc
+++ b/ash/login/ui/login_test_utils.cc
@@ -14,16 +14,14 @@
 constexpr char kPrimaryName[] = "primary";
 constexpr char kSecondaryName[] = "secondary";
 
-mojom::LoginUserInfoPtr CreateUserWithType(const std::string& email,
-                                           user_manager::UserType user_type) {
-  auto user = mojom::LoginUserInfo::New();
-  user->basic_user_info = mojom::UserInfo::New();
-  user->basic_user_info->type = user_type;
-  user->basic_user_info->avatar = mojom::UserAvatar::New();
-  user->basic_user_info->account_id = AccountId::FromUserEmail(email);
-  user->basic_user_info->display_name = base::SplitString(
+LoginUserInfo CreateUserWithType(const std::string& email,
+                                 user_manager::UserType user_type) {
+  LoginUserInfo user;
+  user.basic_user_info.type = user_type;
+  user.basic_user_info.account_id = AccountId::FromUserEmail(email);
+  user.basic_user_info.display_name = base::SplitString(
       email, "@", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)[0];
-  user->basic_user_info->display_email = email;
+  user.basic_user_info.display_email = email;
   return user;
 }
 
@@ -65,27 +63,25 @@
       MakeLoginAuthTestApi(view, target).password_view());
 }
 
-mojom::LoginUserInfoPtr CreateUser(const std::string& email) {
+LoginUserInfo CreateUser(const std::string& email) {
   return CreateUserWithType(email, user_manager::UserType::USER_TYPE_REGULAR);
 }
 
-mojom::LoginUserInfoPtr CreateChildUser(const std::string& email) {
+LoginUserInfo CreateChildUser(const std::string& email) {
   return CreateUserWithType(email, user_manager::UserType::USER_TYPE_CHILD);
 }
 
-mojom::LoginUserInfoPtr CreatePublicAccountUser(const std::string& email) {
-  auto user = mojom::LoginUserInfo::New();
-  user->basic_user_info = mojom::UserInfo::New();
+LoginUserInfo CreatePublicAccountUser(const std::string& email) {
+  LoginUserInfo user;
   std::vector<std::string> email_parts = base::SplitString(
       email, "@", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-  user->basic_user_info->avatar = mojom::UserAvatar::New();
-  user->basic_user_info->account_id = AccountId::FromUserEmail(email);
-  user->basic_user_info->display_name = email_parts[0];
-  user->basic_user_info->display_email = email;
-  user->basic_user_info->type = user_manager::USER_TYPE_PUBLIC_ACCOUNT;
-  user->public_account_info = ash::mojom::PublicAccountInfo::New();
-  user->public_account_info->enterprise_domain = email_parts[1];
-  user->public_account_info->show_expanded_view = true;
+  user.basic_user_info.account_id = AccountId::FromUserEmail(email);
+  user.basic_user_info.display_name = email_parts[0];
+  user.basic_user_info.display_email = email;
+  user.basic_user_info.type = user_manager::USER_TYPE_PUBLIC_ACCOUNT;
+  user.public_account_info.emplace();
+  user.public_account_info->enterprise_domain = email_parts[1];
+  user.public_account_info->show_expanded_view = true;
   return user;
 }
 
diff --git a/ash/login/ui/login_test_utils.h b/ash/login/ui/login_test_utils.h
index f38f7262..1abbfe1 100644
--- a/ash/login/ui/login_test_utils.h
+++ b/ash/login/ui/login_test_utils.h
@@ -8,7 +8,6 @@
 #include "ash/login/ui/lock_contents_view.h"
 #include "ash/login/ui/login_auth_user_view.h"
 #include "ash/login/ui/login_password_view.h"
-#include "ash/public/interfaces/login_user_info.mojom.h"
 
 namespace ui {
 namespace test {
@@ -30,17 +29,17 @@
 LoginPasswordView::TestApi MakeLoginPasswordTestApi(LockContentsView* view,
                                                     AuthTarget auth);
 
-// Utility method to create a new |mojom::LoginUserInfoPtr| instance
+// Utility method to create a new |LoginUserInfo| instance
 // for regular user.
-mojom::LoginUserInfoPtr CreateUser(const std::string& email);
+LoginUserInfo CreateUser(const std::string& email);
 
-// Utility method to create a new |mojom::LoginUserInfoPtr| instance for child
+// Utility method to create a new |LoginUserInfo| instance for child
 // user.
-mojom::LoginUserInfoPtr CreateChildUser(const std::string& email);
+LoginUserInfo CreateChildUser(const std::string& email);
 
-// Utility method to create a new |mojom::LoginUserInfoPtr| instance for
+// Utility method to create a new |LoginUserInfo| instance for
 // public account user.
-mojom::LoginUserInfoPtr CreatePublicAccountUser(const std::string& email);
+LoginUserInfo CreatePublicAccountUser(const std::string& email);
 
 // Returns true if |view| or any child of it has focus.
 bool HasFocusInAnyChildView(const views::View* view);
diff --git a/ash/login/ui/login_user_view.cc b/ash/login/ui/login_user_view.cc
index d55bf8a..e789803 100644
--- a/ash/login/ui/login_user_view.cc
+++ b/ash/login/ui/login_user_view.cc
@@ -15,7 +15,7 @@
 #include "ash/login/ui/views_utils.h"
 #include "ash/public/cpp/ash_constants.h"
 #include "ash/public/cpp/login_constants.h"
-#include "ash/public/interfaces/user_info.mojom.h"
+#include "ash/public/cpp/session/user_info.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/user/rounded_image_view.h"
@@ -121,17 +121,17 @@
   }
   ~UserImage() override = default;
 
-  void UpdateForUser(const mojom::LoginUserInfoPtr& user) {
+  void UpdateForUser(const LoginUserInfo& user) {
     // Set the initial image from |avatar| since we already have it available.
     // Then, decode the bytes via blink's PNG decoder and play any animated
     // frames if they are available.
-    if (!user->basic_user_info->avatar->image.isNull())
-      image_->SetImage(user->basic_user_info->avatar->image);
+    if (!user.basic_user_info.avatar.image.isNull())
+      image_->SetImage(user.basic_user_info.avatar.image);
 
     // Decode the avatar using blink, as blink's PNG decoder supports APNG,
     // which is the format used for the animated avators.
-    if (!user->basic_user_info->avatar->bytes.empty()) {
-      DecodeAnimation(user->basic_user_info->avatar->bytes,
+    if (!user.basic_user_info.avatar.bytes.empty()) {
+      DecodeAnimation(user.basic_user_info.avatar.bytes,
                       base::Bind(&LoginUserView::UserImage::OnImageDecoded,
                                  weak_factory_.GetWeakPtr()));
     }
@@ -204,11 +204,11 @@
   }
   ~UserLabel() override = default;
 
-  void UpdateForUser(const mojom::LoginUserInfoPtr& user) {
-    std::string display_name = user->basic_user_info->display_name;
+  void UpdateForUser(const LoginUserInfo& user) {
+    std::string display_name = user.basic_user_info.display_name;
     // display_name can be empty in debug builds with stub users.
     if (display_name.empty())
-      display_name = user->basic_user_info->display_email;
+      display_name = user.basic_user_info.display_email;
 
     user_name_->SetText(gfx::ElideText(base::UTF8ToUTF16(display_name),
                                        user_name_->font_list(), label_width_,
@@ -424,9 +424,8 @@
 
 LoginUserView::~LoginUserView() = default;
 
-void LoginUserView::UpdateForUser(const mojom::LoginUserInfoPtr& user,
-                                  bool animate) {
-  current_user_ = user->Clone();
+void LoginUserView::UpdateForUser(const LoginUserInfo& user, bool animate) {
+  current_user_ = user;
 
   if (menu_ && menu_->parent()) {
     menu_->parent()->RemoveChildView(menu_);
@@ -434,11 +433,11 @@
   }
 
   menu_ = new LoginUserMenuView(
-      base::UTF8ToUTF16(current_user_->basic_user_info->display_name),
-      base::UTF8ToUTF16(current_user_->basic_user_info->display_email),
-      current_user_->basic_user_info->type, current_user_->is_device_owner,
+      base::UTF8ToUTF16(current_user_.basic_user_info.display_name),
+      base::UTF8ToUTF16(current_user_.basic_user_info.display_email),
+      current_user_.basic_user_info.type, current_user_.is_device_owner,
       dropdown_ /*anchor_view*/, dropdown_ /*bubble_opener*/,
-      current_user_->can_remove /*show_remove_user*/, on_remove_warning_shown_,
+      current_user_.can_remove /*show_remove_user*/, on_remove_warning_shown_,
       on_remove_);
   menu_->SetVisible(false);
 
@@ -574,7 +573,7 @@
 }
 
 void LoginUserView::UpdateCurrentUserState() {
-  auto email = base::UTF8ToUTF16(current_user_->basic_user_info->display_email);
+  auto email = base::UTF8ToUTF16(current_user_.basic_user_info.display_email);
   tap_button_->SetAccessibleName(email);
   if (dropdown_) {
     dropdown_->SetAccessibleName(l10n_util::GetStringFUTF16(
@@ -582,9 +581,9 @@
   }
 
   if (user_domain_) {
-    DCHECK(current_user_->public_account_info);
+    DCHECK(current_user_.public_account_info);
     const base::Optional<std::string>& enterprise_domain =
-        current_user_->public_account_info->enterprise_domain;
+        current_user_.public_account_info->enterprise_domain;
     if (enterprise_domain) {
       user_domain_->SetText(l10n_util::GetStringFUTF16(
           IDS_ASH_LOGIN_PUBLIC_ACCOUNT_INFO_FORMAT,
diff --git a/ash/login/ui/login_user_view.h b/ash/login/ui/login_user_view.h
index 34d04b4..9b90d7f 100644
--- a/ash/login/ui/login_user_view.h
+++ b/ash/login/ui/login_user_view.h
@@ -9,7 +9,7 @@
 #include "ash/login/ui/login_base_bubble_view.h"
 #include "ash/login/ui/login_display_style.h"
 #include "ash/login/ui/login_user_menu_view.h"
-#include "ash/public/interfaces/login_user_info.mojom.h"
+#include "ash/public/cpp/login_types.h"
 #include "base/macros.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/view.h"
@@ -64,7 +64,7 @@
   ~LoginUserView() override;
 
   // Update the user view to display the given user information.
-  void UpdateForUser(const mojom::LoginUserInfoPtr& user, bool animate);
+  void UpdateForUser(const LoginUserInfo& user, bool animate);
 
   // Set if the view must be opaque.
   void SetForceOpaque(bool force_opaque);
@@ -72,7 +72,7 @@
   // Enables or disables tapping the view.
   void SetTapEnabled(bool enabled);
 
-  const mojom::LoginUserInfoPtr& current_user() const { return current_user_; }
+  const LoginUserInfo& current_user() const { return current_user_; }
 
   // views::View:
   const char* GetClassName() const override;
@@ -109,7 +109,7 @@
 
   // The user that is currently being displayed (or will be displayed when an
   // animation completes).
-  mojom::LoginUserInfoPtr current_user_;
+  LoginUserInfo current_user_;
 
   // Used to dispatch opacity update events.
   std::unique_ptr<HoverNotifier> hover_notifier_;
diff --git a/ash/login/ui/login_user_view_unittest.cc b/ash/login/ui/login_user_view_unittest.cc
index c7effa8..424be3c 100644
--- a/ash/login/ui/login_user_view_unittest.cc
+++ b/ash/login/ui/login_user_view_unittest.cc
@@ -42,7 +42,7 @@
                           on_remove_warning_shown, on_remove);
 
     std::string email = "foo@foo.com";
-    mojom::LoginUserInfoPtr user =
+    LoginUserInfo user =
         public_account ? CreatePublicAccountUser(email) : CreateUser(email);
     view->UpdateForUser(user, false /*animate*/);
     container_->AddChildView(view);
@@ -101,7 +101,7 @@
   EXPECT_GT(extra_small_width, 0);
 
   for (int i = 0; i < 25; ++i) {
-    mojom::LoginUserInfoPtr user = CreateUser("user@domain.com");
+    LoginUserInfo user = CreateUser("user@domain.com");
     large->UpdateForUser(user, false /*animate*/);
     small->UpdateForUser(user, false /*animate*/);
     extra_small->UpdateForUser(user, false /*animate*/);
@@ -281,8 +281,7 @@
                   false /*public_account*/);
   LoginUserView::TestApi view_test(view);
 
-  mojom::LoginUserInfoPtr user =
-      CreateUser("verylongusernamethatfillsthebox@domain.com");
+  LoginUserInfo user = CreateUser("verylongusernamethatfillsthebox@domain.com");
   view->UpdateForUser(user, false /*animate*/);
   container_->Layout();
 
diff --git a/ash/login/ui/scrollable_users_list_view.cc b/ash/login/ui/scrollable_users_list_view.cc
index f6d284b19..76b2a02 100644
--- a/ash/login/ui/scrollable_users_list_view.cc
+++ b/ash/login/ui/scrollable_users_list_view.cc
@@ -12,7 +12,6 @@
 #include "ash/login/ui/non_accessible_view.h"
 #include "ash/login/ui/views_utils.h"
 #include "ash/public/cpp/login_constants.h"
-#include "ash/public/interfaces/login_user_info.mojom.h"
 #include "ash/shell.h"
 #include "ash/wallpaper/wallpaper_controller.h"
 #include "base/bind.h"
@@ -286,7 +285,7 @@
 }
 
 ScrollableUsersListView::ScrollableUsersListView(
-    const std::vector<mojom::LoginUserInfoPtr>& users,
+    const std::vector<LoginUserInfo>& users,
     const ActionWithUser& on_tap_user,
     LoginDisplayStyle display_style)
     : display_style_(display_style) {
@@ -346,7 +345,7 @@
 LoginUserView* ScrollableUsersListView::GetUserView(
     const AccountId& account_id) {
   for (auto* view : user_views_) {
-    if (view->current_user()->basic_user_info->account_id == account_id)
+    if (view->current_user().basic_user_info.account_id == account_id)
       return view;
   }
   return nullptr;
diff --git a/ash/login/ui/scrollable_users_list_view.h b/ash/login/ui/scrollable_users_list_view.h
index 68b59e1..5e0e9e0 100644
--- a/ash/login/ui/scrollable_users_list_view.h
+++ b/ash/login/ui/scrollable_users_list_view.h
@@ -10,7 +10,6 @@
 #include "ash/ash_export.h"
 #include "ash/login/ui/login_display_style.h"
 #include "ash/login/ui/login_user_view.h"
-#include "ash/public/interfaces/login_user_info.mojom.h"
 #include "ash/wallpaper/wallpaper_controller_observer.h"
 #include "base/scoped_observer.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -49,7 +48,7 @@
   // Initializes users list with rows for all |users|. The |display_style| is
   // used to determine layout and sizings. |on_user_view_tap| callback is
   // invoked whenever user row is tapped.
-  ScrollableUsersListView(const std::vector<mojom::LoginUserInfoPtr>& users,
+  ScrollableUsersListView(const std::vector<LoginUserInfo>& users,
                           const ActionWithUser& on_tap_user,
                           LoginDisplayStyle display_style);
   ~ScrollableUsersListView() override;
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index 00840b9..e12ddb5 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -89,6 +89,12 @@
     "lock_screen_widget_factory.cc",
     "lock_screen_widget_factory.h",
     "login_constants.h",
+    "login_screen.cc",
+    "login_screen.h",
+    "login_screen_model.cc",
+    "login_screen_model.h",
+    "login_types.cc",
+    "login_types.h",
     "network_icon_image_source.cc",
     "network_icon_image_source.h",
     "new_window_delegate.cc",
diff --git a/ash/public/cpp/app_list/app_list_metrics.cc b/ash/public/cpp/app_list/app_list_metrics.cc
index 3535cb5..2c62ded 100644
--- a/ash/public/cpp/app_list/app_list_metrics.cc
+++ b/ash/public/cpp/app_list/app_list_metrics.cc
@@ -19,6 +19,8 @@
     "Apps.AppListSuggestedChipOpenType.ClamshellMode";
 const char kAppListSuggestionChipOpenTypeHistogramInTablet[] =
     "Apps.AppListSuggestedChipOpenType.TabletMode";
+const char kAppListZeroStateSuggestionOpenTypeHistogram[] =
+    "Apps.AppListZeroStateSuggestionOpenType";
 
 }  // namespace
 
@@ -65,4 +67,9 @@
   }
 }
 
+void RecordZeroStateSuggestionOpenTypeHistogram(SearchResultType type) {
+  UMA_HISTOGRAM_ENUMERATION(kAppListZeroStateSuggestionOpenTypeHistogram, type,
+                            SEARCH_RESULT_TYPE_BOUNDARY);
+}
+
 }  // namespace app_list
diff --git a/ash/public/cpp/app_list/app_list_metrics.h b/ash/public/cpp/app_list/app_list_metrics.h
index d5d2f1b..686d9926 100644
--- a/ash/public/cpp/app_list/app_list_metrics.h
+++ b/ash/public/cpp/app_list/app_list_metrics.h
@@ -51,7 +51,8 @@
   // A result which is a web query.
   OMNIBOX_WEB_QUERY,
   // A result which was a web query that was previously searched.
-  OMNIBOX_HISTORY,
+  // This should be deprecated after M76.
+  OMNIBOX_HISTORY_DEPRECATED,
   // An app result which is an installed playstore app.
   PLAY_STORE_APP,
   // An app result which is an app that was installed on another device.
@@ -70,6 +71,13 @@
   APP_DATA_RESULT_NOTE_DOCUMENT,
   // An omnibox result which is opened via the assistant.
   ASSISTANT_OMNIBOX_RESULT,
+  // A result from omnibox for the query that was previously searched.
+  OMNIBOX_SEARCH_HISTORY,
+  // A result from omnibox for query suggestion.
+  OMNIBOX_SEARCH_SUGGEST,
+  // A result from omnibox for the personalized suggestion.
+  // Currently, it is used for the user's recent query.
+  OMNIBOX_SUGGEST_PERSONALIZED,
   // Boundary is always last.
   SEARCH_RESULT_TYPE_BOUNDARY
 };
@@ -79,6 +87,9 @@
     SearchResultType type,
     bool is_tablet_mode);
 
+ASH_PUBLIC_EXPORT void RecordZeroStateSuggestionOpenTypeHistogram(
+    SearchResultType type);
+
 }  // namespace app_list
 
 #endif  // ASH_PUBLIC_CPP_APP_LIST_APP_LIST_METRICS_H_
diff --git a/ash/public/cpp/login_screen.cc b/ash/public/cpp/login_screen.cc
new file mode 100644
index 0000000..9ff259c
--- /dev/null
+++ b/ash/public/cpp/login_screen.cc
@@ -0,0 +1,30 @@
+// Copyright 2019 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 "ash/public/cpp/login_screen.h"
+
+#include "base/logging.h"
+
+namespace ash {
+
+namespace {
+LoginScreen* g_instance = nullptr;
+}
+
+// static
+LoginScreen* LoginScreen::Get() {
+  return g_instance;
+}
+
+LoginScreen::LoginScreen() {
+  DCHECK_EQ(nullptr, g_instance);
+  g_instance = this;
+}
+
+LoginScreen::~LoginScreen() {
+  DCHECK_EQ(this, g_instance);
+  g_instance = nullptr;
+}
+
+}  // namespace ash
diff --git a/ash/public/cpp/login_screen.h b/ash/public/cpp/login_screen.h
new file mode 100644
index 0000000..660c869d
--- /dev/null
+++ b/ash/public/cpp/login_screen.h
@@ -0,0 +1,33 @@
+// Copyright 2019 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 ASH_PUBLIC_CPP_LOGIN_SCREEN_H_
+#define ASH_PUBLIC_CPP_LOGIN_SCREEN_H_
+
+#include <string>
+
+#include "ash/public/cpp/ash_public_export.h"
+
+namespace ash {
+
+class LoginScreenModel;
+
+// Allows clients (e.g. the browser process) to send messages to the ash
+// login/lock/user-add screens.
+// TODO(estade): move more of mojom::LoginScreen here.
+class ASH_PUBLIC_EXPORT LoginScreen {
+ public:
+  // Returns the singleton instance.
+  static LoginScreen* Get();
+
+  virtual LoginScreenModel* GetModel() = 0;
+
+ protected:
+  LoginScreen();
+  virtual ~LoginScreen();
+};
+
+}  // namespace ash
+
+#endif  // ASH_PUBLIC_CPP_LOGIN_SCREEN_H_
diff --git a/ash/public/cpp/login_screen_model.cc b/ash/public/cpp/login_screen_model.cc
new file mode 100644
index 0000000..c0074701
--- /dev/null
+++ b/ash/public/cpp/login_screen_model.cc
@@ -0,0 +1,11 @@
+// Copyright 2019 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 "ash/public/cpp/login_screen_model.h"
+
+namespace ash {
+
+LoginScreenModel::~LoginScreenModel() = default;
+
+}
diff --git a/ash/public/cpp/login_screen_model.h b/ash/public/cpp/login_screen_model.h
new file mode 100644
index 0000000..441c490
--- /dev/null
+++ b/ash/public/cpp/login_screen_model.h
@@ -0,0 +1,68 @@
+// Copyright 2019 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 ASH_PUBLIC_CPP_LOGIN_SCREEN_MODEL_H_
+#define ASH_PUBLIC_CPP_LOGIN_SCREEN_MODEL_H_
+
+#include <string>
+
+#include "ash/public/cpp/ash_public_export.h"
+
+class AccountId;
+
+namespace ash {
+
+enum class FingerprintState;
+struct EasyUnlockIconOptions;
+struct InputMethodItem;
+struct LocaleItem;
+struct LoginUserInfo;
+struct UserAvatar;
+
+// Provides Chrome access to Ash's login UI. See additional docs for
+// ash::LoginDataDispatcher.
+class ASH_PUBLIC_EXPORT LoginScreenModel {
+ public:
+  // Requests to show the custom icon in the user pod.
+  // |account_id|:  The account id of the user in the user pod.
+  // |icon|:        Information regarding the icon.
+  virtual void ShowEasyUnlockIcon(const AccountId& account_id,
+                                  const EasyUnlockIconOptions& icon) = 0;
+
+  // Set the users who are displayed on the login UI. |users| is filtered
+  // and does not correspond to every user on the device.
+  virtual void SetUserList(const std::vector<LoginUserInfo>& users) = 0;
+
+  // Update the status of fingerprint for |account_id|.
+  virtual void SetFingerprintState(const AccountId& account_id,
+                                   FingerprintState state) = 0;
+
+  // Called when |avatar| for |account_id| has changed.
+  virtual void SetAvatarForUser(const AccountId& account_id,
+                                const UserAvatar& avatar) = 0;
+
+  // Set the public session locales for user with |account_id|.
+  // |locales|:            Available locales for this user.
+  // |default_locale|:     Default locale for this user.
+  // |show_advanced_view|: True if we should show the advanced expanded user
+  //                       view for the public session.
+  virtual void SetPublicSessionLocales(const AccountId& account_id,
+                                       const std::vector<LocaleItem>& locales,
+                                       const std::string& default_locale,
+                                       bool show_advanced_view) = 0;
+
+  // Set the public session keyboard layouts for user with |account_id|.
+  // |locale|: The locale that |keyboard_layouts| can be used for.
+  virtual void SetPublicSessionKeyboardLayouts(
+      const AccountId& account_id,
+      const std::string& locale,
+      const std::vector<InputMethodItem>& keyboard_layouts) = 0;
+
+ protected:
+  virtual ~LoginScreenModel();
+};
+
+}  // namespace ash
+
+#endif  // ASH_PUBLIC_CPP_LOGIN_SCREEN_MODEL_H_
diff --git a/ash/public/cpp/login_types.cc b/ash/public/cpp/login_types.cc
new file mode 100644
index 0000000..47857fb
--- /dev/null
+++ b/ash/public/cpp/login_types.cc
@@ -0,0 +1,56 @@
+// Copyright 2019 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 "ash/public/cpp/login_types.h"
+
+namespace ash {
+
+EasyUnlockIconOptions::EasyUnlockIconOptions() = default;
+EasyUnlockIconOptions::EasyUnlockIconOptions(
+    const EasyUnlockIconOptions& other) = default;
+EasyUnlockIconOptions::EasyUnlockIconOptions(EasyUnlockIconOptions&& other) =
+    default;
+EasyUnlockIconOptions::~EasyUnlockIconOptions() = default;
+
+EasyUnlockIconOptions& EasyUnlockIconOptions::operator=(
+    const EasyUnlockIconOptions& other) = default;
+EasyUnlockIconOptions& EasyUnlockIconOptions::operator=(
+    EasyUnlockIconOptions&& other) = default;
+
+InputMethodItem::InputMethodItem() = default;
+InputMethodItem::InputMethodItem(const InputMethodItem& other) = default;
+InputMethodItem::InputMethodItem(InputMethodItem&& other) = default;
+InputMethodItem::~InputMethodItem() = default;
+
+InputMethodItem& InputMethodItem::operator=(const InputMethodItem& other) =
+    default;
+InputMethodItem& InputMethodItem::operator=(InputMethodItem&& other) = default;
+
+LocaleItem::LocaleItem() = default;
+LocaleItem::LocaleItem(const LocaleItem& other) = default;
+LocaleItem::LocaleItem(LocaleItem&& other) = default;
+LocaleItem::~LocaleItem() = default;
+
+LocaleItem& LocaleItem::operator=(const LocaleItem& other) = default;
+LocaleItem& LocaleItem::operator=(LocaleItem&& other) = default;
+
+PublicAccountInfo::PublicAccountInfo() = default;
+PublicAccountInfo::PublicAccountInfo(const PublicAccountInfo& other) = default;
+PublicAccountInfo::PublicAccountInfo(PublicAccountInfo&& other) = default;
+PublicAccountInfo::~PublicAccountInfo() = default;
+
+PublicAccountInfo& PublicAccountInfo::operator=(
+    const PublicAccountInfo& other) = default;
+PublicAccountInfo& PublicAccountInfo::operator=(PublicAccountInfo&& other) =
+    default;
+
+LoginUserInfo::LoginUserInfo() = default;
+LoginUserInfo::LoginUserInfo(const LoginUserInfo& other) = default;
+LoginUserInfo::LoginUserInfo(LoginUserInfo&& other) = default;
+LoginUserInfo::~LoginUserInfo() = default;
+
+LoginUserInfo& LoginUserInfo::operator=(const LoginUserInfo& other) = default;
+LoginUserInfo& LoginUserInfo::operator=(LoginUserInfo&& other) = default;
+
+}  // namespace ash
diff --git a/ash/public/cpp/login_types.h b/ash/public/cpp/login_types.h
new file mode 100644
index 0000000..ee2e5ee
--- /dev/null
+++ b/ash/public/cpp/login_types.h
@@ -0,0 +1,211 @@
+// Copyright 2019 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 ASH_PUBLIC_CPP_LOGIN_TYPES_H_
+#define ASH_PUBLIC_CPP_LOGIN_TYPES_H_
+
+#include "ash/public/cpp/ash_public_export.h"
+#include "ash/public/cpp/session/user_info.h"
+#include "base/token.h"
+#include "chromeos/components/proximity_auth/public/interfaces/auth_type.mojom.h"
+
+namespace ash {
+
+// Supported multi-profile user behavior values.
+// Keep in sync with the enum in chromeos_user_pod_row.js and user_pod_row.js
+// TODO(estade): change all the enums to use kCamelCase.
+enum class MultiProfileUserBehavior {
+  UNRESTRICTED = 0,
+  PRIMARY_ONLY = 1,
+  NOT_ALLOWED = 2,
+  OWNER_PRIMARY_ONLY = 3,
+};
+
+// Easy unlock icon choices.
+enum class EasyUnlockIconId {
+  // No icon shown.
+  NONE,
+  // The user has clicked the easy unlock icon and disabled easy unlock for this
+  // login/lock session.
+  HARDLOCKED,
+  // Phone could not be found.
+  LOCKED,
+  // Phone found, but it is not unlocked.
+  LOCKED_TO_BE_ACTIVATED,
+  // Phone found, but it is too far away.
+  LOCKED_WITH_PROXIMITY_HINT,
+  // Phone found and unlocked. The user can click to dismiss the login/lock
+  // screen.
+  UNLOCKED,
+  // Scanning for phone.
+  SPINNER,
+};
+
+// The status of fingerprint availability.
+enum class FingerprintState {
+  // The user cannot use fingerprint. This may be because:
+  //  - they are not the primary user
+  //  - they never registered fingerprint
+  //  - the device does not have a fingerprint sensor
+  UNAVAILABLE,
+  // Fingerprint can be used to unlock the device.
+  AVAILABLE,
+  // There have been too many attempts, so now fingerprint is disabled.
+  DISABLED_FROM_ATTEMPTS,
+  // It has been too long since the device was last used.
+  DISABLED_FROM_TIMEOUT,
+  kMaxValue = DISABLED_FROM_TIMEOUT,
+};
+
+// Information about the custom icon in the user pod.
+struct ASH_PUBLIC_EXPORT EasyUnlockIconOptions {
+  EasyUnlockIconOptions();
+  EasyUnlockIconOptions(const EasyUnlockIconOptions& other);
+  EasyUnlockIconOptions(EasyUnlockIconOptions&& other);
+  ~EasyUnlockIconOptions();
+
+  EasyUnlockIconOptions& operator=(const EasyUnlockIconOptions& other);
+  EasyUnlockIconOptions& operator=(EasyUnlockIconOptions&& other);
+
+  // Icon that should be displayed.
+  EasyUnlockIconId icon = EasyUnlockIconId::NONE;
+  // Tooltip that is associated with the icon. This is shown automatically if
+  // |autoshow_tooltip| is true. The user can always see the tooltip if they
+  // hover over the icon. The tooltip should be used for the accessibility label
+  // if it is present.
+  base::string16 tooltip;
+  // If true, the tooltip should be displayed (even if the user is not currently
+  // hovering over the icon, ie, this makes |tooltip| act like a little like a
+  // notification).
+  bool autoshow_tooltip = false;
+  // Accessibility label. Only used if |tooltip| is empty.
+  // TODO(jdufault): Always populate and use |aria_label|, even if |tooltip| is
+  // non-empty.
+  base::string16 aria_label;
+  // If true, clicking the easy unlock icon should fire a hardlock event which
+  // will disable easy unlock. The hardlock event will request a new icon
+  // display via a separate EasyUnlockIconsOption update. See
+  // login_screen.mojom::HardlockPod.
+  bool hardlock_on_click = false;
+};
+
+// Information of each input method. This is used to populate keyboard layouts
+// for public account user.
+struct ASH_PUBLIC_EXPORT InputMethodItem {
+  InputMethodItem();
+  InputMethodItem(const InputMethodItem& other);
+  InputMethodItem(InputMethodItem&& other);
+  ~InputMethodItem();
+
+  InputMethodItem& operator=(const InputMethodItem& other);
+  InputMethodItem& operator=(InputMethodItem&& other);
+
+  // An id that identifies an input method engine (e.g., "t:latn-post",
+  // "pinyin", "hangul").
+  std::string ime_id;
+
+  // Title of the input method.
+  std::string title;
+
+  // Whether this input method is been selected.
+  bool selected = false;
+};
+
+// Information of each available locale. This is used to populate language
+// locales for public account user.
+struct ASH_PUBLIC_EXPORT LocaleItem {
+  LocaleItem();
+  LocaleItem(const LocaleItem& other);
+  LocaleItem(LocaleItem&& other);
+  ~LocaleItem();
+
+  LocaleItem& operator=(const LocaleItem& other);
+  LocaleItem& operator=(LocaleItem&& other);
+
+  // Language code of the locale.
+  std::string language_code;
+
+  // Title of the locale.
+  std::string title;
+
+  // Group name of the locale.
+  base::Optional<std::string> group_name;
+};
+
+// Information about a public account user.
+struct ASH_PUBLIC_EXPORT PublicAccountInfo {
+  PublicAccountInfo();
+  PublicAccountInfo(const PublicAccountInfo& other);
+  PublicAccountInfo(PublicAccountInfo&& other);
+  ~PublicAccountInfo();
+
+  PublicAccountInfo& operator=(const PublicAccountInfo& other);
+  PublicAccountInfo& operator=(PublicAccountInfo&& other);
+
+  // The domain name displayed in the login screen UI.
+  base::Optional<std::string> enterprise_domain;
+
+  // A list of available user locales.
+  std::vector<LocaleItem> available_locales;
+
+  // Default locale for this user.
+  std::string default_locale;
+
+  // Show expanded user view that contains session information/warnings and
+  // locale selection.
+  bool show_expanded_view = false;
+
+  // Show the advanced expanded user view if there are at least two recommended
+  // locales. This will be the case in multilingual environments where users
+  // are likely to want to choose among locales.
+  bool show_advanced_view = false;
+
+  // A list of available keyboard layouts.
+  std::vector<InputMethodItem> keyboard_layouts;
+};
+
+// Info about a user in login/lock screen.
+struct ASH_PUBLIC_EXPORT LoginUserInfo {
+  LoginUserInfo();
+  LoginUserInfo(const LoginUserInfo& other);
+  LoginUserInfo(LoginUserInfo&& other);
+  ~LoginUserInfo();
+
+  LoginUserInfo& operator=(const LoginUserInfo& other);
+  LoginUserInfo& operator=(LoginUserInfo&& other);
+
+  // User's basic information including account id, email, avatar etc.
+  UserInfo basic_user_info;
+
+  // What method the user can use to sign in.
+  proximity_auth::mojom::AuthType auth_type =
+      proximity_auth::mojom::AuthType::OFFLINE_PASSWORD;
+
+  // True if this user has already signed in.
+  bool is_signed_in = false;
+
+  // True if this user is the device owner.
+  bool is_device_owner = false;
+
+  // The initial fingerprint state. There are other mojom methods (ie,
+  // login_screen.mojom::SetFingerprintState) which update the current state.
+  FingerprintState fingerprint_state = FingerprintState::UNAVAILABLE;
+
+  // True if multi-profiles sign in is allowed for this user.
+  bool is_multiprofile_allowed = false;
+
+  // Enforced policy for multi-profiles sign in.
+  MultiProfileUserBehavior multiprofile_policy =
+      MultiProfileUserBehavior::UNRESTRICTED;
+
+  // True if this user can be removed.
+  bool can_remove = false;
+
+  // Contains the public account information if user type is PUBLIC_ACCOUNT.
+  base::Optional<PublicAccountInfo> public_account_info;
+};
+
+}  // namespace ash
+
+#endif  // ASH_PUBLIC_CPP_LOGIN_TYPES_H_
diff --git a/ash/public/cpp/session/user_info.cc b/ash/public/cpp/session/user_info.cc
index fd00b92..8374ce7 100644
--- a/ash/public/cpp/session/user_info.cc
+++ b/ash/public/cpp/session/user_info.cc
@@ -18,26 +18,6 @@
 UserInfo::UserInfo(const UserInfo& other) = default;
 UserInfo::~UserInfo() = default;
 
-mojom::UserInfoPtr UserInfo::ToMojom() const {
-  mojom::UserInfoPtr user_info = mojom::UserInfo::New();
-  user_info->type = type;
-  user_info->account_id = account_id;
-  user_info->service_instance_group = service_instance_group;
-  user_info->display_name = display_name;
-  user_info->display_email = display_email;
-
-  user_info->avatar = mojom::UserAvatar::New();
-  user_info->avatar->image = avatar.image;
-  user_info->avatar->bytes = avatar.bytes;
-
-  user_info->is_new_profile = is_new_profile;
-  user_info->is_ephemeral = is_ephemeral;
-  user_info->is_device_owner = is_device_owner;
-  user_info->has_gaia_account = has_gaia_account;
-  user_info->should_display_managed_ui = should_display_managed_ui;
-  return user_info;
-}
-
 ASH_PUBLIC_EXPORT bool operator==(const UserInfo& a, const UserInfo& b) {
   return a.type == b.type && a.account_id == b.account_id &&
          a.service_instance_group == b.service_instance_group &&
diff --git a/ash/public/cpp/session/user_info.h b/ash/public/cpp/session/user_info.h
index 7ce1d23..6656db1 100644
--- a/ash/public/cpp/session/user_info.h
+++ b/ash/public/cpp/session/user_info.h
@@ -11,7 +11,7 @@
 #include <vector>
 
 #include "ash/public/cpp/ash_public_export.h"
-#include "ash/public/interfaces/user_info.mojom.h"
+#include "ash/public/cpp/session/user_info.h"
 #include "base/optional.h"
 #include "base/token.h"
 #include "components/account_id/account_id.h"
@@ -39,10 +39,6 @@
   UserInfo(const UserInfo& other);
   ~UserInfo();
 
-  // TODO(crbug.com/958206): Remove after login mojom migrates to use this and
-  // fix DetachableBaseHandler.
-  mojom::UserInfoPtr ToMojom() const;
-
   user_manager::UserType type = user_manager::USER_TYPE_REGULAR;
   AccountId account_id;
   base::Optional<base::Token> service_instance_group;
diff --git a/ash/public/interfaces/BUILD.gn b/ash/public/interfaces/BUILD.gn
index 4aba5ef..780ff57 100644
--- a/ash/public/interfaces/BUILD.gn
+++ b/ash/public/interfaces/BUILD.gn
@@ -37,7 +37,6 @@
     "kiosk_next_shell.mojom",
     "locale.mojom",
     "login_screen.mojom",
-    "login_user_info.mojom",
     "media.mojom",
     "night_light_controller.mojom",
     "note_taking_controller.mojom",
@@ -47,7 +46,6 @@
     "tablet_mode.mojom",
     "tray_action.mojom",
     "update.mojom",
-    "user_info.mojom",
     "voice_interaction_controller.mojom",
     "vpn_list.mojom",
     "wallpaper.mojom",
diff --git a/ash/public/interfaces/login_screen.mojom b/ash/public/interfaces/login_screen.mojom
index 6a3367c..7f942a2 100644
--- a/ash/public/interfaces/login_screen.mojom
+++ b/ash/public/interfaces/login_screen.mojom
@@ -4,8 +4,6 @@
 
 module ash.mojom;
 
-import "ash/public/interfaces/user_info.mojom";
-import "ash/public/interfaces/login_user_info.mojom";
 import "chromeos/components/proximity_auth/public/interfaces/auth_type.mojom";
 import "components/account_id/interfaces/account_id.mojom";
 import "mojo/public/mojom/base/string16.mojom";
@@ -78,6 +76,8 @@
 
 // Allows clients (e.g. the browser process) to send messages to the ash
 // login/lock/user-add screens.
+// TODO(estade): this is in the process of being migrated off Mojo. Methods will
+// move to ash::LoginScreen or ash::LoginScreenModel.
 interface LoginScreen {
   // Sets the client interface.
   SetClient(LoginScreenClient client);
@@ -117,16 +117,6 @@
   // Requests to close any displayed error messages in ash lock screen.
   ClearErrors();
 
-  // Requests to show the custom icon in the user pod.
-  // |account_id|:  The account id of the user in the user pod.
-  // |icon|:        Information regarding the icon.
-  ShowUserPodCustomIcon(signin.mojom.AccountId account_id,
-                        EasyUnlockIconOptions icon);
-
-  // Requests to hide the custom icon in the user pod.
-  // |account_id|:  The account id of the user in the user pod.
-  HideUserPodCustomIcon(signin.mojom.AccountId account_id);
-
   // Requests to set the authentication type.
   // |account_id|:    The account id of the user in the user pod.
   // |auth_type|:     Authentication type.
@@ -135,19 +125,11 @@
               proximity_auth.mojom.AuthType auth_type,
               mojo_base.mojom.String16 initial_value);
 
-  // Set the users who are displayed on the login UI. |users| is filtered
-  // and does not correspond to every user on the device.
-  SetUserList(array<LoginUserInfo> users);
-
   // Notification if pin is enabled or disabled for the given user.
   // |account_id|:   The account id of the user in the user pod.
   // |is_enabled|:   True if pin unlock is enabled.
   SetPinEnabledForUser(signin.mojom.AccountId account_id, bool is_enabled);
 
-  // Update the status of fingerprint for |account_id|.
-  SetFingerprintState(signin.mojom.AccountId account_id,
-                      FingerprintState state);
-
   // Called after a fingerprint authentication attempt has been made. If
   // |successful| is true, then the fingerprint authentication attempt was
   // successful and the device should be unlocked. If false, an error message
@@ -155,10 +137,6 @@
   NotifyFingerprintAuthResult(signin.mojom.AccountId account_id,
                               bool successful);
 
-  // Change the user's avatar. Some avatars may take a long time to load and the
-  // login screen may already be visible.
-  SetAvatarForUser(signin.mojom.AccountId account_id, UserAvatar avatar);
-
   // Called when auth should be enabled for the given user. When auth is
   // disabled, the user cannot unlock the device. Auth is enabled by default.
   // |account_id|:            The account id of the user in the user pod.
@@ -196,22 +174,6 @@
   SetPublicSessionDisplayName(signin.mojom.AccountId account_id,
                               string display_name);
 
-  // Set the public session locales for user with |account_id|.
-  // |locales|:            Available locales for this user.
-  // |default_locale|:     Default locale for this user.
-  // |show_advanced_view|: True if we should show the advanced expanded user
-  //                       view for the public session.
-  SetPublicSessionLocales(signin.mojom.AccountId account_id,
-                          array<LocaleItem> locales,
-                          string default_locale,
-                          bool show_advanced_view);
-
-  // Set the public session keyboard layouts for user with |account_id|.
-  // |locale|: The locale that |keyboard_layouts| can be used for.
-  SetPublicSessionKeyboardLayouts(signin.mojom.AccountId account_id,
-                                  string locale,
-                                  array<InputMethodItem> keyboard_layouts);
-
   // Sets whether full management disclosure is needed for the public/managed
   // session login screen.
   SetPublicSessionShowFullManagementDisclosure(
diff --git a/ash/public/interfaces/login_user_info.mojom b/ash/public/interfaces/login_user_info.mojom
deleted file mode 100644
index 16f8b69..0000000
--- a/ash/public/interfaces/login_user_info.mojom
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright 2017 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.
-
-module ash.mojom;
-
-import "ash/public/interfaces/user_info.mojom";
-import "chromeos/components/proximity_auth/public/interfaces/auth_type.mojom";
-import "mojo/public/mojom/base/string16.mojom";
-import "mojo/public/mojom/base/values.mojom";
-
-// Supported multi-profile user behavior values.
-// Keep in sync with the enum in chromeos_user_pod_row.js and user_pod_row.js
-enum MultiProfileUserBehavior {
-  UNRESTRICTED = 0,
-  PRIMARY_ONLY = 1,
-  NOT_ALLOWED = 2,
-  OWNER_PRIMARY_ONLY = 3,
-};
-
-// Easy unlock icon choices.
-enum EasyUnlockIconId {
-  // No icon shown.
-  NONE,
-  // The user has clicked the easy unlock icon and disabled easy unlock for this
-  // login/lock session.
-  HARDLOCKED,
-  // Phone could not be found.
-  LOCKED,
-  // Phone found, but it is not unlocked.
-  LOCKED_TO_BE_ACTIVATED,
-  // Phone found, but it is too far away.
-  LOCKED_WITH_PROXIMITY_HINT,
-  // Phone found and unlocked. The user can click to dismiss the login/lock
-  // screen.
-  UNLOCKED,
-  // Scanning for phone.
-  SPINNER,
-};
-
-// The status of fingerprint availability.
-enum FingerprintState {
-  // The user cannot use fingerprint. This may be because:
-  //  - they are not the primary user
-  //  - they never registered fingerprint
-  //  - the device does not have a fingerprint sensor
-  UNAVAILABLE,
-  // Fingerprint can be used to unlock the device.
-  AVAILABLE,
-  // There have been too many attempts, so now fingerprint is disabled.
-  DISABLED_FROM_ATTEMPTS,
-  // It has been too long since the device was last used.
-  DISABLED_FROM_TIMEOUT,
-};
-
-// Information about the custom icon in the user pod.
-struct EasyUnlockIconOptions {
-  // Icon that should be displayed.
-  EasyUnlockIconId icon;
-  // Tooltip that is associated with the icon. This is shown automatically if
-  // |autoshow_tooltip| is true. The user can always see the tooltip if they
-  // hover over the icon. The tooltip should be used for the accessibility label
-  // if it is present.
-  mojo_base.mojom.String16 tooltip;
-  // If true, the tooltip should be displayed (even if the user is not currently
-  // hovering over the icon, ie, this makes |tooltip| act like a little like a
-  // notification).
-  bool autoshow_tooltip;
-  // Accessibility label. Only used if |tooltip| is empty.
-  // TODO(jdufault): Always populate and use |aria_label|, even if |tooltip| is
-  // non-empty.
-  mojo_base.mojom.String16 aria_label;
-  // If true, clicking the easy unlock icon should fire a hardlock event which
-  // will disable easy unlock. The hardlock event will request a new icon
-  // display via a separate EasyUnlockIconsOption update. See
-  // login_screen.mojom::HardlockPod.
-  bool hardlock_on_click;
-};
-
-// Infomation of each input method. This is used to populate keyboard layouts
-// for public account user.
-struct InputMethodItem {
-  // An id that identifies an input method engine (e.g., "t:latn-post",
-  // "pinyin", "hangul").
-  string ime_id;
-
-  // Title of the imput method.
-  string title;
-
-  // Whether this input method is been selected.
-  bool selected;
-};
-
-// Information of each available locale. This is used to populate language
-// locales for public account user.
-struct LocaleItem {
-  // Language code of the locale.
-  string language_code;
-
-  // Title of the locale.
-  string title;
-
-  // Optional, group name of the locale.
-  string? group_name;
-};
-
-// Infomation about a public account user.
-struct PublicAccountInfo {
-  // Optional, the domain name displayed in the login screen UI.
-  string? enterprise_domain;
-
-  // A list of available user locales.
-  array<LocaleItem> available_locales;
-
-  // Default locale for this user.
-  string default_locale;
-
-  // Show expanded user view that contains session information/warnings and
-  // locale selection.
-  bool show_expanded_view;
-
-  // Show the advanced expanded user view if there are at least two recommended
-  // locales. This will be the case in multilingual environments where users
-  // are likely to want to choose among locales.
-  bool show_advanced_view;
-
-  // A list of available keyboard layouts.
-  array<InputMethodItem> keyboard_layouts;
-};
-
-// Info about a user in login/lock screen.
-struct LoginUserInfo {
-  // User's basic information including account id, email, avatar etc.
-  UserInfo basic_user_info;
-
-  // What method the user can use to sign in.
-  proximity_auth.mojom.AuthType auth_type;
-
-  // True if this user has already signed in.
-  bool is_signed_in;
-
-  // True if this user is the device owner.
-  bool is_device_owner;
-
-  // The initial fingerprint state. There are other mojom methods (ie,
-  // login_screen.mojom::SetFingerprintState) which update the current state.
-  FingerprintState fingerprint_state;
-
-  // True if multi-profiles sign in is allowed for this user.
-  bool is_multiprofile_allowed;
-
-  // Enforced policy for multi-profiles sign in.
-  MultiProfileUserBehavior multiprofile_policy;
-
-  // True if this user can be removed.
-  bool can_remove;
-
-  // Optional, contains the public account information if user type is
-  // PUBLIC_ACCOUNT.
-  PublicAccountInfo? public_account_info;
-};
diff --git a/ash/public/interfaces/typemaps.gni b/ash/public/interfaces/typemaps.gni
index a21e366..7305cb5 100644
--- a/ash/public/interfaces/typemaps.gni
+++ b/ash/public/interfaces/typemaps.gni
@@ -4,6 +4,5 @@
 
 typemaps = [
   "//ash/public/interfaces/shelf_integration_test_api.typemap",
-  "//ash/public/interfaces/user_info.typemap",
   "//ash/public/interfaces/wallpaper.typemap",
 ]
diff --git a/ash/public/interfaces/user_info.mojom b/ash/public/interfaces/user_info.mojom
deleted file mode 100644
index 3e159f9..0000000
--- a/ash/public/interfaces/user_info.mojom
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2017 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.
-
-module ash.mojom;
-
-import "components/account_id/interfaces/account_id.mojom";
-import "mojo/public/mojom/base/token.mojom";
-import "ui/gfx/image/mojo/image.mojom";
-
-// Matches user_manager::UserType.
-enum UserType {
-  // Regular user, has a user name and password.
-  REGULAR,
-
-  // Guest user, logs in without authentication.
-  GUEST,
-
-  // Public account user, logs in without authentication. Available only if
-  // enabled through policy.
-  PUBLIC_ACCOUNT,
-
-  // Supervised user, logs in only with local authentication.
-  SUPERVISED,
-
-  // Kiosk app robot, logs in without authentication.
-  KIOSK,
-
-  // Child user, with supervised options.
-  CHILD,
-
-  // Android app in kiosk mode, logs in without authentication.
-  ARC_KIOSK,
-
-  // Active Directory user. Authenticates against Active Directory server.
-  ACTIVE_DIRECTORY,
-};
-
-// Data for a user's avatar.
-struct UserAvatar {
-  gfx.mojom.ImageSkia image;
-  // The raw bytes for the avatar. Useful if the avatar is animated.
-  // TODO(crbug.com/770373): Use a shared buffer (mojo.Blob), as this may be
-  // large enough to congest IPC.
-  array<uint8> bytes;
-};
-
-// Info about a user. May be sent repeatedly for a single user because
-// individual fields may change (e.g. the avatar image or custodians).
-struct UserInfo {
-  UserType type;
-  signin.mojom.AccountId account_id;
-  mojo_base.mojom.Token? service_instance_group;
-  string display_name;
-  string display_email;
-  UserAvatar avatar;
-  // True if this user has a newly created profile (first time login on the
-  // device)
-  bool is_new_profile;
-  // True if the user's non-cryptohome data (wallpaper, avatar etc.) is
-  // ephemeral. See |UserManager::IsUserNonCryptohomeDataEphemeral| for details.
-  bool is_ephemeral;
-  // True if the user is also the device owner.
-  bool is_device_owner;
-  // True if the user has a gaia account.
-  bool has_gaia_account;
-  // True if should display managed ui.
-  bool should_display_managed_ui;
-};
diff --git a/ash/public/interfaces/user_info.typemap b/ash/public/interfaces/user_info.typemap
deleted file mode 100644
index 5a0e937..0000000
--- a/ash/public/interfaces/user_info.typemap
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright 2017 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.
-
-mojom = "//ash/public/interfaces/user_info.mojom"
-public_headers = [ "//components/user_manager/user_type.h" ]
-traits_headers = [ "//ash/public/interfaces/user_info_traits.h" ]
-public_deps = [
-  "//components/user_manager",
-]
-type_mappings = [ "ash.mojom.UserType=user_manager::UserType" ]
diff --git a/ash/public/interfaces/user_info_traits.h b/ash/public/interfaces/user_info_traits.h
index 77bf2a0..fc24fd2 100644
--- a/ash/public/interfaces/user_info_traits.h
+++ b/ash/public/interfaces/user_info_traits.h
@@ -5,7 +5,7 @@
 #ifndef ASH_PUBLIC_INTERFACES_USER_INFO_TRAITS_H_
 #define ASH_PUBLIC_INTERFACES_USER_INFO_TRAITS_H_
 
-#include "ash/public/interfaces/user_info.mojom.h"
+#include "ash/public/cpp/session/user_info.h"
 #include "components/user_manager/user_type.h"
 
 namespace mojo {
diff --git a/ash/public/interfaces/wallpaper.mojom b/ash/public/interfaces/wallpaper.mojom
index e04f2d2..3e0d540 100644
--- a/ash/public/interfaces/wallpaper.mojom
+++ b/ash/public/interfaces/wallpaper.mojom
@@ -4,7 +4,6 @@
 
 module ash.mojom;
 
-import "ash/public/interfaces/user_info.mojom";
 import "components/account_id/interfaces/account_id.mojom";
 import "mojo/public/mojom/base/file_path.mojom";
 import "mojo/public/mojom/base/time.mojom";
@@ -19,6 +18,34 @@
   TILE,
 };
 
+// Matches user_manager::UserType.
+enum UserType {
+  // Regular user, has a user name and password.
+  REGULAR,
+
+  // Guest user, logs in without authentication.
+  GUEST,
+
+  // Public account user, logs in without authentication. Available only if
+  // enabled through policy.
+  PUBLIC_ACCOUNT,
+
+  // Supervised user, logs in only with local authentication.
+  SUPERVISED,
+
+  // Kiosk app robot, logs in without authentication.
+  KIOSK,
+
+  // Child user, with supervised options.
+  CHILD,
+
+  // Android app in kiosk mode, logs in without authentication.
+  ARC_KIOSK,
+
+  // Active Directory user. Authenticates against Active Directory server.
+  ACTIVE_DIRECTORY,
+};
+
 // User info needed to set wallpapers. Clients must specify the user because
 // it's not always the same with the active user, e.g., when showing wallpapers
 // for different user pods at login screen, or setting wallpapers selectively
@@ -27,7 +54,7 @@
   // The user's account id.
   signin.mojom.AccountId account_id;
 
-  // The user type. Matches user_manager::UserType.
+  // The user type.
   UserType type;
 
   // True if the user's non-cryptohome data (wallpaper, avatar etc.) is
diff --git a/ash/public/interfaces/wallpaper.typemap b/ash/public/interfaces/wallpaper.typemap
index 16b5792f..01525b3 100644
--- a/ash/public/interfaces/wallpaper.typemap
+++ b/ash/public/interfaces/wallpaper.typemap
@@ -3,6 +3,18 @@
 # found in the LICENSE file.
 
 mojom = "//ash/public/interfaces/wallpaper.mojom"
-public_headers = [ "//ash/public/cpp/wallpaper_types.h" ]
-traits_headers = [ "//ash/public/cpp/wallpaper_struct_traits.h" ]
-type_mappings = [ "ash.mojom.WallpaperLayout=ash::WallpaperLayout" ]
+public_headers = [
+  "//ash/public/cpp/wallpaper_types.h",
+  "//components/user_manager/user_type.h",
+]
+traits_headers = [
+  "//ash/public/cpp/wallpaper_struct_traits.h",
+  "//ash/public/interfaces/user_info_traits.h",
+]
+public_deps = [
+  "//components/user_manager",
+]
+type_mappings = [
+  "ash.mojom.WallpaperLayout=ash::WallpaperLayout",
+  "ash.mojom.UserType=user_manager::UserType",
+]
diff --git a/ash/session/session_controller_impl.cc b/ash/session/session_controller_impl.cc
index 39a81b0..56b6be01 100644
--- a/ash/session/session_controller_impl.cc
+++ b/ash/session/session_controller_impl.cc
@@ -12,7 +12,7 @@
 #include "ash/metrics/user_metrics_recorder.h"
 #include "ash/public/cpp/session/session_activation_observer.h"
 #include "ash/public/cpp/session/session_controller_client.h"
-#include "ash/public/interfaces/user_info.mojom.h"
+#include "ash/public/cpp/session/user_info.h"
 #include "ash/session/multiprofiles_intro_dialog.h"
 #include "ash/session/session_aborted_dialog.h"
 #include "ash/session/session_observer.h"
diff --git a/ash/shelf/login_shelf_view.cc b/ash/shelf/login_shelf_view.cc
index 7b14ad19..9239cf1 100644
--- a/ash/shelf/login_shelf_view.cc
+++ b/ash/shelf/login_shelf_view.cc
@@ -589,8 +589,7 @@
   SetLoginDialogState(state);
 }
 
-void LoginShelfView::OnUsersChanged(
-    const std::vector<mojom::LoginUserInfoPtr>& users) {
+void LoginShelfView::OnUsersChanged(const std::vector<LoginUserInfo>& users) {
   login_screen_has_users_ = !users.empty();
   UpdateUi();
 }
diff --git a/ash/shelf/login_shelf_view.h b/ash/shelf/login_shelf_view.h
index 6cffcda..bb992d5 100644
--- a/ash/shelf/login_shelf_view.h
+++ b/ash/shelf/login_shelf_view.h
@@ -41,9 +41,9 @@
 
 // LoginShelfView contains the shelf buttons visible outside of an active user
 // session. ShelfView and LoginShelfView should never be shown together.
-// This view is attached as a LoginDataDispatcher::Observer when the LockScreen
-// is instantiated in kLogin mode. It cannot attach itself because it does not
-// know when the Login is instantiated.
+// This view is attached as a LoginDataDispatcher::Observer when the
+// LockScreen is instantiated in kLogin mode. It cannot attach itself because it
+// does not know when the Login is instantiated.
 class ASH_EXPORT LoginShelfView : public views::View,
                                   public views::ButtonListener,
                                   public TrayActionObserver,
@@ -147,8 +147,7 @@
   void OnOobeDialogStateChanged(mojom::OobeDialogState state) override;
 
   // LoginDataDispatcher::Observer:
-  void OnUsersChanged(
-      const std::vector<mojom::LoginUserInfoPtr>& users) override;
+  void OnUsersChanged(const std::vector<LoginUserInfo>& users) override;
 
   // LocaleChangeObserver:
   void OnLocaleChanged() override;
diff --git a/ash/system/accessibility/autoclick_menu_view.cc b/ash/system/accessibility/autoclick_menu_view.cc
index 668178f..7e2e5cb 100644
--- a/ash/system/accessibility/autoclick_menu_view.cc
+++ b/ash/system/accessibility/autoclick_menu_view.cc
@@ -19,6 +19,7 @@
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/gfx/vector_icon_types.h"
 #include "ui/views/accessibility/view_accessibility.h"
+#include "ui/views/animation/ink_drop_mask.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/separator.h"
@@ -59,6 +60,13 @@
   // views::Button:
   const char* GetClassName() const override { return "AutoclickMenuButton"; }
 
+  // views::ImageButton:
+  std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override {
+    gfx::Rect bounds = GetContentsBounds();
+    return std::make_unique<views::CircleInkDropMask>(
+        size(), bounds.CenterPoint(), bounds.width() / 2);
+  }
+
   // Set the vector icon shown in a circle.
   void SetVectorIcon(const gfx::VectorIcon& icon) {
     icon_ = &icon;
@@ -87,6 +95,10 @@
     views::ImageButton::PaintButtonContents(canvas);
   }
 
+  gfx::Size CalculatePreferredSize() const override {
+    return gfx::Size(size_, size_);
+  }
+
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
     if (!GetEnabled())
       return;
diff --git a/ash/system/network/active_network_icon.cc b/ash/system/network/active_network_icon.cc
index 91888ce..a509d44 100644
--- a/ash/system/network/active_network_icon.cc
+++ b/ash/system/network/active_network_icon.cc
@@ -192,7 +192,7 @@
     network_icon::IconType icon_type,
     bool* animating) {
   // If no network, check for cellular initializing.
-  NetworkStateProperties* default_network = model_->default_network();
+  const NetworkStateProperties* default_network = model_->default_network();
   if (!default_network && cellular_uninitialized_msg_ != 0) {
     if (animating)
       *animating = true;
@@ -205,7 +205,7 @@
 gfx::ImageSkia ActiveNetworkIcon::GetDualImagePrimary(
     network_icon::IconType icon_type,
     bool* animating) {
-  NetworkStateProperties* default_network = model_->default_network();
+  const NetworkStateProperties* default_network = model_->default_network();
   if (default_network && default_network->type == NetworkType::kCellular) {
     if (chromeos::network_config::StateIsConnected(
             default_network->connection_state)) {
@@ -239,7 +239,7 @@
         NetworkType::kCellular, icon_type);
   }
 
-  NetworkStateProperties* active_cellular = model_->active_cellular();
+  const NetworkStateProperties* active_cellular = model_->active_cellular();
   if (!active_cellular) {
     if (animating)
       *animating = false;
@@ -260,7 +260,7 @@
     return GetDefaultImageForNoNetwork(icon_type, animating);
   }
   // Don't show connected Ethernet in the tray unless a VPN is present.
-  NetworkStateProperties* active_vpn = model_->active_vpn();
+  const NetworkStateProperties* active_vpn = model_->active_vpn();
   if (network->type == NetworkType::kEthernet && IsTrayIcon(icon_type) &&
       !active_vpn) {
     if (animating)
@@ -303,7 +303,8 @@
 }
 
 void ActiveNetworkIcon::SetCellularUninitializedMsg() {
-  DeviceStateProperties* cellular = model_->GetDevice(NetworkType::kCellular);
+  const DeviceStateProperties* cellular =
+      model_->GetDevice(NetworkType::kCellular);
   if (cellular && cellular->state == DeviceStateType::kUninitialized) {
     cellular_uninitialized_msg_ = IDS_ASH_STATUS_TRAY_INITIALIZING_CELLULAR;
     uninitialized_state_time_ = base::Time::Now();
diff --git a/ash/system/network/network_feature_pod_button.cc b/ash/system/network/network_feature_pod_button.cc
index 6292119..f52c48d 100644
--- a/ash/system/network/network_feature_pod_button.cc
+++ b/ash/system/network/network_feature_pod_button.cc
@@ -151,7 +151,7 @@
 
   TrayNetworkStateModel* model =
       Shell::Get()->system_tray_model()->network_state_model();
-  NetworkStateProperties* network = model->default_network();
+  const NetworkStateProperties* network = model->default_network();
 
   bool toggled = network || model->GetDeviceState(NetworkType::kWiFi) ==
                                 DeviceStateType::kEnabled;
diff --git a/ash/system/network/network_feature_pod_controller.cc b/ash/system/network/network_feature_pod_controller.cc
index 81184b1..1749a7d 100644
--- a/ash/system/network/network_feature_pod_controller.cc
+++ b/ash/system/network/network_feature_pod_controller.cc
@@ -8,15 +8,14 @@
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "ash/system/model/system_tray_model.h"
 #include "ash/system/network/network_feature_pod_button.h"
+#include "ash/system/network/tray_network_state_model.h"
 #include "ash/system/unified/unified_system_tray_controller.h"
-#include "chromeos/network/network_state.h"
-#include "chromeos/network/network_state_handler.h"
 #include "ui/base/l10n/l10n_util.h"
 
-using chromeos::NetworkHandler;
-using chromeos::NetworkTypePattern;
-using chromeos::NetworkState;
+using chromeos::network_config::mojom::NetworkStateProperties;
+using chromeos::network_config::mojom::NetworkType;
 
 namespace ash {
 
@@ -24,32 +23,23 @@
 
 // Returns true if the network is actually toggled.
 bool SetNetworkEnabled(bool enabled) {
-  const NetworkState* network =
-      NetworkHandler::Get()->network_state_handler()->ConnectedNetworkByType(
-          NetworkTypePattern::NonVirtual());
+  TrayNetworkStateModel* model =
+      Shell::Get()->system_tray_model()->network_state_model();
+  const NetworkStateProperties* network = model->default_network();
 
   // For cellular and tether, users are only allowed to disable them from
   // feature pod toggle.
-  if (!enabled && network && network->Matches(NetworkTypePattern::Cellular())) {
-    NetworkHandler::Get()->network_state_handler()->SetTechnologyEnabled(
-        NetworkTypePattern::Cellular(), false,
-        chromeos::network_handler::ErrorCallback());
+  if (!enabled && network &&
+      (network->type == NetworkType::kCellular ||
+       network->type == NetworkType::kTether)) {
+    model->SetNetworkTypeEnabledState(network->type, false);
     return true;
   }
 
-  if (!enabled && network && network->Matches(NetworkTypePattern::Tether())) {
-    NetworkHandler::Get()->network_state_handler()->SetTechnologyEnabled(
-        NetworkTypePattern::Tether(), false,
-        chromeos::network_handler::ErrorCallback());
-    return true;
-  }
-
-  if (network && !network->Matches(NetworkTypePattern::WiFi()))
+  if (network && network->type != NetworkType::kWiFi)
     return false;
 
-  NetworkHandler::Get()->network_state_handler()->SetTechnologyEnabled(
-      NetworkTypePattern::WiFi(), enabled,
-      chromeos::network_handler::ErrorCallback());
+  model->SetNetworkTypeEnabledState(NetworkType::kWiFi, enabled);
   return true;
 }
 
diff --git a/ash/system/network/tray_network_state_model.cc b/ash/system/network/tray_network_state_model.cc
index 9a1705b..737029e2 100644
--- a/ash/system/network/tray_network_state_model.cc
+++ b/ash/system/network/tray_network_state_model.cc
@@ -68,7 +68,8 @@
   observer_list_.RemoveObserver(observer);
 }
 
-DeviceStateProperties* TrayNetworkStateModel::GetDevice(NetworkType type) {
+const DeviceStateProperties* TrayNetworkStateModel::GetDevice(
+    NetworkType type) const {
   auto iter = devices_.find(type);
   if (iter == devices_.end())
     return nullptr;
@@ -76,10 +77,17 @@
 }
 
 DeviceStateType TrayNetworkStateModel::GetDeviceState(NetworkType type) {
-  DeviceStateProperties* device = GetDevice(type);
+  const DeviceStateProperties* device = GetDevice(type);
   return device ? device->state : DeviceStateType::kUnavailable;
 }
 
+void TrayNetworkStateModel::SetNetworkTypeEnabledState(NetworkType type,
+                                                       bool enabled) {
+  DCHECK(cros_network_config_ptr_);
+  cros_network_config_ptr_->SetNetworkTypeEnabledState(type, enabled,
+                                                       base::DoNothing());
+}
+
 // CrosNetworkConfigObserver
 
 void TrayNetworkStateModel::OnActiveNetworksChanged(
@@ -116,6 +124,7 @@
 }
 
 void TrayNetworkStateModel::GetDeviceStateList() {
+  DCHECK(cros_network_config_ptr_);
   cros_network_config_ptr_->GetDeviceStateList(base::BindOnce(
       &TrayNetworkStateModel::OnGetDeviceStateList, base::Unretained(this)));
 }
diff --git a/ash/system/network/tray_network_state_model.h b/ash/system/network/tray_network_state_model.h
index 94a2a5a..a6a5e68 100644
--- a/ash/system/network/tray_network_state_model.h
+++ b/ash/system/network/tray_network_state_model.h
@@ -43,24 +43,32 @@
   void RemoveObserver(Observer* observer);
 
   // Returns DeviceStateProperties for |type| if it exists or null.
-  chromeos::network_config::mojom::DeviceStateProperties* GetDevice(
-      chromeos::network_config::mojom::NetworkType type);
+  const chromeos::network_config::mojom::DeviceStateProperties* GetDevice(
+      chromeos::network_config::mojom::NetworkType type) const;
 
   // Returns the DeviceStateType for |type| if a device exists or kUnavailable.
   chromeos::network_config::mojom::DeviceStateType GetDeviceState(
       chromeos::network_config::mojom::NetworkType type);
 
-  chromeos::network_config::mojom::NetworkStateProperties* default_network() {
+  // Convenience method to call cros_network_config_ptr_ method.
+  void SetNetworkTypeEnabledState(
+      chromeos::network_config::mojom::NetworkType type,
+      bool enabled);
+
+  const chromeos::network_config::mojom::NetworkStateProperties*
+  default_network() const {
     return default_network_.get();
   }
-  chromeos::network_config::mojom::NetworkStateProperties*
-  active_non_cellular() {
+  const chromeos::network_config::mojom::NetworkStateProperties*
+  active_non_cellular() const {
     return active_non_cellular_.get();
   }
-  chromeos::network_config::mojom::NetworkStateProperties* active_cellular() {
+  const chromeos::network_config::mojom::NetworkStateProperties*
+  active_cellular() const {
     return active_cellular_.get();
   }
-  chromeos::network_config::mojom::NetworkStateProperties* active_vpn() {
+  const chromeos::network_config::mojom::NetworkStateProperties* active_vpn()
+      const {
     return active_vpn_.get();
   }
 
diff --git a/ash/system/network/vpn_feature_pod_controller.cc b/ash/system/network/vpn_feature_pod_controller.cc
index 5739825..cf74531 100644
--- a/ash/system/network/vpn_feature_pod_controller.cc
+++ b/ash/system/network/vpn_feature_pod_controller.cc
@@ -14,15 +14,11 @@
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/unified/feature_pod_button.h"
 #include "ash/system/unified/unified_system_tray_controller.h"
-#include "chromeos/network/network_state.h"
-#include "chromeos/network/network_state_handler.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/paint_vector_icon.h"
 
-using chromeos::NetworkHandler;
-using chromeos::NetworkState;
-using chromeos::NetworkStateHandler;
-using chromeos::NetworkTypePattern;
+using chromeos::network_config::mojom::ConnectionStateType;
+using chromeos::network_config::mojom::NetworkStateProperties;
 
 namespace ash {
 
@@ -39,24 +35,7 @@
     return true;
 
   // Also show the VPN entry if at least one VPN network is configured.
-  NetworkStateHandler* const handler =
-      NetworkHandler::Get()->network_state_handler();
-  if (handler->FirstNetworkByType(NetworkTypePattern::VPN()))
-    return true;
-  return false;
-}
-
-bool IsVPNEnabled() {
-  NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
-  return handler->FirstNetworkByType(NetworkTypePattern::VPN());
-}
-
-bool IsVPNConnected() {
-  NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
-  const NetworkState* vpn =
-      handler->FirstNetworkByType(NetworkTypePattern::VPN());
-  return IsVPNEnabled() &&
-         (vpn->IsConnectedState() || vpn->IsConnectingState());
+  return Shell::Get()->system_tray_model()->network_state_model()->active_vpn();
 }
 
 }  // namespace
@@ -98,18 +77,18 @@
 }
 
 void VPNFeaturePodController::Update() {
-  // NetworkHandler can be uninitialized in unit tests.
-  if (!chromeos::NetworkHandler::IsInitialized())
-    return;
-
   button_->SetVisible(IsVPNVisibleInSystemTray());
   if (!button_->GetVisible())
     return;
 
+  const NetworkStateProperties* vpn =
+      Shell::Get()->system_tray_model()->network_state_model()->active_vpn();
+  bool is_active =
+      vpn && vpn->connection_state != ConnectionStateType::kNotConnected;
   button_->SetSubLabel(l10n_util::GetStringUTF16(
-      IsVPNConnected() ? IDS_ASH_STATUS_TRAY_VPN_CONNECTED_SHORT
-                       : IDS_ASH_STATUS_TRAY_VPN_DISCONNECTED_SHORT));
-  button_->SetToggled(IsVPNEnabled() && IsVPNConnected());
+      is_active ? IDS_ASH_STATUS_TRAY_VPN_CONNECTED_SHORT
+                : IDS_ASH_STATUS_TRAY_VPN_DISCONNECTED_SHORT));
+  button_->SetToggled(is_active);
 }
 
 }  // namespace ash
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index c943603..a03d8be 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -20,6 +20,7 @@
 #include "ash/wm/overview/overview_grid.h"
 #include "ash/wm/overview/overview_item.h"
 #include "ash/wm/overview/overview_session.h"
+#include "ash/wm/splitview/split_view_drag_indicators.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
 #include "base/stl_util.h"
@@ -84,9 +85,12 @@
   event_generator->ClickLeftButton();
 }
 
+// If |drop| is false, the dragged |item| won't be dropped; giving the caller
+// a chance to do some validations before the item is dropped.
 void DragItemToPoint(OverviewItem* item,
                      const gfx::Point& screen_location,
-                     ui::test::EventGenerator* event_generator) {
+                     ui::test::EventGenerator* event_generator,
+                     bool drop = true) {
   DCHECK(item);
 
   const gfx::Point item_center =
@@ -97,7 +101,8 @@
   // rather than the drag to close mode.
   event_generator->MoveMouseBy(50, 0);
   event_generator->MoveMouseTo(screen_location);
-  event_generator->ReleaseLeftButton();
+  if (drop)
+    event_generator->ReleaseLeftButton();
 }
 
 // Defines an observer to test DesksController notifications.
@@ -897,8 +902,7 @@
   auto* overview_controller = Shell::Get()->overview_controller();
   overview_controller->ToggleOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
-  const auto* overview_grid =
-      GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
+  auto* overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
   EXPECT_EQ(1u, overview_grid->size());
 
   auto* overview_session = overview_controller->overview_session();
@@ -934,6 +938,7 @@
   EXPECT_FALSE(DoesActiveDeskContainWindow(window.get()));
   EXPECT_TRUE(overview_session->no_windows_widget_for_testing());
   EXPECT_TRUE(desk_2->windows().contains(window.get()));
+  EXPECT_FALSE(overview_grid->drop_target_widget());
 }
 
 TEST_F(DesksTest, DragWindowToNonMiniViewPoints) {
@@ -983,6 +988,73 @@
   EXPECT_TRUE(DoesActiveDeskContainWindow(window.get()));
 }
 
+class DesksWithSplitViewTest : public AshTestBase {
+ public:
+  DesksWithSplitViewTest() = default;
+  ~DesksWithSplitViewTest() override = default;
+
+  // AshTestBase:
+  void SetUp() override {
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled_features=*/{features::kVirtualDesks,
+                              features::kDragToSnapInClamshellMode},
+        /*disabled_features=*/{});
+
+    AshTestBase::SetUp();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(DesksWithSplitViewTest);
+};
+
+TEST_F(DesksWithSplitViewTest, SuccessfulDragToDeskRemovesSplitViewIndicators) {
+  auto* controller = DesksController::Get();
+  controller->NewDesk();
+  ASSERT_EQ(2u, controller->desks().size());
+  auto window = CreateTestWindow(gfx::Rect(0, 0, 250, 100));
+  wm::ActivateWindow(window.get());
+  EXPECT_EQ(window.get(), wm::GetActiveWindow());
+
+  auto* overview_controller = Shell::Get()->overview_controller();
+  overview_controller->ToggleOverview();
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+  auto* overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
+
+  auto* overview_session = overview_controller->overview_session();
+  auto* overview_item =
+      overview_session->GetOverviewItemForWindow(window.get());
+  ASSERT_TRUE(overview_item);
+  const auto* desks_bar_view = overview_grid->GetDesksBarViewForTesting();
+  ASSERT_TRUE(desks_bar_view);
+  ASSERT_EQ(2u, desks_bar_view->mini_views().size());
+
+  // Drag it to desk_2's mini_view. The overview grid should now show the
+  // "no-windows" widget, and the window should move to desk_2.
+  auto* desk_2_mini_view = desks_bar_view->mini_views()[1].get();
+  DragItemToPoint(overview_item,
+                  desk_2_mini_view->GetBoundsInScreen().CenterPoint(),
+                  GetEventGenerator(), /*drop=*/false);
+  // Validate that before dropping, the SplitView indicators and the drop target
+  // widget are created.
+  EXPECT_TRUE(overview_grid->drop_target_widget());
+  EXPECT_EQ(IndicatorState::kDragArea,
+            overview_session->split_view_drag_indicators()
+                ->current_indicator_state());
+  // Now drop the window, and validate the indicators and the drop target were
+  // removed.
+  GetEventGenerator()->ReleaseLeftButton();
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+  EXPECT_TRUE(overview_grid->empty());
+  EXPECT_FALSE(DoesActiveDeskContainWindow(window.get()));
+  EXPECT_TRUE(overview_session->no_windows_widget_for_testing());
+  EXPECT_FALSE(overview_grid->drop_target_widget());
+  EXPECT_EQ(IndicatorState::kNone,
+            overview_session->split_view_drag_indicators()
+                ->current_indicator_state());
+}
+
 // TODO(afakhry): Add more tests:
 // - Always on top windows are not tracked by any desk.
 // - Reusing containers when desks are removed and created.
diff --git a/ash/wm/overview/overview_controller.cc b/ash/wm/overview/overview_controller.cc
index d51378a3..049d959 100644
--- a/ash/wm/overview/overview_controller.cc
+++ b/ash/wm/overview/overview_controller.cc
@@ -624,14 +624,14 @@
 void OverviewController::OnWindowActivating(ActivationReason reason,
                                             aura::Window* gained_active,
                                             aura::Window* lost_active) {
-  if (overview_session_)
+  if (InOverviewSession())
     overview_session_->OnWindowActivating(reason, gained_active, lost_active);
 }
 
 void OverviewController::OnAttemptToReactivateWindow(
     aura::Window* request_active,
     aura::Window* actual_active) {
-  if (overview_session_) {
+  if (InOverviewSession()) {
     overview_session_->OnWindowActivating(
         ::wm::ActivationChangeObserver::ActivationReason::ACTIVATION_CLIENT,
         request_active, actual_active);
diff --git a/ash/wm/overview/overview_window_drag_controller.cc b/ash/wm/overview/overview_window_drag_controller.cc
index bd5ae68..d6ebce4 100644
--- a/ash/wm/overview/overview_window_drag_controller.cc
+++ b/ash/wm/overview/overview_window_drag_controller.cc
@@ -354,14 +354,26 @@
     const gfx::PointF& location_in_screen) {
   DCHECK_EQ(current_drag_behavior_, DragBehavior::kNormalDrag);
 
-  // Remove the drop target if any (which may exist when SplitView is
-  // enabled either in tablet or clamshell modes).
-  if (item_->overview_grid()->drop_target_widget())
-    item_->overview_grid()->RemoveDropTarget();
-
-  // Attempt to move a window to a different desk.
   const gfx::Point rounded_screen_point =
       gfx::ToRoundedPoint(location_in_screen);
+  if (should_allow_split_view_) {
+    DCHECK(item_->overview_grid()->drop_target_widget());
+    item_->overview_grid()->RemoveDropTarget();
+    // Update the split view divider bar stuatus if necessary. The divider bar
+    // should be placed above the dragged window after drag ends. Note here the
+    // passed parameters |snap_position_| and |location_in_screen| won't be used
+    // in this function for this case, but they are passed in as placeholders.
+    split_view_controller_->OnWindowDragEnded(
+        item_->GetWindow(), snap_position_, rounded_screen_point);
+
+    // Update window grid bounds and |snap_position_| in case the screen
+    // orientation was changed.
+    UpdateDragIndicatorsAndOverviewGrid(location_in_screen);
+    overview_session_->SetSplitViewDragIndicatorsIndicatorState(
+        IndicatorState::kNone, gfx::Point());
+  }
+
+  // Attempt to move a window to a different desk.
   if (virtual_desks_enabled_) {
     item_->SetOpacity(original_opacity_);
 
@@ -378,19 +390,6 @@
   // Attempt to snap a window if SplitView is enabled.
   DCHECK(item_);
   if (should_allow_split_view_) {
-    // Update the split view divider bar stuatus if necessary. The divider bar
-    // should be placed above the dragged window after drag ends. Note here the
-    // passed parameters |snap_position_| and |location_in_screen| won't be used
-    // in this function for this case, but they are passed in as placeholders.
-    split_view_controller_->OnWindowDragEnded(
-        item_->GetWindow(), snap_position_, rounded_screen_point);
-
-    // Update window grid bounds and |snap_position_| in case the screen
-    // orientation was changed.
-    UpdateDragIndicatorsAndOverviewGrid(location_in_screen);
-    overview_session_->SetSplitViewDragIndicatorsIndicatorState(
-        IndicatorState::kNone, gfx::Point());
-
     // If the window was dragged around but should not be snapped, move it
     // back to overview window grid.
     if (!ShouldUpdateDragIndicatorsOrSnap(location_in_screen) ||
@@ -404,6 +403,7 @@
     return DragResult::kSuccessfulDragToSnap;
   }
 
+  item_->set_should_restack_on_animation_end(true);
   overview_session_->PositionWindows(/*animate=*/true);
   return DragResult::kNeverDisambiguated;
 }
diff --git a/base/BUILD.gn b/base/BUILD.gn
index b451a594..ccf6517 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -3330,17 +3330,17 @@
     java_files = [
       "test/android/javatests/src/org/chromium/base/test/ReachedCodeProfiler.java",
       "test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java",
+      "test/android/javatests/src/org/chromium/base/test/BaseJUnit4TestRule.java",
       "test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java",
       "test/android/javatests/src/org/chromium/base/test/BaseChromiumRunnerCommon.java",
       "test/android/javatests/src/org/chromium/base/test/BaseTestResult.java",
-      "test/android/javatests/src/org/chromium/base/test/LifetimeAssertRule.java",
       "test/android/javatests/src/org/chromium/base/test/DestroyActivitiesRule.java",
+      "test/android/javatests/src/org/chromium/base/test/LifetimeAssertRule.java",
       "test/android/javatests/src/org/chromium/base/test/ScreenshotOnFailureStatement.java",
       "test/android/javatests/src/org/chromium/base/test/SetUpTestRule.java",
       "test/android/javatests/src/org/chromium/base/test/SetUpStatement.java",
       "test/android/javatests/src/org/chromium/base/test/TestListInstrumentationRunListener.java",
       "test/android/javatests/src/org/chromium/base/test/TestTraceEvent.java",
-      "test/android/javatests/src/org/chromium/base/test/CommitSharedPreferencesTestRule.java",
       "test/android/javatests/src/org/chromium/base/test/params/ParameterizedRunner.java",
       "test/android/javatests/src/org/chromium/base/test/params/BlockJUnit4RunnerDelegate.java",
       "test/android/javatests/src/org/chromium/base/test/params/BaseJUnit4RunnerDelegate.java",
diff --git a/base/android/android_image_reader_compat_unittest.cc b/base/android/android_image_reader_compat_unittest.cc
index 756ec9f..975a579 100644
--- a/base/android/android_image_reader_compat_unittest.cc
+++ b/base/android/android_image_reader_compat_unittest.cc
@@ -28,7 +28,7 @@
   // version OREO.
   EXPECT_EQ(AndroidImageReader::GetInstance().IsSupported(),
             base::android::BuildInfo::GetInstance()->sdk_int() >=
-                base::android::SDK_VERSION_OREO);
+                base::android::SDK_VERSION_P);
 }
 
 // There should be only 1 instance of AndroidImageReader im memory. Hence 2
diff --git a/base/android/java/src/org/chromium/base/LocaleUtils.java b/base/android/java/src/org/chromium/base/LocaleUtils.java
index a8f07e8..10d1142 100644
--- a/base/android/java/src/org/chromium/base/LocaleUtils.java
+++ b/base/android/java/src/org/chromium/base/LocaleUtils.java
@@ -184,6 +184,16 @@
         return languageTag.substring(0, pos);
     }
 
+    /** @return true if the language is supported by Chrome. */
+    public static boolean isLanguageSupported(String language) {
+        for (String languageTag : BuildConfig.COMPRESSED_LOCALES) {
+            if (toLanguage(languageTag).equals(language)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * @return a language tag string that represents the default locale.
      *         The language tag is well-formed IETF BCP 47 language tag with language and country
diff --git a/base/android/java/src/org/chromium/base/task/TaskTraits.java b/base/android/java/src/org/chromium/base/task/TaskTraits.java
index 0633fc7..63216cd 100644
--- a/base/android/java/src/org/chromium/base/task/TaskTraits.java
+++ b/base/android/java/src/org/chromium/base/task/TaskTraits.java
@@ -33,8 +33,7 @@
 
     // This is a lowest-priority task which may block, for example non-urgent
     // logging or deletion of temporary files as clean-up.
-    public static final TaskTraits BEST_EFFORT_MAY_BLOCK =
-            new TaskTraits().taskPriority(TaskPriority.BEST_EFFORT).mayBlock(true);
+    public static final TaskTraits BEST_EFFORT_MAY_BLOCK = BEST_EFFORT.mayBlock();
 
     // This task affects UI or responsiveness of future user interactions. It is
     // not an immediate response to a user interaction. Most tasks are likely to
@@ -46,20 +45,37 @@
     public static final TaskTraits USER_VISIBLE =
             new TaskTraits().taskPriority(TaskPriority.USER_VISIBLE);
 
+    // USER_VISIBLE + may block.
+    public static final TaskTraits USER_VISIBLE_MAY_BLOCK = USER_VISIBLE.mayBlock();
+
     // This task affects UI immediately after a user interaction.
     // Example: Generating data shown in the UI immediately after a click.
-    // Is is different from the mMayBlock property in that it doesn't contribute
-    // to the creation of additional thread pool threads. This is the highest
-    // possible priority.
     public static final TaskTraits USER_BLOCKING =
             new TaskTraits().taskPriority(TaskPriority.USER_BLOCKING);
 
+    // USER_BLOCKING + may block.
+    public static final TaskTraits USER_BLOCKING_MAY_BLOCK = USER_BLOCKING.mayBlock();
+
     // A bit like requestAnimationFrame, this task will be posted onto the Choreographer
     // and will be run on the android main thread after the next vsync.
-    public static final TaskTraits CHOREOGRAPHER_FRAME =
-            new TaskTraits().setIsChoreographerFrame(true);
+    public static final TaskTraits CHOREOGRAPHER_FRAME = new TaskTraits();
+    static {
+        CHOREOGRAPHER_FRAME.mIsChoreographerFrame = true;
+    }
 
-    public TaskTraits() {}
+    // For convenience of the JNI code, we use primitive types only.
+    // Note shutdown behavior is not supported on android.
+    boolean mPrioritySetExplicitly;
+    int mPriority;
+    boolean mMayBlock;
+    byte mExtensionId;
+    byte mExtensionData[];
+    boolean mIsChoreographerFrame;
+
+    // Derive custom traits from existing trait constants.
+    private TaskTraits() {
+        mPriority = TaskPriority.USER_VISIBLE;
+    }
 
     private TaskTraits(TaskTraits other) {
         mPrioritySetExplicitly = other.mPrioritySetExplicitly;
@@ -69,11 +85,6 @@
         mExtensionData = other.mExtensionData;
     }
 
-    public TaskTraits(byte extensionId, byte[] extensionData) {
-        mExtensionId = extensionId;
-        mExtensionData = extensionData;
-    }
-
     public TaskTraits taskPriority(int taskPriority) {
         TaskTraits taskTraits = new TaskTraits(this);
         taskTraits.mPrioritySetExplicitly = true;
@@ -88,26 +99,12 @@
      * required for the mere use of locks. The thread pool uses this property to work out if
      * additional threads are required.
      */
-    public TaskTraits mayBlock(boolean mayBlock) {
+    public TaskTraits mayBlock() {
         TaskTraits taskTraits = new TaskTraits(this);
-        taskTraits.mMayBlock = mayBlock;
+        taskTraits.mMayBlock = true;
         return taskTraits;
     }
 
-    private TaskTraits setIsChoreographerFrame(boolean isChoreographerFrame) {
-        mIsChoreographerFrame = isChoreographerFrame;
-        return this;
-    }
-
-    // For convenience of the JNI code, we use primitive types only.
-    // Note shutdown behavior is not supported on android.
-    boolean mPrioritySetExplicitly;
-    int mPriority = TaskPriority.USER_VISIBLE;
-    boolean mMayBlock;
-    byte mExtensionId = INVALID_EXTENSION_ID;
-    byte mExtensionData[];
-    boolean mIsChoreographerFrame;
-
     /**
      * @return true if this task is using some TaskTraits extension.
      */
diff --git a/base/android/java/templates/BuildConfig.template b/base/android/java/templates/BuildConfig.template
index 32bddbc..2dcf476d 100644
--- a/base/android/java/templates/BuildConfig.template
+++ b/base/android/java/templates/BuildConfig.template
@@ -46,6 +46,22 @@
     public static MAYBE_FINAL boolean IS_CHROME_BRANDED MAYBE_FALSE;
 #endif
 
+    // Sorted list of locales that have a compressed .pak within assets.
+    // Stored as an array because AssetManager.list() is slow.
+#if defined(COMPRESSED_LOCALE_LIST)
+    public static MAYBE_FINAL String[] COMPRESSED_LOCALES = COMPRESSED_LOCALE_LIST;
+#else
+    public static MAYBE_FINAL String[] COMPRESSED_LOCALES = {};
+#endif
+
+    // Sorted list of locales that have an uncompressed .pak within assets.
+    // Stored as an array because AssetManager.list() is slow.
+#if defined(UNCOMPRESSED_LOCALE_LIST)
+    public static MAYBE_FINAL String[] UNCOMPRESSED_LOCALES = UNCOMPRESSED_LOCALE_LIST;
+#else
+    public static MAYBE_FINAL String[] UNCOMPRESSED_LOCALES = {};
+#endif
+
     // The ID of the android string resource that stores the product version.
     // This layer of indirection is necessary to make the resource dependency
     // optional for android_apk targets/base_java (ex. for cronet).
diff --git a/base/android/javatests/src/org/chromium/base/AdvancedMockContextTest.java b/base/android/javatests/src/org/chromium/base/AdvancedMockContextTest.java
index 20c626d..a11f22c 100644
--- a/base/android/javatests/src/org/chromium/base/AdvancedMockContextTest.java
+++ b/base/android/javatests/src/org/chromium/base/AdvancedMockContextTest.java
@@ -52,7 +52,7 @@
     @SmallTest
     public void testComponentCallbacksForTargetContext() {
         Context targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
-        Application targetApplication = (Application) targetContext.getApplicationContext();
+        Application targetApplication = BaseJUnit4ClassRunner.getApplication();
         AdvancedMockContext context = new AdvancedMockContext(targetContext);
         Callback1 callback1 = new Callback1();
         Callback2 callback2 = new Callback2();
diff --git a/base/android/javatests/src/org/chromium/base/task/PostTaskTest.java b/base/android/javatests/src/org/chromium/base/task/PostTaskTest.java
index c7ad98c..8aad40c 100644
--- a/base/android/javatests/src/org/chromium/base/task/PostTaskTest.java
+++ b/base/android/javatests/src/org/chromium/base/task/PostTaskTest.java
@@ -37,7 +37,7 @@
         // This test should not timeout.
         final Object lock = new Object();
         final AtomicBoolean taskExecuted = new AtomicBoolean();
-        PostTask.postTask(new TaskTraits(), new Runnable() {
+        PostTask.postTask(TaskTraits.USER_BLOCKING, new Runnable() {
             @Override
             public void run() {
                 synchronized (lock) {
@@ -60,7 +60,7 @@
     @Test
     @SmallTest
     public void testCreateSingleThreadTaskRunner() throws Exception {
-        TaskRunner taskQueue = PostTask.createSingleThreadTaskRunner(new TaskTraits());
+        TaskRunner taskQueue = PostTask.createSingleThreadTaskRunner(TaskTraits.USER_BLOCKING);
         // A SingleThreadTaskRunner with default traits will run in the native thread pool
         // and tasks posted won't run until after the native library has loaded.
         assertNotNull(taskQueue);
@@ -70,7 +70,7 @@
     @Test
     @SmallTest
     public void testCreateSequencedTaskRunner() throws Exception {
-        TaskRunner taskQueue = PostTask.createSequencedTaskRunner(new TaskTraits());
+        TaskRunner taskQueue = PostTask.createSequencedTaskRunner(TaskTraits.USER_BLOCKING);
         List<Integer> orderList = new ArrayList<>();
         try {
             SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 1);
@@ -87,7 +87,7 @@
     @Test
     @SmallTest
     public void testCreateTaskRunner() throws Exception {
-        TaskRunner taskQueue = PostTask.createTaskRunner(new TaskTraits());
+        TaskRunner taskQueue = PostTask.createTaskRunner(TaskTraits.USER_BLOCKING);
 
         // This should not timeout.
         try {
diff --git a/base/android/javatests/src/org/chromium/base/task/SequencedTaskRunnerImplTest.java b/base/android/javatests/src/org/chromium/base/task/SequencedTaskRunnerImplTest.java
index 51dc883..fa7f3b6b 100644
--- a/base/android/javatests/src/org/chromium/base/task/SequencedTaskRunnerImplTest.java
+++ b/base/android/javatests/src/org/chromium/base/task/SequencedTaskRunnerImplTest.java
@@ -31,7 +31,7 @@
     @Test
     @SmallTest
     public void testPreNativeTasksRunInOrder() {
-        TaskRunner taskQueue = new SequencedTaskRunnerImpl(new TaskTraits());
+        TaskRunner taskQueue = new SequencedTaskRunnerImpl(TaskTraits.USER_BLOCKING);
         List<Integer> orderList = new ArrayList<>();
         try {
             SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 1);
diff --git a/base/android/javatests/src/org/chromium/base/task/SingleThreadTaskRunnerImplTest.java b/base/android/javatests/src/org/chromium/base/task/SingleThreadTaskRunnerImplTest.java
index 8e68b11..173aebc 100644
--- a/base/android/javatests/src/org/chromium/base/task/SingleThreadTaskRunnerImplTest.java
+++ b/base/android/javatests/src/org/chromium/base/task/SingleThreadTaskRunnerImplTest.java
@@ -56,7 +56,8 @@
     @Test
     @SmallTest
     public void testPreNativePostTask() {
-        TaskRunner taskQueue = new SingleThreadTaskRunnerImpl(mHandler, new TaskTraits(), false);
+        TaskRunner taskQueue =
+                new SingleThreadTaskRunnerImpl(mHandler, TaskTraits.USER_BLOCKING, false);
         List<Integer> orderList = new ArrayList<>();
         SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 1);
         SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 2);
@@ -72,7 +73,7 @@
     public void testBelongsToCurrentThread() {
         // The handler created during test setup belongs to a different thread.
         SingleThreadTaskRunner taskQueue =
-                new SingleThreadTaskRunnerImpl(mHandler, new TaskTraits(), false);
+                new SingleThreadTaskRunnerImpl(mHandler, TaskTraits.USER_BLOCKING, false);
         try {
             Assert.assertFalse(taskQueue.belongsToCurrentThread());
         } finally {
@@ -82,7 +83,7 @@
         // We create a handler belonging to current thread.
         Looper.prepare();
         SingleThreadTaskRunner taskQueueCurrentThread =
-                new SingleThreadTaskRunnerImpl(new Handler(), new TaskTraits(), false);
+                new SingleThreadTaskRunnerImpl(new Handler(), TaskTraits.USER_BLOCKING, false);
         try {
             Assert.assertTrue(taskQueueCurrentThread.belongsToCurrentThread());
         } finally {
@@ -94,9 +95,9 @@
     @SmallTest
     public void testPrioritization() {
         TaskRunner highPriorityQueue =
-                new SingleThreadTaskRunnerImpl(mHandler, new TaskTraits(), true);
+                new SingleThreadTaskRunnerImpl(mHandler, TaskTraits.USER_BLOCKING, true);
         TaskRunner lowPriorityQueue =
-                new SingleThreadTaskRunnerImpl(mHandler, new TaskTraits(), false);
+                new SingleThreadTaskRunnerImpl(mHandler, TaskTraits.USER_BLOCKING, false);
         try {
             List<Integer> orderList = new ArrayList<>();
             // We want to post all these tasks atomically but we're not on the mHandlerThread so
diff --git a/base/android/javatests/src/org/chromium/base/task/TaskRunnerImplTest.java b/base/android/javatests/src/org/chromium/base/task/TaskRunnerImplTest.java
index f4da7e8..0194a63 100644
--- a/base/android/javatests/src/org/chromium/base/task/TaskRunnerImplTest.java
+++ b/base/android/javatests/src/org/chromium/base/task/TaskRunnerImplTest.java
@@ -25,7 +25,7 @@
     @Test
     @SmallTest
     public void testPreNativePostTask() {
-        TaskRunner taskQueue = new TaskRunnerImpl(new TaskTraits());
+        TaskRunner taskQueue = new TaskRunnerImpl(TaskTraits.USER_BLOCKING);
 
         // This should not time out.
         try {
diff --git a/base/android/junit/src/org/chromium/base/metrics/test/ShadowRecordHistogram.java b/base/android/junit/src/org/chromium/base/metrics/test/ShadowRecordHistogram.java
index 8ccbe4e..a9e1965 100644
--- a/base/android/junit/src/org/chromium/base/metrics/test/ShadowRecordHistogram.java
+++ b/base/android/junit/src/org/chromium/base/metrics/test/ShadowRecordHistogram.java
@@ -30,6 +30,12 @@
     }
 
     @Implementation
+    public static void recordBooleanHistogram(String name, boolean sample) {
+        Pair<String, Integer> key = Pair.create(name, sample ? 1 : 0);
+        incrementSampleCount(key);
+    }
+
+    @Implementation
     public static void recordCountHistogram(String name, int sample) {
         Pair<String, Integer> key = Pair.create(name, sample);
         incrementSampleCount(key);
diff --git a/base/android/junit/src/org/chromium/base/task/TaskTraitsTest.java b/base/android/junit/src/org/chromium/base/task/TaskTraitsTest.java
index 417f77b..a99406b 100644
--- a/base/android/junit/src/org/chromium/base/task/TaskTraitsTest.java
+++ b/base/android/junit/src/org/chromium/base/task/TaskTraitsTest.java
@@ -48,7 +48,9 @@
     @SmallTest
     public void testExtensionPresent() {
         String input = "Blub";
-        TaskTraits traits = new TaskTraits(FakeTaskTraitsExtensionDescriptor.ID, input.getBytes());
+        TaskTraits traits = TaskTraits.USER_VISIBLE.mayBlock();
+        traits.mExtensionId = FakeTaskTraitsExtensionDescriptor.ID;
+        traits.mExtensionData = input.getBytes();
         String extension = traits.getExtension(DESC);
         assertEquals(input, extension);
     }
@@ -57,7 +59,9 @@
     @SmallTest
     public void testExtensionNotPresent() {
         String input = "Blub";
-        TaskTraits traits = new TaskTraits((byte) 0x3, input.getBytes());
+        TaskTraits traits = TaskTraits.USER_VISIBLE.mayBlock();
+        traits.mExtensionId = 3;
+        traits.mExtensionData = input.getBytes();
         String extension = traits.getExtension(DESC);
         assertNull(extension);
     }
@@ -66,7 +70,7 @@
     @SmallTest
     public void testSerializeDeserialize() {
         String input = "Blub";
-        TaskTraits traits = new TaskTraits();
+        TaskTraits traits = TaskTraits.USER_VISIBLE;
         String extension = traits.withExtension(DESC, input).getExtension(DESC);
         assertEquals(input, extension);
     }
diff --git a/base/files/memory_mapped_file_win.cc b/base/files/memory_mapped_file_win.cc
index 9735aeb..cb43a87 100644
--- a/base/files/memory_mapped_file_win.cc
+++ b/base/files/memory_mapped_file_win.cc
@@ -12,8 +12,10 @@
 #include "base/files/file_path.h"
 #include "base/strings/string16.h"
 #include "base/threading/scoped_blocking_call.h"
+#include "base/win/pe_image.h"
 
 #include <windows.h>
+#include <winnt.h>  // NOLINT(build/include_order)
 
 namespace base {
 
@@ -31,23 +33,21 @@
     return false;
 
   file_mapping_.Set(::CreateFileMapping(file_.GetPlatformFile(), nullptr,
-                                        PAGE_EXECUTE_READ | SEC_IMAGE, 0, 0,
-                                        NULL));
+                                        PAGE_READONLY | SEC_IMAGE_NO_EXECUTE, 0,
+                                        0, NULL));
   if (!file_mapping_.IsValid())
     return false;
 
   data_ = static_cast<uint8_t*>(
-      ::MapViewOfFile(file_mapping_.Get(),
-                      FILE_MAP_READ | FILE_MAP_EXECUTE | SEC_IMAGE, 0, 0, 0));
+      ::MapViewOfFile(file_mapping_.Get(), FILE_MAP_READ, 0, 0, 0));
   if (!data_)
     return false;
 
   // We need to know how large the mapped file is in some cases
-  int64_t file_len = file_.GetLength();
-  if (!IsValueInRangeForNumericType<size_t>(file_len))
-    return false;
 
-  length_ = static_cast<size_t>(file_len);
+  base::win::PEImage pe_image(data_);
+  length_ = pe_image.GetNTHeaders()->OptionalHeader.SizeOfImage;
+
   return true;
 }
 
diff --git a/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java b/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java
index 046e8e8..4a11bb2 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java
@@ -24,9 +24,11 @@
 import dalvik.system.DexFile;
 
 import org.chromium.base.BuildConfig;
+import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.annotations.MainDex;
 import org.chromium.base.multidex.ChromiumMultiDexInstaller;
+import org.chromium.base.test.util.InMemorySharedPreferencesContext;
 
 import java.io.IOException;
 import java.lang.reflect.Field;
@@ -75,12 +77,14 @@
     private static final String ARGUMENT_LOG_ONLY = "log";
 
     private static final String TAG = "BaseJUnitRunner";
+    static InMemorySharedPreferencesContext sInMemorySharedPreferencesContext;
 
     @Override
     public Application newApplication(ClassLoader cl, String className, Context context)
             throws ClassNotFoundException, IllegalAccessException, InstantiationException {
+        Context targetContext = super.getTargetContext();
         boolean hasUnderTestApk =
-                !getContext().getPackageName().equals(getTargetContext().getPackageName());
+                !getContext().getPackageName().equals(targetContext.getPackageName());
         // When there is an under-test APK, BuildConfig belongs to it and does not indicate whether
         // the test apk is multidex. In this case, just assume it is.
         boolean isTestMultidex = hasUnderTestApk || BuildConfig.IS_MULTIDEX_ENABLED;
@@ -89,14 +93,28 @@
                 // Need hacks to have multidex work when there is an under-test apk :(.
                 ChromiumMultiDexInstaller.install(
                         new BaseChromiumRunnerCommon.MultiDexContextWrapper(
-                                getContext(), getTargetContext()));
-                BaseChromiumRunnerCommon.reorderDexPathElements(
-                        cl, getContext(), getTargetContext());
+                                getContext(), targetContext));
+                BaseChromiumRunnerCommon.reorderDexPathElements(cl, getContext(), targetContext);
             } else {
                 ChromiumMultiDexInstaller.install(getContext());
             }
         }
-        return super.newApplication(cl, className, context);
+
+        // We would ideally be able to wrap |context| here, and pass that to newApplication.
+        // However, there is framework code that assumes Application.getBaseContext() can be
+        // casted to ContextImpl (on KitKat for broadcast receivers. Refer to ActivityThread.java).
+        Application ret = super.newApplication(cl, className, context);
+        sInMemorySharedPreferencesContext = new InMemorySharedPreferencesContext(ret);
+        // Set this as early as possible since Application start-up could access prefs.
+        ContextUtils.initApplicationContextForTests(sInMemorySharedPreferencesContext);
+        return ret;
+    }
+
+    @Override
+    public Context getTargetContext() {
+        // The target context by default points directly at the ContextImpl, which we can't wrap.
+        // Make it instead point at the Application.
+        return sInMemorySharedPreferencesContext;
     }
 
     /**
diff --git a/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java b/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java
index 81579c7..4fa14c6 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java
@@ -4,8 +4,7 @@
 
 package org.chromium.base.test;
 
-import static org.chromium.base.test.BaseChromiumAndroidJUnitRunner.shouldListTests;
-
+import android.app.Application;
 import android.content.Context;
 import android.support.annotation.CallSuper;
 import android.support.test.InstrumentationRegistry;
@@ -13,6 +12,7 @@
 import android.support.test.internal.util.AndroidRunnerParams;
 
 import org.junit.rules.MethodRule;
+import org.junit.rules.RuleChain;
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runner.notification.RunNotifier;
@@ -21,7 +21,6 @@
 import org.junit.runners.model.Statement;
 
 import org.chromium.base.CommandLine;
-import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.test.BaseTestResult.PreTestHook;
 import org.chromium.base.test.params.MethodParamAnnotationRule;
@@ -88,6 +87,11 @@
                 new AndroidRunnerParams(InstrumentationRegistry.getInstrumentation(),
                         InstrumentationRegistry.getArguments(), false, 0L, false));
 
+        assert InstrumentationRegistry.getInstrumentation()
+                        instanceof BaseChromiumAndroidJUnitRunner
+            : "Must use BaseChromiumAndroidJUnitRunner instrumentation with "
+              + "BaseJUnit4ClassRunner, but found: "
+              + InstrumentationRegistry.getInstrumentation().getClass();
         String traceOutput = InstrumentationRegistry.getArguments().getString(EXTRA_TRACE_FILE);
 
         if (traceOutput != null) {
@@ -102,6 +106,12 @@
         }
     }
 
+    /** Returns the singleton Application instance. */
+    public static Application getApplication() {
+        return (Application)
+                BaseChromiumAndroidJUnitRunner.sInMemorySharedPreferencesContext.getBaseContext();
+    }
+
     /**
      * Merge two List into a new ArrayList.
      *
@@ -174,7 +184,14 @@
      */
     @CallSuper
     protected List<TestRule> getDefaultTestRules() {
-        return Arrays.asList(new DestroyActivitiesRule(), new LifetimeAssertRule());
+        // Order is important here. Outer rule setUp's run first, and tearDown's run last.
+        // Base setUp() should go first to initialize ContextUtils and clear out prefs.
+        // Base's tearDown() should come last since it deletes files.
+        // Activities must be destroyed before lifetimes are checked, so DestroyActivitiesRule()
+        // must come last so that its tearDown() runs before LifetimeAssertRule's.
+        return Collections.singletonList(RuleChain.outerRule(new BaseJUnit4TestRule())
+                                                 .around(new LifetimeAssertRule())
+                                                 .around(new DestroyActivitiesRule()));
     }
 
     /**
@@ -204,12 +221,8 @@
      */
     @Override
     public void run(RunNotifier notifier) {
-        // Most tests have an Application subclass that already call this.
-        if (ContextUtils.getApplicationContext() == null) {
-            ContextUtils.initApplicationContext(
-                    InstrumentationRegistry.getTargetContext().getApplicationContext());
-        }
-        if (shouldListTests(InstrumentationRegistry.getArguments())) {
+        if (BaseChromiumAndroidJUnitRunner.shouldListTests(
+                    InstrumentationRegistry.getArguments())) {
             for (Description child : getDescription().getChildren()) {
                 notifier.fireTestStarted(child);
                 notifier.fireTestFinished(child);
@@ -277,13 +290,4 @@
     protected Statement withAfters(FrameworkMethod method, Object test, Statement base) {
         return super.withAfters(method, test, new ScreenshotOnFailureStatement(base));
     }
-
-    @Override
-    protected List<TestRule> classRules() {
-        List<TestRule> result = super.classRules();
-        // Class rules are the outermost TestRules, so CommitSharedPreferencesTestRule will commit
-        // SharedPreferences after all other rules have finished writing them.
-        result.add(new CommitSharedPreferencesTestRule());
-        return result;
-    }
 }
diff --git a/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4TestRule.java b/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4TestRule.java
new file mode 100644
index 0000000..1bb8dcf2
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4TestRule.java
@@ -0,0 +1,80 @@
+// Copyright 2019 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.
+
+package org.chromium.base.test;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.v4.content.ContextCompat;
+import android.text.TextUtils;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.test.util.InMemorySharedPreferencesContext;
+
+import java.io.File;
+import java.util.ArrayList;
+
+/**
+ * Holds setUp / tearDown logic common to all instrumentation tests.
+ */
+class BaseJUnit4TestRule implements TestRule {
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                // Don't tests if there are prior on-disk shared prefs lying around.
+                checkOrDeleteOnDiskSharedPreferences(false);
+
+                InMemorySharedPreferencesContext context =
+                        BaseChromiumAndroidJUnitRunner.sInMemorySharedPreferencesContext;
+                // Reset Application context in case any tests have replaced it.
+                ContextUtils.initApplicationContextForTests(context);
+                // Ensure all tests start with empty (InMemory)SharedPreferences.
+                context.clearSharedPreferences();
+
+                base.evaluate();
+
+                // Do not use try/finally so that preferences asserts do not mask prior exceptions.
+                checkOrDeleteOnDiskSharedPreferences(true);
+            }
+        };
+    }
+
+    private void checkOrDeleteOnDiskSharedPreferences(boolean check) {
+        File dataDir = ContextCompat.getDataDir(InstrumentationRegistry.getTargetContext());
+        File prefsDir = new File(dataDir, "shared_prefs");
+        File[] files = prefsDir.listFiles();
+        if (files == null) {
+            return;
+        }
+        ArrayList<File> badFiles = new ArrayList<>();
+        for (File f : files) {
+            // Multidex support library prefs need to stay or else multidex extraction will occur
+            // needlessly.
+            // WebView prefs need to stay because webview tests have no (good) way of hooking
+            // SharedPreferences for instantiated WebViews.
+            if (!f.getName().endsWith("multidex.version.xml")
+                    && !f.getName().equals("WebViewChromiumPrefs.xml")) {
+                if (check) {
+                    badFiles.add(f);
+                } else {
+                    f.delete();
+                }
+            }
+        }
+        if (!badFiles.isEmpty()) {
+            throw new AssertionError("Found shared prefs file(s).\n"
+                    + "Code should use ContextUtils.getApplicationContext() when accessing "
+                    + "SharedPreferences so that tests are hooked to use InMemorySharedPreferences."
+                    + " This could also mean needing to override getSharedPreferences() on custom "
+                    + " Context subclasses (e.g. ChromeBaseAppCompatActivity does this to make "
+                    + "Preferences screens work).\n"
+                    + "Files:\n * " + TextUtils.join("\n * ", badFiles));
+        }
+    }
+}
diff --git a/base/test/android/javatests/src/org/chromium/base/test/CommitSharedPreferencesTestRule.java b/base/test/android/javatests/src/org/chromium/base/test/CommitSharedPreferencesTestRule.java
deleted file mode 100644
index 05203e0..0000000
--- a/base/test/android/javatests/src/org/chromium/base/test/CommitSharedPreferencesTestRule.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// 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.
-
-package org.chromium.base.test;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-import org.chromium.base.ContextUtils;
-
-class CommitSharedPreferencesTestRule implements TestRule {
-    @Override
-    public Statement apply(Statement statement, Description description) {
-        return new Statement() {
-            @Override
-            public void evaluate() throws Throwable {
-                // Clear app's SharedPreferences before each test to reduce flakiness.
-                // See https://crbug.com/908174, ttps://crbug.com/902774.
-                ContextUtils.getAppSharedPreferences().edit().clear().commit();
-                try {
-                    statement.evaluate();
-                } finally {
-                    // Some disk writes to update SharedPreferences may still be in progress if
-                    // apply() was used after editing. Commit these changes to SharedPreferences
-                    // before reporting the test as finished. See https://crbug.com/916717.
-                    ContextUtils.getAppSharedPreferences().edit().commit();
-                }
-            }
-        };
-    }
-}
diff --git a/base/test/android/javatests/src/org/chromium/base/test/LifetimeAssertRule.java b/base/test/android/javatests/src/org/chromium/base/test/LifetimeAssertRule.java
index aeb3bda..8784470 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/LifetimeAssertRule.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/LifetimeAssertRule.java
@@ -4,16 +4,25 @@
 
 package org.chromium.base.test;
 
-import org.junit.rules.ExternalResource;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
 
 import org.chromium.base.LifetimeAssert;
 
 /**
  * Ensures that all object instances that use LifetimeAssert are destroyed.
  */
-public class LifetimeAssertRule extends ExternalResource {
+public class LifetimeAssertRule implements TestRule {
     @Override
-    protected void after() {
-        LifetimeAssert.assertAllInstancesDestroyedForTesting();
+    public Statement apply(Statement base, Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                base.evaluate();
+                // Do not use try/finally so that lifetime asserts do not mask prior exceptions.
+                LifetimeAssert.assertAllInstancesDestroyedForTesting();
+            }
+        };
     }
 }
diff --git a/base/test/launcher/OWNERS b/base/test/launcher/OWNERS
new file mode 100644
index 0000000..987c0c8
--- /dev/null
+++ b/base/test/launcher/OWNERS
@@ -0,0 +1 @@
+erikchen@chromium.org
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py
index 855277a..2c96173 100755
--- a/build/android/gyp/write_build_config.py
+++ b/build/android/gyp/write_build_config.py
@@ -931,6 +931,14 @@
       '--resource-ids-provider',
       help='Path to the .build_config for the APK that this static library '
       'target uses to generate stable resource IDs.')
+  parser.add_option(
+      '--compressed-locales-provider',
+      help='Path to the .build_config that contains the compressed locales '
+      'Java list for this static library target.')
+  parser.add_option(
+      '--uncompressed-locales-provider',
+      help='Path to the .build_config that contains the uncompressed locales '
+      'Java list for this static library target.')
 
   parser.add_option('--tested-apk-config',
       help='Path to the build config of the tested apk (for an instrumentation '
@@ -1714,11 +1722,27 @@
     config['assets'], config['uncompressed_assets'], locale_paks = (
         _MergeAssets(deps.All('android_assets')))
 
-    deps_info['compressed_locales_java_list'] = _CreateJavaLocaleListFromAssets(
-        config['assets'], locale_paks)
-    deps_info[
-        'uncompressed_locales_java_list'] = _CreateJavaLocaleListFromAssets(
-            config['uncompressed_assets'], locale_paks)
+    if options.compressed_locales_provider:
+      dep_config = GetDepConfig(options.compressed_locales_provider)
+      if dep_config['type'] == 'android_app_bundle':
+        dep_config = GetDepConfig(dep_config['base_module_config'])
+      deps_info['compressed_locales_java_list'] = dep_config[
+          'compressed_locales_java_list']
+    else:
+      deps_info[
+          'compressed_locales_java_list'] = _CreateJavaLocaleListFromAssets(
+              config['assets'], locale_paks)
+
+    if options.uncompressed_locales_provider:
+      dep_config = GetDepConfig(options.uncompressed_locales_provider)
+      if dep_config['type'] == 'android_app_bundle':
+        dep_config = GetDepConfig(dep_config['base_module_config'])
+      deps_info['uncompressed_locales_java_list'] = dep_config[
+          'uncompressed_locales_java_list']
+    else:
+      deps_info[
+          'uncompressed_locales_java_list'] = _CreateJavaLocaleListFromAssets(
+              config['uncompressed_assets'], locale_paks)
 
     config['extra_android_manifests'] = filter(None, (
         d.get('android_manifest') for d in all_resources_deps))
diff --git a/build/android/java/templates/LocaleConfig.template b/build/android/java/templates/LocaleConfig.template
deleted file mode 100644
index 95f6051..0000000
--- a/build/android/java/templates/LocaleConfig.template
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2019 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.
-
-package PACKAGE;
-
-/**
- *  Locale configuration. Generated on a per-target basis.
- */
-public class LocaleConfig {
-
-    // Sorted list of locales that have a compressed .pak within assets.
-    // Stored as an array because AssetManager.list() is slow.
-#if defined(COMPRESSED_LOCALE_LIST)
-    public static final String[] COMPRESSED_LOCALES = COMPRESSED_LOCALE_LIST;
-#else
-    public static final String[] COMPRESSED_LOCALES = {};
-#endif
-
-    // Sorted list of locales that have an uncompressed .pak within assets.
-    // Stored as an array because AssetManager.list() is slow.
-#if defined(UNCOMPRESSED_LOCALE_LIST)
-    public static final String[] UNCOMPRESSED_LOCALES = UNCOMPRESSED_LOCALE_LIST;
-#else
-    public static final String[] UNCOMPRESSED_LOCALES = {};
-#endif
-}
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 669a936..f1d72f7 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -487,6 +487,12 @@
         if (_target.is_resource_ids_provider) {
           args += [ "--resource-ids-provider=$_config" ]
         }
+        if (_target.is_compressed_locales_provider) {
+          args += [ "--compressed-locales-provider=$_config" ]
+        }
+        if (_target.is_uncompressed_locales_provider) {
+          args += [ "--uncompressed-locales-provider=$_config" ]
+        }
       }
       args += [ "--static-library-dependent-configs=$_dependent_configs" ]
     }
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index a1e0e21..5b476a2 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -1886,6 +1886,7 @@
   # Variables:
   #   use_final_fields: True to use final fields. All other variables are
   #       ignored when this is false.
+  #   build_config: Path to build_config used for locale list
   #   enable_multidex: Value for ENABLE_MULTIDEX.
   #   min_sdk_version: Value for MIN_SDK_VERSION.
   #
@@ -1922,6 +1923,15 @@
         if (invoker.enable_multidex) {
           defines += [ "ENABLE_MULTIDEX" ]
         }
+        inputs = [
+          invoker.build_config,
+        ]
+        _rebased_build_config =
+            rebase_path(invoker.build_config, root_build_dir)
+        defines += [
+          "COMPRESSED_LOCALE_LIST=" + "@FileArg($_rebased_build_config:deps_info:compressed_locales_java_list)",
+          "UNCOMPRESSED_LOCALE_LIST=" + "@FileArg($_rebased_build_config:deps_info:uncompressed_locales_java_list)",
+        ]
         if (defined(invoker.min_sdk_version)) {
           defines += [ "_MIN_SDK_VERSION=${invoker.min_sdk_version}" ]
         }
@@ -1934,35 +1944,6 @@
     }
   }
 
-  # Creates LocaleConfig.java, a file containing the list of compressed and
-  # uncompressed locale .pak files in an APK.
-  #
-  # Variables:
-  #   build_config: Path to build_config used for locale lists.
-  #   java_package: Java package for the generated class.
-  template("generate_locale_config_srcjar") {
-    java_cpp_template(target_name) {
-      package_path = string_replace(invoker.java_package, ".", "/")
-      sources = [
-        "//build/android/java/templates/LocaleConfig.template",
-      ]
-      defines = [ "PACKAGE=${invoker.java_package}" ]
-      if (defined(invoker.build_config)) {
-        forward_variables_from(invoker,
-                               [
-                                 "deps",
-                                 "testonly",
-                               ])
-        _rebased_build_config =
-            rebase_path(invoker.build_config, root_build_dir)
-        defines += [
-          "COMPRESSED_LOCALE_LIST=" + "@FileArg($_rebased_build_config:deps_info:compressed_locales_java_list)",
-          "UNCOMPRESSED_LOCALE_LIST=" + "@FileArg($_rebased_build_config:deps_info:uncompressed_locales_java_list)",
-        ]
-      }
-    }
-  }
-
   # Declare an Android app module target, which is used as the basis for an
   # Android APK or an Android app bundle module.
   #
@@ -2041,16 +2022,14 @@
   #   static_library_dependent_targets: A list of scopes describing targets that
   #     use this target as a static library. Common Java code from the targets
   #     listed in static_library_dependent_targets will be moved into this
-  #     target. Scope members are name and is_resource_ids_provider.
+  #     target. Scope members are name, is_resource_ids_provider,
+  #     is_compressed_locales_provider, is_uncompressed_locales_provider.
   #     TODO(estevenson): Add a README for static library targets and document
   #                       additions to "deps_info" in write_build_config.py.
   #   static_library_provider: Specifies a single target that this target will
   #     use as a static library APK. When proguard is enabled, the
   #     static_library_provider target will provide the dex file(s) for this
   #     target.
-  #   locale_config_java_packages: Optional list of java packages. If given, a
-  #     LocaleConfig.java file will be generated for each package, and will
-  #     contain the list of compressed and uncompressed locale pak files.
   template("android_apk_or_module") {
     forward_variables_from(invoker, [ "testonly" ])
 
@@ -2263,8 +2242,6 @@
       _generate_buildconfig_java = invoker.generate_buildconfig_java
     }
 
-    _generate_localeconfig_java = defined(invoker.locale_config_java_packages)
-
     # JNI generation usually goes hand-in-hand with buildconfig generation.
     _generate_final_jni = _generate_buildconfig_java
     if (defined(invoker.generate_final_jni)) {
@@ -2636,6 +2613,7 @@
       generate_build_config_srcjar("${_template_name}__build_config_srcjar") {
         forward_variables_from(invoker, [ "min_sdk_version" ])
         use_final_fields = true
+        build_config = _build_config
         enable_multidex = _enable_multidex
         if (defined(invoker.product_version_resources_dep)) {
           resources_version_variable =
@@ -2648,21 +2626,6 @@
       _srcjar_deps += [ ":${_template_name}__build_config_srcjar" ]
     }
 
-    if (_generate_localeconfig_java) {
-      foreach(_package, invoker.locale_config_java_packages) {
-        _locale_target_name =
-            "${_template_name}_${_package}__locale_config_srcjar"
-        generate_locale_config_srcjar("$_locale_target_name") {
-          build_config = _build_config
-          java_package = _package
-          deps = [
-            ":$_build_config_target",
-          ]
-        }
-        _srcjar_deps += [ ":$_locale_target_name" ]
-      }
-    }
-
     if (_generate_final_jni) {
       generate_jni_registration("${_template_name}__final_jni") {
         target = ":$_template_name"
@@ -3289,7 +3252,6 @@
                                "keystore_path",
                                "load_library_from_apk",
                                "loadable_modules",
-                               "locale_config_java_packages",
                                "min_sdk_version",
                                "native_lib_placeholders",
                                "native_lib_version_arg",
@@ -3406,7 +3368,6 @@
                                "jni_registration_header",
                                "jni_sources_blacklist",
                                "load_library_from_apk",
-                               "locale_config_java_packages",
                                "min_sdk_version",
                                "native_lib_version_arg",
                                "native_lib_version_rule",
diff --git a/buildtools/checkdeps/java_checker.py b/buildtools/checkdeps/java_checker.py
index cc6b234..839622c 100644
--- a/buildtools/checkdeps/java_checker.py
+++ b/buildtools/checkdeps/java_checker.py
@@ -66,7 +66,7 @@
     # TODO(husky): We need some way of determining the "real" path to
     # a generated file -- i.e., where it would be in source control if
     # it weren't generated.
-    if d.startswith('out') or d in ('xcodebuild',):
+    if d.startswith('out') or d in ('xcodebuild', 'AndroidStudioDefault',):
       return True
     # Skip third-party directories.
     if d in ('third_party', 'ThirdParty'):
@@ -87,7 +87,8 @@
     """Build a set of fully-qualified class affected by this patch.
 
     Prescan imported files and build classset to collect full class names
-    with package name. This includes both changed files as well as changed imports.
+    with package name. This includes both changed files as well as changed
+    imports.
 
     Args:
       added_imports : ((file_path, (import_line, import_line, ...), ...)
@@ -115,7 +116,8 @@
     if full_class_name:
       if full_class_name in self._classmap:
         if self._verbose or full_class_name in added_classset:
-          if not any((re.match(i, filepath) for i in self._allow_multiple_definitions)):
+          if not any(re.match(i, filepath) for i in
+                     self._allow_multiple_definitions):
             print 'WARNING: multiple definitions of %s:' % full_class_name
             print '    ' + filepath
             print '    ' + self._classmap[full_class_name]
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index a1be293..2503eb7 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -629,11 +629,7 @@
 } else if (is_mac) {
   chrome_helper_name = chrome_product_full_name + " Helper"
   chrome_framework_name = chrome_product_full_name + " Framework"
-  if (new_mac_bundle_structure) {
-    chrome_framework_version = chrome_version_full
-  } else {
-    chrome_framework_version = "A"
-  }
+  chrome_framework_version = chrome_version_full
 
   group("chrome") {
     deps = [
@@ -808,15 +804,9 @@
     sources = [
       "$root_out_dir/$chrome_framework_name.framework",
     ]
-    if (new_mac_bundle_structure) {
-      outputs = [
-        "{{bundle_contents_dir}}/Frameworks/{{source_file_part}}",
-      ]
-    } else {
-      outputs = [
-        "{{bundle_contents_dir}}/Versions/$chrome_version_full/{{source_file_part}}",
-      ]
-    }
+    outputs = [
+      "{{bundle_contents_dir}}/Frameworks/{{source_file_part}}",
+    ]
     public_deps = [
       # Before bundling the versioned app components, delete any existing
       # versions.
@@ -827,23 +817,13 @@
       ":verify_chrome_framework_order",
     ]
 
-    if (!new_mac_bundle_structure) {
-      sources += [ "$root_out_dir/$chrome_helper_name.app" ]
-      public_deps += [ ":chrome_helper_app" ]
-    }
-
     if (enable_widevine_cdm_host_verification) {
-      if (new_mac_bundle_structure) {
-        # The :chrome_framework_widevine_signature target copies into the
-        # :chrome_framework bundle. But because the signing file depends on the
-        # framework itself, that would cause a cyclical dependency. Instead,
-        # this dependency directly copies the file into the framework's
-        # resources directory.
-        public_deps += [ ":chrome_framework_widevine_signature" ]
-      } else {
-        sources += [ "$root_out_dir/Widevine Resources.bundle" ]
-        public_deps += [ ":widevine_resources_bundle" ]
-      }
+      # The :chrome_framework_widevine_signature target copies into the
+      # :chrome_framework bundle. But because the signing file depends on the
+      # framework itself, that would cause a cyclical dependency. Instead,
+      # this dependency directly copies the file into the framework's
+      # resources directory.
+      public_deps += [ ":chrome_framework_widevine_signature" ]
     }
   }
 
@@ -858,11 +838,7 @@
 
     _old_versions_dir =
         "$root_out_dir/$chrome_product_full_name.app/Contents/Versions"
-    if (new_mac_bundle_structure) {
-      _versions_dir = "$root_out_dir/$chrome_product_full_name.app/Contents/Frameworks/$chrome_framework_name.framework/Versions"
-    } else {
-      _versions_dir = _old_versions_dir
-    }
+    _versions_dir = "$root_out_dir/$chrome_product_full_name.app/Contents/Frameworks/$chrome_framework_name.framework/Versions"
 
     args = [
       "--versions-dir",
@@ -871,16 +847,11 @@
       rebase_path(_stamp_file, root_build_dir),
       "--keep",
       chrome_version_full,
+      "--keep",
+      "Current",
+      "--delete",
+      rebase_path(_old_versions_dir, root_build_dir),
     ]
-
-    if (new_mac_bundle_structure) {
-      args += [
-        "--keep",
-        "Current",
-        "--delete",
-        rebase_path(_old_versions_dir, root_build_dir),
-      ]
-    }
   }
 
   tweak_info_plist("chrome_helper_plist") {
@@ -928,21 +899,12 @@
     ldflags = []
 
     if (is_component_build) {
-      if (new_mac_bundle_structure) {
-        ldflags += [
-          # The helper is in Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/X/Helpers/Chromium Helper.app/Contents/MacOS
-          # so set rpath up to the base.
-          "-rpath",
-          "@loader_path/../../../../../../../../../..",
-        ]
-      } else {
-        ldflags += [
-          # The helper is in Chromium.app/Contents/Versions/X/Chromium Helper.app/Contents/MacOS/
-          # so set rpath up to the base.
-          "-rpath",
-          "@loader_path/../../../../../../..",
-        ]
-      }
+      ldflags += [
+        # The helper is in Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/X/Helpers/Chromium Helper.app/Contents/MacOS
+        # so set rpath up to the base.
+        "-rpath",
+        "@loader_path/../../../../../../../../../..",
+      ]
     }
 
     if (enable_stripping) {
@@ -957,6 +919,7 @@
 
   bundle_data("chrome_framework_helpers") {
     sources = [
+      "$root_out_dir/$chrome_helper_name.app",
       "$root_out_dir/app_mode_loader",
       "$root_out_dir/chrome_crashpad_handler",
     ]
@@ -966,15 +929,11 @@
     ]
 
     public_deps = [
+      ":chrome_helper_app",
       "//chrome/app_shim:app_mode_loader",
       "//components/crash/content/app:chrome_crashpad_handler",
     ]
 
-    if (new_mac_bundle_structure) {
-      sources += [ "$root_out_dir/$chrome_helper_name.app" ]
-      public_deps += [ ":chrome_helper_app" ]
-    }
-
     if (using_sanitizer) {
       # crashpad_handler requires the ASan runtime at its @executable_path.
       sources += [ "$root_out_dir/libclang_rt.asan_osx_dynamic.dylib" ]
@@ -1042,11 +1001,6 @@
       ]
 
       script = "//build/symlink.py"
-      if (new_mac_bundle_structure) {
-        _resources_path = "$root_out_dir/$chrome_product_full_name.app/Contents/Frameworks/$chrome_framework_name.framework/Versions/$chrome_version_full/Resources/inspector"
-      } else {
-        _resources_path = "$root_out_dir/$chrome_product_full_name.app/Contents/Versions/$chrome_version_full/$chrome_framework_name.framework/Resources/inspector"
-      }
 
       args = [
         "-f",
@@ -1056,7 +1010,7 @@
         # Convert the symlink source and destination to an absolute paths, which
         # makes symlinking easier (now pwd manipulation).
         rebase_path("$root_out_dir/resources/inspector"),
-        rebase_path(_resources_path),
+        rebase_path("$root_out_dir/$chrome_product_full_name.app/Contents/Frameworks/$chrome_framework_name.framework/Versions/$chrome_version_full/Resources/inspector"),
       ]
 
       deps = [
@@ -1192,65 +1146,18 @@
       ]
     }
 
-    if (new_mac_bundle_structure) {
-      copy("chrome_framework_widevine_signature") {
-        deps = [
-          ":sign_chrome_framework_for_widevine",
-        ]
+    copy("chrome_framework_widevine_signature") {
+      deps = [
+        ":sign_chrome_framework_for_widevine",
+      ]
 
-        sources = [
-          "$root_out_dir/$chrome_framework_name.sig",
-        ]
+      sources = [
+        "$root_out_dir/$chrome_framework_name.sig",
+      ]
 
-        outputs = [
-          "$root_out_dir/$chrome_framework_name.framework/Resources/{{source_file_part}}",
-        ]
-      }
-    } else {
-      mac_info_plist("widevine_resources_plist") {
-        info_plist = "//third_party/widevine/cdm/widevine_resources.plist"
-        extra_substitutions = [
-          "CHROMIUM_BUNDLE_ID=$chrome_mac_bundle_id",
-          "BUNDLE_ID=widevine-resources",
-        ]
-        executable_name = "Widevine Resources"
-      }
-
-      bundle_data("widevine_resources_plist_bundle_data") {
-        sources = get_target_outputs(":widevine_resources_plist")
-        outputs = [
-          "{{bundle_contents_dir}}/Info.plist",
-        ]
-        public_deps = [
-          ":widevine_resources_plist",
-        ]
-      }
-
-      bundle_data("framework_widevine_signature") {
-        sources = [
-          "$root_out_dir/$chrome_framework_name.sig",
-        ]
-
-        outputs = [
-          "{{bundle_resources_dir}}/{{source_file_part}}",
-        ]
-
-        public_deps = [
-          ":sign_chrome_framework_for_widevine",
-        ]
-      }
-
-      create_bundle("widevine_resources_bundle") {
-        output_name = "Widevine Resources.bundle"
-        bundle_root_dir = "$root_build_dir/$output_name"
-        bundle_contents_dir = "$bundle_root_dir/Contents"
-        bundle_resources_dir = "$bundle_contents_dir/Resources"
-
-        deps = [
-          ":framework_widevine_signature",
-          ":widevine_resources_plist_bundle_data",
-        ]
-      }
+      outputs = [
+        "$root_out_dir/$chrome_framework_name.framework/Resources/{{source_file_part}}",
+      ]
     }
   }
 
@@ -1379,11 +1286,7 @@
       deps += [ ":default_apps" ]
     }
 
-    if (new_mac_bundle_structure) {
-      ldflags = [ "-Wl,-install_name,@executable_path/../Frameworks/$chrome_framework_name.framework/Versions/$chrome_version_full/$chrome_framework_name" ]
-    } else {
-      ldflags = [ "-Wl,-install_name,@executable_path/../Versions/$chrome_version_full/$chrome_framework_name.framework/$chrome_framework_name" ]
-    }
+    ldflags = [ "-Wl,-install_name,@executable_path/../Frameworks/$chrome_framework_name.framework/Versions/$chrome_version_full/$chrome_framework_name" ]
 
     ldflags += [
       "-compatibility_version",
@@ -1394,18 +1297,11 @@
     ]
 
     if (is_component_build) {
-      if (new_mac_bundle_structure) {
-        ldflags += [
-          "-rpath",
-          "@loader_path/../../../../../../../..",
-        ]
-      } else {
-        ldflags += [
-          "-rpath",
-          "@loader_path/../../../../../../..",
-        ]
-      }
-      ldflags += [ "-Wl,-reexport_library,libchrome_dll.dylib" ]
+      ldflags += [
+        "-rpath",
+        "@loader_path/../../../../../../../..",
+        "-Wl,-reexport_library,libchrome_dll.dylib",
+      ]
 
       data_deps = [
         ":chrome_dll",
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 96fa810..07e54cb 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -262,6 +262,7 @@
     ":document_tab_model_info_proto_java",
     ":partner_location_descriptor_proto_java",
     ":thumbnail_cache_entry_proto_java",
+    ":update_proto_java",
     ":usage_stats_proto_java",
     "$google_play_services_package:google_play_services_auth_base_java",
     "$google_play_services_package:google_play_services_base_java",
@@ -387,7 +388,6 @@
     ":chrome_android_java_enums_srcjar",
     ":chrome_android_java_switches_srcjar",
     ":chrome_android_java_google_api_keys_srcjar",
-    ":chrome_locale_config",
     ":photo_picker_aidl",
     ":resource_id_javagen",
     "//chrome:assist_ranker_prediction_enum_javagen",
@@ -455,10 +455,7 @@
 
   # Add the actual implementation where necessary so that downstream targets
   # can provide their own implementations.
-  jar_excluded_patterns = [
-    "*/AppHooksImpl.class",
-    "*/LocaleConfig.class",
-  ]
+  jar_excluded_patterns = [ "*/AppHooksImpl.class" ]
 
   annotation_processor_deps = [
     "//base/android/jni_generator:jni_processor",
@@ -480,10 +477,6 @@
   processor_args_javac = [ "dagger.fastInit=enabled" ]
 }
 
-generate_locale_config_srcjar("chrome_locale_config") {
-  java_package = "org.chromium.chrome.browser"
-}
-
 # This is a list of all base module java dependencies. New features should be
 # added to this list.
 java_group("chrome_all_java") {
@@ -598,6 +591,13 @@
   ]
 }
 
+proto_java_library("update_proto_java") {
+  proto_path = "java/src/org/chromium/chrome/browser/omaha/metrics"
+  sources = [
+    "$proto_path/update_success_tracking.proto",
+  ]
+}
+
 proto_java_library("usage_stats_proto_java") {
   proto_path = "../browser/android/usage_stats"
   sources = [
@@ -1851,10 +1851,14 @@
         {
           name = "//android_webview:trichrome_webview_apk"
           is_resource_ids_provider = true
+          is_compressed_locales_provider = false
+          is_uncompressed_locales_provider = true
         },
         {
           name = ":trichrome_chrome_apk"
           is_resource_ids_provider = false
+          is_compressed_locales_provider = true
+          is_uncompressed_locales_provider = false
         },
       ]
     }
@@ -1871,10 +1875,14 @@
         {
           name = "//android_webview:trichrome_webview_for_bundle_apk"
           is_resource_ids_provider = true
+          is_compressed_locales_provider = false
+          is_uncompressed_locales_provider = true
         },
         {
           name = ":trichrome_chrome_bundle"
           is_resource_ids_provider = false
+          is_compressed_locales_provider = true
+          is_uncompressed_locales_provider = false
         },
       ]
     }
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 2684a37..430eb7f 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1023,6 +1023,9 @@
   "java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateControllerFactory.java",
   "java/src/org/chromium/chrome/browser/omaha/inline/NoopInlineUpdateController.java",
   "java/src/org/chromium/chrome/browser/omaha/inline/PlayInlineUpdateController.java",
+  "java/src/org/chromium/chrome/browser/omaha/metrics/HistogramUtils.java",
+  "java/src/org/chromium/chrome/browser/omaha/metrics/TrackingProvider.java",
+  "java/src/org/chromium/chrome/browser/omaha/metrics/UpdateSuccessMetrics.java",
   "java/src/org/chromium/chrome/browser/omaha/ExponentialBackoffScheduler.java",
   "java/src/org/chromium/chrome/browser/omaha/MarketURLGetter.java",
   "java/src/org/chromium/chrome/browser/omaha/OmahaBase.java",
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index ad0e52f..49c1c99 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -139,6 +139,8 @@
   "junit/src/org/chromium/chrome/browser/omaha/ResponseParserTest.java",
   "junit/src/org/chromium/chrome/browser/omaha/UpdateStatusProviderTest.java",
   "junit/src/org/chromium/chrome/browser/omaha/VersionNumberTest.java",
+  "junit/src/org/chromium/chrome/browser/omaha/metrics/HistogramUtilsTest.java",
+  "junit/src/org/chromium/chrome/browser/omaha/metrics/UpdateSuccessMetricsTest.java",
   "junit/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextTest.java",
   "junit/src/org/chromium/chrome/browser/omnibox/AutocompleteStateUnitTest.java",
   "junit/src/org/chromium/chrome/browser/omnibox/KeyboardHideHelperUnitTest.java",
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni
index 42390c9..2919c64 100644
--- a/chrome/android/chrome_public_apk_tmpl.gni
+++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -135,10 +135,6 @@
       "*ic_lock.*",  # Bottom edge seems misaligned.
     ]
 
-    if (!_is_monochrome) {
-      locale_config_java_packages = [ "org.chromium.chrome.browser" ]
-    }
-
     # Use zh-TW strings for zh-HK (https://crbug.com/780847).
     if (!defined(support_zh_hk)) {
       support_zh_hk = true
@@ -311,11 +307,6 @@
     ]
 
     if (is_monochrome) {
-      locale_config_java_packages = [
-        "org.chromium.chrome.browser",
-        "org.chromium.android_webview",
-      ]
-
       if (invoker.target_type == "android_app_bundle_module") {
         _suffix = bundle_library_suffix
       } else {
diff --git a/chrome/android/features/autofill_assistant/BUILD.gn b/chrome/android/features/autofill_assistant/BUILD.gn
index b324f20..ab79f09 100644
--- a/chrome/android/features/autofill_assistant/BUILD.gn
+++ b/chrome/android/features/autofill_assistant/BUILD.gn
@@ -75,6 +75,7 @@
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryFactoryImpl.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/EditDistance.java",
+    "java/src/org/chromium/chrome/browser/autofill_assistant/FeedbackContext.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/SizeListenableLinearLayout.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantActionsCarouselCoordinator.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantActionsDecoration.java",
@@ -99,6 +100,7 @@
     "java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormSelectionChoice.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/header/AnimatedProgressBar.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderCoordinator.java",
+    "java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderDelegate.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/infobox/AssistantInfoBox.java",
@@ -135,6 +137,7 @@
     "java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormDelegate.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormInput.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormModel.java",
+    "java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderDelegate.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/infobox/AssistantInfoBox.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/infobox/AssistantInfoBoxModel.java",
diff --git a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_form_counter_input.xml b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_form_counter_input.xml
index 1e09a14..1011a9d 100644
--- a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_form_counter_input.xml
+++ b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_form_counter_input.xml
@@ -17,12 +17,6 @@
         android:layout_marginBottom="4dp"
         android:textAppearance="@style/TextAppearance.BlackTitle2"/>
     <LinearLayout
-        android:id="@+id/expandable_section"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical"
-        android:visibility="gone"/>
-    <LinearLayout
         android:id="@+id/expand_label_container"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
diff --git a/chrome/android/features/autofill_assistant/java/res/menu/profile_icon_menu.xml b/chrome/android/features/autofill_assistant/java/res/menu/profile_icon_menu.xml
new file mode 100644
index 0000000..760fbf3
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/java/res/menu/profile_icon_menu.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 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. -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/settings"
+        android:title="@string/preferences" />
+    <item
+        android:id="@+id/send_feedback"
+        android:title="@string/autofill_assistant_send_feedback" />
+</menu>
\ No newline at end of file
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
index a3f0902..36416eb 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
@@ -7,6 +7,8 @@
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.autofill_assistant.metrics.DropOutReason;
 import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayCoordinator;
+import org.chromium.chrome.browser.help.HelpAndFeedback;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
 
 /**
@@ -22,6 +24,9 @@
         // delegate.
     }
 
+    private static final String FEEDBACK_CATEGORY_TAG =
+            "com.android.chrome.USER_INITIATED_FEEDBACK_REPORT_AUTOFILL_ASSISTANT";
+
     private final ChromeActivity mActivity;
     private final Delegate mDelegate;
 
@@ -85,4 +90,13 @@
     public AssistantBottomBarCoordinator getBottomBarCoordinator() {
         return mBottomBarCoordinator;
     }
+
+    /**
+     * Show the Chrome feedback form.
+     */
+    public void showFeedback(String debugContext) {
+        HelpAndFeedback.getInstance(mActivity).showFeedback(mActivity, Profile.getLastUsedProfile(),
+                mActivity.getActivityTab().getUrl(), FEEDBACK_CATEGORY_TAG,
+                FeedbackContext.buildContextString(mActivity, debugContext, 4));
+    }
 }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
index 1703c77e..ef44af0 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
@@ -205,6 +205,11 @@
     }
 
     @CalledByNative
+    private void showFeedback(String debugContext) {
+        mCoordinator.showFeedback(debugContext);
+    }
+
+    @CalledByNative
     private void showSnackbar(String message) {
         mSnackbarController = AssistantSnackbar.show(mActivity, message, this::safeSnackbarResult);
     }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/FeedbackContext.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/FeedbackContext.java
new file mode 100644
index 0000000..45c699c
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/FeedbackContext.java
@@ -0,0 +1,45 @@
+// Copyright 2019 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.
+
+package org.chromium.chrome.browser.autofill_assistant;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import org.chromium.chrome.browser.ChromeActivity;
+
+/**
+ * Automatically extracts context information and serializes it in JSON form.
+ */
+class FeedbackContext extends JSONObject {
+    static String buildContextString(
+            ChromeActivity activity, String debugContext, int indentSpaces) {
+        try {
+            return new FeedbackContext(activity, debugContext).toString(indentSpaces);
+        } catch (JSONException e) {
+            // Note: it is potentially unsafe to return e.getMessage(): the exception message
+            // could be wrangled and used as an attack vector when arriving at the JSON parser.
+            return "{\"error\": \"Failed to convert feedback context to string.\"}";
+        }
+    }
+
+    private FeedbackContext(ChromeActivity activity, String debugContext) throws JSONException {
+        addActivityInformation(activity);
+        addClientContext(debugContext);
+    }
+
+    private void addActivityInformation(ChromeActivity activity) throws JSONException {
+        put("intent-action", activity.getInitialIntent().getAction());
+        put("intent-data", activity.getInitialIntent().getDataString());
+    }
+
+    private void addClientContext(String debugContext) throws JSONException {
+        // Try to parse the debug context as JSON object. If that fails, just add the string as-is.
+        try {
+            put("debug-context", new JSONObject(debugContext));
+        } catch (JSONException encodingException) {
+            put("debug-context", debugContext);
+        }
+    }
+}
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormCounterInput.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormCounterInput.java
index 2cea36f..81f30a8 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormCounterInput.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormCounterInput.java
@@ -58,6 +58,12 @@
     private final String mExpandText;
     private final String mMinimizeText;
     private final List<AssistantFormCounter> mCounters;
+
+    /**
+     * The (minimum) number of counters to show when this input is minimized. The counters with
+     * count > 0 will always be shown, so the number of counters actually shown when this input is
+     * minimized might be strictly larger than |mMinimizedCount|.
+     */
     private final int mMinimizedCount;
     private final long mMinCountersSum;
     private final long mMaxCountersSum;
@@ -91,30 +97,28 @@
         }
 
         // Create the views.
-        ViewGroup expandableSection = root.findViewById(R.id.expandable_section);
         int labelIndex = root.indexOfChild(label);
         List<CounterViewHolder> viewHolders = new ArrayList<>();
         for (int i = 0; i < mCounters.size(); i++) {
             CounterViewHolder viewHolder = new CounterViewHolder(context);
             viewHolders.add(viewHolder);
-            if (i < mMinimizedCount) {
-                // Add the counters below the label.
-                root.addView(viewHolder.mView, labelIndex + i + 1);
-            } else {
-                expandableSection.addView(viewHolder.mView);
-            }
+
+            // Add the counters below the label.
+            root.addView(viewHolder.mView, labelIndex + i + 1);
         }
 
         // Initialize the views and attach listeners.
         initializeCounterViews(mCounters, viewHolders);
 
-        // If some counters are in the expandable section, show the expand label that will expand
-        // the section when clicked.
-        if (expandableSection.getChildCount() > 0) {
-            View expandLabelContainer = root.findViewById(R.id.expand_label_container);
-            TextView expandLabel = root.findViewById(R.id.expand_label);
-            TextView minimizeLabel = root.findViewById(R.id.minimize_label);
-            View chevron = root.findViewById(R.id.chevron);
+        // If some counters are hidden in the minimized state, show the expand label that will show
+        // them once clicked.
+        if (mCounters.size() > mMinimizedCount) {
+            setViewsVisibility(mCounters, viewHolders, /* minimized= */ true);
+
+            ViewGroup expandLabelContainer = root.findViewById(R.id.expand_label_container);
+            TextView expandLabel = expandLabelContainer.findViewById(R.id.expand_label);
+            TextView minimizeLabel = expandLabelContainer.findViewById(R.id.minimize_label);
+            View chevron = expandLabelContainer.findViewById(R.id.chevron);
 
             expandLabel.setText(mExpandText);
             minimizeLabel.setText(mMinimizeText);
@@ -122,35 +126,59 @@
             expandLabelContainer.setVisibility(View.VISIBLE);
 
             expandLabelContainer.setOnClickListener(unusedView -> {
-                expandLabel.setOnClickListener(null);
-                expandableSection.setVisibility(View.VISIBLE);
-                expandLabelContainer.setVisibility(View.GONE);
-                expandLabel.setVisibility(View.GONE);
-            });
-
-            expandLabelContainer.setOnClickListener(unusedView -> {
                 TransitionManager.beginDelayedTransition(
-                        (ViewGroup) expandableSection.getRootView(), EXPAND_TRANSITION);
-                boolean expanded = expandableSection.getVisibility() == View.VISIBLE;
-                if (expanded) {
-                    // Shrink.
-                    expandableSection.setVisibility(View.GONE);
+                        (ViewGroup) root.getRootView(), EXPAND_TRANSITION);
+                boolean shouldMinimize = expandLabel.getVisibility() == View.GONE;
+                if (shouldMinimize) {
                     expandLabel.setVisibility(View.VISIBLE);
                     minimizeLabel.setVisibility(View.GONE);
                     chevron.animate().rotation(0).start();
                 } else {
-                    // Expand.
-                    expandableSection.setVisibility(View.VISIBLE);
                     expandLabel.setVisibility(View.GONE);
                     minimizeLabel.setVisibility(View.VISIBLE);
                     chevron.animate().rotation(180).start();
                 }
+
+                setViewsVisibility(mCounters, viewHolders, shouldMinimize);
             });
         }
 
         return root;
     }
 
+    private void setViewsVisibility(List<AssistantFormCounter> counters,
+            List<CounterViewHolder> viewHolders, boolean minimized) {
+        if (!minimized) {
+            for (CounterViewHolder viewHolder : viewHolders) {
+                viewHolder.mView.setVisibility(View.VISIBLE);
+            }
+            return;
+        }
+
+        // Count the number of counters with count > 0.
+        // TODO(crbug.com/806868): The magic value 0 makes sense for tickets, but might not make
+        // sense for other type of counters. When using this counter input for other things than
+        // tickets, we might want to disable this logic and just show the first mMinimizedCount
+        // counters.
+        int nonZeroCounters = 0;
+        for (int i = 0; i < counters.size(); i++) {
+            if (counters.get(i).getValue() > 0) {
+                nonZeroCounters++;
+            }
+        }
+
+        // Set the views visibility such that:
+        //  - all counters with value > 0 are visible.
+        //  - the first (mMinimizedCount - nonZeroCounters) counters with value = 0 are shown.
+        //  - the remaining counters are hidden.
+        int zeroCountersShown = mMinimizedCount - nonZeroCounters;
+        for (int i = 0; i < counters.size(); i++) {
+            viewHolders.get(i).mView.setVisibility(
+                    counters.get(i).getValue() > 0 || zeroCountersShown-- > 0 ? View.VISIBLE
+                                                                              : View.GONE);
+        }
+    }
+
     private void initializeCounterViews(
             List<AssistantFormCounter> counters, List<CounterViewHolder> views) {
         assert counters.size() == views.size();
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderCoordinator.java
index 0ee8c5e..4387fc1 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderCoordinator.java
@@ -44,7 +44,7 @@
 
         // Bind view and mediator through the model.
         AssistantHeaderViewBinder.ViewHolder viewHolder =
-                new AssistantHeaderViewBinder.ViewHolder(bottomBarView, poodle);
+                new AssistantHeaderViewBinder.ViewHolder(context, bottomBarView, poodle);
         AssistantHeaderViewBinder viewBinder = new AssistantHeaderViewBinder();
         PropertyModelChangeProcessor.create(model, viewHolder, viewBinder);
 
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderDelegate.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderDelegate.java
new file mode 100644
index 0000000..2a51ac4
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderDelegate.java
@@ -0,0 +1,35 @@
+// 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.
+
+package org.chromium.chrome.browser.autofill_assistant.header;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+
+@JNINamespace("autofill_assistant")
+class AssistantHeaderDelegate {
+    private long mNativeAssistantHeaderDelegate;
+
+    @CalledByNative
+    private static AssistantHeaderDelegate create(long nativeAssistantHeaderDelegate) {
+        return new AssistantHeaderDelegate(nativeAssistantHeaderDelegate);
+    }
+
+    private AssistantHeaderDelegate(long nativeAssistantHeaderDelegate) {
+        mNativeAssistantHeaderDelegate = nativeAssistantHeaderDelegate;
+    }
+
+    void onFeedbackButtonClicked() {
+        if (mNativeAssistantHeaderDelegate != 0) {
+            nativeOnFeedbackButtonClicked(mNativeAssistantHeaderDelegate);
+        }
+    }
+
+    @CalledByNative
+    private void clearNativePtr() {
+        mNativeAssistantHeaderDelegate = 0;
+    }
+
+    private native void nativeOnFeedbackButtonClicked(long nativeAssistantHeaderDelegate);
+}
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java
index 5ef7eaf..f5771cf 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java
@@ -28,8 +28,12 @@
 
     static final WritableBooleanPropertyKey SPIN_POODLE = new WritableBooleanPropertyKey();
 
+    static final WritableObjectPropertyKey<Runnable> FEEDBACK_BUTTON_CALLBACK =
+            new WritableObjectPropertyKey<>();
+
     public AssistantHeaderModel() {
-        super(VISIBLE, STATUS_MESSAGE, PROGRESS, PROGRESS_VISIBLE, SPIN_POODLE);
+        super(VISIBLE, STATUS_MESSAGE, PROGRESS, PROGRESS_VISIBLE, SPIN_POODLE,
+                FEEDBACK_BUTTON_CALLBACK);
     }
 
     @CalledByNative
@@ -51,4 +55,9 @@
     private void setSpinPoodle(boolean enabled) {
         set(SPIN_POODLE, enabled);
     }
+
+    @CalledByNative
+    private void setDelegate(AssistantHeaderDelegate delegate) {
+        set(FEEDBACK_BUTTON_CALLBACK, delegate::onFeedbackButtonClicked);
+    }
 }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java
index e8b1618..bc6b3f1 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java
@@ -4,10 +4,15 @@
 
 package org.chromium.chrome.browser.autofill_assistant.header;
 
+import android.content.Context;
+import android.support.annotation.Nullable;
 import android.view.View;
+import android.widget.PopupMenu;
 import android.widget.TextView;
 
 import org.chromium.chrome.autofill_assistant.R;
+import org.chromium.chrome.browser.preferences.PreferencesLauncher;
+import org.chromium.chrome.browser.preferences.autofill_assistant.AutofillAssistantPreferences;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
@@ -27,12 +32,18 @@
         final View mHeader;
         final TextView mStatusMessage;
         final AnimatedProgressBar mProgressBar;
+        final View mProfileIconView;
+        final PopupMenu mProfileIconMenu;
 
-        public ViewHolder(View bottomBarView, AnimatedPoodle poodle) {
+        public ViewHolder(Context context, View bottomBarView, AnimatedPoodle poodle) {
             mPoodle = poodle;
             mHeader = bottomBarView.findViewById(R.id.header);
             mStatusMessage = bottomBarView.findViewById(R.id.status_message);
             mProgressBar = new AnimatedProgressBar(bottomBarView.findViewById(R.id.progress_bar));
+            mProfileIconView = bottomBarView.findViewById(R.id.profile_image);
+            mProfileIconMenu = new PopupMenu(context, mProfileIconView);
+            mProfileIconMenu.inflate(R.menu.profile_icon_menu);
+            mProfileIconView.setOnClickListener(unusedView -> mProfileIconMenu.show());
         }
     }
 
@@ -52,6 +63,8 @@
             setProgressBarVisibility(view, model);
         } else if (AssistantHeaderModel.SPIN_POODLE == propertyKey) {
             view.mPoodle.setSpinEnabled(model.get(AssistantHeaderModel.SPIN_POODLE));
+        } else if (AssistantHeaderModel.FEEDBACK_BUTTON_CALLBACK == propertyKey) {
+            setProfileMenuListener(view, model.get(AssistantHeaderModel.FEEDBACK_BUTTON_CALLBACK));
         } else {
             assert false : "Unhandled property detected in AssistantHeaderViewBinder!";
         }
@@ -65,4 +78,22 @@
             view.mProgressBar.hide();
         }
     }
+
+    private void setProfileMenuListener(ViewHolder view, @Nullable Runnable feedbackCallback) {
+        view.mProfileIconMenu.setOnMenuItemClickListener(item -> {
+            int itemId = item.getItemId();
+            if (itemId == R.id.settings) {
+                PreferencesLauncher.launchSettingsPage(
+                        view.mHeader.getContext(), AutofillAssistantPreferences.class);
+                return true;
+            } else if (itemId == R.id.send_feedback) {
+                if (feedbackCallback != null) {
+                    feedbackCallback.run();
+                }
+                return true;
+            }
+
+            return false;
+        });
+    }
 }
diff --git a/chrome/android/features/autofill_assistant/java/strings/android_chrome_autofill_assistant_strings.grd b/chrome/android/features/autofill_assistant/java/strings/android_chrome_autofill_assistant_strings.grd
index 13e8378..82cec55 100644
--- a/chrome/android/features/autofill_assistant/java/strings/android_chrome_autofill_assistant_strings.grd
+++ b/chrome/android/features/autofill_assistant/java/strings/android_chrome_autofill_assistant_strings.grd
@@ -165,6 +165,9 @@
       <message name="IDS_AUTOFILL_ASSISTANT_SHEET_CLOSED" desc="Text announced when the Autofill Assistant sheet is closed.">
         Google Assistant in Chrome closed.
       </message>
+      <message name="IDS_AUTOFILL_ASSISTANT_SEND_FEEDBACK" desc="Option shown in the menu when clicking the Autofill Assistant profile icon. Clicking this option will open a feedback sharing dialog.">
+        Send feedback
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/chrome/android/java/res/layout/bottom_toolbar_browsing.xml b/chrome/android/java/res/layout/bottom_toolbar_browsing.xml
index 86c535d..78475bd 100644
--- a/chrome/android/java/res/layout/bottom_toolbar_browsing.xml
+++ b/chrome/android/java/res/layout/bottom_toolbar_browsing.xml
@@ -23,7 +23,7 @@
         <org.chromium.chrome.browser.toolbar.HomeButton
             android:id="@+id/home_button"
             app:tint="@color/standard_mode_tint"
-            style="@style/BottomToolbarButton"
+            style="@style/SplitToolbarButton"
             android:contentDescription="@string/accessibility_toolbar_btn_home" />
 
         <TextView
@@ -43,7 +43,7 @@
             android:id="@+id/share_button"
             android:src="@drawable/ic_share_white_24dp"
             app:tint="@color/standard_mode_tint"
-            style="@style/BottomToolbarButton"
+            style="@style/SplitToolbarButton"
             android:contentDescription="@string/share" />
 
         <TextView
@@ -93,7 +93,7 @@
 
         <org.chromium.chrome.browser.toolbar.TabSwitcherButtonView
             android:id="@+id/tab_switcher_button"
-            style="@style/BottomToolbarButton"
+            style="@style/SplitToolbarButton"
             android:contentDescription="@string/accessibility_toolbar_btn_tabswitcher_toggle_default" />
 
         <TextView
@@ -110,7 +110,7 @@
         style="@style/BottomToolbarButtonWrapper" >
 
         <include layout="@layout/bottom_toolbar_menu_button"
-            style="@style/BottomToolbarButton" />
+            style="@style/SplitToolbarButton" />
 
         <TextView
             android:id="@+id/menu_button_label"
diff --git a/chrome/android/java/res/layout/bottom_toolbar_tab_switcher.xml b/chrome/android/java/res/layout/bottom_toolbar_tab_switcher.xml
index fd86594..a70018b 100644
--- a/chrome/android/java/res/layout/bottom_toolbar_tab_switcher.xml
+++ b/chrome/android/java/res/layout/bottom_toolbar_tab_switcher.xml
@@ -28,7 +28,7 @@
 
             <org.chromium.chrome.browser.toolbar.bottom.CloseAllTabsButton
                 android:id="@+id/close_all_tabs_button"
-                style="@style/BottomToolbarButtonWrapper"
+                style="@style/SplitToolbarButton"
                 android:src="@drawable/ic_close_all_tabs"
                 android:contentDescription="@string/accessibility_toolbar_btn_close_all_tabs"
                 app:tint="@color/standard_mode_tint" />
@@ -69,7 +69,7 @@
                 style="@style/BottomToolbarButtonWrapper" >
 
                 <include layout="@layout/bottom_toolbar_menu_button"
-                    style="@style/BottomToolbarButton" />
+                    style="@style/SplitToolbarButton" />
 
                 <TextView
                     android:id="@+id/menu_button_label"
diff --git a/chrome/android/java/res/values-v17/styles.xml b/chrome/android/java/res/values-v17/styles.xml
index 16aa41a..e3ed050 100644
--- a/chrome/android/java/res/values-v17/styles.xml
+++ b/chrome/android/java/res/values-v17/styles.xml
@@ -637,10 +637,13 @@
         <item name="android:layout_width">43dp</item>
         <item name="android:paddingEnd">3.5dp</item>
     </style>
-    <style name="BottomToolbarButton">
+    <style name="BottomToolbarButton" parent="ToolbarButton">
+        <item name="android:layout_height">48dp</item>
+        <item name="android:layout_gravity">center</item>
+    </style>
+    <style name="SplitToolbarButton" parent="BottomToolbarButton">
         <item name="android:layout_height">24dp</item>
         <item name="android:layout_width">24dp</item>
-        <item name="android:layout_gravity">center</item>
         <item name="android:background">@android:color/transparent</item>
         <item name="android:clickable">false</item>
     </style>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivitySessionTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivitySessionTracker.java
index 36aa3d6..50c9ed8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivitySessionTracker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivitySessionTracker.java
@@ -33,7 +33,6 @@
 import org.chromium.chrome.browser.share.ShareHelper;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.util.FeatureUtilities;
-import org.chromium.ui.base.ResourceBundle;
 
 import java.util.Locale;
 
@@ -261,21 +260,11 @@
         String systemLanguage =
                 LocaleUtils.toLanguage(LocaleUtils.toLanguageTag(Locale.getDefault()));
         boolean isWrongLanguage = !systemLanguage.equals(uiLanguage)
-                && isLanguageSupported(
-                        systemLanguage, ResourceBundle.getAvailableCompressedPakLocales());
+                && LocaleUtils.isLanguageSupported(systemLanguage);
         RecordHistogram.recordBooleanHistogram(
                 "Android.Language.WrongLanguageAfterResume", isWrongLanguage);
     }
 
-    private static boolean isLanguageSupported(String language, String[] compressedLocales) {
-        for (String languageTag : compressedLocales) {
-            if (LocaleUtils.toLanguage(languageTag).equals(language)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     /**
      * @return The PowerBroadcastReceiver for the browser process.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java
index a087a28..222725e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java
@@ -52,7 +52,6 @@
 import org.chromium.chrome.browser.vr.VrModuleProvider;
 import org.chromium.components.embedder_support.application.FontPreloadingWorkaround;
 import org.chromium.components.module_installer.ModuleInstaller;
-import org.chromium.ui.base.ResourceBundle;
 
 /**
  * Basic application functionality that should be shared among all browser applications that use
@@ -161,8 +160,6 @@
         }
         AsyncTask.takeOverAndroidThreadPool();
         JNIUtils.setClassLoader(getClassLoader());
-        ResourceBundle.setAvailablePakLocales(
-                LocaleConfig.COMPRESSED_LOCALES, LocaleConfig.UNCOMPRESSED_LOCALES);
         sFirstTraceEvent.end();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBaseAppCompatActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeBaseAppCompatActivity.java
index d9b28c33..b5838f1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBaseAppCompatActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeBaseAppCompatActivity.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser;
 
 import android.content.Context;
+import android.content.SharedPreferences;
 import android.content.res.Configuration;
 import android.os.Bundle;
 import android.support.annotation.CallSuper;
@@ -12,6 +13,7 @@
 import android.support.annotation.StyleRes;
 import android.support.v7.app.AppCompatActivity;
 
+import org.chromium.base.ContextUtils;
 import org.chromium.chrome.browser.night_mode.GlobalNightModeStateProviderHolder;
 import org.chromium.chrome.browser.night_mode.NightModeStateProvider;
 import org.chromium.chrome.browser.night_mode.NightModeUtils;
@@ -105,4 +107,12 @@
     public void onNightModeStateChanged() {
         if (!isFinishing()) recreate();
     }
+
+    /**
+     * Required to make preference fragments use InMemorySharedPreferences in tests.
+     */
+    @Override
+    public SharedPreferences getSharedPreferences(String name, int mode) {
+        return ContextUtils.getApplicationContext().getSharedPreferences(name, mode);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
index 3c99c11..952e38c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
@@ -133,7 +133,9 @@
 
     private boolean mIsInVr;
 
-    protected ContentCaptureConsumer mContentCaptureConsumer;
+    // Indicates if ContentCaptureConsumer should be created, we only try to create it once.
+    private boolean mShouldCreateContentCaptureConsumer = true;
+    private ContentCaptureConsumer mContentCaptureConsumer;
 
     /**
      * This view is created on demand to display debugging information.
@@ -1079,12 +1081,25 @@
 
         if (mTabVisible != null) initializeTab(mTabVisible);
 
+        if (mShouldCreateContentCaptureConsumer) {
+            mContentCaptureConsumer = createContentCaptureConsumer();
+            mShouldCreateContentCaptureConsumer = false;
+        }
         if (mContentCaptureConsumer != null) {
             mContentCaptureConsumer.onWebContentsChanged(getWebContents());
         }
     }
 
     /**
+     * This method is used by subclass to provide ContentCaptureConsumer.
+     *
+     * @return the ContentCaptureConsumer or null if it is not available.
+     */
+    protected ContentCaptureConsumer createContentCaptureConsumer() {
+        return null;
+    }
+
+    /**
      * Sets the correct size for {@link View} on {@code tab} and sets the correct rendering
      * parameters on {@link WebContents} on {@code tab}.
      * @param tab The {@link Tab} to initialize.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java
index 00622b1..c2ebcff 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java
@@ -25,7 +25,6 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.task.AsyncTask;
 import org.chromium.base.task.PostTask;
-import org.chromium.base.task.TaskPriority;
 import org.chromium.base.task.TaskTraits;
 import org.chromium.chrome.browser.ChromeApplication;
 import org.chromium.chrome.browser.ChromeFeatureList;
@@ -218,16 +217,14 @@
         ModuleMetrics.registerLifecycleState(ModuleMetrics.LifecycleState.NOT_LOADED);
 
         mIsModuleLoading = true;
-        new LoadClassTask().executeWithTaskTraits(
-                new TaskTraits().taskPriority(TaskPriority.USER_VISIBLE).mayBlock(true));
+        new LoadClassTask().executeWithTaskTraits(TaskTraits.USER_VISIBLE_MAY_BLOCK);
     }
 
     public void createClassLoader() {
         if (mClassLoader != null) return;
 
         mIsClassLoaderCreating = true;
-        new ClassLoaderTask().executeWithTaskTraits(
-                new TaskTraits().taskPriority(TaskPriority.USER_VISIBLE).mayBlock(true));
+        new ClassLoaderTask().executeWithTaskTraits(TaskTraits.USER_VISIBLE_MAY_BLOCK);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadMediaParserBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadMediaParserBridge.java
index b17e36d..ff24469 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadMediaParserBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadMediaParserBridge.java
@@ -28,8 +28,8 @@
      * Destroys the native object of DownloadMediaParser. This will result in the utility process
      * being destroyed.
      */
-    public void destory() {
-        nativeDestory(mNativeDownloadMediaParserBridge);
+    public void destroy() {
+        nativeDestroy(mNativeDownloadMediaParserBridge);
         mNativeDownloadMediaParserBridge = 0;
     }
 
@@ -44,6 +44,6 @@
 
     private native long nativeInit(
             String mimeType, String filePath, Callback<DownloadMediaData> callback);
-    private native void nativeDestory(long nativeDownloadMediaParserBridge);
+    private native void nativeDestroy(long nativeDownloadMediaParserBridge);
     private native void nativeStart(long nativeDownloadMediaParserBridge);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java
index 7a53809..5664f98 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java
@@ -527,7 +527,7 @@
     }
 
     /** Returns the Omaha SharedPreferences. */
-    static SharedPreferences getSharedPreferences() {
+    public static SharedPreferences getSharedPreferences() {
         return ContextUtils.getApplicationContext().getSharedPreferences(
                 PREF_PACKAGE, Context.MODE_PRIVATE);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateConfigs.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateConfigs.java
index 1c769c1..ab88396 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateConfigs.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateConfigs.java
@@ -56,6 +56,8 @@
 
     private static final String UPDATE_FLOW_PARAM_NAME = "flow";
 
+    private static final String UPDATE_ATTRIBUTION_WINDOW_PARAM_NAME =
+            "update_attribution_window_days";
     private static final String UPDATE_NOTIFICATION_INTERVAL_PARAM_NAME =
             "update_notification_interval_days";
     private static final String UPDATE_NOTIFICATION_STATE_PARAM_NAME = "update_notification_state";
@@ -63,6 +65,7 @@
             "update_notification_experimental_context";
 
     private static final long DEFAULT_UPDATE_NOTIFICATION_INTERVAL = 21 * DateUtils.DAY_IN_MILLIS;
+    private static final long DEFAULT_UPDATE_ATTRIBUTION_WINDOW_MS = 2 * DateUtils.DAY_IN_MILLIS;
 
     /** Possible update flow configurations. */
     @IntDef({UpdateFlowConfiguration.NEVER_SHOW, UpdateFlowConfiguration.INTENT_ONLY,
@@ -200,6 +203,20 @@
     }
 
     /**
+     * @return How long to wait before attributing an update success or failure to the Chrome update
+     * mechanism.
+     */
+    public static long getUpdateAttributionWindowMs() {
+        String configuration = ChromeFeatureList.getFieldTrialParamByFeature(
+                ChromeFeatureList.INLINE_UPDATE_FLOW, UPDATE_ATTRIBUTION_WINDOW_PARAM_NAME);
+        try {
+            return Long.parseLong(configuration) * DateUtils.DAY_IN_MILLIS;
+        } catch (NumberFormatException e) {
+            return DEFAULT_UPDATE_ATTRIBUTION_WINDOW_MS;
+        }
+    }
+
+    /**
      * @return A time interval for scheduling update notification. Unit: mills.
      */
     public static long getUpdateNotificationInterval() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java
index 20dc63b..c2a80e6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java
@@ -6,9 +6,7 @@
 
 import android.app.Activity;
 import android.content.ActivityNotFoundException;
-import android.content.Intent;
 import android.content.res.Resources;
-import android.net.Uri;
 import android.support.annotation.ColorRes;
 import android.support.annotation.DrawableRes;
 import android.support.annotation.NonNull;
@@ -199,9 +197,8 @@
                 if (TextUtils.isEmpty(mStatus.updateUrl)) return;
 
                 try {
-                    Intent launchIntent =
-                            new Intent(Intent.ACTION_VIEW, Uri.parse(mStatus.updateUrl));
-                    activity.startActivity(launchIntent);
+                    UpdateStatusProvider.getInstance().startIntentUpdate(
+                            activity, UpdateInteractionSource.FROM_MENU, false /* newTask */);
                     recordItemClickedHistogram(ITEM_CLICKED_INTENT_LAUNCHED);
                     PrefServiceBridge.getInstance().setClickedUpdateMenuItem(true);
                 } catch (ActivityNotFoundException e) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateNotificationController.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateNotificationController.java
index b69298f..4876c9b0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateNotificationController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateNotificationController.java
@@ -15,9 +15,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
-import android.net.Uri;
 import android.support.annotation.Nullable;
-import android.text.TextUtils;
 
 import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
@@ -36,6 +34,7 @@
 import org.chromium.chrome.browser.notifications.NotificationUmaTracker;
 import org.chromium.chrome.browser.notifications.PendingIntentProvider;
 import org.chromium.chrome.browser.notifications.channels.ChannelDefinitions;
+import org.chromium.chrome.browser.omaha.UpdateStatusProvider.UpdateInteractionSource;
 import org.chromium.chrome.browser.omaha.UpdateStatusProvider.UpdateState;
 import org.chromium.chrome.browser.omaha.UpdateStatusProvider.UpdateStatus;
 
@@ -140,10 +139,6 @@
         Context context = ContextUtils.getApplicationContext();
         Intent clickIntent = new Intent(context, UpdateNotificationReceiver.class)
                                      .putExtra(UPDATE_NOTIFICATION_STATE_EXTRA, status.updateState);
-
-        if (status.updateState == UPDATE_AVAILABLE && !TextUtils.isEmpty(status.updateUrl)) {
-            clickIntent.setData(Uri.parse(status.updateUrl));
-        }
         PendingIntentProvider contentIntent = PendingIntentProvider.getBroadcast(
                 context, 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
         return contentIntent;
@@ -183,10 +178,15 @@
                     IntentHandler.startActivityForTrustedIntent(launchInlineUpdateIntent);
                     break;
                 case UPDATE_AVAILABLE:
-                    if (intent.getData() == null) return;
-                    Intent launchPlayStoreIntent = new Intent(Intent.ACTION_VIEW, intent.getData())
-                                                           .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                    context.startActivity(launchPlayStoreIntent);
+                    Callback<UpdateStatus> intentLauncher = new Callback<UpdateStatus>() {
+                        @Override
+                        public void onResult(UpdateStatus result) {
+                            UpdateStatusProvider.getInstance().startIntentUpdate(context,
+                                    UpdateInteractionSource.FROM_NOTIFICATION, true /* newTask */);
+                            UpdateStatusProvider.getInstance().removeObserver(this);
+                        }
+                    };
+                    UpdateStatusProvider.getInstance().addObserver(intentLauncher);
                     break;
                 default:
                     break;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateStatusProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateStatusProvider.java
index 8d7c569..f10f1fa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateStatusProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateStatusProvider.java
@@ -6,8 +6,11 @@
 
 import android.annotation.TargetApi;
 import android.app.Activity;
+import android.content.ActivityNotFoundException;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.net.Uri;
 import android.os.Build;
 import android.os.Environment;
 import android.os.StatFs;
@@ -34,6 +37,8 @@
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.omaha.inline.InlineUpdateController;
 import org.chromium.chrome.browser.omaha.inline.InlineUpdateControllerFactory;
+import org.chromium.chrome.browser.omaha.metrics.UpdateSuccessMetrics;
+import org.chromium.chrome.browser.omaha.metrics.UpdateSuccessMetrics.UpdateType;
 import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
 import org.chromium.chrome.browser.util.ConversionUtils;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
@@ -121,7 +126,7 @@
          */
         private boolean mIsInlineSimulated;
 
-        UpdateStatus() {}
+        public UpdateStatus() {}
 
         UpdateStatus(UpdateStatus other) {
             updateState = other.updateState;
@@ -137,6 +142,7 @@
 
     private final InlineUpdateController mInlineController;
     private final UpdateQuery mOmahaQuery;
+    private final UpdateSuccessMetrics mMetrics;
     private @Nullable UpdateStatus mStatus;
 
     /** Whether or not we've recorded the initial update status yet. */
@@ -211,6 +217,7 @@
         if (mStatus == null || mStatus.updateState != UpdateState.INLINE_UPDATE_AVAILABLE) return;
         RecordHistogram.recordEnumeratedHistogram(
                 "GoogleUpdate.Inline.UI.Start.Source", source, UpdateInteractionSource.NUM_ENTRIES);
+        mMetrics.startUpdate(UpdateType.INLINE, source);
         mInlineController.startUpdate(activity);
     }
 
@@ -222,6 +229,7 @@
         if (mStatus == null || mStatus.updateState != UpdateState.INLINE_UPDATE_AVAILABLE) return;
         RecordHistogram.recordEnumeratedHistogram(
                 "GoogleUpdate.Inline.UI.Retry.Source", source, UpdateInteractionSource.NUM_ENTRIES);
+        mMetrics.startUpdate(UpdateType.INLINE, source);
         mInlineController.startUpdate(activity);
     }
 
@@ -233,6 +241,31 @@
         mInlineController.completeUpdate();
     }
 
+    /**
+     * Starts the intent update process, if possible
+     * @param context An {@link Context} that will be used to fire off the update intent.
+     * @param source  The source of the action (the UI that caused it).
+     * @param newTask Whether or not to make the intent a new task.
+     * @return        Whether or not the update intent was sent and had a valid handler.
+     */
+    public boolean startIntentUpdate(
+            Context context, @UpdateInteractionSource int source, boolean newTask) {
+        if (mStatus == null || mStatus.updateState != UpdateState.UPDATE_AVAILABLE) return false;
+        if (TextUtils.isEmpty(mStatus.updateUrl)) return false;
+
+        try {
+            mMetrics.startUpdate(UpdateType.INTENT, source);
+
+            Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(mStatus.updateUrl));
+            if (newTask) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            context.startActivity(intent);
+        } catch (ActivityNotFoundException e) {
+            return false;
+        }
+
+        return true;
+    }
+
     // ApplicationStateListener implementation.
     @Override
     public void onActivityStateChange(Activity changedActivity, @ActivityState int newState) {
@@ -252,6 +285,7 @@
     private UpdateStatusProvider() {
         mInlineController = InlineUpdateControllerFactory.create(this::resolveStatus);
         mOmahaQuery = new UpdateQuery(this::resolveStatus);
+        mMetrics = new UpdateSuccessMetrics();
 
         // Note that as a singleton this class never unregisters.
         ApplicationStatus.registerStateListenerForAllActivities(this);
@@ -292,6 +326,7 @@
         if (!mRecordedInitialStatus) {
             RecordHistogram.recordEnumeratedHistogram(
                     "GoogleUpdate.StartUp.State", mStatus.updateState, UpdateState.NUM_ENTRIES);
+            mMetrics.analyzeFirstStatus(mStatus);
             mRecordedInitialStatus = true;
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/metrics/HistogramUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/metrics/HistogramUtils.java
new file mode 100644
index 0000000..f8636cd
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/metrics/HistogramUtils.java
@@ -0,0 +1,85 @@
+// Copyright 2019 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.
+
+package org.chromium.chrome.browser.omaha.metrics;
+
+import org.chromium.base.metrics.CachedMetrics;
+import org.chromium.chrome.browser.omaha.metrics.UpdateProtos.Tracking;
+import org.chromium.chrome.browser.omaha.metrics.UpdateProtos.Tracking.Source;
+import org.chromium.chrome.browser.omaha.metrics.UpdateProtos.Tracking.Type;
+import org.chromium.chrome.browser.omaha.metrics.UpdateSuccessMetrics.AttributionType;
+
+/** A helper class for creating and saving histograms related to update success. */
+class HistogramUtils {
+    /**
+     * Records a histogram for whether or an update is running at the time an update starts.
+     * @param alreadyUpdating Whether or not an update is currently tracked as running.
+     */
+    public static void recordStartedUpdateHistogram(boolean alreadyUpdating) {
+        new CachedMetrics.BooleanHistogramSample("GoogleUpdate.StartingUpdateState")
+                .record(alreadyUpdating);
+    }
+
+    /**
+     * Records success or failure histograms for a current update.
+     * @param attribution The attribution (see {@link UpdateSuccessMetrics#AttributionType}).
+     * @param info        The {@link Tracking} instance that stores the update specifics.
+     * @param success     Whether or not the update was deemed successful.
+     */
+    public static void recordResultHistogram(
+            @AttributionType int attribution, Tracking info, boolean success) {
+        new CachedMetrics.BooleanHistogramSample(buildResultHistogram(attribution, null))
+                .record(success);
+        new CachedMetrics.BooleanHistogramSample(buildResultHistogram(attribution, info))
+                .record(success);
+    }
+
+    /**
+     * Used by {@link #recordResultHistogram(int, Tracking, boolean)}.  Exposed for testing.
+     * @param attribution The attribution (see {@link UpdateSuccessMetrics#AttributionType}).
+     * @param info        The {@link Tracking} instance that stores the update specifics.
+     * @return            The generated histogram name.
+     */
+    private static String buildResultHistogram(@AttributionType int attribution, Tracking info) {
+        String histogram = "GoogleUpdate.Result." + attributionToHistogram(attribution);
+        if (info == null) return histogram;
+
+        histogram += "." + typeToHistogram(info.getType());
+        histogram += "." + sourceToHistogram(info.getSource());
+        return histogram;
+    }
+
+    private static String typeToHistogram(Type type) {
+        if (type == Type.INTENT) {
+            return "Intent";
+        } else if (type == Type.INLINE) {
+            return "Inline";
+        } else {
+            return "Unknown";
+        }
+    }
+
+    private static String sourceToHistogram(Source source) {
+        if (source == Source.FROM_MENU) {
+            return "Menu";
+        } else if (source == Source.FROM_INFOBAR) {
+            return "Infobar";
+        } else if (source == Source.FROM_NOTIFICATION) {
+            return "Notification";
+        } else {
+            return "Unknown";
+        }
+    }
+
+    private static String attributionToHistogram(@AttributionType int attribution) {
+        switch (attribution) {
+            case AttributionType.SESSION:
+                return "Session";
+            case AttributionType.TIME_WINDOW:
+                return "TimeWindow";
+            default:
+                return "Unknown";
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/metrics/TrackingProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/metrics/TrackingProvider.java
new file mode 100644
index 0000000..4d053ea
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/metrics/TrackingProvider.java
@@ -0,0 +1,73 @@
+// Copyright 2019 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.
+
+package org.chromium.chrome.browser.omaha.metrics;
+
+import android.util.Base64;
+
+import org.chromium.base.Promise;
+import org.chromium.base.task.PostTask;
+import org.chromium.base.task.TaskRunner;
+import org.chromium.base.task.TaskTraits;
+import org.chromium.chrome.browser.omaha.OmahaBase;
+import org.chromium.chrome.browser.omaha.metrics.UpdateProtos.Tracking;
+import org.chromium.content_public.browser.UiThreadTaskTraits;
+
+/** A helper class to manage retrieving and storing a persisted instance of {@link Tracking}. */
+class TrackingProvider {
+    private static final String TRACKING_PERSISTENT_KEY = "UpdateProtos_Tracking";
+
+    private final TaskRunner mTaskRunner;
+
+    /**Builds a new instance of TrackingProvider. */
+    public TrackingProvider() {
+        mTaskRunner = PostTask.createSequencedTaskRunner(TaskTraits.BEST_EFFORT);
+    }
+
+    /** @return The persisted instance of {@link Tracking} or {@code null} if none is saved. */
+    public Promise<Tracking> get() {
+        final Promise<Tracking> promise = new Promise<>();
+
+        mTaskRunner.postTask(() -> {
+            Tracking state = null;
+
+            String serialized =
+                    OmahaBase.getSharedPreferences().getString(TRACKING_PERSISTENT_KEY, null);
+            if (serialized != null) {
+                try {
+                    state = Tracking.parseFrom(Base64.decode(serialized, Base64.DEFAULT));
+                } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+                }
+            }
+
+            final Tracking finalState = state;
+            PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> promise.fulfill(finalState));
+        });
+
+        return promise;
+    }
+
+    /** Clears any persisted instance of {@link Tracking}. */
+    public void clear() {
+        mTaskRunner.postTask(()
+                                     -> OmahaBase.getSharedPreferences()
+                                                .edit()
+                                                .remove(TRACKING_PERSISTENT_KEY)
+                                                .apply());
+    }
+
+    /**
+     * Persists {@code state}, overwriting any currently persisted instance of {@link Tracking}.
+     * @param state The new instance of {@link Tracking} to persist.
+     */
+    public void put(Tracking state) {
+        mTaskRunner.postTask(() -> {
+            String serialized = Base64.encodeToString(state.toByteArray(), Base64.DEFAULT);
+            OmahaBase.getSharedPreferences()
+                    .edit()
+                    .putString(TRACKING_PERSISTENT_KEY, serialized)
+                    .apply();
+        });
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/metrics/UpdateSuccessMetrics.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/metrics/UpdateSuccessMetrics.java
new file mode 100644
index 0000000..1bc4666
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/metrics/UpdateSuccessMetrics.java
@@ -0,0 +1,167 @@
+// Copyright 2019 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.
+
+package org.chromium.chrome.browser.omaha.metrics;
+
+import android.support.annotation.IntDef;
+import android.text.TextUtils;
+
+import org.chromium.base.VisibleForTesting;
+import org.chromium.chrome.browser.omaha.UpdateConfigs;
+import org.chromium.chrome.browser.omaha.UpdateStatusProvider.UpdateInteractionSource;
+import org.chromium.chrome.browser.omaha.UpdateStatusProvider.UpdateState;
+import org.chromium.chrome.browser.omaha.UpdateStatusProvider.UpdateStatus;
+import org.chromium.chrome.browser.omaha.metrics.UpdateProtos.Tracking;
+import org.chromium.chrome.browser.omaha.metrics.UpdateProtos.Tracking.Source;
+import org.chromium.chrome.browser.omaha.metrics.UpdateProtos.Tracking.Type;
+import org.chromium.chrome.browser.omaha.metrics.UpdateSuccessMetrics.UpdateType;
+import org.chromium.components.version_info.VersionConstants;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A helper class for tracking whether or not an update was successful.  This tracker works across
+ * restarts, as update success cannot be immediately determined.
+ */
+public class UpdateSuccessMetrics {
+    /** The type of update currently running.  Used for identifying which metric to tag. */
+    @IntDef({UpdateType.INTENT, UpdateType.INLINE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UpdateType {
+        /** The update is using the intent mechanism. */
+        int INTENT = 0;
+
+        /** The update is using the inline mechanism. */
+        int INLINE = 1;
+    }
+
+    /** How we are attributing the success. */
+    @IntDef({AttributionType.SESSION, AttributionType.TIME_WINDOW})
+    @Retention(RetentionPolicy.SOURCE)
+    @interface AttributionType {
+        /**
+         * Success is determined by looking at whether or not the version at the start of an update
+         * is different from the current version, assuming we are not still updating.  This happens
+         * the first time we detect that an update is not currently active (e.g. next session).
+         */
+        int SESSION = 0;
+
+        /**
+         * Success is determined by looking at whether or not the version at the start of an update
+         * is different from the current version, assuming we are not still updating, based on a
+         * time window.  This means that if an update is successful within a specific window, even
+         * if it does not happen immediately, it is flagged as success.  If the window expires
+         * without an update, it is considered a failure.
+         */
+        int TIME_WINDOW = 1;
+    }
+
+    private final TrackingProvider mProvider;
+
+    /** Creates an instance of UpdateSuccessMetrics. */
+    public UpdateSuccessMetrics() {
+        this(new TrackingProvider());
+    }
+
+    /**
+     * Creates an instance of UpdateSuccessMetrics.
+     * @param provider The {@link TrackingProvider} to use.  This is meant to facilitate testing.
+     */
+    @VisibleForTesting
+    UpdateSuccessMetrics(TrackingProvider provider) {
+        mProvider = provider;
+    }
+
+    /**
+     * To be called right before we are about to interact with the Play Store for an update.
+     * @param type   The type of update (see {@link #UpdateType}).
+     * @param source The source of the update (see {@link
+     *         UpdateStatusProvider#UpdateInteractionSource}).
+     */
+    public void startUpdate(@UpdateType int type, @UpdateInteractionSource int source) {
+        mProvider.get().then(state -> {
+            HistogramUtils.recordStartedUpdateHistogram(state != null);
+
+            // We're using System.currentTimeMillis() here to track time across restarts.
+            Tracking info = Tracking.newBuilder()
+                                    .setTimestampMs(System.currentTimeMillis())
+                                    .setVersion(VersionConstants.PRODUCT_VERSION)
+                                    .setType(getProtoType(type))
+                                    .setSource(getProtoSource(source))
+                                    .setRecordedSession(false)
+                                    .build();
+
+            mProvider.put(info);
+        });
+    }
+
+    /**
+     * To be called when Chrome first loads and determines the current update status.  This will
+     * determine update success or failure based on previously persisted state and calls to
+     * {@link #startUpdate(int, int)}.
+     * @param status The current {@link UpdateStatus}.
+     */
+    public void analyzeFirstStatus(UpdateStatus status) {
+        if (isUpdateInProgress(status.updateState)) return;
+
+        mProvider.get().then(state -> {
+            if (state == null) return;
+
+            // We're using System.currentTimeMillis() here to track time across restarts.
+            long timedelta = System.currentTimeMillis() - state.getTimestampMs();
+            boolean expired = timedelta > UpdateConfigs.getUpdateAttributionWindowMs();
+            boolean success =
+                    !TextUtils.equals(state.getVersion(), VersionConstants.PRODUCT_VERSION);
+
+            if (!state.getRecordedSession()) {
+                HistogramUtils.recordResultHistogram(AttributionType.SESSION, state, success);
+            }
+
+            if (success || expired) {
+                HistogramUtils.recordResultHistogram(AttributionType.TIME_WINDOW, state, success);
+            }
+
+            if (success || expired) {
+                mProvider.clear();
+            } else if (!state.getRecordedSession()) {
+                mProvider.put(state.toBuilder().setRecordedSession(true).build());
+            }
+        });
+    }
+
+    private static boolean isUpdateInProgress(@UpdateState int state) {
+        switch (state) {
+            case UpdateState.INLINE_UPDATE_DOWNLOADING:
+            case UpdateState.INLINE_UPDATE_READY: // Intentional fallthrough.
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    private static Type getProtoType(@UpdateType int type) {
+        switch (type) {
+            case UpdateType.INTENT:
+                return Type.INTENT;
+            case UpdateType.INLINE:
+                return Type.INLINE;
+            default:
+                return Type.UNKNOWN_TYPE;
+        }
+    }
+
+    private static Source getProtoSource(@UpdateInteractionSource int source) {
+        switch (source) {
+            case UpdateInteractionSource.FROM_MENU:
+                return Source.FROM_MENU;
+            case UpdateInteractionSource.FROM_INFOBAR:
+                return Source.FROM_INFOBAR;
+            case UpdateInteractionSource.FROM_NOTIFICATION:
+                return Source.FROM_NOTIFICATION;
+            default:
+                return Source.UNKNOWN_SOURCE;
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/metrics/update_success_tracking.proto b/chrome/android/java/src/org/chromium/chrome/browser/omaha/metrics/update_success_tracking.proto
new file mode 100644
index 0000000..2f35772
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/metrics/update_success_tracking.proto
@@ -0,0 +1,43 @@
+// Copyright 2019 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package omaha_metrics;
+
+option java_package = "org.chromium.chrome.browser.omaha.metrics";
+option java_outer_classname = "UpdateProtos";
+
+message Tracking {
+  // Timestamp since epoch of when the update started.
+  optional int64 timestamp_ms = 1;
+
+  // The Chrome version string at the time the update started.
+  optional string version = 2;
+
+  enum Type {
+    UNKNOWN_TYPE = -1;
+
+    INTENT = 0;
+    INLINE = 1;
+  }
+  // The type of update that was started.
+  optional Type type = 3 [default = UNKNOWN_TYPE];
+
+  enum Source {
+    UNKNOWN_SOURCE = -1;
+
+    FROM_MENU = 0;
+    FROM_INFOBAR = 1;
+    FROM_NOTIFICATION = 2;
+  }
+  // The UI surface that was interacted with to start the update.
+  optional Source source = 4 [default = UNKNOWN_SOURCE];
+
+  // Whether or not the update success or failure has been recorded for session
+  // attribution.
+  optional bool recorded_session = 5;
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentInstrument.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentInstrument.java
index a12077c..8067993 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentInstrument.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentInstrument.java
@@ -10,7 +10,6 @@
 import org.chromium.base.task.PostTask;
 import org.chromium.chrome.browser.widget.prefeditor.EditableOption;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
-import org.chromium.payments.mojom.PaymentCurrencyAmount;
 import org.chromium.payments.mojom.PaymentDetailsModifier;
 import org.chromium.payments.mojom.PaymentItem;
 import org.chromium.payments.mojom.PaymentMethodChangeResponse;
@@ -176,15 +175,6 @@
             InstrumentDetailsCallback callback);
 
     /**
-     * DO NOT USE. DEPRECATED. Instead of this method, please override updateWith(response),
-     * noUpdatedPaymentDetails(), and isChangingPaymentMethod().
-     * TODO(rouslan): Remove this method when all dependencies stop using it.
-     * https://crbug.com/884680
-     */
-    public void onPaymentDetailsUpdate(
-            @Nullable PaymentCurrencyAmount total, @Nullable String error) {}
-
-    /**
      * Update the payment information in response to payment method change event.
      *
      * @param response The merchant's response to the payment method change event.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
index 43d31e2..48c2518 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -57,6 +57,7 @@
 import org.chromium.components.payments.PaymentHandlerHost;
 import org.chromium.components.payments.PaymentHandlerHost.PaymentHandlerHostDelegate;
 import org.chromium.components.payments.PaymentValidator;
+import org.chromium.components.payments.UrlUtil;
 import org.chromium.components.url_formatter.UrlFormatter;
 import org.chromium.content_public.browser.RenderFrameHost;
 import org.chromium.content_public.browser.WebContents;
@@ -426,9 +427,7 @@
         mRequestPayerEmail = options != null && options.requestPayerEmail;
         mShippingType = options == null ? PaymentShippingType.SHIPPING : options.shippingType;
 
-        if (!OriginSecurityChecker.isSchemeCryptographic(mWebContents.getLastCommittedUrl())
-                && !OriginSecurityChecker.isOriginLocalhostOrFile(
-                        mWebContents.getLastCommittedUrl())) {
+        if (!UrlUtil.isOriginAllowedToUseWebPaymentApis(mWebContents.getLastCommittedUrl())) {
             Log.d(TAG, "Only localhost, file://, and cryptographic scheme origins allowed");
             // Don't show any UI. Resolve .canMakePayment() with "false". Reject .show() with
             // "NotSupportedError".
@@ -1894,8 +1893,7 @@
         // If |mWebContents| is destroyed, don't bother checking the localhost or file:// scheme
         // exemption. It doesn't really matter anyways.
         return mWebContents.isDestroyed()
-                || !OriginSecurityChecker.isOriginLocalhostOrFile(
-                        mWebContents.getLastCommittedUrl())
+                || !UrlUtil.isLocalDevelopmentUrl(mWebContents.getLastCommittedUrl())
                 || sIsLocalCanMakePaymentQueryQuotaEnforcedForTest;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java
index 3ba5c02..15c7065 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java
@@ -21,7 +21,6 @@
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.components.payments.OriginSecurityChecker;
 import org.chromium.components.payments.PaymentHandlerHost;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.content_public.browser.WebContents;
@@ -257,12 +256,7 @@
             public void onPageLoadFinished(Tab tab, String url) {
                 // Notify closing payment app window so as to abort payment if unsecure.
                 WebContents webContents = tab.getWebContents();
-                if (!OriginSecurityChecker.isOriginSecure(webContents.getLastCommittedUrl())
-                        || (!OriginSecurityChecker.isSchemeCryptographic(
-                                    webContents.getLastCommittedUrl())
-                                   && !OriginSecurityChecker.isOriginLocalhostOrFile(
-                                              webContents.getLastCommittedUrl()))
-                        || !SslValidityChecker.isSslCertificateValid(webContents)) {
+                if (!SslValidityChecker.isValidPageInPaymentHandlerWindow(webContents)) {
                     onClosingPaymentAppWindow(webContents);
                 }
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/SslValidityChecker.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/SslValidityChecker.java
index 6db6671..7d632377 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/SslValidityChecker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/SslValidityChecker.java
@@ -20,7 +20,18 @@
         return nativeIsSslCertificateValid(webContents);
     }
 
+    /**
+     * Returns true for web contents that is allowed in a payment handler window.
+     *
+     * @param webContents The web contents to check.
+     * @return Whether the web contents is a allowed in a payment handler window.
+     */
+    public static boolean isValidPageInPaymentHandlerWindow(WebContents webContents) {
+        return nativeIsValidPageInPaymentHandlerWindow(webContents);
+    }
+
     private SslValidityChecker() {}
 
     private static native boolean nativeIsSslCertificateValid(WebContents webContents);
+    private static native boolean nativeIsValidPageInPaymentHandlerWindow(WebContents webContents);
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBaseListPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBaseListPreference.java
index 864c539..f984723 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBaseListPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBaseListPreference.java
@@ -5,9 +5,7 @@
 package org.chromium.chrome.browser.preferences;
 
 import android.content.Context;
-import android.os.Build;
 import android.preference.ListPreference;
-import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.TextView;
@@ -46,20 +44,4 @@
         if (ManagedPreferencesUtils.onClickPreference(mManagedPrefDelegate, this)) return;
         super.onClick();
     }
-
-    @Override
-    public void setValue(String value) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
-            super.setValue(value);
-            return;
-        }
-
-        // Work around an Android bug where notifyChanged() wasn't called on pre-KitKat devices,
-        // causing the summary text not to be updated. http://crbug.com/446137
-        String original = getValue();
-        super.setValue(value);
-        if (!TextUtils.equals(original, value)) {
-            notifyChanged();
-        }
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/NotificationCategory.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/NotificationCategory.java
index 7305fe3..5248e18 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/NotificationCategory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/NotificationCategory.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.preferences.website;
 
 import android.content.Context;
-import android.os.Build;
 import android.support.v4.app.NotificationManagerCompat;
 
 import org.chromium.chrome.browser.ChromeFeatureList;
@@ -24,9 +23,7 @@
 
     @Override
     protected boolean enabledForChrome(Context context) {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT
-                || !ChromeFeatureList.isEnabled(
-                           ChromeFeatureList.APP_NOTIFICATION_STATUS_MESSAGING)) {
+        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.APP_NOTIFICATION_STATUS_MESSAGING)) {
             return super.enabledForChrome(context);
         }
         NotificationManagerCompat manager = NotificationManagerCompat.from(context);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePermissionsFetcher.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePermissionsFetcher.java
index 6c751d5..c3feaea 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePermissionsFetcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePermissionsFetcher.java
@@ -47,22 +47,6 @@
             return new OriginAndEmbedder(origin, embedder);
         }
 
-        private static boolean isEqual(Object o1, Object o2) {
-            // Returns true iff o1 == o2, handling nulls.
-            return (o1 == o2) || (o1 != null && o1.equals(o2));
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            // Prior to KitKat, android.util.Pair would crash with a NullPointerException in this
-            // method. This override specialises the post-Kitkat implementation to this class, and
-            // correctly handles nulls.
-            if (!(o instanceof OriginAndEmbedder)) return false;
-
-            OriginAndEmbedder p = (OriginAndEmbedder) o;
-            return isEqual(p.first, first) && isEqual(p.second, second);
-        }
-
         @Override
         public int hashCode() {
             // This is the calculation used by Arrays#hashCode().
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfAndroidBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfAndroidBridge.java
index 7d59ce8..d19b308 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfAndroidBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfAndroidBridge.java
@@ -107,6 +107,15 @@
     public static void dismissEntry(Profile profile, String guid) {
         SendTabToSelfAndroidBridgeJni.get().dismissEntry(profile, guid);
     }
+    /**
+     * Mark the entry associated with the GUID as opened.
+     *
+     * @param profile Profile of the user to mark entry as opened.
+     * @param guid The GUID of the entry to mark as opened.
+     */
+    public static void markEntryOpened(Profile profile, String guid) {
+        SendTabToSelfAndroidBridgeJni.get().markEntryOpened(profile, guid);
+    }
 
     /**
      * Return whether the feature is available for the current user Profile and
@@ -167,6 +176,8 @@
 
         void dismissEntry(Profile profile, String guid);
 
+        void markEntryOpened(Profile profile, String guid);
+
         SendTabToSelfEntry getEntryByGUID(Profile profile, String guid);
 
         boolean isFeatureAvailable(WebContents webContents);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
index a433c6f..093372d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
@@ -29,7 +29,6 @@
 import org.chromium.base.task.BackgroundOnlyAsyncTask;
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.SequencedTaskRunner;
-import org.chromium.base.task.TaskPriority;
 import org.chromium.base.task.TaskRunner;
 import org.chromium.base.task.TaskTraits;
 import org.chromium.chrome.browser.UrlConstants;
@@ -223,8 +222,7 @@
         mObservers = new ObserverList<>();
         mObservers.addObserver(observer);
         mPreferences = ContextUtils.getAppSharedPreferences();
-        TaskTraits taskTraits =
-                new TaskTraits().mayBlock(true).taskPriority(TaskPriority.USER_BLOCKING);
+        TaskTraits taskTraits = TaskTraits.USER_BLOCKING_MAY_BLOCK;
         if (FeatureUtilities.isTabPersistentStoreTaskRunnerEnabled()) {
             mSequencedTaskRunner = PostTask.createSequencedTaskRunner(taskTraits);
         } else {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/RestoreHistogramTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/RestoreHistogramTest.java
index 66b87c1..0a8ef87 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/RestoreHistogramTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/RestoreHistogramTest.java
@@ -12,7 +12,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.ContextUtils;
 import org.chromium.base.PathUtils;
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.library_loader.LibraryProcessType;
@@ -41,10 +40,6 @@
         PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX);
     }
 
-    private void clearPrefs() {
-        ContextUtils.getAppSharedPreferences().edit().clear().apply();
-    }
-
     /**
      * Test that the fundamental method for writing the histogram
      * {@link ChromeBackupAgent#recordRestoreHistogram()} works correctly
@@ -70,7 +65,6 @@
                         ChromeBackupAgent.RestoreStatus.RESTORE_STATUS_RECORDED);
 
         // Check behavior with no preference set
-        clearPrefs();
         ChromeBackupAgent.recordRestoreHistogram();
         Assert.assertEquals(1, noRestoreDelta.getDelta());
         Assert.assertEquals(0, restoreCompletedDelta.getDelta());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/DataSaverAppMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/DataSaverAppMenuTest.java
index 60f757d..87bf500 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/DataSaverAppMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/DataSaverAppMenuTest.java
@@ -13,7 +13,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.ContextUtils;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.RetryOnFailure;
@@ -109,7 +108,6 @@
     @Feature({"Browser", "Main"})
     public void testMenuDataSaver() throws Throwable {
         mActivityTestRule.runOnUiThread((Runnable) () -> {
-            ContextUtils.getAppSharedPreferences().edit().clear().apply();
             // Data Saver hasn't been turned on, the footer shouldn't show.
             Assert.assertEquals(0, mAppMenuHandler.getDelegate().getFooterResourceId());
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistencePolicyTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistencePolicyTest.java
index 06bbdbf..59d3792 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistencePolicyTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistencePolicyTest.java
@@ -62,7 +62,7 @@
     private TestTabModelDirectory mMockDirectory;
     private AdvancedMockContext mAppContext;
     private SequencedTaskRunner mSequencedTaskRunner =
-            PostTask.createSequencedTaskRunner(new TaskTraits());
+            PostTask.createSequencedTaskRunner(TaskTraits.USER_VISIBLE);
     @Rule
     public UiThreadTestRule mRule = new UiThreadTestRule();
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/datareduction/DataReductionSavingsMilestonePromoTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/datareduction/DataReductionSavingsMilestonePromoTest.java
index 55657f8..832b0c0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/datareduction/DataReductionSavingsMilestonePromoTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/datareduction/DataReductionSavingsMilestonePromoTest.java
@@ -12,7 +12,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.ContextUtils;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -38,7 +37,6 @@
 
     @Before
     public void setUp() throws InterruptedException {
-        ContextUtils.getAppSharedPreferences().edit().clear().apply();
         mActivityTestRule.startMainActivityOnBlankPage();
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/instantapps/InstantAppsHandlerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/instantapps/InstantAppsHandlerTest.java
index 994b400..56dc84c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/instantapps/InstantAppsHandlerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/instantapps/InstantAppsHandlerTest.java
@@ -15,7 +15,6 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 
-import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
@@ -68,11 +67,6 @@
         editor.apply();
     }
 
-    @After
-    public void tearDown() throws Exception {
-        ContextUtils.getAppSharedPreferences().edit().clear().apply();
-    }
-
     @Test
     @SmallTest
     public void testInstantAppsDisabled_incognito() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerHomepageIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerHomepageIntegrationTest.java
index 913716e..66cc037 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerHomepageIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerHomepageIntegrationTest.java
@@ -6,7 +6,6 @@
 
 import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
-import android.content.SharedPreferences;
 import android.net.Uri;
 import android.os.Build;
 import android.support.test.InstrumentationRegistry;
@@ -22,7 +21,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.ContextUtils;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
@@ -63,13 +61,6 @@
 
     @Before
     public void setUp() throws InterruptedException {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            // TODO(newt): Remove this once SharedPreferences is cleared automatically at the
-            // beginning of every test. http://crbug.com/441859
-            SharedPreferences sp = ContextUtils.getAppSharedPreferences();
-            sp.edit().clear().apply();
-        });
-
         mActivityTestRule.startMainActivityFromLauncher();
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/autofill_assistant/AutofillAssistantPreferencesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/autofill_assistant/AutofillAssistantPreferencesTest.java
index c4325e2..57b0c1a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/autofill_assistant/AutofillAssistantPreferencesTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/autofill_assistant/AutofillAssistantPreferencesTest.java
@@ -13,7 +13,6 @@
 import android.support.test.filters.SmallTest;
 
 import org.junit.Assert;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TestRule;
@@ -49,11 +48,6 @@
     public IntentsTestRule<HistoryActivity> mHistoryActivityTestRule =
             new IntentsTestRule<>(HistoryActivity.class, false, false);
 
-    @Before
-    public void setUp() {
-        clearAutofillAssistantSwitch();
-    }
-
     /**
      * Set the |PREF_AUTOFILL_ASSISTANT_SWITCH| shared preference to the given |value|.
      * @param value The value to set the preference to.
@@ -76,16 +70,6 @@
     }
 
     /**
-     * Removes the |PREF_AUTOFILL_ASSISTANT_SWITCH| shared preference.
-     */
-    private void clearAutofillAssistantSwitch() {
-        ContextUtils.getAppSharedPreferences()
-                .edit()
-                .remove(AutofillAssistantPreferences.PREF_AUTOFILL_ASSISTANT_SWITCH)
-                .apply();
-    }
-
-    /**
      * Ensure that the on/off switch in "Autofill Assistant" settings works.
      */
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrDeviceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrDeviceTest.java
index 27f96b1..4602dd6 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrDeviceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrDeviceTest.java
@@ -25,7 +25,6 @@
 import org.chromium.base.test.params.ParameterSet;
 import org.chromium.base.test.params.ParameterizedRunner;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.ChromeSwitches;
@@ -138,7 +137,6 @@
      */
     @Test
     @MediumTest
-    @DisabledTest(message = "https://crbug.com/758296")
     @CommandLineFlags
             .Remove({"enable-webvr"})
             @CommandLineFlags.Add({"enable-features=WebXR"})
@@ -174,7 +172,6 @@
      */
     @Test
     @MediumTest
-    @DisabledTest(message = "https://crbug.com/758296")
     @CommandLineFlags
             .Remove({"enable-webvr"})
             @CommandLineFlags.Add({"enable-features=WebXR"})
@@ -208,7 +205,6 @@
      */
     @Test
     @MediumTest
-    @DisabledTest(message = "https://crbug.com/758296")
     @CommandLineFlags
             .Remove({"enable-webvr"})
             @CommandLineFlags.Add({"enable-features=WebXR"})
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/OfflineNotificationBackgroundTaskUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/OfflineNotificationBackgroundTaskUnitTest.java
index 623c742..f7eb910 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/OfflineNotificationBackgroundTaskUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/OfflineNotificationBackgroundTaskUnitTest.java
@@ -41,7 +41,6 @@
 import org.robolectric.shadows.multidex.ShadowMultiDex;
 
 import org.chromium.base.Callback;
-import org.chromium.base.ContextUtils;
 import org.chromium.base.library_loader.ProcessInitException;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.DeviceConditions;
@@ -157,7 +156,6 @@
         mCalendar.set(2017, 1, 1, 0, 0, 0);
 
         OfflineNotificationBackgroundTask.setCalendarForTesting(mCalendar);
-        clearPrefs();
     }
 
     @After
@@ -166,10 +164,6 @@
         verify(mPrefetchedPagesNotifier, never()).showNotification("");
     }
 
-    private void clearPrefs() {
-        ContextUtils.getAppSharedPreferences().edit().clear().apply();
-    }
-
     /**
      * Runs mOfflineNotificationBackgroundTask with the given params.
      * Asserts that reschedule was called exactly once and returns the reschedule value.
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omaha/metrics/HistogramUtilsTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omaha/metrics/HistogramUtilsTest.java
new file mode 100644
index 0000000..79fa625
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omaha/metrics/HistogramUtilsTest.java
@@ -0,0 +1,140 @@
+// Copyright 2019 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.
+
+package org.chromium.chrome.browser.omaha.metrics;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.metrics.CachedMetrics;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.omaha.metrics.UpdateProtos.Tracking;
+import org.chromium.chrome.browser.omaha.metrics.UpdateProtos.Tracking.Source;
+import org.chromium.chrome.browser.omaha.metrics.UpdateProtos.Tracking.Type;
+import org.chromium.chrome.browser.omaha.metrics.UpdateSuccessMetrics.AttributionType;
+
+/** Tests for HistogramUtils. */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+public class HistogramUtilsTest {
+    /** Tests {@link HistogramUtils#recordStartedUpdateHistogram(boolean)} */
+    @Test
+    public void testStartHistogram() {
+        HistogramUtils.recordStartedUpdateHistogram(false);
+        CachedMetrics.commitCachedMetrics();
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "GoogleUpdate.StartingUpdateState", 0));
+        ShadowRecordHistogram.reset();
+
+        HistogramUtils.recordStartedUpdateHistogram(true);
+        CachedMetrics.commitCachedMetrics();
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "GoogleUpdate.StartingUpdateState", 1));
+        ShadowRecordHistogram.reset();
+    }
+
+    /** Tests {@link HistogramUtils#recordResultHistogram(int, Tracking, boolean)}. */
+    @Test
+    public void testResultHistogram() {
+        validateHistogram(-3, Type.UNKNOWN_TYPE, Source.UNKNOWN_SOURCE,
+                "GoogleUpdate.Result.Unknown", "Unknown.Unknown");
+        validateHistogram(-3, Type.UNKNOWN_TYPE, Source.FROM_MENU, "GoogleUpdate.Result.Unknown",
+                "Unknown.Menu");
+        validateHistogram(-3, Type.UNKNOWN_TYPE, Source.FROM_INFOBAR, "GoogleUpdate.Result.Unknown",
+                "Unknown.Infobar");
+        validateHistogram(-3, Type.UNKNOWN_TYPE, Source.FROM_NOTIFICATION,
+                "GoogleUpdate.Result.Unknown", "Unknown.Notification");
+        validateHistogram(-3, Type.INTENT, Source.UNKNOWN_SOURCE, "GoogleUpdate.Result.Unknown",
+                "Intent.Unknown");
+        validateHistogram(
+                -3, Type.INTENT, Source.FROM_MENU, "GoogleUpdate.Result.Unknown", "Intent.Menu");
+        validateHistogram(-3, Type.INTENT, Source.FROM_INFOBAR, "GoogleUpdate.Result.Unknown",
+                "Intent.Infobar");
+        validateHistogram(-3, Type.INTENT, Source.FROM_NOTIFICATION, "GoogleUpdate.Result.Unknown",
+                "Intent.Notification");
+        validateHistogram(-3, Type.INLINE, Source.UNKNOWN_SOURCE, "GoogleUpdate.Result.Unknown",
+                "Inline.Unknown");
+        validateHistogram(
+                -3, Type.INLINE, Source.FROM_MENU, "GoogleUpdate.Result.Unknown", "Inline.Menu");
+        validateHistogram(-3, Type.INLINE, Source.FROM_INFOBAR, "GoogleUpdate.Result.Unknown",
+                "Inline.Infobar");
+        validateHistogram(-3, Type.INLINE, Source.FROM_NOTIFICATION, "GoogleUpdate.Result.Unknown",
+                "Inline.Notification");
+
+        validateHistogram(AttributionType.SESSION, Type.UNKNOWN_TYPE, Source.UNKNOWN_SOURCE,
+                "GoogleUpdate.Result.Session", "Unknown.Unknown");
+        validateHistogram(AttributionType.SESSION, Type.UNKNOWN_TYPE, Source.FROM_MENU,
+                "GoogleUpdate.Result.Session", "Unknown.Menu");
+        validateHistogram(AttributionType.SESSION, Type.UNKNOWN_TYPE, Source.FROM_INFOBAR,
+                "GoogleUpdate.Result.Session", "Unknown.Infobar");
+        validateHistogram(AttributionType.SESSION, Type.UNKNOWN_TYPE, Source.FROM_NOTIFICATION,
+                "GoogleUpdate.Result.Session", "Unknown.Notification");
+        validateHistogram(AttributionType.SESSION, Type.INTENT, Source.UNKNOWN_SOURCE,
+                "GoogleUpdate.Result.Session", "Intent.Unknown");
+        validateHistogram(AttributionType.SESSION, Type.INTENT, Source.FROM_MENU,
+                "GoogleUpdate.Result.Session", "Intent.Menu");
+        validateHistogram(AttributionType.SESSION, Type.INTENT, Source.FROM_INFOBAR,
+                "GoogleUpdate.Result.Session", "Intent.Infobar");
+        validateHistogram(AttributionType.SESSION, Type.INTENT, Source.FROM_NOTIFICATION,
+                "GoogleUpdate.Result.Session", "Intent.Notification");
+        validateHistogram(AttributionType.SESSION, Type.INLINE, Source.UNKNOWN_SOURCE,
+                "GoogleUpdate.Result.Session", "Inline.Unknown");
+        validateHistogram(AttributionType.SESSION, Type.INLINE, Source.FROM_MENU,
+                "GoogleUpdate.Result.Session", "Inline.Menu");
+        validateHistogram(AttributionType.SESSION, Type.INLINE, Source.FROM_INFOBAR,
+                "GoogleUpdate.Result.Session", "Inline.Infobar");
+        validateHistogram(AttributionType.SESSION, Type.INLINE, Source.FROM_NOTIFICATION,
+                "GoogleUpdate.Result.Session", "Inline.Notification");
+
+        validateHistogram(AttributionType.TIME_WINDOW, Type.UNKNOWN_TYPE, Source.UNKNOWN_SOURCE,
+                "GoogleUpdate.Result.TimeWindow", "Unknown.Unknown");
+        validateHistogram(AttributionType.TIME_WINDOW, Type.UNKNOWN_TYPE, Source.FROM_MENU,
+                "GoogleUpdate.Result.TimeWindow", "Unknown.Menu");
+        validateHistogram(AttributionType.TIME_WINDOW, Type.UNKNOWN_TYPE, Source.FROM_INFOBAR,
+                "GoogleUpdate.Result.TimeWindow", "Unknown.Infobar");
+        validateHistogram(AttributionType.TIME_WINDOW, Type.UNKNOWN_TYPE, Source.FROM_NOTIFICATION,
+                "GoogleUpdate.Result.TimeWindow", "Unknown.Notification");
+        validateHistogram(AttributionType.TIME_WINDOW, Type.INTENT, Source.UNKNOWN_SOURCE,
+                "GoogleUpdate.Result.TimeWindow", "Intent.Unknown");
+        validateHistogram(AttributionType.TIME_WINDOW, Type.INTENT, Source.FROM_MENU,
+                "GoogleUpdate.Result.TimeWindow", "Intent.Menu");
+        validateHistogram(AttributionType.TIME_WINDOW, Type.INTENT, Source.FROM_INFOBAR,
+                "GoogleUpdate.Result.TimeWindow", "Intent.Infobar");
+        validateHistogram(AttributionType.TIME_WINDOW, Type.INTENT, Source.FROM_NOTIFICATION,
+                "GoogleUpdate.Result.TimeWindow", "Intent.Notification");
+        validateHistogram(AttributionType.TIME_WINDOW, Type.INLINE, Source.UNKNOWN_SOURCE,
+                "GoogleUpdate.Result.TimeWindow", "Inline.Unknown");
+        validateHistogram(AttributionType.TIME_WINDOW, Type.INLINE, Source.FROM_MENU,
+                "GoogleUpdate.Result.TimeWindow", "Inline.Menu");
+        validateHistogram(AttributionType.TIME_WINDOW, Type.INLINE, Source.FROM_INFOBAR,
+                "GoogleUpdate.Result.TimeWindow", "Inline.Infobar");
+        validateHistogram(AttributionType.TIME_WINDOW, Type.INLINE, Source.FROM_NOTIFICATION,
+                "GoogleUpdate.Result.TimeWindow", "Inline.Notification");
+    }
+
+    private static void validateHistogram(@AttributionType int attributionType, Type type,
+            Source source, String base, String suffix) {
+        Tracking info = Tracking.newBuilder().setType(type).setSource(source).build();
+
+        HistogramUtils.recordResultHistogram(attributionType, info, false);
+        validateHistogram(base, suffix, false);
+        HistogramUtils.recordResultHistogram(attributionType, info, true);
+        validateHistogram(base, suffix, true);
+    }
+
+    private static void validateHistogram(String base, String suffix, boolean success) {
+        CachedMetrics.commitCachedMetrics();
+        int value = success ? 1 : 0;
+        Assert.assertEquals(1, RecordHistogram.getHistogramValueCountForTesting(base, value));
+        Assert.assertEquals(
+                1, RecordHistogram.getHistogramValueCountForTesting(base + "." + suffix, value));
+        ShadowRecordHistogram.reset();
+    }
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omaha/metrics/UpdateSuccessMetricsTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omaha/metrics/UpdateSuccessMetricsTest.java
new file mode 100644
index 0000000..6a4bf3c
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omaha/metrics/UpdateSuccessMetricsTest.java
@@ -0,0 +1,343 @@
+// Copyright 2019 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.
+
+package org.chromium.chrome.browser.omaha.metrics;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.argThat;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.Looper;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.Shadows;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.Promise;
+import org.chromium.base.metrics.CachedMetrics;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.omaha.UpdateStatusProvider.UpdateInteractionSource;
+import org.chromium.chrome.browser.omaha.UpdateStatusProvider.UpdateState;
+import org.chromium.chrome.browser.omaha.UpdateStatusProvider.UpdateStatus;
+import org.chromium.chrome.browser.omaha.metrics.UpdateProtos.Tracking;
+import org.chromium.chrome.browser.omaha.metrics.UpdateProtos.Tracking.Source;
+import org.chromium.chrome.browser.omaha.metrics.UpdateProtos.Tracking.Type;
+import org.chromium.chrome.browser.omaha.metrics.UpdateSuccessMetrics.UpdateType;
+import org.chromium.components.version_info.VersionConstants;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/** Tests the API surface of UpdateSuccessMetrics. */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+public class UpdateSuccessMetricsTest {
+    private static final int FAILED = 0;
+    private static final int SUCCESS = 1;
+
+    private static final int NOT_UPDATING = 0;
+    private static final int UPDATING = 1;
+
+    @Mock
+    private TrackingProvider mProvider;
+
+    private UpdateSuccessMetrics mMetrics;
+
+    @Rule
+    public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Before
+    public void setUp() {
+        Map<String, Boolean> featureList = new HashMap<>();
+        // The value we use does not matter.  ChromeFeatureList just needs to be initialized.
+        featureList.put(ChromeFeatureList.INLINE_UPDATE_FLOW, false);
+        ChromeFeatureList.setTestFeatures(featureList);
+
+        mMetrics = new UpdateSuccessMetrics(mProvider);
+    }
+
+    @After
+    public void tearDown() {
+        ShadowRecordHistogram.reset();
+    }
+
+    /** Tests that StartTracking properly persists the right tracking information. */
+    @Test
+    public void testStartTracking() {
+        when(mProvider.get()).thenReturn(Promise.fulfilled(null));
+
+        InOrder order = inOrder(mProvider);
+
+        mMetrics.startUpdate(UpdateType.INLINE, UpdateInteractionSource.FROM_MENU);
+        mMetrics.startUpdate(UpdateType.INLINE, UpdateInteractionSource.FROM_INFOBAR);
+        mMetrics.startUpdate(UpdateType.INLINE, UpdateInteractionSource.FROM_NOTIFICATION);
+        mMetrics.startUpdate(UpdateType.INTENT, UpdateInteractionSource.FROM_MENU);
+        mMetrics.startUpdate(UpdateType.INTENT, UpdateInteractionSource.FROM_INFOBAR);
+        mMetrics.startUpdate(UpdateType.INTENT, UpdateInteractionSource.FROM_NOTIFICATION);
+
+        Shadows.shadowOf(Looper.myLooper()).runToEndOfTasks();
+        order.verify(mProvider).put(argThat(new TrackingMatcher(Type.INLINE, Source.FROM_MENU)));
+        order.verify(mProvider).put(argThat(new TrackingMatcher(Type.INLINE, Source.FROM_INFOBAR)));
+        order.verify(mProvider).put(
+                argThat(new TrackingMatcher(Type.INLINE, Source.FROM_NOTIFICATION)));
+        order.verify(mProvider).put(argThat(new TrackingMatcher(Type.INTENT, Source.FROM_MENU)));
+        order.verify(mProvider).put(argThat(new TrackingMatcher(Type.INTENT, Source.FROM_INFOBAR)));
+        order.verify(mProvider).put(
+                argThat(new TrackingMatcher(Type.INTENT, Source.FROM_NOTIFICATION)));
+
+        Assert.assertEquals(6,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "GoogleUpdate.StartingUpdateState", NOT_UPDATING));
+    }
+
+    /**
+     * Tests that StartTracking properly persists the right tracking information even when already
+     * tracking an update.
+     */
+    @Test
+    public void testStartTrackingWhenAlreadyTracking() {
+        when(mProvider.get())
+                .thenReturn(Promise.fulfilled(buildProto(Type.INLINE, Source.FROM_MENU)));
+
+        InOrder order = inOrder(mProvider);
+
+        mMetrics.startUpdate(UpdateType.INLINE, UpdateInteractionSource.FROM_MENU);
+        mMetrics.startUpdate(UpdateType.INLINE, UpdateInteractionSource.FROM_INFOBAR);
+        mMetrics.startUpdate(UpdateType.INLINE, UpdateInteractionSource.FROM_NOTIFICATION);
+        mMetrics.startUpdate(UpdateType.INTENT, UpdateInteractionSource.FROM_MENU);
+        mMetrics.startUpdate(UpdateType.INTENT, UpdateInteractionSource.FROM_INFOBAR);
+        mMetrics.startUpdate(UpdateType.INTENT, UpdateInteractionSource.FROM_NOTIFICATION);
+
+        Shadows.shadowOf(Looper.myLooper()).runToEndOfTasks();
+        order.verify(mProvider).put(argThat(new TrackingMatcher(Type.INLINE, Source.FROM_MENU)));
+        order.verify(mProvider).put(argThat(new TrackingMatcher(Type.INLINE, Source.FROM_INFOBAR)));
+        order.verify(mProvider).put(
+                argThat(new TrackingMatcher(Type.INLINE, Source.FROM_NOTIFICATION)));
+        order.verify(mProvider).put(argThat(new TrackingMatcher(Type.INTENT, Source.FROM_MENU)));
+        order.verify(mProvider).put(argThat(new TrackingMatcher(Type.INTENT, Source.FROM_INFOBAR)));
+        order.verify(mProvider).put(
+                argThat(new TrackingMatcher(Type.INTENT, Source.FROM_NOTIFICATION)));
+
+        Assert.assertEquals(6,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "GoogleUpdate.StartingUpdateState", UPDATING));
+    }
+
+    /** Tests having no persisted state. */
+    @Test
+    public void testAnalyzeNoState() {
+        when(mProvider.get()).thenReturn(Promise.fulfilled(null));
+
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.NONE));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.UPDATE_AVAILABLE));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.UNSUPPORTED_OS_VERSION));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.INLINE_UPDATE_AVAILABLE));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.INLINE_UPDATE_DOWNLOADING));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.INLINE_UPDATE_READY));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.INLINE_UPDATE_FAILED));
+
+        Shadows.shadowOf(Looper.myLooper()).runToEndOfTasks();
+        verify(mProvider, never()).clear();
+        verify(mProvider, never()).put(any());
+    }
+
+    /** Tests still updating when called. */
+    @Test
+    public void testAnalyzeStillUpdating() {
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.INLINE_UPDATE_DOWNLOADING));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.INLINE_UPDATE_READY));
+
+        Shadows.shadowOf(Looper.myLooper()).runToEndOfTasks();
+        verify(mProvider, never()).get();
+        verify(mProvider, never()).clear();
+        verify(mProvider, never()).put(any());
+    }
+
+    /** Tests recording a session success. */
+    @Test
+    public void testRecordSessionSuccess() {
+        when(mProvider.get())
+                .thenReturn(Promise.fulfilled(buildProto(
+                        System.currentTimeMillis(), "---", Type.INLINE, Source.FROM_MENU, false)));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.NONE));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.UPDATE_AVAILABLE));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.UNSUPPORTED_OS_VERSION));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.INLINE_UPDATE_AVAILABLE));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.INLINE_UPDATE_FAILED));
+
+        Shadows.shadowOf(Looper.myLooper()).runToEndOfTasks();
+        verify(mProvider, times(5)).clear();
+        verify(mProvider, never()).put(any());
+        Assert.assertEquals(5,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "GoogleUpdate.Result.Session.Inline.Menu", SUCCESS));
+        Assert.assertEquals(5,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "GoogleUpdate.Result.TimeWindow.Inline.Menu", SUCCESS));
+    }
+
+    /** Tests recording a session failure. */
+    @Test
+    public void testRecordSessionFailure() {
+        when(mProvider.get())
+                .thenReturn(Promise.fulfilled(buildProto(System.currentTimeMillis(),
+                        VersionConstants.PRODUCT_VERSION, Type.INLINE, Source.FROM_MENU, false)));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.NONE));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.UPDATE_AVAILABLE));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.UNSUPPORTED_OS_VERSION));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.INLINE_UPDATE_AVAILABLE));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.INLINE_UPDATE_FAILED));
+
+        Shadows.shadowOf(Looper.myLooper()).runToEndOfTasks();
+        verify(mProvider, never()).clear();
+        verify(mProvider, times(5))
+                .put(argThat(new TrackingMatcher(
+                        VersionConstants.PRODUCT_VERSION, Type.INLINE, Source.FROM_MENU, true)));
+        Assert.assertEquals(5,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "GoogleUpdate.Result.Session.Inline.Menu", FAILED));
+        Assert.assertEquals(0,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "GoogleUpdate.Result.TimeWindow.Inline.Menu", FAILED));
+    }
+
+    /** Tests recording a time window success. */
+    @Test
+    public void testRecordTimeWindowSuccess() {
+        when(mProvider.get())
+                .thenReturn(Promise.fulfilled(
+                        buildProto(1, "---", Type.INLINE, Source.FROM_MENU, false)));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.NONE));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.UPDATE_AVAILABLE));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.UNSUPPORTED_OS_VERSION));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.INLINE_UPDATE_AVAILABLE));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.INLINE_UPDATE_FAILED));
+
+        Shadows.shadowOf(Looper.myLooper()).runToEndOfTasks();
+        verify(mProvider, times(5)).clear();
+        verify(mProvider, never()).put(any());
+        Assert.assertEquals(5,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "GoogleUpdate.Result.Session.Inline.Menu", SUCCESS));
+        Assert.assertEquals(5,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "GoogleUpdate.Result.TimeWindow.Inline.Menu", SUCCESS));
+    }
+
+    /** Tests recording a time window failure. */
+    @Test
+    public void testRecordTimeWindowFailure() {
+        when(mProvider.get())
+                .thenReturn(Promise.fulfilled(buildProto(1, VersionConstants.PRODUCT_VERSION,
+                        Type.INLINE, Source.FROM_MENU, false)));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.NONE));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.UPDATE_AVAILABLE));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.UNSUPPORTED_OS_VERSION));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.INLINE_UPDATE_AVAILABLE));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.INLINE_UPDATE_FAILED));
+
+        Shadows.shadowOf(Looper.myLooper()).runToEndOfTasks();
+        verify(mProvider, times(5)).clear();
+        verify(mProvider, never()).put(any());
+        Assert.assertEquals(5,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "GoogleUpdate.Result.Session.Inline.Menu", FAILED));
+        Assert.assertEquals(5,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "GoogleUpdate.Result.TimeWindow.Inline.Menu", FAILED));
+    }
+
+    /** Tests recording session failure only happens once. */
+    @Test
+    public void testNoDuplicateSessionFailures() {
+        when(mProvider.get())
+                .thenReturn(Promise.fulfilled(buildProto(
+                        1, VersionConstants.PRODUCT_VERSION, Type.INLINE, Source.FROM_MENU, true)));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.NONE));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.UPDATE_AVAILABLE));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.UNSUPPORTED_OS_VERSION));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.INLINE_UPDATE_AVAILABLE));
+        mMetrics.analyzeFirstStatus(buildStatus(UpdateState.INLINE_UPDATE_FAILED));
+
+        Shadows.shadowOf(Looper.myLooper()).runToEndOfTasks();
+        verify(mProvider, times(5)).clear();
+        verify(mProvider, never()).put(any());
+        CachedMetrics.commitCachedMetrics();
+        Assert.assertEquals(0,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "GoogleUpdate.Result.Session.Inline.Menu", FAILED));
+        Assert.assertEquals(5,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "GoogleUpdate.Result.TimeWindow.Inline.Menu", FAILED));
+    }
+
+    private static Tracking buildProto(Type type, Source source) {
+        return Tracking.newBuilder()
+                .setTimestampMs(System.currentTimeMillis())
+                .setVersion(VersionConstants.PRODUCT_VERSION)
+                .setType(type)
+                .setSource(source)
+                .setRecordedSession(false)
+                .build();
+    }
+
+    private static Tracking buildProto(
+            long timestamp, String version, Type type, Source source, boolean recordedSession) {
+        return Tracking.newBuilder()
+                .setTimestampMs(timestamp)
+                .setVersion(version)
+                .setType(type)
+                .setSource(source)
+                .setRecordedSession(recordedSession)
+                .build();
+    }
+
+    private static UpdateStatus buildStatus(@UpdateState int state) {
+        UpdateStatus status = new UpdateStatus();
+        status.updateState = state;
+        return status;
+    }
+
+    private static class TrackingMatcher implements ArgumentMatcher<Tracking> {
+        private final String mVersion;
+        private final boolean mRecordedSession;
+        private final Type mType;
+        private final Source mSource;
+
+        public TrackingMatcher(Type type, Source source) {
+            this(VersionConstants.PRODUCT_VERSION, type, source, false);
+        }
+
+        public TrackingMatcher(String version, Type type, Source source, boolean recordedSession) {
+            mVersion = version;
+            mType = type;
+            mSource = source;
+            mRecordedSession = recordedSession;
+        }
+
+        @Override
+        public boolean matches(Tracking argument) {
+            return mVersion.equals(argument.getVersion()) && mType.equals(argument.getType())
+                    && mSource.equals(argument.getSource())
+                    && mRecordedSession == argument.getRecordedSession();
+        }
+    }
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfAndroidBridgeTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfAndroidBridgeTest.java
index 490a789..9ef182e 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfAndroidBridgeTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfAndroidBridgeTest.java
@@ -154,6 +154,13 @@
 
     @Test
     @SmallTest
+    public void testMarkEntryOpened() {
+        SendTabToSelfAndroidBridge.markEntryOpened(mProfile, GUID);
+        verify(mNativeMock).markEntryOpened(eq(mProfile), eq(GUID));
+    }
+
+    @Test
+    @SmallTest
     public void testDeleteEntry() {
         SendTabToSelfAndroidBridge.deleteEntry(mProfile, GUID);
         verify(mNativeMock).deleteEntry(eq(mProfile), eq(GUID));
diff --git a/chrome/app/chrome_exe_main_mac.cc b/chrome/app/chrome_exe_main_mac.cc
index 4f6ca44..1039ed50 100644
--- a/chrome/app/chrome_exe_main_mac.cc
+++ b/chrome/app/chrome_exe_main_mac.cc
@@ -18,7 +18,6 @@
 
 #include <memory>
 
-#include "chrome/common/buildflags.h"
 #include "chrome/common/chrome_version.h"
 
 #if defined(HELPER_EXECUTABLE)
@@ -61,25 +60,13 @@
     }
   }
 
-#if BUILDFLAG(NEW_MAC_BUNDLE_STRUCTURE)
   // The helper lives within the versioned framework directory, so simply
   // go up to find the main dylib.
   const char rel_path[] = "../../../../" PRODUCT_FULLNAME_STRING " Framework";
 #else
-  const char rel_path[] =
-      "../../../" PRODUCT_FULLNAME_STRING
-      " Framework.framework/" PRODUCT_FULLNAME_STRING " Framework";
-#endif  // NEW_MAC_BUNDLE_STRUCTURE
-#else
-#if BUILDFLAG(NEW_MAC_BUNDLE_STRUCTURE)
   const char rel_path[] = "../Frameworks/" PRODUCT_FULLNAME_STRING
                           " Framework.framework/Versions/" CHROME_VERSION_STRING
                           "/" PRODUCT_FULLNAME_STRING " Framework";
-#else
-  const char rel_path[] =
-      "../Versions/" CHROME_VERSION_STRING "/" PRODUCT_FULLNAME_STRING
-      " Framework.framework/" PRODUCT_FULLNAME_STRING " Framework";
-#endif  // NEW_MAC_BUNDLE_STRUCTURE
 #endif  // defined(HELPER_EXECUTABLE)
 
   // Slice off the last part of the main executable path, and append the
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index 4f16780..489a759 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -569,7 +569,7 @@
     std::string process_type =
         command_line.GetSwitchValueASCII(switches::kProcessType);
     bool is_browser_process = process_type.empty();
-    gwp_asan::EnableForMalloc(is_canary_dev, is_browser_process);
+    gwp_asan::EnableForMalloc(is_canary_dev || is_browser_process);
   }
 #endif
 
diff --git a/chrome/app/chrome_main_mac.mm b/chrome/app/chrome_main_mac.mm
index 354916f..fc34e35 100644
--- a/chrome/app/chrome_main_mac.mm
+++ b/chrome/app/chrome_main_mac.mm
@@ -27,15 +27,10 @@
   NSBundle* base_bundle = chrome::OuterAppBundle();
   base::mac::SetBaseBundleID([[base_bundle bundleIdentifier] UTF8String]);
 
-#if BUILDFLAG(NEW_MAC_BUNDLE_STRUCTURE)
   base::FilePath child_exe_path =
       chrome::GetFrameworkBundlePath()
           .Append("Helpers")
           .Append(chrome::kHelperProcessExecutablePath);
-#else
-  base::FilePath child_exe_path = chrome::GetVersionedDirectory().Append(
-      chrome::kHelperProcessExecutablePath);
-#endif
 
   // On the Mac, the child executable lives at a predefined location within
   // the app bundle's versioned directory.
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index 558d552c..5440f03 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -428,43 +428,23 @@
                desc="Message shown on the download shelf when the download is known to change settings in the browser in a malicious way.">
         <ph name="FILE_NAME">$1<ex>bla.exe</ex></ph> may be dangerous, so Chromium has blocked it.
       </message>
-      <message name="IDS_PROMPT_DOWNLOAD_CHANGES_SETTINGS_IN_ADVANCED_PROTECTION"
-               desc="Message shown on the download shelf when the download is known to change settings in the browser in a malicious way. This variant is shown when the user is enrolled in the Advanced Protection program.">
-        <ph name="FILE_NAME">$1<ex>bla.exe</ex></ph> may be dangerous, so Chromium has blocked it. You're defended by Advanced Protection.
-      </message>
 
       <message name="IDS_PROMPT_MALICIOUS_DOWNLOAD_URL"
          desc="Message shown to the user to validate the download when the download url is classified to lead to malware by the safebrowsing database.">
         This file is dangerous, so Chromium has blocked it.
       </message>
-      <message name="IDS_PROMPT_MALICIOUS_DOWNLOAD_URL_IN_ADVANCED_PROTECTION"
-         desc="Message shown to the user to validate the download when the download url is classified to lead to malware by the safebrowsing database. This variant is shown when the user is enrolled in this Advanced Protection program.">
-        This file is dangerous, so Chromium has blocked it. You're defended by Advanced Protection.
-      </message>
       <message name="IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT"
          desc="Message shown to the user to validate the download when the download content is classified to lead to malware by safebrowsing.">
         <ph name="FILE_NAME">$1<ex>malware.exe</ex></ph> is dangerous, so Chromium has blocked it.
       </message>
-      <message name="IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT_IN_ADVANCED_PROTECTION"
-         desc="Message shown to the user to validate the download when the download content is classified to lead to malware by safebrowsing. This variant is shown when the user is enrolled in the Advanced Protection program.">
-        <ph name="FILE_NAME">$1<ex>malware.exe</ex></ph> is dangerous, so Chromium has blocked it. You're defended by Advanced Protection.
-      </message>
       <message name="IDS_BLOCK_REASON_DANGEROUS_DOWNLOAD"
          desc="Message shown to the user on chrome://downloads page to explain that this download is blocked because it is malware.">
         This file is dangerous, so Chromium has blocked it.
       </message>
-      <message name="IDS_BLOCK_REASON_DANGEROUS_DOWNLOAD_IN_ADVANCED_PROTECTION"
-         desc="Message shown to the user on chrome://downloads page to explain that this download is blocked because it is malware. This variant is shown when the user is enrolled in the Advanced Protection program.">
-        This file is dangerous, so Chromium has blocked it. You're defended by Advanced Protection.
-      </message>
       <message name="IDS_BLOCK_REASON_UNWANTED_DOWNLOAD"
          desc="Message shown to the user on chrome://downloads page to explain that this download is blocked because it is unwanted sofware.">
         This file may be dangerous, so Chromium has blocked it.
       </message>
-      <message name="IDS_BLOCK_REASON_UNWANTED_DOWNLOAD_IN_ADVANCED_PROTECTION"
-         desc="Message shown to the user on chrome://downloads page to explain that this download is blocked because it is unwanted sofware. This variant is shown when the user is enrolled in the Advanced Protection program.">
-        This file may be dangerous, so Chromium has blocked it. You're defended by Advanced Protection.
-      </message>
 
       <!-- Abandon in-progress downloads confirmation dialog -->
       <if expr="not is_macosx">
diff --git a/chrome/app/file_pre_reader_win.cc b/chrome/app/file_pre_reader_win.cc
index 957f9d2..b345300 100644
--- a/chrome/app/file_pre_reader_win.cc
+++ b/chrome/app/file_pre_reader_win.cc
@@ -54,11 +54,8 @@
     base::MemoryMappedFile mapped_file;
     if (mapped_file.Initialize(file_path,
                                base::MemoryMappedFile::READ_CODE_IMAGE)) {
-      // RefSet data indicates we touch only the first half of the DLL
-      // so prefetch approximately the first half.
-
       _WIN32_MEMORY_RANGE_ENTRY address_range = {mapped_file.data(),
-                                                 mapped_file.length() / 2};
+                                                 mapped_file.length()};
 
       // NB: PrefetchVirtualMemory requires the file to be opened with
       // only read access or it will fail.
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 247d308..a1e20aa 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -1548,7 +1548,7 @@
       </message>
       <message name="IDS_PROMPT_UNCOMMON_DOWNLOAD_CONTENT_IN_ADVANCED_PROTECTION"
          desc="Message shown to the user to validate the download when the download content is classified as uncommon by safebrowsing. This variant is shown when the user is enrolled in the Advanced Protection program.">
-        <ph name="FILE_NAME">$1<ex>bla.exe</ex></ph> is not commonly downloaded and may be dangerous. You're defended by Advanced Protection.
+        <ph name="FILE_NAME">$1<ex>bla.exe</ex></ph> was blocked by Advanced Protection.
       </message>
       <message name="IDS_BLOCK_REASON_UNCOMMON_DOWNLOAD"
          desc="Message shown to the user on chrome://downloads page to explain that this download is blocked because it is uncommon.">
@@ -1556,7 +1556,7 @@
       </message>
       <message name="IDS_BLOCK_REASON_UNCOMMON_DOWNLOAD_IN_ADVANCED_PROTECTION"
          desc="Message shown to the user on chrome://downloads page to explain that this download is blocked because it is uncommon. This variant is shown when the user is enrolled in the Advanced Protection program.">
-        This file is not commonly downloaded and may be dangerous. You're defended by Advanced Protection.
+        This file was blocked by Advanced Protection.
       </message>
       <message name="IDS_BLOCK_REASON_GENERIC_DOWNLOAD"
          desc="Message shown to the user on chrome://downloads page to explain that this download is blocked because it may be dangerous.">
@@ -5075,14 +5075,38 @@
       <message name="IDS_NTP_CUSTOMIZE_BUTTON_LABEL" desc="The label for customize button on the New Tab Page.">
         Customize
       </message>
-      <message name="IDS_NTP_CUSTOMIZE_MENU_BACKGROUNDS_LABEL" desc="The label for the backgrounds option in the customization menu on the New Tab Page.">
-       Backgrounds
+      <message name="IDS_NTP_CUSTOMIZE_MENU_BACKGROUND_LABEL" desc="The label for the backgrounds option in the customization menu on the New Tab Page.">
+        Background
       </message>
       <message name="IDS_NTP_CUSTOMIZE_MENU_SHORTCUTS_LABEL" desc="The label for the shortcuts option in the customization menu on the New Tab Page.">
         Shortcuts
       </message>
-      <message name="IDS_NTP_CUSTOMIZE_MENU_COLORS_LABEL" desc="The label for the colors and themes option in the customization menu on the New Tab Page">
-        Colors and themes
+      <message name="IDS_NTP_CUSTOMIZE_MENU_COLOR_LABEL" desc="The label for the colors and themes option in the customization menu on the New Tab Page">
+        Color and theme
+      </message>
+      <message name="IDS_NTP_CUSTOMIZE_NO_BACKGROUND_LABEL" desc="The label for the 'No background' tile in the customization menu on the New Tab Page">
+        No background
+      </message>
+      <message name="IDS_NTP_CUSTOMIZE_UPLOAD_FROM_DEVICE_LABEL" desc="The label for the 'Upload from device' tile in the customization menu on the New Tab Page">
+        Upload from device
+      </message>
+      <message name="IDS_NTP_CUSTOMIZE_HIDE_SHORTCUTS_LABEL" desc="The label for the option to hide shortcuts in the customization menu on the New Tab Page">
+        Hide shortcuts
+      </message>
+      <message name="IDS_NTP_CUSTOMIZE_HIDE_SHORTCUTS_DESC" desc="The description for the option to hide shortcuts in the customization menu on the New Tab Page">
+        Don't show shortcuts on this page
+      </message>
+      <message name="IDS_NTP_CUSTOMIZE_MY_SHORTCUTS_LABEL" desc="The label for the option to show user selected shortcuts in the customization menu on the New Tab Page">
+        My shortcuts
+      </message>
+      <message name="IDS_NTP_CUSTOMIZE_MOST_VISITED_LABEL" desc="The label for the option to show most visited sites in the customization menu on the New Tab Page">
+        Most visited sites
+      </message>
+      <message name="IDS_NTP_CUSTOMIZE_MY_SHORTCUTS_DESC" desc="The description for the option to show user selected shortcuts in the customization menu on the New Tab Page">
+        Shortcuts are suggested based on websites you visit often
+      </message>
+      <message name="IDS_NTP_CUSTOMIZE_MOST_VISITED_DESC" desc="The description for the option to show most visited sites in the customization menu on the New Tab Page">
+        Shortcuts are curated by you
       </message>
 
       <!--Tooltip strings-->
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_HIDE_SHORTCUTS_DESC.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_HIDE_SHORTCUTS_DESC.png.sha1
new file mode 100644
index 0000000..55ecbc7a
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_HIDE_SHORTCUTS_DESC.png.sha1
@@ -0,0 +1 @@
+2e0e726ed31b1006a9574ef4ecbd2c8ef151dca0
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_HIDE_SHORTCUTS_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_HIDE_SHORTCUTS_LABEL.png.sha1
new file mode 100644
index 0000000..55ecbc7a
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_HIDE_SHORTCUTS_LABEL.png.sha1
@@ -0,0 +1 @@
+2e0e726ed31b1006a9574ef4ecbd2c8ef151dca0
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_MENU_BACKGROUNDS_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_MENU_BACKGROUNDS_LABEL.png.sha1
deleted file mode 100644
index e0173cb..0000000
--- a/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_MENU_BACKGROUNDS_LABEL.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-229762ef40c102558b7d537c673006431f0e7d87
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_MENU_BACKGROUND_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_MENU_BACKGROUND_LABEL.png.sha1
new file mode 100644
index 0000000..93fae8e
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_MENU_BACKGROUND_LABEL.png.sha1
@@ -0,0 +1 @@
+63fb9364c7a8666cd39494ad3304ba87334fc2d5
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_MENU_COLORS_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_MENU_COLORS_LABEL.png.sha1
deleted file mode 100644
index e0173cb..0000000
--- a/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_MENU_COLORS_LABEL.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-229762ef40c102558b7d537c673006431f0e7d87
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_MENU_COLOR_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_MENU_COLOR_LABEL.png.sha1
new file mode 100644
index 0000000..05b5cc5
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_MENU_COLOR_LABEL.png.sha1
@@ -0,0 +1 @@
+15e8cfbd5e682fda59dd42133a9ee41c9fcb7633
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_MOST_VISITED_DESC.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_MOST_VISITED_DESC.png.sha1
new file mode 100644
index 0000000..55ecbc7a
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_MOST_VISITED_DESC.png.sha1
@@ -0,0 +1 @@
+2e0e726ed31b1006a9574ef4ecbd2c8ef151dca0
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_MOST_VISITED_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_MOST_VISITED_LABEL.png.sha1
new file mode 100644
index 0000000..55ecbc7a
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_MOST_VISITED_LABEL.png.sha1
@@ -0,0 +1 @@
+2e0e726ed31b1006a9574ef4ecbd2c8ef151dca0
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_MY_SHORTCUTS_DESC.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_MY_SHORTCUTS_DESC.png.sha1
new file mode 100644
index 0000000..55ecbc7a
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_MY_SHORTCUTS_DESC.png.sha1
@@ -0,0 +1 @@
+2e0e726ed31b1006a9574ef4ecbd2c8ef151dca0
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_MY_SHORTCUTS_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_MY_SHORTCUTS_LABEL.png.sha1
new file mode 100644
index 0000000..55ecbc7a
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_MY_SHORTCUTS_LABEL.png.sha1
@@ -0,0 +1 @@
+2e0e726ed31b1006a9574ef4ecbd2c8ef151dca0
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_NO_BACKGROUND_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_NO_BACKGROUND_LABEL.png.sha1
new file mode 100644
index 0000000..93fae8e
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_NO_BACKGROUND_LABEL.png.sha1
@@ -0,0 +1 @@
+63fb9364c7a8666cd39494ad3304ba87334fc2d5
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_UPLOAD_FROM_DEVICE_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_UPLOAD_FROM_DEVICE_LABEL.png.sha1
new file mode 100644
index 0000000..93fae8e
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_UPLOAD_FROM_DEVICE_LABEL.png.sha1
@@ -0,0 +1 @@
+63fb9364c7a8666cd39494ad3304ba87334fc2d5
\ No newline at end of file
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index 5dec1cc..1c44c5a 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -439,44 +439,24 @@
                desc="Message shown on the download shelf when the download is known to change settings in the browser.">
         <ph name="FILE_NAME">$1<ex>bla.exe</ex></ph> may be dangerous, so Chrome has blocked it.
       </message>
-      <message name="IDS_PROMPT_DOWNLOAD_CHANGES_SETTINGS_IN_ADVANCED_PROTECTION"
-               desc="Message shown on the download shelf when the download is known to change settings in the browser. This variant is shown when the user is enrolled in the Advanced Protection program.">
-        <ph name="FILE_NAME">$1<ex>bla.exe</ex></ph> may be dangerous, so Chrome has blocked it. You're defended by Advanced Protection.
-      </message>
 
       <message name="IDS_PROMPT_MALICIOUS_DOWNLOAD_URL"
          desc="Message shown to the user to validate the download when the download url is classified to lead to malware by the safebrowsing database.">
         This file is dangerous, so Chrome has blocked it.
       </message>
-      <message name="IDS_PROMPT_MALICIOUS_DOWNLOAD_URL_IN_ADVANCED_PROTECTION"
-         desc="Message shown to the user to validate the download when the download url is classified to lead to malware by the safebrowsing database. This variant is shown when the user is enrolled in the Advanced Protection program.">
-        This file is dangerous, so Chrome has blocked it. Your'e defended by Advanced Protection.
-      </message>
       <message name="IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT"
          desc="Message shown to the user to validate the download when the download content is classified to lead to malware by safebrowsing.">
         <ph name="FILE_NAME">$1<ex>malware.exe</ex></ph> is dangerous, so Chrome has blocked it.
       </message>
-      <message name="IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT_IN_ADVANCED_PROTECTION"
-         desc="Message shown to the user to validate the download when the download content is classified to lead to malware by safebrowsing. This variant is shown when the user is enrolled in the Advanced Protection program.">
-        <ph name="FILE_NAME">$1<ex>malware.exe</ex></ph> is dangerous, so Chrome has blocked it. You're defended by Advanced Protection.
-      </message>
       <message name="IDS_BLOCK_REASON_DANGEROUS_DOWNLOAD"
          desc="Message shown to the user on chrome://downloads page to explain that this download is blocked because it is malware.">
         This file is dangerous, so Chrome has blocked it.
       </message>
-      <message name="IDS_BLOCK_REASON_DANGEROUS_DOWNLOAD_IN_ADVANCED_PROTECTION"
-         desc="Message shown to the user on chrome://downloads page to explain that this download is blocked because it is malware. This variant is shown when the user is enrolled in the Advanced Protection program.">
-        This file is dangerous, so Chrome has blocked it. You're defended by Advanced Protection.
-      </message>
 
       <message name="IDS_BLOCK_REASON_UNWANTED_DOWNLOAD"
          desc="Message shown to the user on chrome://downloads page to explain that this download is blocked because it is unwanted sofware.">
         This file may be dangerous, so Chrome has blocked it.
       </message>
-      <message name="IDS_BLOCK_REASON_UNWANTED_DOWNLOAD_IN_ADVANCED_PROTECTION"
-         desc="Message shown to the user on chrome://downloads page to explain that this download is blocked because it is unwanted sofware. This variant is shown when the user is enrolled in the Advanced Protection program.">
-        This file may be dangerous, so Chrome has blocked it. You're defended by Advanced Protection.
-      </message>
 
       <!-- Abandon in-progress downloads confirmation dialog -->
       <if expr="not is_macosx">
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index d56428f..bccc024 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2147,6 +2147,8 @@
       "android/app_hooks.h",
       "android/autofill_assistant/assistant_form_delegate.cc",
       "android/autofill_assistant/assistant_form_delegate.h",
+      "android/autofill_assistant/assistant_header_delegate.cc",
+      "android/autofill_assistant/assistant_header_delegate.h",
       "android/autofill_assistant/assistant_overlay_delegate.cc",
       "android/autofill_assistant/assistant_overlay_delegate.h",
       "android/autofill_assistant/assistant_payment_request_delegate.cc",
@@ -2610,9 +2612,6 @@
       "android/widget/thumbnail_generator.cc",
       "android/widget/thumbnail_generator.h",
       "autofill/accessory_controller.h",
-      "autofill/address_accessory_controller.h",
-      "autofill/address_accessory_controller_impl.cc",
-      "autofill/address_accessory_controller_impl.h",
       "autofill/android/personal_data_manager_android.cc",
       "autofill/android/personal_data_manager_android.h",
       "autofill/android/phone_number_util_android.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 908b203..c966846 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2224,13 +2224,6 @@
      kOsDesktop,
      FEATURE_VALUE_TYPE(
          autofill::features::kAutofillSaveCardImprovedUserConsent)},
-    {"enable-autofill-save-credit-card-uses-strike-system-v2",
-     flag_descriptions::kEnableAutofillSaveCreditCardUsesStrikeSystemV2Name,
-     flag_descriptions::
-         kEnableAutofillSaveCreditCardUsesStrikeSystemV2Description,
-     kOsAll,
-     FEATURE_VALUE_TYPE(
-         autofill::features::kAutofillSaveCreditCardUsesStrikeSystemV2)},
     {"enable-autofill-send-experiment-ids-in-payments-rpcs",
      flag_descriptions::kEnableAutofillSendExperimentIdsInPaymentsRPCsName,
      flag_descriptions::
@@ -3136,12 +3129,6 @@
      flag_descriptions::kEnableSyncUSSBookmarksDescription, kOsAll,
      FEATURE_VALUE_TYPE(switches::kSyncUSSBookmarks)},
 
-#if defined(OS_ANDROID)
-    {"incognito-strings", flag_descriptions::kIncognitoStringsName,
-     flag_descriptions::kIncognitoStringsDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(features::kIncognitoStrings)},
-#endif
-
     {"enable-lookalike-url-navigation-suggestions",
      flag_descriptions::kLookalikeUrlNavigationSuggestionsName,
      flag_descriptions::kLookalikeUrlNavigationSuggestionsDescription, kOsAll,
diff --git a/chrome/browser/android/autofill_assistant/assistant_header_delegate.cc b/chrome/browser/android/autofill_assistant/assistant_header_delegate.cc
new file mode 100644
index 0000000..f2d9b5e
--- /dev/null
+++ b/chrome/browser/android/autofill_assistant/assistant_header_delegate.cc
@@ -0,0 +1,37 @@
+// Copyright 2019 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 "chrome/browser/android/autofill_assistant/assistant_header_delegate.h"
+
+#include "chrome/browser/android/autofill_assistant/ui_controller_android.h"
+#include "jni/AssistantHeaderDelegate_jni.h"
+
+using base::android::AttachCurrentThread;
+
+namespace autofill_assistant {
+
+AssistantHeaderDelegate::AssistantHeaderDelegate(
+    UiControllerAndroid* ui_controller)
+    : ui_controller_(ui_controller) {
+  java_assistant_header_delegate_ = Java_AssistantHeaderDelegate_create(
+      AttachCurrentThread(), reinterpret_cast<intptr_t>(this));
+}
+
+AssistantHeaderDelegate::~AssistantHeaderDelegate() {
+  Java_AssistantHeaderDelegate_clearNativePtr(AttachCurrentThread(),
+                                              java_assistant_header_delegate_);
+}
+
+void AssistantHeaderDelegate::OnFeedbackButtonClicked(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& jcaller) {
+  ui_controller_->OnFeedbackButtonClicked();
+}
+
+base::android::ScopedJavaGlobalRef<jobject>
+AssistantHeaderDelegate::GetJavaObject() {
+  return java_assistant_header_delegate_;
+}
+
+}  // namespace autofill_assistant
diff --git a/chrome/browser/android/autofill_assistant/assistant_header_delegate.h b/chrome/browser/android/autofill_assistant/assistant_header_delegate.h
new file mode 100644
index 0000000..9ca6b89
--- /dev/null
+++ b/chrome/browser/android/autofill_assistant/assistant_header_delegate.h
@@ -0,0 +1,32 @@
+// Copyright 2019 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 CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_ASSISTANT_HEADER_DELEGATE_H_
+#define CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_ASSISTANT_HEADER_DELEGATE_H_
+
+#include "base/android/scoped_java_ref.h"
+
+namespace autofill_assistant {
+class UiControllerAndroid;
+// Delegate class for the assistant header, to react on clicks on its buttons.
+class AssistantHeaderDelegate {
+ public:
+  explicit AssistantHeaderDelegate(UiControllerAndroid* ui_controller);
+  ~AssistantHeaderDelegate();
+
+  void OnFeedbackButtonClicked(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& jcaller);
+
+  base::android::ScopedJavaGlobalRef<jobject> GetJavaObject();
+
+ private:
+  UiControllerAndroid* ui_controller_;
+
+  // Java-side AssistantHeaderDelegate object.
+  base::android::ScopedJavaGlobalRef<jobject> java_assistant_header_delegate_;
+};
+}  // namespace autofill_assistant
+
+#endif  // CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_ASSISTANT_HEADER_DELEGATE_H_
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index 6113de2..21084fa 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -80,6 +80,7 @@
     JNIEnv* env,
     const base::android::JavaRef<jobject>& jactivity)
     : overlay_delegate_(this),
+      header_delegate_(this),
       payment_request_delegate_(this),
       form_delegate_(this),
       weak_ptr_factory_(this) {
@@ -93,6 +94,10 @@
   Java_AssistantOverlayModel_setDelegate(env, GetOverlayModel(),
                                          overlay_delegate_.GetJavaObject());
 
+  // Register header_delegate_ as delegate for clicks on header buttons.
+  Java_AssistantHeaderModel_setDelegate(env, GetHeaderModel(),
+                                        header_delegate_.GetJavaObject());
+
   // Register payment_request_delegate_ as delegate for the payment request UI.
   Java_AssistantPaymentRequestModel_setDelegate(
       env, GetPaymentRequestModel(), payment_request_delegate_.GetJavaObject());
@@ -107,6 +112,7 @@
 
   client_ = client;
   ui_delegate_ = ui_delegate;
+  captured_debug_context_.clear();
 
   JNIEnv* env = AttachCurrentThread();
   auto java_web_contents = web_contents->GetJavaWebContents();
@@ -293,6 +299,13 @@
                                           GetHeaderModel(), enabled);
 }
 
+void UiControllerAndroid::OnFeedbackButtonClicked() {
+  JNIEnv* env = AttachCurrentThread();
+  Java_AutofillAssistantUiController_showFeedback(
+      env, java_object_,
+      base::android::ConvertUTF8ToJavaString(env, GetDebugContext()));
+}
+
 void UiControllerAndroid::Shutdown(Metrics::DropOutReason reason) {
   client_->Shutdown(reason);
 }
@@ -328,6 +341,13 @@
   std::move(action).Run();
 }
 
+std::string UiControllerAndroid::GetDebugContext() {
+  if (captured_debug_context_.empty() && ui_delegate_) {
+    return ui_delegate_->GetDebugContext();
+  }
+  return captured_debug_context_;
+}
+
 void UiControllerAndroid::DestroySelf() {
   client_->DestroyUI();
 }
@@ -551,6 +571,7 @@
   // Capture the debug context, for including into a feedback possibly sent
   // later, after the delegate has been possibly destroyed.
   DCHECK(ui_delegate_);
+  captured_debug_context_ = ui_delegate_->GetDebugContext();
   AutofillAssistantState final_state = ui_delegate_->GetState();
   ui_delegate_ = nullptr;
 
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.h b/chrome/browser/android/autofill_assistant/ui_controller_android.h
index c2b52e3..9de00b0 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.h
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.h
@@ -12,6 +12,7 @@
 #include "base/android/scoped_java_ref.h"
 #include "base/macros.h"
 #include "chrome/browser/android/autofill_assistant/assistant_form_delegate.h"
+#include "chrome/browser/android/autofill_assistant/assistant_header_delegate.h"
 #include "chrome/browser/android/autofill_assistant/assistant_overlay_delegate.h"
 #include "chrome/browser/android/autofill_assistant/assistant_payment_request_delegate.h"
 #include "components/autofill_assistant/browser/chip.h"
@@ -84,6 +85,9 @@
   void UpdateTouchableArea();
   void OnUserInteractionInsideTouchableArea();
 
+  // Called by AssistantHeaderDelegate:
+  void OnFeedbackButtonClicked();
+
   // Called by AssistantPaymentRequestDelegate:
   void OnShippingAddressChanged(
       std::unique_ptr<autofill::AutofillProfile> address);
@@ -139,6 +143,7 @@
   // A pointer to the ui_delegate. nullptr until Attach() is called.
   UiDelegate* ui_delegate_ = nullptr;
   AssistantOverlayDelegate overlay_delegate_;
+  AssistantHeaderDelegate header_delegate_;
   AssistantPaymentRequestDelegate payment_request_delegate_;
   AssistantFormDelegate form_delegate_;
 
@@ -157,6 +162,7 @@
   void AllowShowingSoftKeyboard(bool enabled);
   void ExpandBottomSheet();
   void SetSpinPoodle(bool enabled);
+  std::string GetDebugContext();
   void DestroySelf();
   void Shutdown(Metrics::DropOutReason reason);
   void UpdateActions();
@@ -175,6 +181,10 @@
   // Makes the whole of AA invisible or visible again.
   void SetVisible(bool visible);
 
+  // Debug context captured previously. If non-empty, GetDebugContext() returns
+  // this context.
+  std::string captured_debug_context_;
+
   // Java-side AutofillAssistantUiController object.
   base::android::ScopedJavaGlobalRef<jobject> java_object_;
 
diff --git a/chrome/browser/android/download/download_media_parser_bridge.cc b/chrome/browser/android/download/download_media_parser_bridge.cc
index 512f6d6..754b1e0 100644
--- a/chrome/browser/android/download/download_media_parser_bridge.cc
+++ b/chrome/browser/android/download/download_media_parser_bridge.cc
@@ -70,7 +70,7 @@
 
 DownloadMediaParserBridge::~DownloadMediaParserBridge() = default;
 
-void DownloadMediaParserBridge::Destory(JNIEnv* env, jobject obj) {
+void DownloadMediaParserBridge::Destroy(JNIEnv* env, jobject obj) {
   delete this;
 }
 
diff --git a/chrome/browser/android/download/download_media_parser_bridge.h b/chrome/browser/android/download/download_media_parser_bridge.h
index a028055..aa27dbd 100644
--- a/chrome/browser/android/download/download_media_parser_bridge.h
+++ b/chrome/browser/android/download/download_media_parser_bridge.h
@@ -24,7 +24,7 @@
       DownloadMediaParser::ParseCompleteCB parse_complete_cb);
   ~DownloadMediaParserBridge();
 
-  void Destory(JNIEnv* env, jobject obj);
+  void Destroy(JNIEnv* env, jobject obj);
   void Start(JNIEnv* env, jobject obj);
 
  private:
diff --git a/chrome/browser/android/send_tab_to_self/send_tab_to_self_android_bridge.cc b/chrome/browser/android/send_tab_to_self/send_tab_to_self_android_bridge.cc
index 0d528e4..f4562241 100644
--- a/chrome/browser/android/send_tab_to_self/send_tab_to_self_android_bridge.cc
+++ b/chrome/browser/android/send_tab_to_self/send_tab_to_self_android_bridge.cc
@@ -187,6 +187,18 @@
   }
 }
 
+// Marks the entry with the associated GUID as opened.
+static void JNI_SendTabToSelfAndroidBridge_MarkEntryOpened(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& j_profile,
+    const JavaParamRef<jstring>& j_guid) {
+  SendTabToSelfModel* model = GetModel(j_profile);
+  if (model->IsReady()) {
+    const std::string guid = ConvertJavaStringToUTF8(env, j_guid);
+    model->MarkEntryOpened(guid);
+  }
+}
+
 // Returns whether the feature is available for the specified |web_contents|.
 static jboolean JNI_SendTabToSelfAndroidBridge_IsFeatureAvailable(
     JNIEnv* env,
diff --git a/chrome/browser/android/vr/gvr_input_delegate.cc b/chrome/browser/android/vr/gvr_input_delegate.cc
index e27e817..ec32242 100644
--- a/chrome/browser/android/vr/gvr_input_delegate.cc
+++ b/chrome/browser/android/vr/gvr_input_delegate.cc
@@ -55,8 +55,11 @@
       device::GamepadButton(pressed, touched, value);
 
   if (touched) {
-    // data.touch_pos values are clamped to [0.0, 1.0], so normalize them to
-    // [-1.0, 1.0]
+    // data.touch_pos values reported by the GVR Android SDK are clamped to
+    // [0.0, 1.0], with (0, 0) corresponding to the top-left of the touchpad.
+    // Normalize the values to use X axis range -1 (left) to 1 (right) and Y
+    // axis range -1 (top) to 1 (bottom).
+    // TODO(https://crbug.com/966060): Revisit this if the convention changes.
     gamepad.axes[0] = (data.touch_pos.x() * 2.0) - 1.0;
     gamepad.axes[1] = (data.touch_pos.y() * 2.0) - 1.0;
   } else {
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index b9acc12..73b214e 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -179,7 +179,6 @@
   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                                 base::BlockingType::MAY_BLOCK);
 
-#if BUILDFLAG(NEW_MAC_BUNDLE_STRUCTURE)
   // Go up five levels from the versioned sub-directory of the framework, which
   // is at C.app/Contents/Frameworks/C.framework/Versions/V.
   base::FilePath app_bundle_path = chrome::GetFrameworkBundlePath()
@@ -188,10 +187,6 @@
                                        .DirName()
                                        .DirName()
                                        .DirName();
-#else
-  base::FilePath app_bundle_path =
-      chrome::GetVersionedDirectory().DirName().DirName().DirName();
-#endif
   base::ScopedCFTypeRef<CFStringRef> app_bundle_path_cfstring(
       base::SysUTF8ToCFStringRef(app_bundle_path.value()));
   CFPreferencesSetAppValue(
diff --git a/chrome/browser/autofill/address_accessory_controller.h b/chrome/browser/autofill/address_accessory_controller.h
deleted file mode 100644
index b867127..0000000
--- a/chrome/browser/autofill/address_accessory_controller.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2019 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 CHROME_BROWSER_AUTOFILL_ADDRESS_ACCESSORY_CONTROLLER_H_
-#define CHROME_BROWSER_AUTOFILL_ADDRESS_ACCESSORY_CONTROLLER_H_
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "chrome/browser/autofill/accessory_controller.h"
-#include "content/public/browser/web_contents_user_data.h"
-
-namespace autofill {
-
-// Interface for address-specific keyboard accessory controller between the
-// ManualFillingController and the autofill backend.
-//
-// There is a single instance per WebContents that can be accessed by calling:
-//     AddressAccessoryController::GetOrCreate(web_contents);
-// On the first call, an instance is attached to |web_contents|, so it can be
-// returned by subsequent calls.
-class AddressAccessoryController
-    : public base::SupportsWeakPtr<AddressAccessoryController>,
-      public AccessoryController {
- public:
-  AddressAccessoryController() = default;
-  ~AddressAccessoryController() override = default;
-
-  // Returns true if the accessory controller may exist for |web_contents|.
-  // Otherwise it returns false.
-  static bool AllowedForWebContents(content::WebContents* web_contents);
-
-  // Returns a reference to the unique AddressAccessoryController associated
-  // with |web_contents|. A new instance is created if the first time this
-  // function is called. Only valid to be called if
-  // |AddressAccessoryController::AllowedForWebContents(web_contents)|.
-  static AddressAccessoryController* GetOrCreate(
-      content::WebContents* web_contents);
-
-  // Returns a reference to the unique AddressAccessoryController associated
-  // with |web_contents|. Returns null if no such instance exists.
-  static AddressAccessoryController* GetIfExisting(
-      content::WebContents* web_contents);
-
-  // Fetches suggestions and propagates them to the frontend.
-  virtual void RefreshSuggestions() = 0;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(AddressAccessoryController);
-};
-
-}  // namespace autofill
-
-#endif  // CHROME_BROWSER_AUTOFILL_ADDRESS_ACCESSORY_CONTROLLER_H_
diff --git a/chrome/browser/autofill/address_accessory_controller_impl.cc b/chrome/browser/autofill/address_accessory_controller_impl.cc
deleted file mode 100644
index 0f4a0df..0000000
--- a/chrome/browser/autofill/address_accessory_controller_impl.cc
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright 2019 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 "chrome/browser/autofill/address_accessory_controller_impl.h"
-
-#include <algorithm>
-#include <utility>
-
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/autofill/manual_filling_controller.h"
-#include "chrome/browser/autofill/manual_filling_utils.h"
-#include "chrome/browser/autofill/personal_data_manager_factory.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/vr/vr_tab_helper.h"
-#include "components/autofill/content/browser/content_autofill_driver.h"
-#include "components/autofill/core/browser/personal_data_manager.h"
-#include "components/autofill/core/common/autofill_features.h"
-#include "components/strings/grit/components_strings.h"
-#include "content/public/browser/web_contents.h"
-#include "ui/base/l10n/l10n_util.h"
-
-namespace autofill {
-namespace {
-
-// Defines which types to load from the Personal data manager and add as field
-// to the address sheet. Order matters.
-constexpr ServerFieldType kTypesToInclude[] = {
-    // TODO(crbug.com/965494): Possibly, the names should be in a single chip.
-    ServerFieldType::NAME_FIRST,
-    ServerFieldType::NAME_MIDDLE,
-    ServerFieldType::NAME_LAST,
-    ServerFieldType::COMPANY_NAME,
-    ServerFieldType::ADDRESS_HOME_LINE1,
-    ServerFieldType::ADDRESS_HOME_LINE2,
-    ServerFieldType::ADDRESS_HOME_ZIP,
-    ServerFieldType::ADDRESS_HOME_CITY,
-    ServerFieldType::ADDRESS_HOME_STATE,
-    ServerFieldType::ADDRESS_HOME_COUNTRY,
-    ServerFieldType::PHONE_HOME_WHOLE_NUMBER,
-    ServerFieldType::EMAIL_ADDRESS,
-};
-
-void AddProfileInfoAsSelectableField(UserInfo* info,
-                                     const AutofillProfile* profile,
-                                     ServerFieldType type) {
-  base::string16 field = profile->GetRawInfo(type);
-  if (type == ServerFieldType::NAME_MIDDLE && field.empty()) {
-    field = profile->GetRawInfo(ServerFieldType::NAME_MIDDLE_INITIAL);
-  }
-  info->add_field(UserInfo::Field(field, field, /*is_password=*/false,
-                                  /*selectable=*/true));
-}
-
-UserInfo Translate(const AutofillProfile* profile) {
-  UserInfo info;
-  for (ServerFieldType server_field_type : kTypesToInclude) {
-    AddProfileInfoAsSelectableField(&info, profile, server_field_type);
-  }
-  return info;
-}
-
-std::vector<UserInfo> UserInfosForProfiles(
-    const std::vector<AutofillProfile*>& profiles) {
-  std::vector<UserInfo> infos(profiles.size());
-  std::transform(profiles.begin(), profiles.end(), infos.begin(), Translate);
-  return infos;
-}
-
-std::vector<FooterCommand> CreateManageAddressesFooter() {
-  return {FooterCommand(l10n_util::GetStringUTF16(
-      IDS_AUTOFILL_ADDRESS_SHEET_ALL_ADDRESSES_LINK))};
-}
-
-}  // namespace
-
-AddressAccessoryControllerImpl::~AddressAccessoryControllerImpl() = default;
-
-// static
-bool AddressAccessoryController::AllowedForWebContents(
-    content::WebContents* web_contents) {
-  DCHECK(web_contents) << "Need valid WebContents to attach controller to!";
-  if (vr::VrTabHelper::IsInVr(web_contents)) {
-    return false;  // TODO(crbug.com/865749): Re-Enable if possible.
-  }
-  return base::FeatureList::IsEnabled(
-      autofill::features::kAutofillKeyboardAccessory);
-}
-
-// static
-AddressAccessoryController* AddressAccessoryController::GetOrCreate(
-    content::WebContents* web_contents) {
-  DCHECK(AddressAccessoryController::AllowedForWebContents(web_contents));
-
-  AddressAccessoryControllerImpl::CreateForWebContents(web_contents);
-  return AddressAccessoryControllerImpl::FromWebContents(web_contents);
-}
-
-// static
-AddressAccessoryController* AddressAccessoryController::GetIfExisting(
-    content::WebContents* web_contents) {
-  return AddressAccessoryControllerImpl::FromWebContents(web_contents);
-}
-
-void AddressAccessoryControllerImpl::OnFillingTriggered(
-    const UserInfo::Field& selection) {
-  // Since the data we fill is scoped to the profile and not to a frame, we can
-  // fill the focused frame - we basically behave like a keyboard here.
-  autofill::ContentAutofillDriver* driver =
-      autofill::ContentAutofillDriver::GetForRenderFrameHost(
-          web_contents_->GetFocusedFrame());
-  if (!driver)
-    return;
-  driver->RendererShouldFillFieldWithValue(selection.display_text());
-}
-
-void AddressAccessoryControllerImpl::RefreshSuggestions() {
-  std::vector<AutofillProfile*> profiles = GetProfiles();
-  base::string16 title_or_empty_message;
-  if (profiles.empty())
-    title_or_empty_message =
-        l10n_util::GetStringUTF16(IDS_AUTOFILL_ADDRESS_SHEET_EMPTY_MESSAGE);
-  GetManualFillingController()->RefreshSuggestionsForField(
-      /*is_fillable=*/true,
-      autofill::CreateAccessorySheetData(
-          autofill::FallbackSheetType::ADDRESS, title_or_empty_message,
-          UserInfosForProfiles(profiles), CreateManageAddressesFooter()));
-}
-
-// static
-void AddressAccessoryControllerImpl::CreateForWebContentsForTesting(
-    content::WebContents* web_contents,
-    base::WeakPtr<ManualFillingController> mf_controller,
-    autofill::PersonalDataManager* personal_data_manager) {
-  DCHECK(web_contents) << "Need valid WebContents to attach controller to!";
-  DCHECK(!FromWebContents(web_contents)) << "Controller already attached!";
-  DCHECK(mf_controller);
-
-  web_contents->SetUserData(
-      UserDataKey(),
-      base::WrapUnique(new AddressAccessoryControllerImpl(
-          web_contents, std::move(mf_controller), personal_data_manager)));
-}
-
-AddressAccessoryControllerImpl::AddressAccessoryControllerImpl(
-    content::WebContents* web_contents)
-    : web_contents_(web_contents),
-      personal_data_manager_for_testing_(nullptr) {}
-
-// Additional creation functions in unit tests only:
-AddressAccessoryControllerImpl::AddressAccessoryControllerImpl(
-    content::WebContents* web_contents,
-    base::WeakPtr<ManualFillingController> mf_controller,
-    autofill::PersonalDataManager* personal_data_manager)
-    : web_contents_(web_contents),
-      mf_controller_(std::move(mf_controller)),
-      personal_data_manager_for_testing_(personal_data_manager) {}
-
-std::vector<AutofillProfile*> AddressAccessoryControllerImpl::GetProfiles() {
-  const autofill::PersonalDataManager* data_manager =
-      personal_data_manager_for_testing_;
-  if (!data_manager)
-    data_manager = autofill::PersonalDataManagerFactory::GetForProfile(
-        Profile::FromBrowserContext(web_contents_->GetBrowserContext()));
-  if (!data_manager)
-    return {};  // No data available yet or anymore!
-  return data_manager->GetProfilesToSuggest();
-}
-
-base::WeakPtr<ManualFillingController>
-AddressAccessoryControllerImpl::GetManualFillingController() {
-  if (!mf_controller_)
-    mf_controller_ = ManualFillingController::GetOrCreate(web_contents_);
-  DCHECK(mf_controller_);
-  return mf_controller_;
-}
-
-WEB_CONTENTS_USER_DATA_KEY_IMPL(AddressAccessoryControllerImpl)
-
-}  // namespace autofill
diff --git a/chrome/browser/autofill/address_accessory_controller_impl.h b/chrome/browser/autofill/address_accessory_controller_impl.h
deleted file mode 100644
index 40c3c2b..0000000
--- a/chrome/browser/autofill/address_accessory_controller_impl.h
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2019 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 CHROME_BROWSER_AUTOFILL_ADDRESS_ACCESSORY_CONTROLLER_IMPL_H_
-#define CHROME_BROWSER_AUTOFILL_ADDRESS_ACCESSORY_CONTROLLER_IMPL_H_
-
-#include <vector>
-
-#include "base/callback_forward.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "chrome/browser/autofill/address_accessory_controller.h"
-#include "content/public/browser/web_contents_user_data.h"
-#include "url/gurl.h"
-
-class ManualFillingController;
-
-namespace autofill {
-class AutofillProfile;
-class PersonalDataManager;
-
-// Use either AddressAccessoryController::GetOrCreate or
-// AddressAccessoryController::GetIfExisting to obtain instances of this class.
-// This class exists for every tab and should never store state based on the
-// contents of one of its frames.
-class AddressAccessoryControllerImpl
-    : public AddressAccessoryController,
-      public content::WebContentsUserData<AddressAccessoryControllerImpl> {
- public:
-  ~AddressAccessoryControllerImpl() override;
-
-  // AccessoryController:
-  void OnFillingTriggered(const autofill::UserInfo::Field& selection) override;
-
-  // AddressAccessoryController:
-  void RefreshSuggestions() override;
-
-  // Like |CreateForWebContents|, it creates the controller and attaches it to
-  // the given |web_contents|. Additionally, it allows inject a manual filling
-  // controller.
-  static void CreateForWebContentsForTesting(
-      content::WebContents* web_contents,
-      base::WeakPtr<ManualFillingController> mf_controller,
-      autofill::PersonalDataManager* personal_data_manager);
-
- private:
-  friend class content::WebContentsUserData<AddressAccessoryControllerImpl>;
-
-  // Required for construction via |CreateForWebContents|:
-  explicit AddressAccessoryControllerImpl(content::WebContents* contents);
-
-  std::vector<autofill::AutofillProfile*> GetProfiles();
-
-  // Constructor that allows to inject a mock or fake view.
-  AddressAccessoryControllerImpl(
-      content::WebContents* web_contents,
-      base::WeakPtr<ManualFillingController> mf_controller,
-      autofill::PersonalDataManager* personal_data_manager);
-
-  // Lazy-initializes and returns the ManualFillingController for the current
-  // |web_contents_|. The lazy initialization allows injecting mocks for tests.
-  base::WeakPtr<ManualFillingController> GetManualFillingController();
-
-  // The tab for which this class is scoped.
-  content::WebContents* web_contents_;
-
-  // The password accessory controller object to forward client requests to.
-  base::WeakPtr<ManualFillingController> mf_controller_;
-
-  // The data manager used to retrieve the profiles in tests.
-  const autofill::PersonalDataManager* personal_data_manager_for_testing_;
-
-  WEB_CONTENTS_USER_DATA_KEY_DECL();
-
-  DISALLOW_COPY_AND_ASSIGN(AddressAccessoryControllerImpl);
-};
-
-}  // namespace autofill
-
-#endif  // CHROME_BROWSER_AUTOFILL_ADDRESS_ACCESSORY_CONTROLLER_IMPL_H_
diff --git a/chrome/browser/autofill/address_accessory_controller_impl_unittest.cc b/chrome/browser/autofill/address_accessory_controller_impl_unittest.cc
deleted file mode 100644
index fbf2452..0000000
--- a/chrome/browser/autofill/address_accessory_controller_impl_unittest.cc
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright 2019 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 "chrome/browser/autofill/address_accessory_controller_impl.h"
-
-#include <algorithm>
-#include <memory>
-#include <ostream>
-#include <string>
-#include <vector>
-
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/mock_callback.h"
-#include "chrome/browser/autofill/mock_manual_filling_controller.h"
-#include "chrome/grit/generated_resources.h"
-#include "chrome/test/base/chrome_render_view_host_test_harness.h"
-#include "components/autofill/core/browser/autofill_test_utils.h"
-#include "components/autofill/core/browser/test_personal_data_manager.h"
-#include "components/strings/grit/components_strings.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/l10n/l10n_util.h"
-
-namespace autofill {
-namespace {
-using autofill::UserInfo;
-using base::ASCIIToUTF16;
-using testing::_;
-using testing::ByMove;
-using testing::Mock;
-using testing::NiceMock;
-using testing::Return;
-using testing::SaveArg;
-using testing::StrictMock;
-using FillingSource = ManualFillingController::FillingSource;
-
-base::string16 addresses_empty_str() {
-  return l10n_util::GetStringUTF16(IDS_AUTOFILL_ADDRESS_SHEET_EMPTY_MESSAGE);
-}
-
-base::string16 manage_addresses_str() {
-  return l10n_util::GetStringUTF16(
-      IDS_AUTOFILL_ADDRESS_SHEET_ALL_ADDRESSES_LINK);
-}
-
-// Creates a AccessorySheetData::Builder with a "Manage Addresses" footer.
-AccessorySheetData::Builder AddressAccessorySheetDataBuilder(
-    const base::string16& title) {
-  return AccessorySheetData::Builder(FallbackSheetType::ADDRESS, title)
-      .AppendFooterCommand(manage_addresses_str());
-}
-
-}  // namespace
-
-class AddressAccessoryControllerTest : public ChromeRenderViewHostTestHarness {
- public:
-  AddressAccessoryControllerTest() {}
-
-  void SetUp() override {
-    ChromeRenderViewHostTestHarness::SetUp();
-    personal_data_manager_.SetAutofillProfileEnabled(true);
-
-    AddressAccessoryControllerImpl::CreateForWebContentsForTesting(
-        web_contents(), mock_manual_filling_controller_.AsWeakPtr(),
-        &personal_data_manager_);
-  }
-
-  void TearDown() override { personal_data_manager_.ClearProfiles(); }
-
-  AddressAccessoryController* controller() {
-    return AddressAccessoryControllerImpl::FromWebContents(web_contents());
-  }
-
- protected:
-  StrictMock<MockManualFillingController> mock_manual_filling_controller_;
-  autofill::TestPersonalDataManager personal_data_manager_;
-};
-
-TEST_F(AddressAccessoryControllerTest, IsNotRecreatedForSameWebContents) {
-  AddressAccessoryControllerImpl* initial_controller =
-      AddressAccessoryControllerImpl::FromWebContents(web_contents());
-  EXPECT_NE(nullptr, initial_controller);
-  AddressAccessoryControllerImpl::CreateForWebContents(web_contents());
-  EXPECT_EQ(AddressAccessoryControllerImpl::FromWebContents(web_contents()),
-            initial_controller);
-}
-
-TEST_F(AddressAccessoryControllerTest, RefreshSuggestionsCallsUI) {
-  AutofillProfile canadian = test::GetFullValidProfileForCanada();
-  personal_data_manager_.AddProfile(canadian);
-
-  autofill::AccessorySheetData result(autofill::FallbackSheetType::PASSWORD,
-                                      base::string16());
-  EXPECT_CALL(mock_manual_filling_controller_, RefreshSuggestionsForField(
-                                                   /*is_fillable=*/true, _))
-      .WillOnce(SaveArg<1>(&result));
-
-  controller()->RefreshSuggestions();
-
-  ASSERT_EQ(
-      result,
-      AddressAccessorySheetDataBuilder(base::string16())
-          .AddUserInfo()
-          .AppendSimpleField(canadian.GetRawInfo(ServerFieldType::NAME_FIRST))
-          .AppendSimpleField(canadian.GetRawInfo(ServerFieldType::NAME_MIDDLE))
-          .AppendSimpleField(canadian.GetRawInfo(ServerFieldType::NAME_LAST))
-          .AppendSimpleField(canadian.GetRawInfo(ServerFieldType::COMPANY_NAME))
-          .AppendSimpleField(
-              canadian.GetRawInfo(ServerFieldType::ADDRESS_HOME_LINE1))
-          .AppendSimpleField(
-              canadian.GetRawInfo(ServerFieldType::ADDRESS_HOME_LINE2))
-          .AppendSimpleField(
-              canadian.GetRawInfo(ServerFieldType::ADDRESS_HOME_ZIP))
-          .AppendSimpleField(
-              canadian.GetRawInfo(ServerFieldType::ADDRESS_HOME_CITY))
-          .AppendSimpleField(
-              canadian.GetRawInfo(ServerFieldType::ADDRESS_HOME_STATE))
-          .AppendSimpleField(
-              canadian.GetRawInfo(ServerFieldType::ADDRESS_HOME_COUNTRY))
-          .AppendSimpleField(
-              canadian.GetRawInfo(ServerFieldType::PHONE_HOME_WHOLE_NUMBER))
-          .AppendSimpleField(
-              canadian.GetRawInfo(ServerFieldType::EMAIL_ADDRESS))
-          .Build());
-}
-
-TEST_F(AddressAccessoryControllerTest, ProvidesEmptySuggestionsMessage) {
-  autofill::AccessorySheetData result(autofill::FallbackSheetType::PASSWORD,
-                                      base::string16());
-  EXPECT_CALL(mock_manual_filling_controller_, RefreshSuggestionsForField(
-                                                   /*is_fillable=*/true, _))
-      .WillOnce(SaveArg<1>(&result));
-
-  controller()->RefreshSuggestions();
-
-  ASSERT_EQ(result,
-            AddressAccessorySheetDataBuilder(addresses_empty_str()).Build());
-}
-
-}  // namespace autofill
diff --git a/chrome/browser/autofill/credit_card_accessory_controller_impl.cc b/chrome/browser/autofill/credit_card_accessory_controller_impl.cc
index 101831a..bdc95f5 100644
--- a/chrome/browser/autofill/credit_card_accessory_controller_impl.cc
+++ b/chrome/browser/autofill/credit_card_accessory_controller_impl.cc
@@ -4,9 +4,7 @@
 
 #include "chrome/browser/autofill/credit_card_accessory_controller_impl.h"
 
-#include <algorithm>
 #include <iterator>
-#include <utility>
 #include <vector>
 
 #include "base/strings/utf_string_conversions.h"
@@ -72,7 +70,7 @@
 
   bool has_suggestions = !info_to_add.empty();
   GetManualFillingController()->RefreshSuggestionsForField(
-      /*is_fillable=*/true,
+      mojom::FocusedFieldType::kFillableTextField,
       autofill::CreateAccessorySheetData(
           FallbackSheetType::CREDIT_CARD, GetTitle(has_suggestions),
           std::move(info_to_add), std::move(footer_commands)));
diff --git a/chrome/browser/autofill/credit_card_accessory_controller_impl_unittest.cc b/chrome/browser/autofill/credit_card_accessory_controller_impl_unittest.cc
index 27368ee..89d9027 100644
--- a/chrome/browser/autofill/credit_card_accessory_controller_impl_unittest.cc
+++ b/chrome/browser/autofill/credit_card_accessory_controller_impl_unittest.cc
@@ -62,8 +62,9 @@
   autofill::AccessorySheetData result(autofill::FallbackSheetType::PASSWORD,
                                       base::string16());
 
-  EXPECT_CALL(mock_mf_controller_, RefreshSuggestionsForField(
-                                       /*is_fillable=*/true, _))
+  EXPECT_CALL(mock_mf_controller_,
+              RefreshSuggestionsForField(
+                  mojom::FocusedFieldType::kFillableTextField, _))
       .WillOnce(SaveArg<1>(&result));
 
   controller_.RefreshSuggestionsForField();
diff --git a/chrome/browser/autofill/manual_filling_controller.h b/chrome/browser/autofill/manual_filling_controller.h
index 861aa52..8ba8e5b 100644
--- a/chrome/browser/autofill/manual_filling_controller.h
+++ b/chrome/browser/autofill/manual_filling_controller.h
@@ -13,6 +13,7 @@
 #include "base/strings/string16.h"
 #include "components/autofill/core/browser/ui/accessory_sheet_data.h"
 #include "components/autofill/core/common/filling_status.h"
+#include "components/autofill/core/common/mojom/autofill_types.mojom.h"
 #include "content/public/browser/web_contents_user_data.h"
 #include "ui/gfx/image/image.h"
 
@@ -59,10 +60,9 @@
   // --------------------------------------------
 
   // Forwards |accessory_sheet_data| to the view provided by a type-specific
-  // controller to be shown on the accessory sheet. If a field lost focus,
-  // |is_fillable| should be false.
+  // controller to be shown on the accessory sheet.
   virtual void RefreshSuggestionsForField(
-      bool is_fillable,
+      autofill::mojom::FocusedFieldType focused_field_type,
       const autofill::AccessorySheetData& accessory_sheet_data) = 0;
 
   // Completes a filling attempt by recording metrics, giving feedback to the
diff --git a/chrome/browser/autofill/manual_filling_controller_impl.cc b/chrome/browser/autofill/manual_filling_controller_impl.cc
index ac859f4..4946246 100644
--- a/chrome/browser/autofill/manual_filling_controller_impl.cc
+++ b/chrome/browser/autofill/manual_filling_controller_impl.cc
@@ -14,9 +14,11 @@
 #include "chrome/browser/password_manager/password_generation_controller.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/autofill/core/common/autofill_features.h"
+#include "components/autofill/core/common/autofill_util.h"
 #include "content/public/browser/web_contents.h"
 
 using autofill::AccessorySheetData;
+using autofill::mojom::FocusedFieldType;
 
 ManualFillingControllerImpl::~ManualFillingControllerImpl() = default;
 
@@ -60,17 +62,16 @@
 }
 
 void ManualFillingControllerImpl::RefreshSuggestionsForField(
-    bool is_fillable,
+    FocusedFieldType focused_field_type,
     const AccessorySheetData& accessory_sheet_data) {
   view_->OnItemsAvailable(accessory_sheet_data);
 
   // TODO(crbug.com/905669): The decision for showing the sheet or not will need
   // to take into account if Autofill suggestions are also available.
-  if (is_fillable) {
+  if (autofill::IsFillable(focused_field_type))
     view_->SwapSheetWithKeyboard();
-  } else {
+  else
     view_->CloseAccessorySheet();
-  }
 }
 
 void ManualFillingControllerImpl::ShowWhenKeyboardIsVisible(
diff --git a/chrome/browser/autofill/manual_filling_controller_impl.h b/chrome/browser/autofill/manual_filling_controller_impl.h
index 04eaf5e..fbb98d76 100644
--- a/chrome/browser/autofill/manual_filling_controller_impl.h
+++ b/chrome/browser/autofill/manual_filling_controller_impl.h
@@ -26,7 +26,7 @@
 
   // ManualFillingController:
   void RefreshSuggestionsForField(
-      bool is_fillable,
+      autofill::mojom::FocusedFieldType focused_field_type,
       const autofill::AccessorySheetData& accessory_sheet_data) override;
   void OnFilledIntoFocusedField(autofill::FillingStatus status) override;
   void ShowWhenKeyboardIsVisible(FillingSource source) override;
diff --git a/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc b/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc
index 28edcc1..af9f349 100644
--- a/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc
+++ b/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc
@@ -21,6 +21,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
+using autofill::mojom::FocusedFieldType;
 using testing::_;
 using testing::AnyNumber;
 using testing::NiceMock;
@@ -36,8 +37,8 @@
       void(const std::map<base::string16, const autofill::PasswordForm*>&,
            const url::Origin&));
   MOCK_METHOD1(OnFilledIntoFocusedField, void(autofill::FillingStatus));
-  MOCK_METHOD4(RefreshSuggestionsForField,
-               void(const url::Origin&, bool, bool, bool));
+  MOCK_METHOD3(RefreshSuggestionsForField,
+               void(const url::Origin&, FocusedFieldType, bool));
   MOCK_METHOD0(DidNavigateMainFrame, void());
   MOCK_METHOD2(GetFavicon,
                void(int, base::OnceCallback<void(const gfx::Image&)>));
@@ -133,8 +134,8 @@
   EXPECT_CALL(*view(), CloseAccessorySheet());
   EXPECT_CALL(*view(), SwapSheetWithKeyboard())
       .Times(0);  // Don't touch the keyboard!
-  controller()->RefreshSuggestionsForField(
-      /*is_fillable=*/false, dummy_accessory_sheet_data());
+  controller()->RefreshSuggestionsForField(FocusedFieldType::kUnfillableElement,
+                                           dummy_accessory_sheet_data());
 }
 
 // TODO(fhorschig): Check for recorded metrics here or similar to this.
@@ -145,8 +146,8 @@
 
   EXPECT_CALL(*view(), CloseAccessorySheet()).Times(0);
   EXPECT_CALL(*view(), SwapSheetWithKeyboard());
-  controller()->RefreshSuggestionsForField(
-      /*is_fillable=*/true, dummy_accessory_sheet_data());
+  controller()->RefreshSuggestionsForField(FocusedFieldType::kFillableTextField,
+                                           dummy_accessory_sheet_data());
 }
 
 // TODO(fhorschig): Check for recorded metrics here or similar to this.
diff --git a/chrome/browser/autofill/mock_manual_filling_controller.h b/chrome/browser/autofill/mock_manual_filling_controller.h
index c7407ff..8133006 100644
--- a/chrome/browser/autofill/mock_manual_filling_controller.h
+++ b/chrome/browser/autofill/mock_manual_filling_controller.h
@@ -18,7 +18,8 @@
   MOCK_METHOD1(OnAutomaticGenerationStatusChanged, void(bool));
   MOCK_METHOD1(OnFilledIntoFocusedField, void(autofill::FillingStatus));
   MOCK_METHOD2(RefreshSuggestionsForField,
-               void(bool, const autofill::AccessorySheetData&));
+               void(autofill::mojom::FocusedFieldType,
+                    const autofill::AccessorySheetData&));
   MOCK_METHOD1(ShowWhenKeyboardIsVisible,
                void(ManualFillingController::FillingSource));
   MOCK_METHOD1(Hide, void(ManualFillingController::FillingSource));
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
index fd5560f..7300be2 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -848,16 +848,10 @@
       // TODO(crbug.com/884817): Respect |delete_begin_| and |delete_end_| and
       // only clear out entries whose last strikes were created in that
       // timeframe.
-      if (base::FeatureList::IsEnabled(
-              autofill::features::kAutofillSaveCreditCardUsesStrikeSystemV2) ||
-          base::FeatureList::IsEnabled(
-              autofill::features::
-                  kAutofillLocalCardMigrationUsesStrikeSystemV2)) {
-        autofill::StrikeDatabase* strike_database =
-            autofill::StrikeDatabaseFactory::GetForProfile(profile_);
-        if (strike_database)
-          strike_database->ClearAllStrikes();
-      }
+      autofill::StrikeDatabase* strike_database =
+          autofill::StrikeDatabaseFactory::GetForProfile(profile_);
+      if (strike_database)
+        strike_database->ClearAllStrikes();
 
       // Ask for a call back when the above calls are finished.
       web_data_service->GetDBTaskRunner()->PostTaskAndReply(
diff --git a/chrome/browser/chromeos/login/lock/screen_locker.cc b/chrome/browser/chromeos/login/lock/screen_locker.cc
index 9211aef..b756921a 100644
--- a/chrome/browser/chromeos/login/lock/screen_locker.cc
+++ b/chrome/browser/chromeos/login/lock/screen_locker.cc
@@ -8,6 +8,9 @@
 #include <vector>
 
 #include "ash/public/cpp/ash_switches.h"
+#include "ash/public/cpp/login_screen.h"
+#include "ash/public/cpp/login_screen_model.h"
+#include "ash/public/cpp/login_types.h"
 #include "ash/public/interfaces/constants.mojom.h"
 #include "base/bind.h"
 #include "base/command_line.h"
@@ -762,9 +765,8 @@
     if (quick_unlock_storage->fingerprint_storage()->ExceededUnlockAttempts()) {
       VLOG(1) << "Fingerprint unlock is disabled because it reached maximum"
               << " unlock attempt.";
-      delegate_->SetFingerprintState(
-          user.GetAccountId(),
-          ash::mojom::FingerprintState::DISABLED_FROM_ATTEMPTS);
+      ash::LoginScreen::Get()->GetModel()->SetFingerprintState(
+          user.GetAccountId(), ash::FingerprintState::DISABLED_FROM_ATTEMPTS);
       delegate_->ShowErrorMessage(IDS_LOGIN_ERROR_FINGERPRINT_MAX_ATTEMPT,
                                   HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT);
     }
@@ -810,8 +812,8 @@
       if (quick_unlock_storage->fingerprint_storage()
               ->IsFingerprintAvailable()) {
         VLOG(1) << "Require strong auth to make fingerprint unlock available.";
-        delegate_->SetFingerprintState(
-            account_id, ash::mojom::FingerprintState::DISABLED_FROM_TIMEOUT);
+        ash::LoginScreen::Get()->GetModel()->SetFingerprintState(
+            account_id, ash::FingerprintState::DISABLED_FROM_TIMEOUT);
       }
     }
   }
diff --git a/chrome/browser/chromeos/login/lock/screen_locker.h b/chrome/browser/chromeos/login/lock/screen_locker.h
index 38a5874..4ea1628 100644
--- a/chrome/browser/chromeos/login/lock/screen_locker.h
+++ b/chrome/browser/chromeos/login/lock/screen_locker.h
@@ -10,7 +10,6 @@
 #include <string>
 
 #include "ash/public/interfaces/login_screen.mojom.h"
-#include "ash/public/interfaces/login_user_info.mojom.h"
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
@@ -58,10 +57,6 @@
     // Called by ScreenLocker to notify that ash lock animation finishes.
     virtual void OnAshLockAnimationFinished() = 0;
 
-    // Called when fingerprint state has changed.
-    virtual void SetFingerprintState(const AccountId& account_id,
-                                     ash::mojom::FingerprintState state) = 0;
-
     // Called after a fingerprint authentication attempt.
     virtual void NotifyFingerprintAuthResult(const AccountId& account_id,
                                              bool success) = 0;
diff --git a/chrome/browser/chromeos/login/lock/screen_locker_unittest.cc b/chrome/browser/chromeos/login/lock/screen_locker_unittest.cc
index ca7211c..8dd0533 100644
--- a/chrome/browser/chromeos/login/lock/screen_locker_unittest.cc
+++ b/chrome/browser/chromeos/login/lock/screen_locker_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/chromeos/login/lock/screen_locker.h"
 
+#include "ash/public/cpp/login_screen_model.h"
+#include "ash/public/cpp/login_types.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
diff --git a/chrome/browser/chromeos/login/lock/views_screen_locker.cc b/chrome/browser/chromeos/login/lock/views_screen_locker.cc
index 0126b51..c5199b6 100644
--- a/chrome/browser/chromeos/login/lock/views_screen_locker.cc
+++ b/chrome/browser/chromeos/login/lock/views_screen_locker.cc
@@ -9,6 +9,8 @@
 #include <utility>
 
 #include "ash/public/cpp/ash_features.h"
+#include "ash/public/cpp/login_screen.h"
+#include "ash/public/cpp/login_screen_model.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/i18n/time_formatting.h"
@@ -102,10 +104,6 @@
 void ViewsScreenLocker::Init() {
   lock_time_ = base::TimeTicks::Now();
   user_selection_screen_->Init(screen_locker_->users());
-  LoginScreenClient::Get()->login_screen()->SetUserList(
-      user_selection_screen_->UpdateAndReturnUserListForMojo());
-  LoginScreenClient::Get()->login_screen()->SetAllowLoginAsGuest(
-      false /*show_guest*/);
   if (!ime_state_.get())
     ime_state_ = input_method::InputMethodManager::Get()->GetActiveIMEState();
 
@@ -126,6 +124,12 @@
 
 void ViewsScreenLocker::OnLockScreenReady() {
   lock_screen_ready_ = true;
+
+  ash::LoginScreen::Get()->GetModel()->SetUserList(
+      user_selection_screen_->UpdateAndReturnUserListForAsh());
+  LoginScreenClient::Get()->login_screen()->SetAllowLoginAsGuest(
+      false /*show_guest*/);
+
   user_selection_screen_->InitEasyUnlock();
   UMA_HISTOGRAM_TIMES("LockScreen.LockReady",
                       base::TimeTicks::Now() - lock_time_);
@@ -151,13 +155,6 @@
   SessionControllerClientImpl::Get()->NotifyChromeLockAnimationsComplete();
 }
 
-void ViewsScreenLocker::SetFingerprintState(
-    const AccountId& account_id,
-    ash::mojom::FingerprintState state) {
-  LoginScreenClient::Get()->login_screen()->SetFingerprintState(account_id,
-                                                                state);
-}
-
 void ViewsScreenLocker::NotifyFingerprintAuthResult(const AccountId& account_id,
                                                     bool success) {
   LoginScreenClient::Get()->login_screen()->NotifyFingerprintAuthResult(
diff --git a/chrome/browser/chromeos/login/lock/views_screen_locker.h b/chrome/browser/chromeos/login/lock/views_screen_locker.h
index 4e00bdf..655f8ad 100644
--- a/chrome/browser/chromeos/login/lock/views_screen_locker.h
+++ b/chrome/browser/chromeos/login/lock/views_screen_locker.h
@@ -43,8 +43,6 @@
                         HelpAppLauncher::HelpTopic help_topic_id) override;
   void ClearErrors() override;
   void OnAshLockAnimationFinished() override;
-  void SetFingerprintState(const AccountId& account_id,
-                           ash::mojom::FingerprintState state) override;
   void NotifyFingerprintAuthResult(const AccountId& account_id,
                                    bool success) override;
 
diff --git a/chrome/browser/chromeos/login/lock_screen_utils.cc b/chrome/browser/chromeos/login/lock_screen_utils.cc
index ad19865..eb1a1db 100644
--- a/chrome/browser/chromeos/login/lock_screen_utils.cc
+++ b/chrome/browser/chromeos/login/lock_screen_utils.cc
@@ -159,27 +159,21 @@
       rate);
 }
 
-std::vector<ash::mojom::LocaleItemPtr> FromListValueToLocaleItem(
+std::vector<ash::LocaleItem> FromListValueToLocaleItem(
     std::unique_ptr<base::ListValue> locales) {
-  std::vector<ash::mojom::LocaleItemPtr> result;
+  std::vector<ash::LocaleItem> result;
   for (const auto& locale : *locales) {
     const base::DictionaryValue* dictionary;
     if (!locale.GetAsDictionary(&dictionary))
       continue;
 
-    ash::mojom::LocaleItemPtr locale_item = ash::mojom::LocaleItem::New();
-    std::string language_code;
-    dictionary->GetString("value", &language_code);
-    locale_item->language_code = language_code;
-
-    std::string title;
-    dictionary->GetString("title", &title);
-    locale_item->title = title;
-
+    ash::LocaleItem locale_item;
+    dictionary->GetString("value", &locale_item.language_code);
+    dictionary->GetString("title", &locale_item.title);
     std::string group_name;
     dictionary->GetString("optionGroupName", &group_name);
     if (!group_name.empty())
-      locale_item->group_name = group_name;
+      locale_item.group_name = group_name;
     result.push_back(std::move(locale_item));
   }
   return result;
diff --git a/chrome/browser/chromeos/login/lock_screen_utils.h b/chrome/browser/chromeos/login/lock_screen_utils.h
index 66f8090..88e4e01 100644
--- a/chrome/browser/chromeos/login/lock_screen_utils.h
+++ b/chrome/browser/chromeos/login/lock_screen_utils.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_LOCK_SCREEN_UTILS_H_
 #define CHROME_BROWSER_CHROMEOS_LOGIN_LOCK_SCREEN_UTILS_H_
 
-#include "ash/public/interfaces/login_user_info.mojom.h"
+#include "ash/public/cpp/login_types.h"
 #include "ui/base/ime/chromeos/input_method_manager.h"
 
 class AccountId;
@@ -36,8 +36,8 @@
 // Update the keyboard settings for |account_id|.
 void SetKeyboardSettings(const AccountId& account_id);
 
-// Covert a ListValue of locale info to a list of mojo struct LocaleItem.
-std::vector<ash::mojom::LocaleItemPtr> FromListValueToLocaleItem(
+// Covert a ListValue of locale info to a list of ash struct LocaleItem.
+std::vector<ash::LocaleItem> FromListValueToLocaleItem(
     std::unique_ptr<base::ListValue> locales);
 
 }  // namespace lock_screen_utils
diff --git a/chrome/browser/chromeos/login/screens/user_selection_screen.cc b/chrome/browser/chromeos/login/screens/user_selection_screen.cc
index 2c12df7..6d8088a 100644
--- a/chrome/browser/chromeos/login/screens/user_selection_screen.cc
+++ b/chrome/browser/chromeos/login/screens/user_selection_screen.cc
@@ -9,6 +9,7 @@
 #include <memory>
 #include <utility>
 
+#include "ash/public/cpp/login_types.h"
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/command_line.h"
@@ -159,21 +160,21 @@
 }
 
 // Determines the initial fingerprint state for the given user.
-ash::mojom::FingerprintState GetInitialFingerprintState(
+ash::FingerprintState GetInitialFingerprintState(
     const user_manager::User* user) {
   // User must be logged in.
   if (!user->is_logged_in())
-    return ash::mojom::FingerprintState::UNAVAILABLE;
+    return ash::FingerprintState::UNAVAILABLE;
 
   // Quick unlock storage must be available.
   quick_unlock::QuickUnlockStorage* quick_unlock_storage =
       quick_unlock::QuickUnlockFactory::GetForUser(user);
   if (!quick_unlock_storage)
-    return ash::mojom::FingerprintState::UNAVAILABLE;
+    return ash::FingerprintState::UNAVAILABLE;
 
   // Fingerprint is not registered for this account.
   if (!quick_unlock_storage->fingerprint_storage()->HasRecord())
-    return ash::mojom::FingerprintState::UNAVAILABLE;
+    return ash::FingerprintState::UNAVAILABLE;
 
   // Fingerprint unlock attempts should not be exceeded, as the lock screen has
   // not been displayed yet.
@@ -182,14 +183,14 @@
 
   // It has been too long since the last authentication.
   if (!quick_unlock_storage->HasStrongAuth())
-    return ash::mojom::FingerprintState::DISABLED_FROM_TIMEOUT;
+    return ash::FingerprintState::DISABLED_FROM_TIMEOUT;
 
   // Auth is available.
   if (quick_unlock_storage->IsFingerprintAuthenticationAvailable())
-    return ash::mojom::FingerprintState::AVAILABLE;
+    return ash::FingerprintState::AVAILABLE;
 
   // Default to unavailabe.
-  return ash::mojom::FingerprintState::UNAVAILABLE;
+  return ash::FingerprintState::UNAVAILABLE;
 }
 
 // Returns true if dircrypto migration check should be performed.
@@ -248,7 +249,7 @@
 
 void GetMultiProfilePolicy(const user_manager::User* user,
                            bool* out_is_allowed,
-                           ash::mojom::MultiProfileUserBehavior* out_policy) {
+                           ash::MultiProfileUserBehavior* out_policy) {
   const std::string& user_id = user->GetAccountId().GetUserEmail();
   MultiProfileUserController* multi_profile_user_controller =
       ChromeUserManager::Get()->GetMultiProfileUserController();
@@ -405,9 +406,9 @@
   user_dict->SetBoolean(kKeySignedIn, user->is_logged_in());
   user_dict->SetBoolean(kKeyIsOwner, is_owner);
   user_dict->SetBoolean(kKeyIsActiveDirectory, user->IsActiveDirectoryUser());
-  user_dict->SetBoolean(kKeyAllowFingerprint,
-                        GetInitialFingerprintState(user) ==
-                            ash::mojom::FingerprintState::AVAILABLE);
+  user_dict->SetBoolean(
+      kKeyAllowFingerprint,
+      GetInitialFingerprintState(user) == ash::FingerprintState::AVAILABLE);
 
   FillMultiProfileUserPrefs(user, user_dict, is_signin_to_add);
 
@@ -428,7 +429,7 @@
   }
 
   bool is_user_allowed;
-  ash::mojom::MultiProfileUserBehavior policy;
+  ash::MultiProfileUserBehavior policy;
   GetMultiProfilePolicy(user, &is_user_allowed, &policy);
   user_dict->SetBoolean(kKeyMultiProfilesAllowed, is_user_allowed);
   user_dict->SetInteger(kKeyMultiProfilesPolicy, static_cast<int>(policy));
@@ -474,33 +475,32 @@
 }
 
 // static
-ash::mojom::UserAvatarPtr UserSelectionScreen::BuildMojoUserAvatarForUser(
-    const user_manager::User* user) {
-  auto avatar = ash::mojom::UserAvatar::New();
-  if (!user->GetImage().isNull()) {
-    avatar->image = user->GetImage();
-  } else {
-    avatar->image = *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
+ash::UserAvatar UserSelectionScreen::BuildAshUserAvatarForUser(
+    const user_manager::User& user) {
+  ash::UserAvatar avatar;
+  avatar.image = user.GetImage();
+  if (avatar.image.isNull()) {
+    avatar.image = *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
         IDR_LOGIN_DEFAULT_USER);
   }
 
   // TODO(jdufault): Unify image handling between this code and
   // user_image_source::GetUserImageInternal.
-  auto load_image_from_resource = [&](int resource_id) {
+  auto load_image_from_resource = [&avatar](int resource_id) {
     auto& rb = ui::ResourceBundle::GetSharedInstance();
     base::StringPiece avatar_data =
         rb.GetRawDataResourceForScale(resource_id, rb.GetMaxScaleFactor());
-    avatar->bytes.assign(avatar_data.begin(), avatar_data.end());
+    avatar.bytes.assign(avatar_data.begin(), avatar_data.end());
   };
-  if (user->has_image_bytes()) {
-    avatar->bytes.assign(
-        user->image_bytes()->front(),
-        user->image_bytes()->front() + user->image_bytes()->size());
-  } else if (user->HasDefaultImage()) {
+  if (user.has_image_bytes()) {
+    avatar.bytes.assign(
+        user.image_bytes()->front(),
+        user.image_bytes()->front() + user.image_bytes()->size());
+  } else if (user.HasDefaultImage()) {
     int resource_id = chromeos::default_user_image::kDefaultImageResourceIDs
-        [user->image_index()];
+        [user.image_index()];
     load_image_from_resource(resource_id);
-  } else if (user->image_is_stub()) {
+  } else if (user.image_is_stub()) {
     load_image_from_resource(IDR_LOGIN_DEFAULT_USER);
   }
 
@@ -796,9 +796,9 @@
   return users_list;
 }
 
-std::vector<ash::mojom::LoginUserInfoPtr>
-UserSelectionScreen::UpdateAndReturnUserListForMojo() {
-  std::vector<ash::mojom::LoginUserInfoPtr> user_info_list;
+std::vector<ash::LoginUserInfo>
+UserSelectionScreen::UpdateAndReturnUserListForAsh() {
+  std::vector<ash::LoginUserInfo> user_info_list;
 
   const AccountId owner = GetOwnerAccountId();
   const bool is_signin_to_add = IsSigninToAdd();
@@ -819,34 +819,33 @@
                    : proximity_auth::mojom::AuthType::OFFLINE_PASSWORD);
     user_auth_type_map_[account_id] = initial_auth_type;
 
-    ash::mojom::LoginUserInfoPtr user_info = ash::mojom::LoginUserInfo::New();
-    user_info->basic_user_info = ash::mojom::UserInfo::New();
-    user_info->basic_user_info->type = user->GetType();
-    user_info->basic_user_info->account_id = user->GetAccountId();
-    user_info->basic_user_info->display_name =
+    ash::LoginUserInfo user_info;
+    user_info.basic_user_info.type = user->GetType();
+    user_info.basic_user_info.account_id = user->GetAccountId();
+    user_info.basic_user_info.display_name =
         base::UTF16ToUTF8(user->GetDisplayName());
-    user_info->basic_user_info->display_email = user->display_email();
-    user_info->basic_user_info->avatar = BuildMojoUserAvatarForUser(user);
-    user_info->auth_type = initial_auth_type;
-    user_info->is_signed_in = user->is_logged_in();
-    user_info->is_device_owner = is_owner;
-    user_info->can_remove = CanRemoveUser(user);
-    user_info->fingerprint_state = GetInitialFingerprintState(user);
+    user_info.basic_user_info.display_email = user->display_email();
+    user_info.basic_user_info.avatar = BuildAshUserAvatarForUser(*user);
+    user_info.auth_type = initial_auth_type;
+    user_info.is_signed_in = user->is_logged_in();
+    user_info.is_device_owner = is_owner;
+    user_info.can_remove = CanRemoveUser(user);
+    user_info.fingerprint_state = GetInitialFingerprintState(user);
 
     // Fill multi-profile data.
     if (!is_signin_to_add) {
-      user_info->is_multiprofile_allowed = true;
+      user_info.is_multiprofile_allowed = true;
     } else {
-      GetMultiProfilePolicy(user, &user_info->is_multiprofile_allowed,
-                            &user_info->multiprofile_policy);
+      GetMultiProfilePolicy(user, &user_info.is_multiprofile_allowed,
+                            &user_info.multiprofile_policy);
     }
 
     // Fill public session data.
     if (user->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT) {
-      user_info->public_account_info = ash::mojom::PublicAccountInfo::New();
       std::string domain;
+      user_info.public_account_info.emplace();
       if (GetEnterpriseDomain(&domain))
-        user_info->public_account_info->enterprise_domain = domain;
+        user_info.public_account_info->enterprise_domain = domain;
 
       const std::vector<std::string>* public_session_recommended_locales =
           public_session_recommended_locales_.find(account_id) ==
@@ -859,22 +858,22 @@
           GetPublicSessionLocales(public_session_recommended_locales,
                                   &selected_locale, &has_multiple_locales);
       DCHECK(available_locales);
-      user_info->public_account_info->available_locales =
+      user_info.public_account_info->available_locales =
           lock_screen_utils::FromListValueToLocaleItem(
               std::move(available_locales));
-      user_info->public_account_info->default_locale = selected_locale;
-      user_info->public_account_info->show_advanced_view = has_multiple_locales;
+      user_info.public_account_info->default_locale = selected_locale;
+      user_info.public_account_info->show_advanced_view = has_multiple_locales;
       // Do not show expanded view when in demo mode.
-      user_info->public_account_info->show_expanded_view =
+      user_info.public_account_info->show_expanded_view =
           !DemoSession::IsDeviceInDemoMode();
     }
 
-    user_info->can_remove = CanRemoveUser(user);
+    user_info.can_remove = CanRemoveUser(user);
 
     // Send a request to get keyboard layouts for default locale.
     if (is_public_account && LoginScreenClient::HasInstance()) {
       LoginScreenClient::Get()->RequestPublicSessionKeyboardLayouts(
-          account_id, user_info->public_account_info->default_locale);
+          account_id, user_info.public_account_info->default_locale);
     }
 
     user_info_list.push_back(std::move(user_info));
diff --git a/chrome/browser/chromeos/login/screens/user_selection_screen.h b/chrome/browser/chromeos/login/screens/user_selection_screen.h
index 83cd070..be173f3 100644
--- a/chrome/browser/chromeos/login/screens/user_selection_screen.h
+++ b/chrome/browser/chromeos/login/screens/user_selection_screen.h
@@ -9,6 +9,8 @@
 #include <string>
 #include <vector>
 
+#include "ash/public/cpp/login_types.h"
+#include "ash/public/cpp/session/user_info.h"
 #include "ash/public/interfaces/login_screen.mojom.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
@@ -112,13 +114,12 @@
   // Determines if user auth status requires online sign in.
   static bool ShouldForceOnlineSignIn(const user_manager::User* user);
 
-  // Builds a |UserAvatarPtr| instance which contains the current image for
-  // |user|.
-  static ash::mojom::UserAvatarPtr BuildMojoUserAvatarForUser(
-      const user_manager::User* user);
+  // Builds a |UserAvatar| instance which contains the current image for |user|.
+  static ash::UserAvatar BuildAshUserAvatarForUser(
+      const user_manager::User& user);
 
   std::unique_ptr<base::ListValue> UpdateAndReturnUserListForWebUI();
-  std::vector<ash::mojom::LoginUserInfoPtr> UpdateAndReturnUserListForMojo();
+  std::vector<ash::LoginUserInfo> UpdateAndReturnUserListForAsh();
   void SetUsersLoaded(bool loaded);
 
  protected:
diff --git a/chrome/browser/chromeos/login/ui/login_display_mojo.cc b/chrome/browser/chromeos/login/ui/login_display_mojo.cc
index c0c60e2..67d67e8 100644
--- a/chrome/browser/chromeos/login/ui/login_display_mojo.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_mojo.cc
@@ -4,7 +4,9 @@
 
 #include "chrome/browser/chromeos/login/ui/login_display_mojo.h"
 
-#include "ash/public/interfaces/login_user_info.mojom.h"
+#include "ash/public/cpp/login_screen.h"
+#include "ash/public/cpp/login_screen_model.h"
+#include "ash/public/cpp/login_types.h"
 #include "base/bind.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
@@ -58,7 +60,6 @@
   // login screen multiple times. Views-login only supports initialization once.
   if (!initialized_) {
     client->SetDelegate(host_);
-
     client->login_screen()->ShowLoginScreen(
         base::BindOnce(&LoginDisplayMojo::OnLoginScreenShown,
                        weak_factory_.GetWeakPtr(), filtered_users.empty()));
@@ -66,8 +67,12 @@
 
   UserSelectionScreen* user_selection_screen = host_->user_selection_screen();
   user_selection_screen->Init(filtered_users);
-  client->login_screen()->SetUserList(
-      user_selection_screen->UpdateAndReturnUserListForMojo());
+  // The login screen will not be ready for the user list until
+  // ShowLoginScreen() is finished.
+  if (ash::LoginScreen::Get()->GetModel()) {
+    ash::LoginScreen::Get()->GetModel()->SetUserList(
+        user_selection_screen->UpdateAndReturnUserListForAsh());
+  }
   client->login_screen()->SetAllowLoginAsGuest(show_guest);
   user_selection_screen->SetUsersLoaded(true /*loaded*/);
 
@@ -258,14 +263,17 @@
 }
 
 void LoginDisplayMojo::OnUserImageChanged(const user_manager::User& user) {
-  LoginScreenClient::Get()->login_screen()->SetAvatarForUser(
+  ash::LoginScreen::Get()->GetModel()->SetAvatarForUser(
       user.GetAccountId(),
-      UserSelectionScreen::BuildMojoUserAvatarForUser(&user));
+      UserSelectionScreen::BuildAshUserAvatarForUser(user));
 }
 
 void LoginDisplayMojo::OnLoginScreenShown(bool users_empty, bool did_show) {
   CHECK(did_show);
 
+  ash::LoginScreen::Get()->GetModel()->SetUserList(
+      host_->user_selection_screen()->UpdateAndReturnUserListForAsh());
+
   // login-prompt-visible is recorded and tracked to verify boot performance
   // does not regress. Autotests may also depend on it (ie,
   // login_SameSessionTwice).
diff --git a/chrome/browser/chromeos/login/ui/login_display_mojo.h b/chrome/browser/chromeos/login/ui/login_display_mojo.h
index b21fa05..52b7b25 100644
--- a/chrome/browser/chromeos/login/ui/login_display_mojo.h
+++ b/chrome/browser/chromeos/login/ui/login_display_mojo.h
@@ -19,6 +19,7 @@
 
 // Interface used by UI-agnostic code to send messages to views-based login
 // screen.
+// TODO(estade): rename to LoginDisplayAsh.
 class LoginDisplayMojo : public LoginDisplay,
                          public SigninScreenHandlerDelegate,
                          public user_manager::UserManager::Observer {
diff --git a/chrome/browser/chromeos/login/user_board_view_mojo.cc b/chrome/browser/chromeos/login/user_board_view_mojo.cc
index 4bd827b..e206b19 100644
--- a/chrome/browser/chromeos/login/user_board_view_mojo.cc
+++ b/chrome/browser/chromeos/login/user_board_view_mojo.cc
@@ -6,6 +6,9 @@
 
 #include <utility>
 
+#include "ash/public/cpp/login_screen.h"
+#include "ash/public/cpp/login_screen_model.h"
+#include "ash/public/cpp/login_types.h"
 #include "chrome/browser/chromeos/login/lock_screen_utils.h"
 #include "chrome/browser/ui/ash/login_screen_client.h"
 
@@ -13,48 +16,47 @@
 
 namespace {
 
-ash::mojom::EasyUnlockIconId GetEasyUnlockIconIdFromUserPodCustomIconId(
+ash::EasyUnlockIconId GetEasyUnlockIconIdFromUserPodCustomIconId(
     proximity_auth::ScreenlockBridge::UserPodCustomIcon icon) {
   switch (icon) {
     case proximity_auth::ScreenlockBridge::USER_POD_CUSTOM_ICON_NONE:
-      return ash::mojom::EasyUnlockIconId::NONE;
+      return ash::EasyUnlockIconId::NONE;
     case proximity_auth::ScreenlockBridge::USER_POD_CUSTOM_ICON_HARDLOCKED:
-      return ash::mojom::EasyUnlockIconId::HARDLOCKED;
+      return ash::EasyUnlockIconId::HARDLOCKED;
     case proximity_auth::ScreenlockBridge::USER_POD_CUSTOM_ICON_LOCKED:
-      return ash::mojom::EasyUnlockIconId::LOCKED;
+      return ash::EasyUnlockIconId::LOCKED;
     case proximity_auth::ScreenlockBridge::
         USER_POD_CUSTOM_ICON_LOCKED_TO_BE_ACTIVATED:
-      return ash::mojom::EasyUnlockIconId::LOCKED_TO_BE_ACTIVATED;
+      return ash::EasyUnlockIconId::LOCKED_TO_BE_ACTIVATED;
     case proximity_auth::ScreenlockBridge::
         USER_POD_CUSTOM_ICON_LOCKED_WITH_PROXIMITY_HINT:
-      return ash::mojom::EasyUnlockIconId::LOCKED_WITH_PROXIMITY_HINT;
+      return ash::EasyUnlockIconId::LOCKED_WITH_PROXIMITY_HINT;
     case proximity_auth::ScreenlockBridge::USER_POD_CUSTOM_ICON_UNLOCKED:
-      return ash::mojom::EasyUnlockIconId::UNLOCKED;
+      return ash::EasyUnlockIconId::UNLOCKED;
     case proximity_auth::ScreenlockBridge::USER_POD_CUSTOM_ICON_SPINNER:
-      return ash::mojom::EasyUnlockIconId::SPINNER;
+      return ash::EasyUnlockIconId::SPINNER;
   }
 }
 
 // Converts parameters to a mojo struct that can be sent to the
 // screenlock view-based UI.
-ash::mojom::EasyUnlockIconOptionsPtr ToEasyUnlockIconOptionsPtr(
+ash::EasyUnlockIconOptions ToEasyUnlockIconOptions(
     const proximity_auth::ScreenlockBridge::UserPodCustomIconOptions&
         icon_options) {
-  ash::mojom::EasyUnlockIconOptionsPtr options =
-      ash::mojom::EasyUnlockIconOptions::New();
-  options->icon =
+  ash::EasyUnlockIconOptions options;
+  options.icon =
       GetEasyUnlockIconIdFromUserPodCustomIconId(icon_options.icon());
 
   if (!icon_options.tooltip().empty()) {
-    options->tooltip = icon_options.tooltip();
-    options->autoshow_tooltip = icon_options.autoshow_tooltip();
+    options.tooltip = icon_options.tooltip();
+    options.autoshow_tooltip = icon_options.autoshow_tooltip();
   }
 
   if (!icon_options.aria_label().empty())
-    options->aria_label = icon_options.aria_label();
+    options.aria_label = icon_options.aria_label();
 
   if (icon_options.hardlock_on_click())
-    options->hardlock_on_click = true;
+    options.hardlock_on_click = true;
 
   return options;
 }
@@ -78,7 +80,7 @@
     const std::string& default_locale,
     bool multiple_recommended_locales) {
   DCHECK(locales);
-  LoginScreenClient::Get()->login_screen()->SetPublicSessionLocales(
+  ash::LoginScreen::Get()->GetModel()->SetPublicSessionLocales(
       account_id,
       lock_screen_utils::FromListValueToLocaleItem(std::move(locales)),
       default_locale, multiple_recommended_locales);
@@ -113,16 +115,12 @@
     const AccountId& account_id,
     const proximity_auth::ScreenlockBridge::UserPodCustomIconOptions&
         icon_options) {
-  ash::mojom::EasyUnlockIconOptionsPtr icon =
-      ToEasyUnlockIconOptionsPtr(icon_options);
-  if (!icon)
-    return;
-  LoginScreenClient::Get()->login_screen()->ShowUserPodCustomIcon(
-      account_id, std::move(icon));
+  ash::LoginScreen::Get()->GetModel()->ShowEasyUnlockIcon(
+      account_id, ToEasyUnlockIconOptions(icon_options));
 }
 
 void UserBoardViewMojo::HideUserPodCustomIcon(const AccountId& account_id) {
-  LoginScreenClient::Get()->login_screen()->HideUserPodCustomIcon(account_id);
+  ash::LoginScreen::Get()->GetModel()->ShowEasyUnlockIcon(account_id, {});
 }
 
 void UserBoardViewMojo::SetAuthType(const AccountId& account_id,
diff --git a/chrome/browser/chromeos/login/users/multi_profile_user_controller.cc b/chrome/browser/chromeos/login/users/multi_profile_user_controller.cc
index 0694306..5347d0cf 100644
--- a/chrome/browser/chromeos/login/users/multi_profile_user_controller.cc
+++ b/chrome/browser/chromeos/login/users/multi_profile_user_controller.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "ash/public/cpp/login_types.h"
 #include "base/bind.h"
 #include "chrome/browser/chromeos/login/users/multi_profile_user_controller_delegate.h"
 #include "chrome/browser/chromeos/policy/policy_cert_service.h"
@@ -122,17 +123,17 @@
 }
 
 // static
-ash::mojom::MultiProfileUserBehavior
+ash::MultiProfileUserBehavior
 MultiProfileUserController::UserBehaviorStringToEnum(
     const std::string& behavior) {
   if (behavior == kBehaviorPrimaryOnly)
-    return ash::mojom::MultiProfileUserBehavior::PRIMARY_ONLY;
+    return ash::MultiProfileUserBehavior::PRIMARY_ONLY;
   if (behavior == kBehaviorNotAllowed)
-    return ash::mojom::MultiProfileUserBehavior::NOT_ALLOWED;
+    return ash::MultiProfileUserBehavior::NOT_ALLOWED;
   if (behavior == kBehaviorOwnerPrimaryOnly)
-    return ash::mojom::MultiProfileUserBehavior::OWNER_PRIMARY_ONLY;
+    return ash::MultiProfileUserBehavior::OWNER_PRIMARY_ONLY;
 
-  return ash::mojom::MultiProfileUserBehavior::UNRESTRICTED;
+  return ash::MultiProfileUserBehavior::UNRESTRICTED;
 }
 
 bool MultiProfileUserController::IsUserAllowedInSession(
diff --git a/chrome/browser/chromeos/login/users/multi_profile_user_controller.h b/chrome/browser/chromeos/login/users/multi_profile_user_controller.h
index b071b621..3a874ce 100644
--- a/chrome/browser/chromeos/login/users/multi_profile_user_controller.h
+++ b/chrome/browser/chromeos/login/users/multi_profile_user_controller.h
@@ -9,7 +9,6 @@
 #include <string>
 #include <vector>
 
-#include "ash/public/interfaces/login_user_info.mojom.h"
 #include "base/macros.h"
 
 class PrefChangeRegistrar;
@@ -17,6 +16,10 @@
 class PrefService;
 class Profile;
 
+namespace ash {
+enum class MultiProfileUserBehavior;
+}
+
 namespace user_prefs {
 class PrefRegistrySyncable;
 }
@@ -72,7 +75,7 @@
   static UserAllowedInSessionReason GetPrimaryUserPolicy();
 
   // Returns the user behavior in MultiProfileUserBehavior enum.
-  static ash::mojom::MultiProfileUserBehavior UserBehaviorStringToEnum(
+  static ash::MultiProfileUserBehavior UserBehaviorStringToEnum(
       const std::string& behavior);
 
   // Returns true if user allowed to be in the current session. If |reason| not
diff --git a/chrome/browser/download/download_ui_model.cc b/chrome/browser/download/download_ui_model.cc
index 49e41b7..2fc05de 100644
--- a/chrome/browser/download/download_ui_model.cc
+++ b/chrome/browser/download/download_ui_model.cc
@@ -250,18 +250,9 @@
       gfx::ElideFilename(GetFileNameToReportUser(), font_list, base_width,
                          gfx::Typesetter::BROWSER);
 
-  bool request_ap_verdicts = false;
-#if defined(FULL_SAFE_BROWSING)
-  request_ap_verdicts = safe_browsing::AdvancedProtectionStatusManager::
-      RequestsAdvancedProtectionVerdicts(profile());
-#endif
-
   switch (GetDangerType()) {
     case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: {
-      return l10n_util::GetStringUTF16(
-          request_ap_verdicts
-              ? IDS_PROMPT_MALICIOUS_DOWNLOAD_URL_IN_ADVANCED_PROTECTION
-              : IDS_PROMPT_MALICIOUS_DOWNLOAD_URL);
+      return l10n_util::GetStringUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_URL);
     }
     case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: {
       if (IsExtensionDownload()) {
@@ -274,13 +265,15 @@
     }
     case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
     case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: {
-      return l10n_util::GetStringFUTF16(
-          request_ap_verdicts
-              ? IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT_IN_ADVANCED_PROTECTION
-              : IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT,
-          elided_filename);
+      return l10n_util::GetStringFUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT,
+                                        elided_filename);
     }
     case download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: {
+      bool request_ap_verdicts = false;
+#if defined(FULL_SAFE_BROWSING)
+      request_ap_verdicts = safe_browsing::AdvancedProtectionStatusManager::
+          RequestsAdvancedProtectionVerdicts(profile());
+#endif
       return l10n_util::GetStringFUTF16(
           request_ap_verdicts
               ? IDS_PROMPT_UNCOMMON_DOWNLOAD_CONTENT_IN_ADVANCED_PROTECTION
@@ -288,11 +281,8 @@
           elided_filename);
     }
     case download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: {
-      return l10n_util::GetStringFUTF16(
-          request_ap_verdicts
-              ? IDS_PROMPT_DOWNLOAD_CHANGES_SETTINGS_IN_ADVANCED_PROTECTION
-              : IDS_PROMPT_DOWNLOAD_CHANGES_SETTINGS,
-          elided_filename);
+      return l10n_util::GetStringFUTF16(IDS_PROMPT_DOWNLOAD_CHANGES_SETTINGS,
+                                        elided_filename);
     }
     case download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
     case download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
diff --git a/chrome/browser/download/notification/download_item_notification.cc b/chrome/browser/download/notification/download_item_notification.cc
index daeb76a..7db1242 100644
--- a/chrome/browser/download/notification/download_item_notification.cc
+++ b/chrome/browser/download/notification/download_item_notification.cc
@@ -689,14 +689,9 @@
   DCHECK(item_->IsDangerous());
   base::string16 elided_filename =
       item_->GetFileNameToReportUser().LossyDisplayName();
-  bool requests_ap_verdicts = safe_browsing::AdvancedProtectionStatusManager::
-      RequestsAdvancedProtectionVerdicts(profile());
   switch (item_->GetDangerType()) {
     case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: {
-      return l10n_util::GetStringUTF16(
-          requests_ap_verdicts
-              ? IDS_PROMPT_MALICIOUS_DOWNLOAD_URL_IN_ADVANCED_PROTECTION
-              : IDS_PROMPT_MALICIOUS_DOWNLOAD_URL);
+      return l10n_util::GetStringUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_URL);
     }
     case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: {
       if (IsExtensionDownload(item_.get())) {
@@ -709,13 +704,13 @@
     }
     case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
     case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: {
-      return l10n_util::GetStringFUTF16(
-          requests_ap_verdicts
-              ? IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT_IN_ADVANCED_PROTECTION
-              : IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT,
-          elided_filename);
+      return l10n_util::GetStringFUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT,
+                                        elided_filename);
     }
     case download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: {
+      bool requests_ap_verdicts =
+          safe_browsing::AdvancedProtectionStatusManager::
+              RequestsAdvancedProtectionVerdicts(profile());
       return l10n_util::GetStringFUTF16(
           requests_ap_verdicts
               ? IDS_PROMPT_UNCOMMON_DOWNLOAD_CONTENT_IN_ADVANCED_PROTECTION
@@ -723,11 +718,8 @@
           elided_filename);
     }
     case download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: {
-      return l10n_util::GetStringFUTF16(
-          requests_ap_verdicts
-              ? IDS_PROMPT_DOWNLOAD_CHANGES_SETTINGS_IN_ADVANCED_PROTECTION
-              : IDS_PROMPT_DOWNLOAD_CHANGES_SETTINGS,
-          elided_filename);
+      return l10n_util::GetStringFUTF16(IDS_PROMPT_DOWNLOAD_CHANGES_SETTINGS,
+                                        elided_filename);
     }
     case download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
     case download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index dc14e35..693bc27 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2113,11 +2113,6 @@
     "expiry_milestone": -1
   },
   {
-    "name": "incognito-strings",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "installable-ink-drop",
     "owners": [ "collinbaker", "pbos" ],
     "expiry_milestone": 77
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 6dcef66..1ad9a7f 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -465,14 +465,6 @@
     "If enabled, adds a [No thanks] button to credit card save bubbles and "
     "updates their title headers.";
 
-const char kEnableAutofillSaveCreditCardUsesStrikeSystemV2Name[] =
-    "Enable limit on offering to save the same credit card repeatedly using "
-    "the updated strike system implementation";
-const char kEnableAutofillSaveCreditCardUsesStrikeSystemV2Description[] =
-    "If enabled, uses the updated strike system implementation to prevent "
-    "popping up the credit card offer-to-save prompt if it has repeatedly been "
-    "ignored, declined, or failed.";
-
 const char kEnableAutofillSendExperimentIdsInPaymentsRPCsName[] =
     "Send experiment flag IDs in calls to Google Payments";
 const char kEnableAutofillSendExperimentIdsInPaymentsRPCsDescription[] =
@@ -2230,10 +2222,6 @@
 const char kIdentityDiscDescription[] =
     "Enables Identity Disc, profile avatar icon button in toolbar.";
 
-const char kIncognitoStringsName[] = "Alternate incognito strings";
-const char kIncognitoStringsDescription[] =
-    "Show alternate incognito strings if enabled.";
-
 const char kInterestFeedContentSuggestionsDescription[] =
     "Use the interest feed to render content suggestions. Currently "
     "content "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 7d21981..c096282 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -293,9 +293,6 @@
 extern const char kEnableAutofillSaveCardImprovedUserConsentName[];
 extern const char kEnableAutofillSaveCardImprovedUserConsentDescription[];
 
-extern const char kEnableAutofillSaveCreditCardUsesStrikeSystemV2Name[];
-extern const char kEnableAutofillSaveCreditCardUsesStrikeSystemV2Description[];
-
 extern const char kEnableAutofillToolkitViewsCreditCardDialogsMac[];
 extern const char kEnableAutofillToolkitViewsCreditCardDialogsMacDescription[];
 
@@ -1331,9 +1328,6 @@
 extern const char kIdentityDiscName[];
 extern const char kIdentityDiscDescription[];
 
-extern const char kIncognitoStringsName[];
-extern const char kIncognitoStringsDescription[];
-
 extern const char kInterestFeedContentSuggestionsName[];
 extern const char kInterestFeedContentSuggestionsDescription[];
 
diff --git a/chrome/browser/media/android/cdm/media_drm_origin_id_manager.cc b/chrome/browser/media/android/cdm/media_drm_origin_id_manager.cc
index 47ade91..63887c8 100644
--- a/chrome/browser/media/android/cdm/media_drm_origin_id_manager.cc
+++ b/chrome/browser/media/android/cdm/media_drm_origin_id_manager.cc
@@ -192,6 +192,10 @@
 // (successfully or not).
 class MediaDrmProvisionHelper {
  public:
+  using ProvisionedOriginIdCB = base::OnceCallback<void(
+      bool success,
+      const MediaDrmOriginIdManager::MediaDrmOriginId& origin_id)>;
+
   MediaDrmProvisionHelper() {
     DVLOG(1) << __func__;
     DCHECK(media::MediaDrmBridge::IsPerOriginProvisioningSupported());
@@ -202,7 +206,7 @@
                                 ->GetSharedURLLoaderFactory());
   }
 
-  void Provision(MediaDrmOriginIdManager::ProvisionedOriginIdCB callback) {
+  void Provision(ProvisionedOriginIdCB callback) {
     DVLOG(1) << __func__;
 
     complete_callback_ = std::move(callback);
@@ -267,7 +271,7 @@
   }
 
   media::CreateFetcherCB create_fetcher_cb_;
-  MediaDrmOriginIdManager::ProvisionedOriginIdCB complete_callback_;
+  ProvisionedOriginIdCB complete_callback_;
   base::UnguessableToken origin_id_;
   scoped_refptr<media::MediaDrmBridge> media_drm_bridge_;
 };
@@ -345,7 +349,7 @@
   // Reject any pending requests.
   while (!pending_provisioned_origin_id_cbs_.empty()) {
     std::move(pending_provisioned_origin_id_cbs_.front())
-        .Run(false, base::nullopt);
+        .Run(GetOriginIdStatus::kFailure, base::nullopt);
     pending_provisioned_origin_id_cbs_.pop();
   }
 }
@@ -403,7 +407,8 @@
   }
 
   // There is an origin ID available so pass it to the caller.
-  std::move(callback).Run(true, origin_id);
+  std::move(callback).Run(GetOriginIdStatus::kSuccessWithPreProvisionedOriginId,
+                          origin_id);
 }
 
 void MediaDrmOriginIdManager::StartProvisioningAsync() {
@@ -467,7 +472,8 @@
       base::queue<ProvisionedOriginIdCB> pending_requests;
       pending_requests.swap(pending_provisioned_origin_id_cbs_);
       while (!pending_requests.empty()) {
-        std::move(pending_requests.front()).Run(false, base::nullopt);
+        std::move(pending_requests.front())
+            .Run(GetOriginIdStatus::kFailure, base::nullopt);
         pending_requests.pop();
       }
     }
@@ -481,7 +487,9 @@
   // origin IDs in the preference.
   DCHECK(origin_id);
   if (!pending_provisioned_origin_id_cbs_.empty()) {
-    std::move(pending_provisioned_origin_id_cbs_.front()).Run(true, origin_id);
+    std::move(pending_provisioned_origin_id_cbs_.front())
+        .Run(GetOriginIdStatus::kSuccessWithNewlyProvisionedOriginId,
+             origin_id);
     pending_provisioned_origin_id_cbs_.pop();
   } else {
     DictionaryPrefUpdate update(pref_service_, kMediaDrmOriginIds);
diff --git a/chrome/browser/media/android/cdm/media_drm_origin_id_manager.h b/chrome/browser/media/android/cdm/media_drm_origin_id_manager.h
index 0854973..d311acb 100644
--- a/chrome/browser/media/android/cdm/media_drm_origin_id_manager.h
+++ b/chrome/browser/media/android/cdm/media_drm_origin_id_manager.h
@@ -32,12 +32,19 @@
 // destroyed when the Profile goes away.
 class MediaDrmOriginIdManager : public KeyedService {
  public:
+  enum class GetOriginIdStatus {
+    kSuccessWithPreProvisionedOriginId = 0,
+    kSuccessWithNewlyProvisionedOriginId = 1,
+    kFailure = 2,
+  };
+
   using MediaDrmOriginId = media::MediaDrmStorage::MediaDrmOriginId;
 
   // |success| is true if an origin ID was obtained and |origin_id| is
   // not null, false otherwise.
   using ProvisionedOriginIdCB =
-      base::OnceCallback<void(bool success, const MediaDrmOriginId& origin_id)>;
+      base::OnceCallback<void(GetOriginIdStatus status,
+                              const MediaDrmOriginId& origin_id)>;
   using ProvisioningResultCB = base::RepeatingCallback<bool()>;
 
   static void RegisterProfilePrefs(PrefRegistrySimple* registry);
diff --git a/chrome/browser/media/android/cdm/media_drm_origin_id_manager_unittest.cc b/chrome/browser/media/android/cdm/media_drm_origin_id_manager_unittest.cc
index 13968cd..0c829433 100644
--- a/chrome/browser/media/android/cdm/media_drm_origin_id_manager_unittest.cc
+++ b/chrome/browser/media/android/cdm/media_drm_origin_id_manager_unittest.cc
@@ -72,11 +72,14 @@
     MediaDrmOriginId result;
 
     origin_id_manager_->GetOriginId(base::BindOnce(
-        [](base::OnceClosure callback, MediaDrmOriginId* result, bool success,
+        [](base::OnceClosure callback, MediaDrmOriginId* result,
+           MediaDrmOriginIdManager::GetOriginIdStatus status,
            const MediaDrmOriginId& origin_id) {
-          // If |success| = true, then |origin_id| should be not null.
-          // If |success| = false, then |origin_id| should be null.
-          EXPECT_EQ(success, origin_id.has_value());
+          // If |status| = kFailure, then |origin_id| should be null.
+          // Otherwise (successful), |origin_id| should be not null.
+          EXPECT_EQ(
+              status != MediaDrmOriginIdManager::GetOriginIdStatus::kFailure,
+              origin_id.has_value());
           *result = origin_id;
           std::move(callback).Run();
         },
diff --git a/chrome/browser/media/android/cdm/media_drm_storage_factory.cc b/chrome/browser/media/android/cdm/media_drm_storage_factory.cc
index 484b6b3..d27c309 100644
--- a/chrome/browser/media/android/cdm/media_drm_storage_factory.cc
+++ b/chrome/browser/media/android/cdm/media_drm_storage_factory.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/logging.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/optional.h"
 #include "chrome/browser/media/android/cdm/media_drm_origin_id_manager.h"
 #include "chrome/browser/media/android/cdm/media_drm_origin_id_manager_factory.h"
@@ -27,9 +28,50 @@
 namespace {
 
 using MediaDrmOriginId = media::MediaDrmStorage::MediaDrmOriginId;
+using GetOriginIdStatus = MediaDrmOriginIdManager::GetOriginIdStatus;
 using OriginIdReadyCB =
     base::OnceCallback<void(bool success, const MediaDrmOriginId& origin_id)>;
 
+// These values are reported to UMA. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class GetOriginIdResult {
+  kSuccessWithPreProvisionedOriginId = 0,
+  kSuccessWithNewlyProvisionedOriginId = 1,
+  kSuccessWithUnprovisionedOriginId = 2,
+  kFailureOnPerAppProvisioningDevice = 3,
+  kFailureOnNonPerAppProvisioningDevice = 4,
+  kFailureWithNoFactory = 5,
+  kMaxValue = kFailureWithNoFactory,
+};
+
+GetOriginIdResult ConvertGetOriginIdStatusToResult(GetOriginIdStatus status) {
+  switch (status) {
+    case GetOriginIdStatus::kSuccessWithPreProvisionedOriginId:
+      return GetOriginIdResult::kSuccessWithPreProvisionedOriginId;
+    case GetOriginIdStatus::kSuccessWithNewlyProvisionedOriginId:
+      return GetOriginIdResult::kSuccessWithNewlyProvisionedOriginId;
+    case GetOriginIdStatus::kFailure:
+      break;
+  }
+
+  return media::MediaDrmBridge::IsPerApplicationProvisioningSupported()
+             ? GetOriginIdResult::kFailureOnPerAppProvisioningDevice
+             : GetOriginIdResult::kFailureOnNonPerAppProvisioningDevice;
+}
+
+// Update UMA with |result|.
+void ReportResultToUma(GetOriginIdResult result) {
+  base::UmaHistogramEnumeration("Media.EME.MediaDrm.GetOriginIdResult", result);
+}
+
+// Update UMA with |status|, and then pass |origin_id| to |callback|.
+void ReportStatusToUmaAndNotifyCaller(OriginIdReadyCB callback,
+                                      GetOriginIdStatus status,
+                                      const MediaDrmOriginId& origin_id) {
+  ReportResultToUma(ConvertGetOriginIdStatusToResult(status));
+  std::move(callback).Run(status != GetOriginIdStatus::kFailure, origin_id);
+}
+
 void CreateOriginIdWithMediaDrmOriginIdManager(Profile* profile,
                                                OriginIdReadyCB callback) {
   // Only need to origin IDs if MediaDrm supports it.
@@ -38,11 +80,13 @@
   auto* origin_id_manager =
       MediaDrmOriginIdManagerFactory::GetForProfile(profile);
   if (!origin_id_manager) {
+    ReportResultToUma(GetOriginIdResult::kFailureWithNoFactory);
     std::move(callback).Run(false, base::nullopt);
     return;
   }
 
-  origin_id_manager->GetOriginId(std::move(callback));
+  origin_id_manager->GetOriginId(
+      base::BindOnce(&ReportStatusToUmaAndNotifyCaller, std::move(callback)));
 }
 
 void CreateOriginId(OriginIdReadyCB callback) {
@@ -52,6 +96,7 @@
   auto origin_id = base::UnguessableToken::Create();
   DVLOG(2) << __func__ << ": origin_id = " << origin_id;
 
+  ReportResultToUma(GetOriginIdResult::kSuccessWithUnprovisionedOriginId);
   std::move(callback).Run(true, origin_id);
 }
 
diff --git a/chrome/browser/offline_pages/android/prefetch_background_task_android.cc b/chrome/browser/offline_pages/android/prefetch_background_task_android.cc
index eb576f4..cd1eacd 100644
--- a/chrome/browser/offline_pages/android/prefetch_background_task_android.cc
+++ b/chrome/browser/offline_pages/android/prefetch_background_task_android.cc
@@ -8,9 +8,10 @@
 #include "base/android/jni_string.h"
 #include "base/logging.h"
 #include "base/time/time.h"
-#include "chrome/browser/android/profile_key_util.h"
 #include "chrome/browser/offline_pages/prefetch/prefetch_service_factory.h"
-#include "chrome/browser/profiles/profile_key.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_android.h"
+#include "chrome/browser/profiles/profile_manager.h"
 #include "components/offline_pages/core/prefetch/prefetch_background_task.h"
 #include "components/offline_pages/core/prefetch/prefetch_dispatcher.h"
 #include "components/offline_pages/core/prefetch/prefetch_service.h"
@@ -29,11 +30,11 @@
     JNIEnv* env,
     const JavaParamRef<jobject>& jcaller,
     const JavaParamRef<jstring>& gcm_token) {
-  ProfileKey* profile_key = android::GetMainProfileKey();
-  DCHECK(profile_key);
+  Profile* profile = ProfileManager::GetLastUsedProfile();
+  DCHECK(profile);
 
   PrefetchService* prefetch_service =
-      PrefetchServiceFactory::GetForKey(profile_key);
+      PrefetchServiceFactory::GetForKey(profile->GetProfileKey());
   if (!prefetch_service)
     return false;
 
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index 2431db2..a953e83 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -1020,8 +1020,7 @@
 
 void ChromePasswordManagerClient::FocusedInputChanged(
     password_manager::PasswordManagerDriver* driver,
-    bool is_fillable,
-    bool is_password_field) {
+    autofill::mojom::FocusedFieldType focused_field_type) {
 #if defined(OS_ANDROID)
   if (PasswordAccessoryController::AllowedForWebContents(web_contents())) {
     bool is_manual_generation_available =
@@ -1033,8 +1032,7 @@
         content_driver->render_frame_host()->GetLastCommittedOrigin();
 
     PasswordAccessoryController::GetOrCreate(web_contents())
-        ->RefreshSuggestionsForField(last_committed_origin, is_fillable,
-                                     is_password_field,
+        ->RefreshSuggestionsForField(last_committed_origin, focused_field_type,
                                      is_manual_generation_available);
   }
 #endif  // defined(OS_ANDROID)
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.h b/chrome/browser/password_manager/chrome_password_manager_client.h
index 9839a03..8c6c01c40 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.h
+++ b/chrome/browser/password_manager/chrome_password_manager_client.h
@@ -72,9 +72,9 @@
       bool has_generated_password,
       bool is_update) override;
   void HideManualFallbackForSaving() override;
-  void FocusedInputChanged(password_manager::PasswordManagerDriver* driver,
-                           bool is_fillable,
-                           bool is_password_field) override;
+  void FocusedInputChanged(
+      password_manager::PasswordManagerDriver* driver,
+      autofill::mojom::FocusedFieldType focused_field_type) override;
   bool PromptUserToChooseCredentials(
       std::vector<std::unique_ptr<autofill::PasswordForm>> local_forms,
       const GURL& origin,
diff --git a/chrome/browser/password_manager/password_accessory_controller.h b/chrome/browser/password_manager/password_accessory_controller.h
index d063671..a076bbb 100644
--- a/chrome/browser/password_manager/password_accessory_controller.h
+++ b/chrome/browser/password_manager/password_accessory_controller.h
@@ -15,6 +15,7 @@
 #include "base/strings/string16.h"
 #include "chrome/browser/autofill/accessory_controller.h"
 #include "components/autofill/core/common/filling_status.h"
+#include "components/autofill/core/common/mojom/autofill_types.mojom.h"
 #include "components/autofill/core/common/password_generation_util.h"
 #include "content/public/browser/web_contents_user_data.h"
 #include "ui/gfx/image/image.h"
@@ -70,12 +71,10 @@
   virtual void OnFilledIntoFocusedField(autofill::FillingStatus status) = 0;
 
   // Makes sure, that all shown suggestions are appropriate for the currently
-  // focused field and for fields that lost the focus. If a field lost focus,
-  // |is_fillable| will be false.
+  // focused field and for fields that lost the focus.
   virtual void RefreshSuggestionsForField(
       const url::Origin& origin,
-      bool is_fillable,
-      bool is_password_field,
+      autofill::mojom::FocusedFieldType focused_field_type,
       bool is_manual_generation_available) = 0;
 
   // Reacts to a navigation on the main frame, e.g. by clearing caches.
diff --git a/chrome/browser/password_manager/password_accessory_controller_impl.cc b/chrome/browser/password_manager/password_accessory_controller_impl.cc
index ebb0744..552d7e7 100644
--- a/chrome/browser/password_manager/password_accessory_controller_impl.cc
+++ b/chrome/browser/password_manager/password_accessory_controller_impl.cc
@@ -36,6 +36,7 @@
 using autofill::FooterCommand;
 using autofill::PasswordForm;
 using autofill::UserInfo;
+using autofill::mojom::FocusedFieldType;
 using FillingSource = ManualFillingController::FillingSource;
 
 namespace {
@@ -181,13 +182,17 @@
 
 void PasswordAccessoryControllerImpl::RefreshSuggestionsForField(
     const url::Origin& origin,
-    bool is_fillable,
-    bool is_password_field,
+    FocusedFieldType focused_field_type,
     bool is_manual_generation_available) {
   std::vector<UserInfo> info_to_add;
   std::vector<FooterCommand> footer_commands_to_add;
 
-  if (is_fillable) {
+  const bool is_password_field =
+      focused_field_type == FocusedFieldType::kFillablePasswordField;
+
+  if (focused_field_type == FocusedFieldType::kFillableTextField ||
+      focused_field_type == FocusedFieldType::kFillableUsernameField ||
+      focused_field_type == FocusedFieldType::kFillablePasswordField) {
     const std::vector<PasswordAccessorySuggestion> suggestions =
         GetSuggestions();
     info_to_add.resize(suggestions.size());
@@ -195,12 +200,12 @@
                    [is_password_field](const auto& x) {
                      return Translate(is_password_field, x);
                    });
+  }
 
-    if (is_password_field && is_manual_generation_available) {
-      base::string16 generate_password_title = l10n_util::GetStringUTF16(
-          IDS_PASSWORD_MANAGER_ACCESSORY_GENERATE_PASSWORD_BUTTON_TITLE);
-      footer_commands_to_add.push_back(FooterCommand(generate_password_title));
-    }
+  if (is_password_field && is_manual_generation_available) {
+    base::string16 generate_password_title = l10n_util::GetStringUTF16(
+        IDS_PASSWORD_MANAGER_ACCESSORY_GENERATE_PASSWORD_BUTTON_TITLE);
+    footer_commands_to_add.push_back(FooterCommand(generate_password_title));
   }
 
   base::string16 manage_passwords_title = l10n_util::GetStringUTF16(
@@ -210,13 +215,13 @@
   bool has_suggestions = !info_to_add.empty();
 
   GetManualFillingController()->RefreshSuggestionsForField(
-      is_fillable,
+      focused_field_type,
       autofill::CreateAccessorySheetData(
           autofill::FallbackSheetType::PASSWORD,
           GetTitle(has_suggestions, GetFocusedFrameOrigin()),
           std::move(info_to_add), std::move(footer_commands_to_add)));
 
-  if (is_password_field && is_fillable) {
+  if (is_password_field) {
     GetManualFillingController()->ShowWhenKeyboardIsVisible(
         FillingSource::PASSWORD_FALLBACKS);
   } else {