Bug 1513938 - Enforce a Bundle size limit and drop `privateSession` if exceeds it. r=JanH, a=jcristau FENNEC_64_0_2_BUILD1 FENNEC_64_0_2_RELEASE
authorPetru Lingurar <petru.lingurar@softvision.ro>
Fri, 21 Dec 2018 08:56:47 +0000
changeset 501492 65621d0fe1262af0643cec37c23b2d9ec42588ad
parent 501491 c58ea2229c337e9981ab9d3650b04fd3d249b141
child 501493 66e935177d0f46f945066ce9bbac41e56fe2add2
push id1892
push userjcristau@mozilla.com
push dateTue, 08 Jan 2019 17:35:14 +0000
treeherdermozilla-release@65621d0fe126 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersJanH, jcristau
bugs1513938, 1515592
milestone64.0.2
Bug 1513938 - Enforce a Bundle size limit and drop `privateSession` if exceeds it. r=JanH, a=jcristau The `privateSession` key would normally allow persisting the Private Browsing session across OOMs in Activity's Bundle. We need to do that to avoid storing private, sensible data on disk like we do with the normal browsing session. In some cases `privateSession` would contain a lot of data which, along with other possible concurrent transactions could overflow Binder's buffer which has a limited fixed size, currently 1Mb. To avoid this, we will drop `privateSession` from the Bundle if the resulting size is greater than a _speculative_ size of 300KBs which would mean that in the case of an OOM all Private Browsing state would be lost. Bug 1515592 is filed to investigate for a better solution. Differential Revision: https://phabricator.services.mozilla.com/D15067
mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -208,18 +208,16 @@ public class BrowserApp extends GeckoApp
 
     // TODO: Replace with kinto endpoint.
     private static final String SWITCHBOARD_SERVER = "https://firefox.settings.services.mozilla.com/v1/buckets/fennec/collections/experiments/records";
 
     private static final String STATE_ABOUT_HOME_TOP_PADDING = "abouthome_top_padding";
 
     private static final String BROWSER_SEARCH_TAG = "browser_search";
 
-    private static final int MAX_BUNDLE_SIZE = 300000; // 300 kilobytes
-
     // Request ID for startActivityForResult.
     public static final int ACTIVITY_REQUEST_PREFERENCES = 1001;
     private static final int ACTIVITY_REQUEST_TAB_QUEUE = 2001;
     public static final int ACTIVITY_REQUEST_FIRST_READERVIEW_BOOKMARK = 3001;
     public static final int ACTIVITY_RESULT_FIRST_READERVIEW_BOOKMARKS_GOTO_BOOKMARKS = 3002;
     public static final int ACTIVITY_RESULT_FIRST_READERVIEW_BOOKMARKS_IGNORE = 3003;
     public static final int ACTIVITY_REQUEST_TRIPLE_READERVIEW = 4001;
     public static final int ACTIVITY_RESULT_TRIPLE_READERVIEW_ADD_BOOKMARK = 4002;
@@ -2273,17 +2271,17 @@ public class BrowserApp extends GeckoApp
         mDynamicToolbar.onSaveInstanceState(outState);
         outState.putInt(STATE_ABOUT_HOME_TOP_PADDING, mHomeScreenContainer.getPaddingTop());
 
         // Under key “android:viewHierarchyState” the OS stores another object of type Bundle.
         // This bundle stores the state of the view which is in a SparseArray that can grow pretty big.
         // This in some cases can lead to TransactionTooLargeException as per
         // [https://developer.android.com/reference/android/os/TransactionTooLargeException] it's
         // specified that the limit is fixed to 1MB per process.
-        if (getBundleSizeInBytes(outState) > MAX_BUNDLE_SIZE) {
+        if (getBundleSizeInBytes(outState) > MAX_BUNDLE_SIZE_BYTES) {
             outState.remove("android:viewHierarchyState");
         }
     }
 
     /**
      * Attempts to switch to an open tab with the given URL.
      * <p>
      * If the tab exists, this method cancels any in-progress editing as well as
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -104,16 +104,17 @@ import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
 import static org.mozilla.gecko.Tabs.INTENT_EXTRA_SESSION_UUID;
 import static org.mozilla.gecko.Tabs.INTENT_EXTRA_TAB_ID;
 import static org.mozilla.gecko.Tabs.INVALID_TAB_ID;
 import static org.mozilla.gecko.mma.MmaDelegate.DOWNLOAD_MEDIA_SAVED_IMAGE;
 import static org.mozilla.gecko.mma.MmaDelegate.READER_AVAILABLE;
+import static org.mozilla.gecko.util.JavaUtil.getBundleSizeInBytes;
 
 public abstract class GeckoApp extends GeckoActivity
                                implements AnchoredPopup.OnVisibilityChangeListener,
                                           BundleEventListener,
                                           GeckoMenu.Callback,
                                           GeckoMenu.MenuPresenter,
                                           GeckoScreenOrientation.OrientationChangeListener,
                                           GeckoSession.ContentDelegate,
@@ -151,16 +152,21 @@ public abstract class GeckoApp extends G
      * Originally, this was only used for the telemetry core ping logic. To avoid
      * having to write custom migration logic, we just keep the original pref key.
      * Be aware of {@link org.mozilla.gecko.fxa.EnvironmentUtils#GECKO_PREFS_IS_FIRST_RUN}.
      */
     public static final String PREFS_IS_FIRST_RUN = "telemetry-isFirstRun";
 
     public static final String SAVED_STATE_IN_BACKGROUND   = "inBackground";
     public static final String SAVED_STATE_PRIVATE_SESSION = "privateSession";
+    /**
+     * Speculative value for the maximum size the Activity Bundle can have in the hope to avoid
+     * TransactionTooLarge exceptions.
+     */
+    protected static final int MAX_BUNDLE_SIZE_BYTES = 300_000;
 
     // 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;
@@ -628,16 +634,21 @@ public abstract class GeckoApp extends G
         }
         synchronized (this) {
             if (GeckoThread.isRunning() && mPrivateBrowsingSessionOutdated) {
                 try {
                     wait(MAX_PRIVATE_TABS_UPDATE_WAIT_MSEC);
                 } catch (final InterruptedException e) { }
             }
             outState.putString(SAVED_STATE_PRIVATE_SESSION, mPrivateBrowsingSession);
+
+            // Make sure we are not bloating the Bundle which can result in TransactionTooLargeException
+            if (getBundleSizeInBytes(outState) > MAX_BUNDLE_SIZE_BYTES) {
+                outState.remove(SAVED_STATE_PRIVATE_SESSION);
+            }
         }
 
         outState.putBoolean(SAVED_STATE_IN_BACKGROUND, isApplicationInBackground());
     }
 
     public void addTab(int flags) { }
 
     public void addTab() { }