Bug 1513938 - Enforce a Bundle size limit and drop `privateSession` if exceeds it. r=JanH, a=lizzard
authorPetru Lingurar <petru.lingurar@softvision.ro>
Fri, 21 Dec 2018 08:56:47 +0000
changeset 509195 226db4f6cb068a6a5f312bc6299c400cf0488b44
parent 509194 3911d8355cbb5ecf55f7f9d38e29f1f84dc9c594
child 509196 2616da988292a87904ae33c30e5bd40dc29d715e
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersJanH, lizzard
bugs1513938, 1515592
milestone65.0
Bug 1513938 - Enforce a Bundle size limit and drop `privateSession` if exceeds it. r=JanH, a=lizzard 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
@@ -209,18 +209,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;
@@ -2294,17 +2292,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;
@@ -630,16 +636,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() { }