Commit d7b52b67 authored by Sky Malice's avatar Sky Malice Committed by Commit Bot

Updated FirstRunActivity#exitFirstRun() to skip FRE for pending intent.

The old handling of exitFirstRun() simply relaunched the pending intent.
This turned out to be a problem because the intent dispatcher read the
durable settings for if the FRE was needed, and would decide it was.
Interestingly, there can only be one FirstRunActivity active at a time,
and so the relaunch was actually ignored. This would cause the existing
FirstRunActivity get stuck because the replacement activity was never
started.

This fixes the problem by introducing a new "ephemeral" static variable
that remembers we just decided to skip the FRE, so the next intent
dispatch/routing will be aware of this decision.

Bug: 1106987
Change-Id: Id8502956236f76c93091452fe580ae0d6fea2c80
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2343553Reviewed-by: default avatarYusuf Ozuysal <yusufo@chromium.org>
Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Reviewed-by: default avatarWenyu Fu <wenyufu@chromium.org>
Commit-Queue: Sky Malice <skym@chromium.org>
Cr-Commit-Position: refs/heads/master@{#799483}
parent e15527d2
...@@ -117,7 +117,7 @@ public class CustomTabsConnectionService extends CustomTabsService { ...@@ -117,7 +117,7 @@ public class CustomTabsConnectionService extends CustomTabsService {
private boolean isFirstRunDone() { private boolean isFirstRunDone() {
if (mBindIntent == null) return true; if (mBindIntent == null) return true;
boolean firstRunNecessary = FirstRunFlowSequencer.checkIfFirstRunIsNecessary(false); boolean firstRunNecessary = FirstRunFlowSequencer.checkIfFirstRunIsNecessary(false, true);
if (!firstRunNecessary) { if (!firstRunNecessary) {
mBindIntent = null; mBindIntent = null;
return true; return true;
......
...@@ -401,11 +401,19 @@ public class FirstRunActivity extends FirstRunActivityBase implements FirstRunPa ...@@ -401,11 +401,19 @@ public class FirstRunActivity extends FirstRunActivityBase implements FirstRunPa
SearchWidgetProvider.updateCachedEngineName(); SearchWidgetProvider.updateCachedEngineName();
if (sObserver != null) sObserver.onUpdateCachedEngineName(); if (sObserver != null) sObserver.onUpdateCachedEngineName();
exitFirstRun(); launchPendingIntentAndFinish();
} }
@Override @Override
public void exitFirstRun() { public void exitFirstRun() {
// This is important because the first run, when completed, will re-launch the original
// intent. The re-launched intent will still need to know to avoid the FRE.
FirstRunStatus.setEphemeralSkipFirstRun(true);
launchPendingIntentAndFinish();
}
private void launchPendingIntentAndFinish() {
if (!sendFirstRunCompletePendingIntent()) { if (!sendFirstRunCompletePendingIntent()) {
finish(); finish();
} else { } else {
......
...@@ -18,6 +18,7 @@ import org.chromium.base.CommandLine; ...@@ -18,6 +18,7 @@ import org.chromium.base.CommandLine;
import org.chromium.base.IntentUtils; import org.chromium.base.IntentUtils;
import org.chromium.base.Log; import org.chromium.base.Log;
import org.chromium.chrome.browser.IntentHandler; import org.chromium.chrome.browser.IntentHandler;
import org.chromium.chrome.browser.LaunchIntentDispatcher;
import org.chromium.chrome.browser.flags.CachedFeatureFlags; import org.chromium.chrome.browser.flags.CachedFeatureFlags;
import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.flags.ChromeSwitches; import org.chromium.chrome.browser.flags.ChromeSwitches;
...@@ -220,11 +221,26 @@ public abstract class FirstRunFlowSequencer { ...@@ -220,11 +221,26 @@ public abstract class FirstRunFlowSequencer {
} }
/** /**
* Checks if the First Run needs to be launched. * Checks if the First Run Experience needs to be launched.
* @param preferLightweightFre Whether to prefer the Lightweight First Run Experience. * @param preferLightweightFre Whether to prefer the Lightweight First Run Experience.
* @param fromIntent Intent used to launch the caller.
* @return Whether the First Run Experience needs to be launched. * @return Whether the First Run Experience needs to be launched.
*/ */
public static boolean checkIfFirstRunIsNecessary(boolean preferLightweightFre) { public static boolean checkIfFirstRunIsNecessary(
boolean preferLightweightFre, Intent fromIntent) {
boolean isCct = fromIntent.getBooleanExtra(
FirstRunActivityBase.EXTRA_CHROME_LAUNCH_INTENT_IS_CCT, false)
|| LaunchIntentDispatcher.isCustomTabIntent(fromIntent);
return checkIfFirstRunIsNecessary(preferLightweightFre, isCct);
}
/**
* Checks if the First Run Experience needs to be launched.
* @param preferLightweightFre Whether to prefer the Lightweight First Run Experience.
* @param isCct Whether this check is being made in the context of a CCT.
* @return Whether the First Run Experience needs to be launched.
*/
public static boolean checkIfFirstRunIsNecessary(boolean preferLightweightFre, boolean isCct) {
// If FRE is disabled (e.g. in tests), proceed directly to the intent handling. // If FRE is disabled (e.g. in tests), proceed directly to the intent handling.
if (CommandLine.getInstance().hasSwitch(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE) if (CommandLine.getInstance().hasSwitch(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE)
|| ApiCompatibilityUtils.isDemoUser() || ApiCompatibilityUtils.isDemoUser()
...@@ -235,6 +251,13 @@ public abstract class FirstRunFlowSequencer { ...@@ -235,6 +251,13 @@ public abstract class FirstRunFlowSequencer {
// Promo pages are removed, so there is nothing else to show in FRE. // Promo pages are removed, so there is nothing else to show in FRE.
return false; return false;
} }
if (isCct && FirstRunStatus.isEphemeralSkipFirstRun()) {
// Domain policies may have caused CCTs to skip the FRE. While this needs to be figured
// out at runtime for each app restart, it should apply to all CCTs for the duration of
// the app's lifetime.
// TODO(https://crbug.com/1108582): Replace this with a shared pref.
return false;
}
return !preferLightweightFre return !preferLightweightFre
|| (!FirstRunStatus.shouldSkipWelcomePage() || (!FirstRunStatus.shouldSkipWelcomePage()
&& !FirstRunStatus.getLightweightFirstRunFlowComplete()); && !FirstRunStatus.getLightweightFirstRunFlowComplete());
...@@ -253,7 +276,7 @@ public abstract class FirstRunFlowSequencer { ...@@ -253,7 +276,7 @@ public abstract class FirstRunFlowSequencer {
public static boolean launch(Context caller, Intent fromIntent, boolean requiresBroadcast, public static boolean launch(Context caller, Intent fromIntent, boolean requiresBroadcast,
boolean preferLightweightFre) { boolean preferLightweightFre) {
// Check if the user needs to go through First Run at all. // Check if the user needs to go through First Run at all.
if (!checkIfFirstRunIsNecessary(preferLightweightFre)) return false; if (!checkIfFirstRunIsNecessary(preferLightweightFre, fromIntent)) return false;
String intentUrl = IntentHandler.getUrlFromIntent(fromIntent); String intentUrl = IntentHandler.getUrlFromIntent(fromIntent);
Uri uri = intentUrl != null ? Uri.parse(intentUrl) : null; Uri uri = intentUrl != null ? Uri.parse(intentUrl) : null;
......
...@@ -16,6 +16,36 @@ public class FirstRunStatus { ...@@ -16,6 +16,36 @@ public class FirstRunStatus {
// Whether the first run flow is triggered in the current browser session. // Whether the first run flow is triggered in the current browser session.
private static boolean sFirstRunTriggered; private static boolean sFirstRunTriggered;
// Whether the first run flow should be skipped for the current browser session.
private static boolean sEphemeralSkipFirstRun;
/** @param triggered whether the first run flow is triggered in the current browser session. */
public static void setFirstRunTriggered(boolean triggered) {
sFirstRunTriggered = triggered;
}
/** @return whether first run flow is triggered in the current browser session. */
public static boolean isFirstRunTriggered() {
return sFirstRunTriggered;
}
/**
* @param skip Whether the first run flow should be skipped for the current session for app
* entry points that allow for this (e.g. CCTs via Enterprise policy). Not saved to
* durable storage, and will be erased when the process is restarted.
*/
public static void setEphemeralSkipFirstRun(boolean skip) {
sEphemeralSkipFirstRun = skip;
}
/**
* @return Whether the first run flow should be skipped for the current session for app entry
* points that allow for this.
*/
public static boolean isEphemeralSkipFirstRun() {
return sEphemeralSkipFirstRun;
}
/** /**
* Sets the "main First Run Experience flow complete" preference. * Sets the "main First Run Experience flow complete" preference.
* @param isComplete Whether the main First Run Experience flow is complete * @param isComplete Whether the main First Run Experience flow is complete
...@@ -39,22 +69,6 @@ public class FirstRunStatus { ...@@ -39,22 +69,6 @@ public class FirstRunStatus {
ChromeSwitches.FORCE_FIRST_RUN_FLOW_COMPLETE_FOR_TESTING); ChromeSwitches.FORCE_FIRST_RUN_FLOW_COMPLETE_FOR_TESTING);
} }
/**
* Sets whether the first run flow is triggered in the current browser session.
* @param triggered
* @return
*/
public static void setFirstRunTriggered(boolean triggered) {
sFirstRunTriggered = triggered;
}
/**
* Returns whether first run flow is triggered in the current browser session.
*/
public static boolean isFirstRunTriggered() {
return sFirstRunTriggered;
}
/** /**
* Sets the preference to skip the welcome page from the main First Run Experience. * Sets the preference to skip the welcome page from the main First Run Experience.
* @param isSkip Whether the welcome page should be skpped * @param isSkip Whether the welcome page should be skpped
......
...@@ -76,8 +76,11 @@ class NativeInitializationController { ...@@ -76,8 +76,11 @@ class NativeInitializationController {
return; return;
} }
assert mBackgroundTasksComplete == null; // This is a fairly low cost way to check if fetching the variations seed is needed. It can
boolean fetchVariationsSeed = FirstRunFlowSequencer.checkIfFirstRunIsNecessary(false); // produces false positives, but that's okay. There's a later mechanism that checks a
// dedicated durable field to make sure the actual network request is only made once.
boolean fetchVariationsSeed = FirstRunFlowSequencer.checkIfFirstRunIsNecessary(
false, mActivityDelegate.getInitialIntent());
mBackgroundTasksComplete = false; mBackgroundTasksComplete = false;
new AsyncInitTaskRunner() { new AsyncInitTaskRunner() {
......
...@@ -390,7 +390,7 @@ public class SearchWidgetProvider extends AppWidgetProvider { ...@@ -390,7 +390,7 @@ public class SearchWidgetProvider extends AppWidgetProvider {
} }
static boolean shouldShowFullString() { static boolean shouldShowFullString() {
boolean freIsNotNecessary = !FirstRunFlowSequencer.checkIfFirstRunIsNecessary(false); boolean freIsNotNecessary = !FirstRunFlowSequencer.checkIfFirstRunIsNecessary(false, false);
boolean noNeedToCheckForSearchDialog = boolean noNeedToCheckForSearchDialog =
!LocaleManager.getInstance().needToCheckForSearchEnginePromo(); !LocaleManager.getInstance().needToCheckForSearchEnginePromo();
return freIsNotNecessary && noNeedToCheckForSearchDialog; return freIsNotNecessary && noNeedToCheckForSearchDialog;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment