Bug 1363321 - Part 2 - Reset crash loop counter after a presumably succesful startup. r?jchen draft
authorJan Henning <jh+bugzilla@buttercookie.de>
Tue, 09 May 2017 23:12:54 +0200
changeset 575893 0dd99c8cfbdc937f8ac0ff72390c832e2179d96d
parent 575619 7b9f92a892c1677839f7ec17fa1d0052e68c6ced
child 628035 40a05c824b9340a6c0b99d94582584a6a2f60034
push id58187
push usermozilla@buttercookie.de
push dateWed, 10 May 2017 22:37:59 +0000
reviewersjchen
bugs1363321
milestone55.0a1
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
mobile/android/app/mobile.js
mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GeckoBackgroundThread.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ThreadUtils.java
--- 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);
     }