Bug 1363321 - Part 2 - Reset crash loop counter after a presumably succesful startup. r?jchen
At the moment, our crash loop counter that turns off session restoring when multiple crashes happen in a row is reset only after a successful background-foreground cycle.
Since its purpose is to break out of startup crash loops caused by the currently loaded tab that is automatically loaded after starting the app, it makes sense to also reset it if we didn't crash soon after starting up. Otherwise, if the user remains within Firefox, another crash long after starting would still count towards the crash loop counter.
Desktop already employs a similar logic for deciding whether to automatically enable safe mode or not after a crash. Therefore, we use the same amount of time (30 s) after starting Gecko for resetting the counter.
We also take the opportunity to make our logic a bit more conservative and only turn off session restoring after two successive crashes in a row (desktop uses 3 by default).
MozReview-Commit-ID: CQDutkDgmfc
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -136,17 +136,17 @@ pref("browser.sessionhistory.max_entries
pref("browser.sessionhistory.contentViewerTimeout", 360);
pref("browser.sessionhistory.bfcacheIgnoreMemoryPressure", false);
/* session store */
pref("browser.sessionstore.resume_from_crash", true);
pref("browser.sessionstore.interval", 10000); // milliseconds
pref("browser.sessionstore.backupInterval", 120000); // milliseconds -> 2 minutes
pref("browser.sessionstore.max_tabs_undo", 10);
-pref("browser.sessionstore.max_resumed_crashes", 1);
+pref("browser.sessionstore.max_resumed_crashes", 2);
pref("browser.sessionstore.privacy_level", 0); // saving data: 0 = all, 1 = unencrypted sites, 2 = never
pref("browser.sessionstore.debug_logging", false);
/* these should help performance */
pref("mozilla.widget.force-24bpp", true);
pref("mozilla.widget.use-buffer-pixmap", true);
pref("mozilla.widget.disable-native-theme", true);
pref("layout.reflow.synthMouseMove", false);
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -178,16 +178,20 @@ public abstract class GeckoApp
public static final String SAVED_STATE_IN_BACKGROUND = "inBackground";
public static final String SAVED_STATE_PRIVATE_SESSION = "privateSession";
// Delay before running one-time "cleanup" tasks that may be needed
// after a version upgrade.
private static final int CLEANUP_DEFERRAL_SECONDS = 15;
+ // Length of time in ms during which crashes are classified as startup crashes
+ // for crash loop detection purposes.
+ private static final int STARTUP_PHASE_DURATION_MS = 30 * 1000;
+
private static boolean sAlreadyLoaded;
protected boolean mIgnoreLastSelectedTab;
protected static WeakReference<GeckoApp> mLastActiveGeckoApp;
protected RelativeLayout mRootLayout;
protected RelativeLayout mMainLayout;
@@ -739,16 +743,24 @@ public abstract class GeckoApp
// This method is cheap, so don't spawn a new runnable.
final HealthRecorder rec = mHealthRecorder;
if (rec != null) {
rec.recordGeckoStartupTime(mGeckoReadyStartupTimer.getElapsed());
}
((GeckoApplication) getApplicationContext()).onDelayedStartup();
+ // Reset the crash loop counter if we remain alive for at least half a minute.
+ ThreadUtils.postDelayedToBackgroundThread(new Runnable() {
+ @Override
+ public void run() {
+ getSharedPreferences().edit().putInt(PREFS_CRASHED_COUNT, 0).apply();
+ }
+ }, STARTUP_PHASE_DURATION_MS);
+
} else if ("Gecko:Exited".equals(event)) {
// Gecko thread exited first; let GeckoApp die too.
doShutdown();
} else if ("Sanitize:Finished".equals(event)) {
if (message.getBoolean("shutdown", false)) {
// Gecko is shutting down and has called our sanitize handlers,
// so we can start exiting, too.
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GeckoBackgroundThread.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GeckoBackgroundThread.java
@@ -68,9 +68,13 @@ final class GeckoBackgroundThread extend
/*package*/ static synchronized void post(final Runnable runnable) {
if (thread == null) {
startThread(runnable);
return;
}
getHandler().post(runnable);
}
+
+ /*package*/ static void postDelayed(final Runnable runnable, final long timeout) {
+ getHandler().postDelayed(runnable, timeout);
+ }
}
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ThreadUtils.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ThreadUtils.java
@@ -110,16 +110,20 @@ public final class ThreadUtils {
public static Handler getBackgroundHandler() {
return GeckoBackgroundThread.getHandler();
}
public static void postToBackgroundThread(Runnable runnable) {
GeckoBackgroundThread.post(runnable);
}
+ public static void postDelayedToBackgroundThread(Runnable runnable, long timeout) {
+ GeckoBackgroundThread.postDelayed(runnable, timeout);
+ }
+
public static void assertOnUiThread(final AssertBehavior assertBehavior) {
assertOnThread(getUiThread(), assertBehavior);
}
public static void assertOnUiThread() {
assertOnThread(getUiThread(), AssertBehavior.THROW);
}