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 {
private boolean isFirstRunDone() {
if (mBindIntent == null) return true;
boolean firstRunNecessary = FirstRunFlowSequencer.checkIfFirstRunIsNecessary(false);
boolean firstRunNecessary = FirstRunFlowSequencer.checkIfFirstRunIsNecessary(false, true);
if (!firstRunNecessary) {
mBindIntent = null;
return true;
......
......@@ -401,11 +401,19 @@ public class FirstRunActivity extends FirstRunActivityBase implements FirstRunPa
SearchWidgetProvider.updateCachedEngineName();
if (sObserver != null) sObserver.onUpdateCachedEngineName();
exitFirstRun();
launchPendingIntentAndFinish();
}
@Override
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()) {
finish();
} else {
......
......@@ -18,6 +18,7 @@ import org.chromium.base.CommandLine;
import org.chromium.base.IntentUtils;
import org.chromium.base.Log;
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.ChromeFeatureList;
import org.chromium.chrome.browser.flags.ChromeSwitches;
......@@ -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 fromIntent Intent used to launch the caller.
* @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 (CommandLine.getInstance().hasSwitch(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE)
|| ApiCompatibilityUtils.isDemoUser()
......@@ -235,6 +251,13 @@ public abstract class FirstRunFlowSequencer {
// Promo pages are removed, so there is nothing else to show in FRE.
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
|| (!FirstRunStatus.shouldSkipWelcomePage()
&& !FirstRunStatus.getLightweightFirstRunFlowComplete());
......@@ -253,7 +276,7 @@ public abstract class FirstRunFlowSequencer {
public static boolean launch(Context caller, Intent fromIntent, boolean requiresBroadcast,
boolean preferLightweightFre) {
// 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);
Uri uri = intentUrl != null ? Uri.parse(intentUrl) : null;
......
......@@ -16,6 +16,36 @@ public class FirstRunStatus {
// Whether the first run flow is triggered in the current browser session.
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.
* @param isComplete Whether the main First Run Experience flow is complete
......@@ -39,22 +69,6 @@ public class FirstRunStatus {
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.
* @param isSkip Whether the welcome page should be skpped
......
......@@ -76,8 +76,11 @@ class NativeInitializationController {
return;
}
assert mBackgroundTasksComplete == null;
boolean fetchVariationsSeed = FirstRunFlowSequencer.checkIfFirstRunIsNecessary(false);
// This is a fairly low cost way to check if fetching the variations seed is needed. It can
// 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;
new AsyncInitTaskRunner() {
......
......@@ -390,7 +390,7 @@ public class SearchWidgetProvider extends AppWidgetProvider {
}
static boolean shouldShowFullString() {
boolean freIsNotNecessary = !FirstRunFlowSequencer.checkIfFirstRunIsNecessary(false);
boolean freIsNotNecessary = !FirstRunFlowSequencer.checkIfFirstRunIsNecessary(false, false);
boolean noNeedToCheckForSearchDialog =
!LocaleManager.getInstance().needToCheckForSearchEnginePromo();
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