From: csagan5 <32685696+csagan5@users.noreply.github.com>
Date: Fri, 29 Apr 2022 00:31:49 +0200
Subject: Welcome screen

Allow toggling automatic updates

License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html
---
 chrome/android/chrome_java_resources.gni      |   1 +
 chrome/android/chrome_java_sources.gni        |   2 +
 .../android/java/res/layout/fre_tosanduma.xml | 160 +++++++++
 chrome/android/java/res/values/dimens.xml     |  10 +
 chrome/android/java/res/values/styles.xml     |  24 ++
 .../firstrun/ChildAccountStatusSupplier.java  |   8 -
 .../browser/firstrun/FirstRunActivity.java    |  21 +-
 .../firstrun/FirstRunActivityBase.java        |   6 +-
 .../firstrun/FirstRunFlowSequencer.java       |  52 +--
 .../browser/firstrun/FirstRunUtils.java       |  16 +-
 .../firstrun/ToSAndUMAFirstRunFragment.java   | 336 ++++++++++++++++++
 .../firstrun/TosAndUmaFragmentView.java       | 336 ++++++++++++++++++
 .../strings/android_chrome_strings.grd        |  24 +-
 13 files changed, 910 insertions(+), 86 deletions(-)
 create mode 100644 chrome/android/java/res/layout/fre_tosanduma.xml
 create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/firstrun/ToSAndUMAFirstRunFragment.java
 create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/firstrun/TosAndUmaFragmentView.java

diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_java_resources.gni
--- a/chrome/android/chrome_java_resources.gni
+++ b/chrome/android/chrome_java_resources.gni
@@ -523,6 +523,7 @@ chrome_java_resources = [
   "java/res/layout/find_in_page.xml",
   "java/res/layout/find_toolbar.xml",
   "java/res/layout/fre_tos_privacy_disclaimer.xml",
+  "java/res/layout/fre_tosanduma.xml",
   "java/res/layout/history_clear_browsing_data_header.xml",
   "java/res/layout/history_item_view.xml",
   "java/res/layout/history_main.xml",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -650,6 +650,8 @@ chrome_java_sources = [
   "java/src/org/chromium/chrome/browser/firstrun/SyncConsentFirstRunFragment.java",
   "java/src/org/chromium/chrome/browser/firstrun/TabbedModeFirstRunActivity.java",
   "java/src/org/chromium/chrome/browser/firstrun/TosDialogBehaviorSharedPrefInvalidator.java",
+  "java/src/org/chromium/chrome/browser/firstrun/ToSAndUMAFirstRunFragment.java",
+  "java/src/org/chromium/chrome/browser/firstrun/TosAndUmaFragmentView.java",
   "java/src/org/chromium/chrome/browser/flags/BadFlagsSnackbarManager.java",
   "java/src/org/chromium/chrome/browser/fonts/FontPreloader.java",
   "java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java",
diff --git a/chrome/android/java/res/layout/fre_tosanduma.xml b/chrome/android/java/res/layout/fre_tosanduma.xml
new file mode 100644
--- /dev/null
+++ b/chrome/android/java/res/layout/fre_tosanduma.xml
@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2015 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<!-- Most of the placement in this layout is controlled by TosAndUmaFragmentView#onMeasure. When changing the layout in this file, be sure to also check on the view object to see what is changing to avoid unexpected behavior. -->
+<org.chromium.chrome.browser.firstrun.TosAndUmaFragmentView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <ScrollView
+        android:id="@+id/scroll_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_above="@id/fre_bottom_group"
+        android:fillViewport="true">
+
+        <!-- The orientation of this view is changed dynamically to give a nicer layout when in
+        landscape mode on devices with small screens. -->
+        <LinearLayout
+            android:id="@+id/fre_main_layout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:animateLayoutChanges="true"
+            android:gravity="center_horizontal">
+
+            <ImageView
+                android:id="@+id/image"
+                android:layout_width="wrap_content"
+                android:layout_height="@dimen/fre_tos_image_height"
+                android:layout_marginHorizontal="@dimen/fre_vertical_spacing"
+                android:layout_marginBottom="@dimen/fre_image_bottom_margin"
+                android:importantForAccessibility="no"
+                android:src="@drawable/fre_product_logo" />
+
+            <LinearLayout
+                android:id="@+id/fre_title_and_content"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical">
+
+                <TextView
+                    android:id="@+id/title"
+                    android:text="@string/fre_welcome"
+                    style="@style/FreWelcomePageTitle" />
+
+                <!-- The FrameLayout here is to facilitate adding a proper content description for
+                 the loading view. During development, it didn't seem possible to override the
+                 LoadingView contentDescription in XML, but if there's support for this at some
+                 point then we can remove the FrameLayout. -->
+                <FrameLayout
+                    android:id="@+id/loading_view_container"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center_horizontal"
+                    android:visibility="gone"
+                    android:contentDescription="@string/sync_loading">
+
+                    <org.chromium.ui.widget.LoadingView
+                        android:id="@+id/progress_spinner_large"
+                        style="@style/Widget.AppCompat.ProgressBar"
+                        android:layout_height="@dimen/fre_loading_spinner_size"
+                        android:layout_width="@dimen/fre_loading_spinner_size"
+                        android:visibility="gone"/>
+
+                </FrameLayout>
+
+                <LinearLayout
+                    android:id="@+id/fre_content_wrapper"
+                    android:layout_width="match_parent"
+                    android:layout_height="0dp"
+                    android:layout_weight="1"
+                    android:layout_marginEnd="@dimen/fre_content_margin"
+                    android:gravity="center_vertical"
+                    android:orientation="vertical" >
+
+                    <org.chromium.ui.widget.TextViewWithClickableSpans
+                        android:id="@+id/tos_and_privacy"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginTop="@dimen/fre_vertical_spacing"
+                        android:layout_marginBottom="@dimen/fre_tos_bottom_margin"
+                        android:lineSpacingMultiplier="1.4"
+                        android:textAppearance="@style/TextAppearance.TextMedium.Primary" />
+
+                    <CheckBox
+                        android:id="@+id/auto_updater_checkbox"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:lineSpacingMultiplier="1.4"
+                        android:text="@string/auto_updater_check"
+                        android:paddingStart="@dimen/fre_tos_checkbox_padding"
+                        android:textAppearance="@style/TextAppearance.TextMedium.Primary" />
+                </LinearLayout>
+
+                <include
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center"
+                    android:layout_marginEnd="@dimen/fre_content_margin"
+                    android:layout_marginTop="@dimen/fre_policy_privacy_disclaimer_top_margin"
+                    android:layout_marginBottom="@dimen/fre_vertical_spacing"
+                    android:visibility="gone"
+                    layout="@layout/fre_tos_privacy_disclaimer" />
+            </LinearLayout>
+        </LinearLayout>
+    </ScrollView>
+
+    <FrameLayout
+        android:id="@+id/fre_bottom_group"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_marginVertical="@dimen/fre_button_vertical_margin"
+        android:layout_marginHorizontal="@dimen/fre_content_margin">
+
+        <org.chromium.ui.widget.ButtonCompat
+            android:id="@+id/terms_accept"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:text="@string/fre_accept_continue"
+            android:animateLayoutChanges="true"
+            style="@style/FreAcceptTosButton" />
+
+        <!-- Same location as the button; marginBottom is adjusted for the different size. -->
+        <ProgressBar
+            android:id="@+id/progress_spinner"
+            style="@style/Widget.AppCompat.ProgressBar"
+            android:layout_gravity="center"
+            android:layout_width="@dimen/fre_bottom_loading_spinner_size"
+            android:layout_height="@dimen/fre_bottom_loading_spinner_size"/>
+    </FrameLayout>
+
+    <ImageView
+        android:id="@+id/shadow"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/action_bar_shadow_height"
+        android:layout_gravity="bottom"
+        android:layout_above="@id/fre_bottom_group"
+        android:background="@drawable/modern_toolbar_shadow"
+        android:scaleY="-1"
+        android:visibility="gone"
+        android:importantForAccessibility="no" />
+
+    <!-- Empty TextView to preload fonts for following pages. See https://crbug.com/1119990#c20 -->
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearance.Headline.Primary"
+        android:visibility="gone"/>
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearance.Button.Text.Filled"
+        android:visibility="gone"/>
+
+</org.chromium.chrome.browser.firstrun.TosAndUmaFragmentView>
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -111,6 +111,16 @@ found in the LICENSE file.
     <dimen name="fre_loading_spinner_size">48dp</dimen>
     <dimen name="fre_policy_privacy_disclaimer_icon_size">18dp</dimen>
     <dimen name="fre_policy_privacy_disclaimer_icon_padding">8dp</dimen>
+    <dimen name="fre_image_bottom_margin">36dp</dimen>
+    <dimen name="fre_vertical_spacing">32dp</dimen>
+    <dimen name="fre_content_margin">24dp</dimen>
+    <dimen name="fre_tos_bottom_margin">16dp</dimen>
+    <dimen name="fre_tos_checkbox_padding">12dp</dimen>
+    <dimen name="fre_bottom_loading_spinner_size">24dp</dimen>
+    <dimen name="fre_policy_privacy_disclaimer_top_margin">16dp</dimen>
+    <dimen name="fre_button_vertical_margin">24dp</dimen>
+    <dimen name="fre_landscape_top_padding">72dp</dimen>
+    <dimen name="fre_button_vertical_margin_small">16dp</dimen>
 
     <!-- Account Signin dimensions -->
     <!-- The Account Signin page appears in the First Run Experience (amongst other places), so uses
diff --git a/chrome/android/java/res/values/styles.xml b/chrome/android/java/res/values/styles.xml
--- a/chrome/android/java/res/values/styles.xml
+++ b/chrome/android/java/res/values/styles.xml
@@ -257,6 +257,30 @@ found in the LICENSE file.
         <item name="android:layout_weight">1</item>
     </style>
 
+    <!-- First Run Experience -->
+    <!-- Avoid using @font/accent_font, a downloaded font, on text that could appear in the first
+         couple of frames of app start. It may not be ready yet, see https://crbug.com/1119990 -->
+    <style name="TextAppearance.FreFirstFrameTitle" parent="TextAppearance.Headline.Primary" >
+        <item name="android:fontFamily">sans-serif</item>
+    </style>
+    <!-- Match the fontFamily in ui/android/java/res/values/styles.xml -->
+    <style name="TextAppearance.FreFirstFrameButton" parent="TextAppearance.Button.Text.Filled">
+        <item name="android:fontFamily">sans-serif-medium</item>
+    </style>
+    <style name="FreTitle">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:gravity">center</item>
+        <item name="android:lineSpacingMultiplier">1.4</item>
+        <item name="android:textAppearance">@style/TextAppearance.Headline.Primary</item>
+    </style>
+    <style name="FreWelcomePageTitle" parent="FreTitle">
+        <item name="android:textAppearance">@style/TextAppearance.FreFirstFrameTitle</item>
+    </style>
+    <style name="FreAcceptTosButton" parent="FilledButton.Flat">
+        <item name="android:textAppearance">@style/TextAppearance.FreFirstFrameButton</item>
+    </style>
+
     <!-- Generic Overlay Panel styles -->
     <style name="OverlayPanelTextViewLayout">
         <item name="android:layout_width">match_parent</item>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ChildAccountStatusSupplier.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ChildAccountStatusSupplier.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ChildAccountStatusSupplier.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ChildAccountStatusSupplier.java
@@ -13,7 +13,6 @@ import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.supplier.OneshotSupplier;
 import org.chromium.base.supplier.OneshotSupplierImpl;
 import org.chromium.components.signin.AccountManagerFacade;
-import org.chromium.components.signin.AccountUtils;
 
 /**
  * Fetches the child account status to be used by other FRE components.
@@ -41,13 +40,6 @@ public class ChildAccountStatusSupplier implements OneshotSupplier<Boolean> {
     public ChildAccountStatusSupplier(AccountManagerFacade accountManagerFacade,
             FirstRunAppRestrictionInfo appRestrictionInfo) {
         mChildAccountStatusStartTime = SystemClock.elapsedRealtime();
-
-        appRestrictionInfo.getHasAppRestriction(this::onAppRestrictionDetected);
-
-        accountManagerFacade.getAccounts().then(accounts -> {
-            AccountUtils.checkChildAccountStatusLegacy(accountManagerFacade, accounts,
-                    (isChild, account) -> onChildAccountStatusReady(isChild));
-        });
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
@@ -29,9 +29,7 @@ import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.fonts.FontPreloader;
 import org.chromium.chrome.browser.metrics.UmaUtils;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
-import org.chromium.chrome.browser.signin.SigninCheckerProvider;
-import org.chromium.chrome.browser.signin.SigninFirstRunFragment;
-import org.chromium.chrome.browser.signin.services.FREMobileIdentityConsistencyFieldTrial;
+import org.chromium.chrome.browser.firstrun.ToSAndUMAFirstRunFragment;
 import org.chromium.components.browser_ui.modaldialog.AppModalPresenter;
 import org.chromium.components.feature_engagement.EventConstants;
 import org.chromium.components.metrics.LowEntropySource;
@@ -121,7 +119,7 @@ public class FirstRunActivity extends FirstRunActivityBase implements FirstRunPa
     /** Creates first page and sets up adapter. Should result UI being shown on the screen. */
     private void createFirstPage() {
         BooleanSupplier showWelcomePage = () -> !FirstRunStatus.shouldSkipWelcomePage();
-        mPages.add(new FirstRunPage<>(SigninFirstRunFragment.class, showWelcomePage));
+        mPages.add(new FirstRunPage<>(ToSAndUMAFirstRunFragment.class, showWelcomePage));
         mFreProgressStates.add(MobileFreProgress.WELCOME_SHOWN);
         mPagerAdapter = new FirstRunPagerAdapter(FirstRunActivity.this, mPages);
         mPager.setAdapter(mPagerAdapter);
@@ -143,13 +141,11 @@ public class FirstRunActivity extends FirstRunActivityBase implements FirstRunPa
         //
         // TODO(b/245912657): explicitly sign in supervised users in {@link
         // SigninFirstRunMediator#handleContinueWithNative} rather than relying on SigninChecker.
-        SigninCheckerProvider.get();
 
         mFirstRunFlowSequencer.updateFirstRunProperties(mFreProperties);
 
         BooleanSupplier showSearchEnginePromo =
                 () -> mFreProperties.getBoolean(SHOW_SEARCH_ENGINE_PAGE);
-        BooleanSupplier showSyncConsent = () -> mFreProperties.getBoolean(SHOW_SYNC_CONSENT_PAGE);
 
         // An optional page to select a default search engine.
         if (showSearchEnginePromo.getAsBoolean()) {
@@ -158,11 +154,6 @@ public class FirstRunActivity extends FirstRunActivityBase implements FirstRunPa
             mFreProgressStates.add(MobileFreProgress.DEFAULT_SEARCH_ENGINE_SHOWN);
         }
 
-        // An optional sync consent page, the visibility of this page will be decided on the fly
-        // according to the situation.
-        mPages.add(new FirstRunPage<>(SyncConsentFirstRunFragment.class, showSyncConsent));
-        mFreProgressStates.add(MobileFreProgress.SYNC_CONSENT_SHOWN);
-
         if (mPagerAdapter != null) {
             mPagerAdapter.notifyDataSetChanged();
         }
@@ -207,10 +198,6 @@ public class FirstRunActivity extends FirstRunActivityBase implements FirstRunPa
 
     @Override
     public void triggerLayoutInflation() {
-        // Generate trial group as early as possible to guarantee it's available by the time native
-        // needs to register the synthetic trial group. See https://crbug.com/1295692 for details.
-        FREMobileIdentityConsistencyFieldTrial.createFirstRunVariationsTrial();
-
         super.triggerLayoutInflation();
 
         initializeStateFromLaunchData();
@@ -500,10 +487,6 @@ public class FirstRunActivity extends FirstRunActivityBase implements FirstRunPa
     public void acceptTermsOfService(boolean allowMetricsAndCrashUploading) {
         assert mNativeInitializationPromise.isFulfilled();
 
-        // If default is true then it corresponds to opt-out and false corresponds to opt-in.
-        UmaUtils.recordMetricsReportingDefaultOptIn(!DEFAULT_METRICS_AND_CRASH_REPORTING);
-        RecordHistogram.recordMediumTimesHistogram("MobileFre.FromLaunch.TosAccepted",
-                SystemClock.elapsedRealtime() - mIntentCreationElapsedRealtimeMs);
         FirstRunUtils.acceptTermsOfService(allowMetricsAndCrashUploading);
         FirstRunStatus.setSkipWelcomePage(true);
         flushPersistentData();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivityBase.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivityBase.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivityBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivityBase.java
@@ -33,8 +33,6 @@ import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileManagerUtils;
 import org.chromium.components.browser_ui.widget.gesture.BackPressHandler;
 import org.chromium.components.policy.PolicyService;
-import org.chromium.components.signin.AccountManagerFacade;
-import org.chromium.components.signin.AccountManagerFacadeProvider;
 
 /** Base class for First Run Experience. */
 public abstract class FirstRunActivityBase
@@ -104,9 +102,7 @@ public abstract class FirstRunActivityBase
     @Override
     @CallSuper
     public void triggerLayoutInflation() {
-        AccountManagerFacade accountManagerFacade = AccountManagerFacadeProvider.getInstance();
-        mChildAccountStatusSupplier =
-                new ChildAccountStatusSupplier(accountManagerFacade, mFirstRunAppRestrictionInfo);
+        mChildAccountStatusSupplier = new ChildAccountStatusSupplier(null, null);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java
@@ -27,14 +27,9 @@ import org.chromium.chrome.browser.locale.LocaleManager;
 import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.search_engines.SearchEnginePromoType;
-import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
-import org.chromium.chrome.browser.signin.services.SigninManager;
 import org.chromium.components.crash.CrashKeyIndex;
 import org.chromium.components.crash.CrashKeys;
 import org.chromium.components.embedder_support.util.UrlConstants;
-import org.chromium.components.signin.AccountManagerFacadeProvider;
-import org.chromium.components.signin.identitymanager.ConsentLevel;
-import org.chromium.components.signin.identitymanager.IdentityManager;
 
 import java.util.List;
 
@@ -65,38 +60,19 @@ public abstract class FirstRunFlowSequencer  {
         /** Returns true if the sync consent promo page should be shown. */
         boolean shouldShowSyncConsentPage(
                 Activity activity, List<Account> accounts, boolean isChild) {
-            if (isChild) {
-                // Always show the sync consent page for child account.
-                return true;
-            }
-            assert mProfileSupplier.get() != null;
-            final IdentityManager identityManager =
-                    IdentityServicesProvider.get().getIdentityManager(mProfileSupplier.get());
-            if (identityManager.hasPrimaryAccount(ConsentLevel.SYNC) || !isSyncAllowed()) {
-                // No need to show the sync consent page if users already consented to sync or
-                // if sync is not allowed.
                 return false;
-            }
-                // Show the sync consent page only to the signed-in users.
-            return identityManager.hasPrimaryAccount(ConsentLevel.SIGNIN);
         }
 
         /** @return true if the Search Engine promo page should be shown. */
         @VisibleForTesting
         public boolean shouldShowSearchEnginePage() {
-            @SearchEnginePromoType
-            int searchPromoType = LocaleManager.getInstance().getSearchEnginePromoShowType();
-            return searchPromoType == SearchEnginePromoType.SHOW_NEW
-                    || searchPromoType == SearchEnginePromoType.SHOW_EXISTING;
+            return false;
         }
 
         /** @return true if Sync is allowed for the current user. */
         @VisibleForTesting
         protected boolean isSyncAllowed() {
-            SigninManager signinManager =
-                    IdentityServicesProvider.get().getSigninManager(mProfileSupplier.get());
-            return FirstRunUtils.canAllowSync() && !signinManager.isSigninDisabledByPolicy()
-                    && signinManager.isSigninSupported(/*requireUpdatedPlayServices=*/false);
+            return false;
         }
     }
 
@@ -147,12 +123,8 @@ public abstract class FirstRunFlowSequencer  {
      *                                  method.
      */
     void start() {
-        AccountManagerFacadeProvider.getInstance().getAccounts().then(accounts -> {
-            RecordHistogram.recordCount1MHistogram(
-                    "Signin.AndroidDeviceAccountsNumberWhenEnteringFRE",
-                    Math.min(accounts.size(), 2));
-            setAccountList(accounts);
-        });
+        mIsChild = false;
+        maybeProcessFreEnvironmentPreNative();
     }
 
     @VisibleForTesting
@@ -177,14 +149,10 @@ public abstract class FirstRunFlowSequencer  {
     }
 
     private void maybeProcessFreEnvironmentPreNative() {
-        // Wait till both child account status and the list of accounts are available.
-        if (mIsChild == null || mGoogleAccounts == null) return;
-
         if (mIsFlowKnown) return;
         mIsFlowKnown = true;
 
         Bundle freProperties = new Bundle();
-        freProperties.putBoolean(SyncConsentFirstRunFragment.IS_CHILD_ACCOUNT, mIsChild);
 
         onFlowIsKnown(freProperties);
     }
@@ -195,8 +163,8 @@ public abstract class FirstRunFlowSequencer  {
      * @param freProperties Resulting FRE properties bundle.
      */
     public void updateFirstRunProperties(Bundle freProperties) {
-        freProperties.putBoolean(
-                FirstRunActivity.SHOW_SYNC_CONSENT_PAGE, shouldShowSyncConsentPage());
+        if (freProperties == null)
+          throw new RuntimeException("attempting to update null FRE properties");
         freProperties.putBoolean(
                 FirstRunActivity.SHOW_SEARCH_ENGINE_PAGE, shouldShowSearchEnginePage());
     }
@@ -297,13 +265,17 @@ public abstract class FirstRunFlowSequencer  {
             if (!(caller instanceof Activity)) {
                 freIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             }
-            IntentUtils.safeStartActivity(caller, freIntent);
+            if (!IntentUtils.safeStartActivity(caller, freIntent)) {
+              throw new RuntimeException("Cannot start FirstRunExperience activity");
+            }
         } else {
             // First Run requires that the Intent contains NEW_TASK so that it doesn't sit on top
             // of something else.
             Intent newIntent = new Intent(fromIntent);
             newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            IntentUtils.safeStartActivity(caller, newIntent);
+            if (!IntentUtils.safeStartActivity(caller, newIntent)) {
+              throw new RuntimeException("Cannot start FirstRunExperience activity");
+            }
         }
         return true;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunUtils.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunUtils.java
@@ -18,9 +18,6 @@ import org.chromium.chrome.browser.metrics.ChangeMetricsReportingStateCalledFrom
 import org.chromium.chrome.browser.metrics.UmaSessionStats;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
-import org.chromium.components.signin.AccountManagerFacade;
-import org.chromium.components.signin.AccountManagerFacadeProvider;
-import org.chromium.components.signin.AccountUtils;
 import org.chromium.ui.accessibility.AccessibilityState;
 
 /** Provides first run related utility functions. */
@@ -83,16 +80,12 @@ public class FirstRunUtils {
      * @return Whether or not sync is allowed on this device.
      */
     static boolean canAllowSync() {
-        return (hasGoogleAccountAuthenticator() && hasSyncPermissions()) || hasGoogleAccounts();
+        return false;
     }
 
     @VisibleForTesting
     static boolean hasGoogleAccountAuthenticator() {
-        if (sHasGoogleAccountAuthenticator == null) {
-            AccountManagerFacade accountHelper = AccountManagerFacadeProvider.getInstance();
-            sHasGoogleAccountAuthenticator = accountHelper.hasGoogleAccountAuthenticator();
-        }
-        return sHasGoogleAccountAuthenticator;
+        return false;
     }
 
     @VisibleForTesting
@@ -102,10 +95,7 @@ public class FirstRunUtils {
 
     @VisibleForTesting
     static boolean hasGoogleAccounts() {
-        return !AccountUtils
-                        .getAccountsIfFulfilledOrEmpty(
-                                AccountManagerFacadeProvider.getInstance().getAccounts())
-                        .isEmpty();
+        return false;
     }
 
     @SuppressLint("InlinedApi")
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ToSAndUMAFirstRunFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ToSAndUMAFirstRunFragment.java
new file mode 100644
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ToSAndUMAFirstRunFragment.java
@@ -0,0 +1,336 @@
+// Copyright 2015 The Chromium Authors
+// 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.firstrun;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.text.method.LinkMovementMethod;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+import android.content.SharedPreferences;
+import org.chromium.chrome.browser.omaha.OmahaBase;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.fragment.app.Fragment;
+
+import org.chromium.base.Log;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManagerImpl;
+import org.chromium.components.version_info.VersionInfo;
+import org.chromium.ui.modaldialog.ModalDialogManagerHolder;
+import org.chromium.ui.text.NoUnderlineClickableSpan;
+import org.chromium.ui.text.SpanApplier;
+import org.chromium.ui.text.SpanApplier.SpanInfo;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * The First Run Experience fragment that allows the user to accept Terms of Service ("ToS") and
+ * Privacy Notice, and to opt-in to the usage statistics and crash reports collection ("UMA",
+ * User Metrics Analysis) as defined in the Chrome Privacy Notice.
+ */
+public class ToSAndUMAFirstRunFragment
+        extends Fragment implements FirstRunFragment {
+    /** Alerts about some methods once ToSAndUMAFirstRunFragment executes them. */
+    public interface Observer {
+        /** See {@link #onNativeInitialized}. */
+        public void onNativeInitialized();
+        public void onPolicyServiceInitialized();
+        public void onHideLoadingUIComplete();
+    }
+
+    private static boolean sShowUmaCheckBoxForTesting;
+
+    @Nullable
+    private static ToSAndUMAFirstRunFragment.Observer sObserver;
+
+    private boolean mNativeInitialized;
+    private boolean mPolicyServiceInitialized;
+    private boolean mTosButtonClicked;
+    private boolean mAllowMetricsAndCrashUploading;
+    private boolean mUserInteractedWithUmaCheckbox;
+
+    private Button mAcceptButton;
+    private CheckBox mAutoUpdaterCheckBox;
+    private boolean mAutoUpdaterChecked;
+    private TextView mTosAndPrivacy;
+    private View mTitle;
+    private View mProgressSpinner;
+
+    private long mTosAcceptedTime;
+
+    @Override
+    public View onCreateView(
+            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.fre_tosanduma, container, false);
+    }
+
+    @Override
+    public void onAttach(@NonNull Context context) {
+        super.onAttach(context);
+        getPageDelegate().getPolicyLoadListener().onAvailable(this::onPolicyServiceInitialized);
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+
+        mTitle = view.findViewById(R.id.title);
+        mProgressSpinner = view.findViewById(R.id.progress_spinner);
+        mProgressSpinner.setVisibility(View.GONE);
+        mAcceptButton = (Button) view.findViewById(R.id.terms_accept);
+        mAutoUpdaterCheckBox = (CheckBox) view.findViewById(R.id.auto_updater_checkbox);
+        mTosAndPrivacy = (TextView) view.findViewById(R.id.tos_and_privacy);
+
+        // Register event listeners.
+        mAcceptButton.setOnClickListener((v) -> onTosButtonClicked());
+        mAutoUpdaterCheckBox.setOnCheckedChangeListener(
+                ((compoundButton, isChecked) -> {
+            mAutoUpdaterChecked = isChecked;
+        }));
+
+        // Make TextView links clickable.
+        mTosAndPrivacy.setMovementMethod(LinkMovementMethod.getInstance());
+
+        updateView();
+
+        // If this page should be skipped, it can be one of the following cases:
+        //   1. Native hasn't been initialized yet and this page will be skipped once that happens.
+        //   2. The user has moved back to this page after advancing past it. In this case, this
+        //      may not even be the same object as before, as the fragment may have been re-created.
+        //
+        // In case 1, hide all the elements except for Chrome logo and the spinner until native gets
+        // initialized at which point the activity will skip the page.
+        // We distinguish case 1 from case 2 by the value of |mNativeInitialized|, as that is set
+        // via onAttachFragment() from FirstRunActivity - which is before this onViewCreated().
+        boolean isW = isWaitingForNativeAndPolicyInit();
+        boolean ssw = FirstRunStatus.shouldSkipWelcomePage();
+        if (isW && ssw) {
+            setSpinnerVisible(true);
+        }
+    }
+
+    @Override
+    public void setInitialA11yFocus() {
+        // Ignore calls before view is created.
+        if (mTitle == null) return;
+        mTitle.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+    }
+
+    @Override
+    public void setUserVisibleHint(boolean isVisibleToUser) {
+        super.setUserVisibleHint(isVisibleToUser);
+
+        // This may be called before onViewCreated(), in which case the below is not yet relevant.
+        if (mTitle == null) return;
+
+        if (!isVisibleToUser) {
+            // Restore original enabled & visibility states, in case the user returns to the page.
+            setSpinnerVisible(false);
+        } else {
+            // On certain versions of Android, the checkbox will appear unchecked upon revisiting
+            // the page.  Force it to the end state of the drawable animation as a work around.
+            // crbug.com/666258
+            mAutoUpdaterCheckBox.jumpDrawablesToCurrentState();
+        }
+    }
+
+    @Override
+    public void onNativeInitialized() {
+        assert !mNativeInitialized;
+
+        mNativeInitialized = true;
+        tryMarkTermsAccepted(false);
+
+        if (mPolicyServiceInitialized) {
+            onNativeAndPolicyServiceInitialized();
+        }
+
+        if (sObserver != null) {
+            sObserver.onNativeInitialized();
+        }
+    }
+
+    @Override
+    public void reset() {
+        // We cannot pass the welcome page when native or policy is not initialized. When this page
+        // is revisited, this means this page is persist and we should re-show the ToS And UMA.
+        assert !isWaitingForNativeAndPolicyInit();
+
+        setSpinnerVisible(false);
+        mAutoUpdaterCheckBox.setChecked(true);
+    }
+
+    private void updateView() {
+        // Avoid early calls.
+        if (getPageDelegate() == null) {
+            return;
+        }
+
+        updateTosText();
+
+        updateReportCheckbox();
+    }
+
+    private SpanInfo buildPrivacyPolicyLink(String suffix, int url) {
+        NoUnderlineClickableSpan clickableSpan =
+                new NoUnderlineClickableSpan(getContext(), (view1) -> {
+                    if (!isAdded()) return;
+                    getPageDelegate().showInfoPage(url);
+                });
+
+        return new SpanInfo("<PRIVACY_LINK" + suffix + ">", "</PRIVACY_LINK" + suffix + ">", clickableSpan);
+    }
+
+    private void updateTosText() {
+        List<SpanInfo> spans = new LinkedList<SpanInfo>();
+
+        spans.add(buildPrivacyPolicyLink("1", R.string.adblock_wiki_url));
+
+        spans.add(buildPrivacyPolicyLink("2", R.string.adblock_updater_privacy_policy_url));
+
+        spans.add(buildPrivacyPolicyLink("3", R.string.auto_updates_wiki_url));
+
+        spans.add(buildPrivacyPolicyLink("4", R.string.bromite_updater_privacy_policy_url));
+
+        String tosString = getString(R.string.bromite_fre_footer_privacy_policy);
+
+        mTosAndPrivacy.setText(SpanApplier.applySpans(tosString, spans.toArray(new SpanInfo[0])));
+    }
+
+    private void updateReportCheckbox() {
+        mAutoUpdaterCheckBox.setChecked(true);
+    }
+
+    private void onPolicyServiceInitialized(boolean onDevicePolicyFound) {
+        assert !mPolicyServiceInitialized;
+
+        mPolicyServiceInitialized = true;
+        tryMarkTermsAccepted(false);
+
+        if (mNativeInitialized) {
+            onNativeAndPolicyServiceInitialized();
+        }
+
+        if (sObserver != null) {
+            sObserver.onPolicyServiceInitialized();
+        }
+    }
+
+    private void onNativeAndPolicyServiceInitialized() {
+        // Once we have native & policies, Check whether metrics reporting are permitted by policy
+        // and update interface accordingly.
+        updateView();
+    }
+
+    private void onTosButtonClicked() {
+        mTosButtonClicked = true;
+        mTosAcceptedTime = SystemClock.elapsedRealtime();
+
+        // save updater configuration only on button click
+        SharedPreferences.Editor sharedPreferenceEditor = OmahaBase.getSharedPreferences().edit();
+        sharedPreferenceEditor.putBoolean(OmahaBase.PREF_ALLOW_INLINE_UPDATE, mAutoUpdaterChecked);
+        sharedPreferenceEditor.apply();
+
+        tryMarkTermsAccepted(true);
+    }
+
+    /**
+     * This should be called Tos button is clicked for a fresh new FRE, or when native and policies
+     * are initialized if Tos has ever been accepted.
+     *
+     * @param fromButtonClicked Whether called from {@link #onTosButtonClicked()}.
+     */
+    private void tryMarkTermsAccepted(boolean fromButtonClicked) {
+        boolean isW = isWaitingForNativeAndPolicyInit();
+        if (!mTosButtonClicked || isW) {
+            if (fromButtonClicked) setSpinnerVisible(true);
+            return;
+        }
+
+        // In cases where the attempt is triggered other than button click, the ToS should have been
+        // accepted by the user already.
+        if (!fromButtonClicked) {
+            RecordHistogram.recordTimesHistogram("MobileFre.TosFragment.SpinnerVisibleDuration",
+                    SystemClock.elapsedRealtime() - mTosAcceptedTime);
+        }
+        getPageDelegate().acceptTermsOfService(false);
+        getPageDelegate().advanceToNextPage();
+    }
+
+    private void setSpinnerVisible(boolean spinnerVisible) {
+        // When the progress spinner is visible, we hide the other UI elements so that
+        // the user can't interact with them.
+        boolean otherElementVisible = !spinnerVisible;
+
+        setTosAndUmaVisible(otherElementVisible);
+        mTitle.setVisibility(otherElementVisible ? View.VISIBLE : View.INVISIBLE);
+        mProgressSpinner.setVisibility(spinnerVisible ? View.VISIBLE : View.GONE);
+    }
+
+    private boolean isWaitingForNativeAndPolicyInit() {
+        return !mNativeInitialized || !mPolicyServiceInitialized;
+    }
+
+    private boolean getUmaCheckBoxInitialState() {
+        // Metrics and crash reporting could not be permitted by policy.
+        if (!isWaitingForNativeAndPolicyInit()
+                && !PrivacyPreferencesManagerImpl.getInstance()
+                            .isUsageAndCrashReportingPermittedByPolicy()) {
+            return false;
+        }
+
+        // A user could start FRE and accept terms of service, then close the browser and start
+        // again. In this case we rely on whatever state the user has already set.
+        if (FirstRunUtils.didAcceptTermsOfService()) {
+            return PrivacyPreferencesManagerImpl.getInstance()
+                    .isUsageAndCrashReportingPermittedByUser();
+        }
+
+        return FirstRunActivity.DEFAULT_METRICS_AND_CRASH_REPORTING;
+    }
+
+    // Exposed methods for ToSAndUMACCTFirstRunFragment
+
+    protected void setTosAndUmaVisible(boolean isVisible) {
+        int visibility = isVisible ? View.VISIBLE : View.GONE;
+
+        mAcceptButton.setVisibility(visibility);
+        mTosAndPrivacy.setVisibility(visibility);
+        mAutoUpdaterCheckBox.setVisibility(visibility);
+    }
+
+    protected View getToSAndPrivacyText() {
+        return mTosAndPrivacy;
+    }
+
+    protected void onHideLoadingUIComplete() {
+        if (sObserver != null) {
+            sObserver.onHideLoadingUIComplete();
+        }
+    }
+
+    @VisibleForTesting
+    public static void setShowUmaCheckBoxForTesting(boolean showForTesting) {
+        sShowUmaCheckBoxForTesting = showForTesting;
+    }
+
+    @VisibleForTesting
+    public static void setObserverForTesting(ToSAndUMAFirstRunFragment.Observer observer) {
+        assert observer == null || sObserver == null;
+        sObserver = observer;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/TosAndUmaFragmentView.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/TosAndUmaFragmentView.java
new file mode 100644
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/TosAndUmaFragmentView.java
@@ -0,0 +1,336 @@
+// Copyright 2020 The Chromium Authors
+// 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.firstrun;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.ScrollView;
+
+import org.chromium.chrome.R;
+
+/**
+ * Base view for fre_tosanduma.xml. This view may change child view placement when changing screen
+ * dimensions (e.g. on rotation).
+ *
+ * See https://crbug.com/1151537 for illustration.
+ */
+public class TosAndUmaFragmentView extends RelativeLayout {
+    private ScrollView mScrollView;
+
+    private LinearLayout mMainLayout;
+
+    // The "title and content" contains the mTitle, mContentWrapper, and mLoadingSpinner that is
+    // visible when waiting for policy to be loaded.
+    private View mTitleAndContent;
+
+    // The "content wrapper" contains the ToS text and the UMA check box.
+    private View mContentWrapper;
+
+    // The "bottom group" contains the accept & continue button, and a small spinner that displays
+    // in its place when waiting for C++ to load before processing the FRE screen.
+    private View mBottomGroup;
+
+    private View mTitle;
+    private View mLogo;
+    private View mLoadingSpinnerContainer;
+    private View mPrivacyDisclaimer;
+    private View mShadow;
+
+    private int mLastHeight;
+    private int mLastWidth;
+
+    // Spacing params
+    private int mImageBottomMargin;
+    private int mVerticalSpacing;
+    private int mImageSize;
+    private int mLoadingSpinnerSize;
+    private int mLandscapeTopPadding;
+    private int mHeadlineSize;
+    private int mContentMargin;
+    private int mAcceptButtonHeight;
+    private int mBottomGroupVerticalMarginRegular;
+    private int mBottomGroupVerticalMarginSmall;
+
+    // Store the bottom margins for different screen orientations. We are using a smaller bottom
+    // margin when the content becomes scrollable. Storing margins per orientation because there are
+    // cases where content is scrollable in landscape mode while not in portrait mode.
+    private int mBottomMarginPortrait;
+    private int mBottomMarginLandscape;
+
+    /**
+     * Constructor for inflating via XML.
+     */
+    public TosAndUmaFragmentView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mScrollView = findViewById(R.id.scroll_view);
+
+        mMainLayout = findViewById(R.id.fre_main_layout);
+        mTitleAndContent = findViewById(R.id.fre_title_and_content);
+        mContentWrapper = findViewById(R.id.fre_content_wrapper);
+        mBottomGroup = findViewById(R.id.fre_bottom_group);
+
+        mTitle = findViewById(R.id.title);
+        mLogo = findViewById(R.id.image);
+        mLoadingSpinnerContainer = findViewById(R.id.loading_view_container);
+        mPrivacyDisclaimer = findViewById(R.id.privacy_disclaimer);
+        mShadow = findViewById(R.id.shadow);
+
+        // Set up shadow.
+        // Needed when scrolling to/away from the bottom of the ScrollView.
+        mScrollView.getViewTreeObserver().addOnScrollChangedListener(this::updateShadowVisibility);
+        // Needed when other elements are added / removed from ScrollView.
+        mScrollView.getViewTreeObserver().addOnGlobalLayoutListener(this::updateShadowVisibility);
+
+        // Cache resource dimensions that used in #onMeasure.
+        mImageBottomMargin = getResources().getDimensionPixelSize(R.dimen.fre_image_bottom_margin);
+        mVerticalSpacing = getResources().getDimensionPixelSize(R.dimen.fre_vertical_spacing);
+        mImageSize = getResources().getDimensionPixelSize(R.dimen.fre_tos_image_height);
+        mLoadingSpinnerSize =
+                getResources().getDimensionPixelSize(R.dimen.fre_loading_spinner_size);
+        mLandscapeTopPadding =
+                getResources().getDimensionPixelSize(R.dimen.fre_landscape_top_padding);
+        mHeadlineSize = getResources().getDimensionPixelSize(R.dimen.headline_size);
+        mContentMargin = getResources().getDimensionPixelSize(R.dimen.fre_content_margin);
+        mAcceptButtonHeight = getResources().getDimensionPixelSize(R.dimen.min_touch_target_size);
+
+        mBottomGroupVerticalMarginRegular =
+                getResources().getDimensionPixelSize(R.dimen.fre_button_vertical_margin);
+        mBottomGroupVerticalMarginSmall =
+                getResources().getDimensionPixelSize(R.dimen.fre_button_vertical_margin_small);
+
+        // Default bottom margin to "regular", consistent with what is defined in xml.
+        mBottomMarginPortrait = mBottomGroupVerticalMarginRegular;
+        mBottomMarginLandscape = mBottomGroupVerticalMarginRegular;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        // For an alternate layout in horizontal mode for screens of a certain size. These are why
+        // the padding is set manually.
+
+        // This assumes that view's layout_width is set to match_parent.
+        assert MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY;
+
+        int width = MeasureSpec.getSize(widthMeasureSpec);
+        int height = MeasureSpec.getSize(heightMeasureSpec);
+
+        // If the layout orientation does not change, there's no need to recalculate layout
+        // attributes.
+        if (width != mLastWidth || height != mLastHeight) {
+            mLastHeight = height;
+            mLastWidth = width;
+
+            boolean useWideScreenLayout = shouldUseWideScreen(width, height);
+
+            mMainLayout.setOrientation(
+                    useWideScreenLayout ? LinearLayout.HORIZONTAL : LinearLayout.VERTICAL);
+
+            mTitleAndContent.setPaddingRelative(mTitleAndContent.getPaddingStart(),
+                    getTitleAndContentLayoutTopPadding(useWideScreenLayout),
+                    mTitleAndContent.getPaddingEnd(), mTitleAndContent.getPaddingBottom());
+
+            setLogoLayoutParams(useWideScreenLayout, height);
+            setTitleLayoutParams(useWideScreenLayout);
+            setSpinnerLayoutParams(useWideScreenLayout, width, height);
+
+            setContentLayoutParams(useWideScreenLayout);
+            setPrivacyDisclaimerLayoutParams(useWideScreenLayout);
+
+            setBottomGroupLayoutParams(useWideScreenLayout);
+        }
+
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        // Do another round of view adjustments that depends on sizes assigned to children views in
+        // super#onMeasure. If the state of any view is changed in this process, trigger another
+        // round of measure to make changes take effect.
+        boolean changed = doPostMeasureAdjustment();
+        if (changed) {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+    }
+
+    private boolean shouldUseWideScreen(int width, int height) {
+        int maxButtonBarHeight = mAcceptButtonHeight + 2 * mBottomGroupVerticalMarginRegular;
+        return (height >= mImageSize + 2 * maxButtonBarHeight) && (width > 1.5 * height);
+    }
+
+    /**
+     * Adjust views after measure, when every view components has an initial size assigned.
+     * @return Whether any change happened to children views.
+     */
+    private boolean doPostMeasureAdjustment() {
+        boolean changed = updateShadowVisibility();
+        changed |= assignSmallBottomMarginIfNecessary();
+        return changed;
+    }
+
+    private boolean updateShadowVisibility() {
+        int newVisibility = mScrollView.canScrollVertically(1) ? VISIBLE : GONE;
+        if (newVisibility == mShadow.getVisibility()) {
+            return false;
+        }
+        mShadow.setVisibility(newVisibility);
+        return true;
+    }
+
+    /**
+     * When content is scrollable, use a smaller margin to present more content on screen.
+     * Note that once we change to using a smaller margin we currently will never switch back to the
+     * default margin size (e.g. enter then exit multi-window).
+     *
+     * TODO(https://crbug.com/1159198): Adjust the margin according to the size of
+     * TosAndUmaFragmentView.
+     */
+    private boolean assignSmallBottomMarginIfNecessary() {
+        // Check the width and height of TosAndUmaFragmentView. This function may be executed
+        // between transitioning from landscape to portrait. If the current measure spec (mLastWidth
+        // and mLastHeight) is different than the size actually measured (getHeight() &&
+        // getWidth()), the results from mScrollView#canScrollVertically could be stale. In such
+        // cases, it is safe to early return here, as current measure is in transition and a
+        // follow-up measure will be triggered when the measured spec and actual size matches.
+        if (getHeight() != mLastHeight || getWidth() != mLastWidth) {
+            return false;
+        }
+
+        // Do not assign margins if the content is not scrollable.
+        if (!mScrollView.canScrollVertically(1) && !mScrollView.canScrollVertically(-1)) {
+            return false;
+        }
+
+        MarginLayoutParams params = (MarginLayoutParams) mBottomGroup.getLayoutParams();
+        if (params.bottomMargin == mBottomGroupVerticalMarginSmall) {
+            return false;
+        }
+
+        if (shouldUseLandscapeBottomMargin()) {
+            mBottomMarginLandscape = mBottomGroupVerticalMarginSmall;
+        } else {
+            mBottomMarginPortrait = mBottomGroupVerticalMarginSmall;
+        }
+        params.setMargins(params.leftMargin, mBottomGroupVerticalMarginSmall, params.rightMargin,
+                mBottomGroupVerticalMarginSmall);
+        mBottomGroup.setLayoutParams(params);
+        return true;
+    }
+
+    private void setSpinnerLayoutParams(boolean useWideScreen, int width, int height) {
+        LinearLayout.LayoutParams spinnerParams =
+                (LinearLayout.LayoutParams) mLoadingSpinnerContainer.getLayoutParams();
+
+        // Adjust the spinner placement. If in portrait mode, the spinner is placed in the region
+        // below the title; If in wide screen mode, the spinner is placed in the center of
+        // the entire screen. Because we cannot get the exact size for headline,
+        // the spinner placement is approximately centered in this case.
+        if (useWideScreen) {
+            int freImageWidth = mImageSize + mVerticalSpacing * 2;
+            int spinnerStartMargin =
+                    Math.max(0, (width / 2) - freImageWidth - mLoadingSpinnerSize / 2);
+
+            int topContentHeight = mHeadlineSize + mLandscapeTopPadding;
+            int spinnerTopMargin =
+                    Math.max(0, height / 2 - topContentHeight - mLoadingSpinnerSize / 2);
+
+            spinnerParams.gravity = Gravity.START;
+            spinnerParams.setMarginStart(spinnerStartMargin);
+            spinnerParams.topMargin = spinnerTopMargin;
+        } else {
+            // Use the same padding between title and logo for the spinner.
+            // TODO(crbug.com/1128123): Switch from top margin to an approach that will center the
+            //  spinner in the bottom half of the screen.
+            int spinnerTopMargin = mImageBottomMargin;
+
+            spinnerParams.gravity = Gravity.CENTER_HORIZONTAL;
+            spinnerParams.setMarginStart(0);
+            spinnerParams.topMargin = spinnerTopMargin;
+        }
+
+        mLoadingSpinnerContainer.setLayoutParams(spinnerParams);
+    }
+
+    private void setLogoLayoutParams(boolean useWideScreen, int height) {
+        LinearLayout.LayoutParams logoLayoutParams =
+                (LinearLayout.LayoutParams) mLogo.getLayoutParams();
+        if (useWideScreen) {
+            // When using the wide screen layout, we want to vertically center the logo on the start
+            // side of the screen. While we have no padding on the main layout when using the wide
+            // screen, we'll calculate the space needed and set it as top margin above the logo to
+            // make it centered.
+            int topMargin = (height - mImageSize) / 2;
+
+            // We only use the wide screen layout when the screen height is tall enough to
+            // accommodate the image and some padding. But just in case that calculation fails,
+            // ensure topMargin isn't negative.
+            assert topMargin > 0;
+            logoLayoutParams.topMargin = Math.max(0, topMargin);
+            logoLayoutParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
+        } else {
+            // Otherwise, in tall screen mode, we want the align the baseline of the title to the
+            // center of the screen. While calculation is done in a similar way, we are putting
+            // mVerticalSpacing for marginTop as minimum to avoid 0dp spacing between top and logo
+            // on small screen devices.
+            int freImageHeight = mImageSize + mImageBottomMargin;
+            logoLayoutParams.topMargin =
+                    Math.max(mVerticalSpacing, (height / 2 - freImageHeight - mHeadlineSize));
+            logoLayoutParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+        }
+    }
+
+    private void setTitleLayoutParams(boolean useWideScreen) {
+        LinearLayout.LayoutParams titleParams =
+                (LinearLayout.LayoutParams) mTitle.getLayoutParams();
+        titleParams.gravity = useWideScreen ? Gravity.START : Gravity.CENTER;
+    }
+
+    private void setContentLayoutParams(boolean useWideScreen) {
+        MarginLayoutParams contentWrapperLayoutParams =
+                (MarginLayoutParams) mContentWrapper.getLayoutParams();
+        contentWrapperLayoutParams.setMarginStart(useWideScreen ? 0 : mContentMargin);
+        mContentWrapper.setLayoutParams(contentWrapperLayoutParams);
+    }
+
+    private void setPrivacyDisclaimerLayoutParams(boolean useWideScreen) {
+        LinearLayout.LayoutParams privacyDisclaimerParams =
+                (LinearLayout.LayoutParams) mPrivacyDisclaimer.getLayoutParams();
+        privacyDisclaimerParams.gravity = useWideScreen ? Gravity.START : Gravity.CENTER;
+        privacyDisclaimerParams.setMarginStart(useWideScreen ? 0 : mContentMargin);
+        mPrivacyDisclaimer.setLayoutParams(privacyDisclaimerParams);
+    }
+
+    private void setBottomGroupLayoutParams(boolean useWideScreen) {
+        RelativeLayout.LayoutParams bottomGroupParams =
+                (RelativeLayout.LayoutParams) mBottomGroup.getLayoutParams();
+        int removedRule =
+                useWideScreen ? RelativeLayout.CENTER_HORIZONTAL : RelativeLayout.ALIGN_PARENT_END;
+        int addedRule =
+                useWideScreen ? RelativeLayout.ALIGN_PARENT_END : RelativeLayout.CENTER_HORIZONTAL;
+        bottomGroupParams.removeRule(removedRule);
+        bottomGroupParams.addRule(addedRule);
+
+        int bottomMargin =
+                shouldUseLandscapeBottomMargin() ? mBottomMarginLandscape : mBottomMarginPortrait;
+        bottomGroupParams.setMargins(bottomGroupParams.leftMargin, bottomMargin,
+                bottomGroupParams.rightMargin, bottomMargin);
+        mBottomGroup.setLayoutParams(bottomGroupParams);
+    }
+
+    private int getTitleAndContentLayoutTopPadding(boolean useWideScreen) {
+        return useWideScreen ? mLandscapeTopPadding : 0;
+    }
+
+    private boolean shouldUseLandscapeBottomMargin() {
+        return mLastWidth > mLastHeight;
+    }
+}
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -3301,7 +3301,29 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
         <ph name="APP_NAME">%1$s<ex>Google Maps</ex></ph> will open in Chrome. By continuing, you agree to the <ph name="BEGIN_LINK1">&lt;LINK1&gt;</ph>Google Terms of Service<ph name="END_LINK1">&lt;/LINK1&gt;</ph>, and the <ph name="BEGIN_LINK2">&lt;LINK2&gt;</ph>Google Chrome and ChromeOS Additional Terms of Service<ph name="END_LINK2">&lt;/LINK2&gt;</ph>. The <ph name="BEGIN_LINK3">&lt;LINK3&gt;</ph>Privacy Policy<ph name="END_LINK3">&lt;/LINK3&gt;</ph> also applies.
       </message>
       <message name="IDS_FRE_ACCEPT_CONTINUE" desc="Text for first page accept and continue button [CHAR_LIMIT=20]">
-        Accept &amp; continue
+        Continue
+      </message>
+      <message name="IDS_ADBLOCK_UPDATER_PRIVACY_POLICY_URL" desc="URL for privacy policy for the ad block updater" translateable="false">
+        https://docs.github.com/en/github/site-policy/github-privacy-statement#github-pages
+      </message>
+      <message name="IDS_BROMITE_UPDATER_PRIVACY_POLICY_URL" desc="URL for privacy policy for the Bromite auto updater" translateable="false">
+        https://docs.github.com/en/github/site-policy/github-privacy-statement#github-pages
+      </message>
+      <message name="IDS_BROMITE_FRE_FOOTER_PRIVACY_POLICY" desc="Message explaining the privacy policy of the file hosting service provider for adblock updates and Bromite app automatic updates">
+        <ph name="BEGIN_PRIVACY_LINK1">&lt;PRIVACY_LINK1&gt;</ph>Automatic ad block filters updates<ph name="END_PRIVACY_LINK1">&lt;/PRIVACY_LINK1&gt;</ph> are subject to the <ph name="BEGIN_PRIVACY_LINK2">&lt;PRIVACY_LINK2&gt;</ph>GitHub Privacy statement<ph name="END_PRIVACY_LINK2">&lt;/PRIVACY_LINK2&gt;</ph>; they cannot be disabled.
+        The following checkbox controls instead <ph name="BEGIN_PRIVACY_LINK3">&lt;PRIVACY_LINK3&gt;</ph>automatic app updates<ph name="END_PRIVACY_LINK3">&lt;/PRIVACY_LINK3&gt;</ph> which are also subject to the <ph name="BEGIN_PRIVACY_LINK4">&lt;PRIVACY_LINK4&gt;</ph>GitHub Privacy statement<ph name="END_PRIVACY_LINK4">&lt;/PRIVACY_LINK4&gt;</ph>.
+      </message>
+      <message name="IDS_AUTO_UPDATER_CHECK" desc="Message for the checkbox for automatic Bromite updates">
+        Automatic checks for Bromite app updates
+      </message>
+      <message name="IDS_UPDATER_PRIVACY_POLICY_URL" desc="URL for GitHub privacy statement" translateable="false">
+        https://docs.github.com/en/github/site-policy/github-privacy-statement#github-pages
+      </message>
+      <message name="IDS_ADBLOCK_WIKI_URL" desc="URL for Bromite wiki page about ad blocking" translateable="false">
+        https://github.com/bromite/bromite/wiki/AdBlocking
+      </message>
+      <message name="IDS_AUTO_UPDATES_WIKI_URL" desc="URL for Bromite wiki page about automatic updates" translateable="false">
+        https://github.com/bromite/bromite/wiki/AutomaticUpdates
       </message>
       <message name="IDS_FRE_WELCOME" desc="Text for greeting the user on Chrome First Run">
         Welcome to Chrome
--
2.25.1