Bug 1433968 - Add crash reporting control to GeckoRuntimeSettings r=esawin,jchen
☠☠ backed out by 3e7ffd532e0a ☠ ☠
authorJames Willcox <snorp@snorp.net>
Thu, 19 Apr 2018 15:13:56 -0500
changeset 417745 62f865eed952837e551e28ad76f9472525ad0a06
parent 417744 5ed1e3af37bac190cc189ae3c1aedb40f152410a
child 417746 64cccb490a2a93cf744421cf37c485415df44798
push id33979
push userdluca@mozilla.com
push dateThu, 10 May 2018 21:59:38 +0000
treeherdermozilla-central@aabfe960ab59 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersesawin, jchen
bugs1433968
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1433968 - Add crash reporting control to GeckoRuntimeSettings r=esawin,jchen MozReview-Commit-ID: TQ7hvekIVJ
mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TestRunnerActivity.java
mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java
mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/process/IChildProcess.aidl
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoProcessManager.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoServiceChildProcess.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/ParcelableUtils.java
mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
widget/android/nsAppShell.cpp
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TestRunnerActivity.java
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TestRunnerActivity.java
@@ -115,16 +115,20 @@ public class TestRunnerActivity extends 
             final GeckoRuntimeSettings.Builder runtimeSettingsBuilder =
                 new GeckoRuntimeSettings.Builder();
             runtimeSettingsBuilder.arguments(new String[] { "-purgecaches" });
             final Bundle extras = intent.getExtras();
             if (extras != null) {
                 runtimeSettingsBuilder.extras(extras);
             }
 
+            runtimeSettingsBuilder
+                    .nativeCrashReportingEnabled(true)
+                    .javaCrashReportingEnabled(true);
+
             sRuntime = GeckoRuntime.create(this, runtimeSettingsBuilder.build());
             sRuntime.setDelegate(new GeckoRuntime.Delegate() {
                 @Override
                 public void onShutdown() {
                     mKillProcessOnDestroy = true;
                     finish();
                 }
             });
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java
@@ -842,17 +842,20 @@ public class GeckoSessionTestRule extend
         final Class<?>[] classes = CALLBACK_CLASSES.toArray(new Class<?>[CALLBACK_CLASSES.size()]);
         mCallbackProxy = Proxy.newProxyInstance(GeckoSession.class.getClassLoader(),
                                                 classes, recorder);
 
         if (sRuntime == null) {
             final GeckoRuntimeSettings.Builder runtimeSettingsBuilder =
                 new GeckoRuntimeSettings.Builder();
             runtimeSettingsBuilder.arguments(new String[] { "-purgecaches" })
-                                  .extras(InstrumentationRegistry.getArguments());
+                    .extras(InstrumentationRegistry.getArguments())
+                    .nativeCrashReportingEnabled(true)
+                    .javaCrashReportingEnabled(true);
+
             sRuntime = GeckoRuntime.create(
                 InstrumentationRegistry.getTargetContext(),
                 runtimeSettingsBuilder.build());
         }
 
         sRuntime.getSettings().setRemoteDebuggingEnabled(mWithDevTools);
 
         mMainSession = new GeckoSession(settings);
--- a/mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/process/IChildProcess.aidl
+++ b/mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/process/IChildProcess.aidl
@@ -6,15 +6,15 @@ package org.mozilla.gecko.process;
 
 import org.mozilla.gecko.process.IProcessManager;
 
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 
 interface IChildProcess {
     int getPid();
-    boolean start(in IProcessManager procMan, in String[] args, in Bundle extras,
+    boolean start(in IProcessManager procMan, in String[] args, in Bundle extras, int flags,
                   in ParcelFileDescriptor prefsPfd, in ParcelFileDescriptor ipcPfd,
                   in ParcelFileDescriptor crashReporterPfd,
                   in ParcelFileDescriptor crashAnnotationPfd);
 
     void crash();
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
@@ -106,17 +106,17 @@ import android.widget.AbsoluteLayout;
 
 public class GeckoAppShell
 {
     private static final String LOGTAG = "GeckoAppShell";
 
     // We have static members only.
     private GeckoAppShell() { }
 
-    private static final CrashHandler CRASH_HANDLER = new CrashHandler() {
+    private static class GeckoCrashHandler extends CrashHandler {
         @Override
         protected String getAppPackageName() {
             final Context appContext = getAppContext();
             if (appContext == null) {
                 return "<unknown>";
             }
             return appContext.getPackageName();
         }
@@ -169,20 +169,28 @@ public class GeckoAppShell
                 // Only use Java crash reporter if enabled on official build.
                 return super.reportException(thread, exc);
             }
             return false;
         }
     };
 
     private static String sAppNotes;
+    private static CrashHandler sCrashHandler;
 
-    public static CrashHandler ensureCrashHandling() {
-        // Crash handling is automatically enabled when GeckoAppShell is loaded.
-        return CRASH_HANDLER;
+    public static synchronized CrashHandler ensureCrashHandling() {
+        if (sCrashHandler == null) {
+            sCrashHandler = new GeckoCrashHandler();
+        }
+
+        return sCrashHandler;
+    }
+
+    public static synchronized boolean isCrashHandlingEnabled() {
+        return sCrashHandler != null;
     }
 
     @WrapForJNI(exceptionMode = "ignore")
     /* package */ static synchronized String getAppNotes() {
         return sAppNotes;
     }
 
     public static synchronized void appendAppNotesToCrashReport(final String notes) {
@@ -275,18 +283,20 @@ public class GeckoAppShell
      */
 
     @WrapForJNI(exceptionMode = "ignore")
     private static String getExceptionStackTrace(Throwable e) {
         return CrashHandler.getExceptionStackTrace(CrashHandler.getRootException(e));
     }
 
     @WrapForJNI(exceptionMode = "ignore")
-    private static void handleUncaughtException(Throwable e) {
-        CRASH_HANDLER.uncaughtException(null, e);
+    private static synchronized void handleUncaughtException(Throwable e) {
+        if (sCrashHandler != null) {
+            sCrashHandler.uncaughtException(null, e);
+        }
     }
 
     private static float getLocationAccuracy(Location location) {
         float radius = location.getAccuracy();
         return (location.hasAccuracy() && radius > 0) ? radius : 1001;
     }
 
     // Permissions are explicitly checked when requesting content permission.
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
@@ -23,17 +23,16 @@ import android.os.Looper;
 import android.os.Message;
 import android.os.MessageQueue;
 import android.os.SystemClock;
 import android.support.annotation.Nullable;
 import android.text.TextUtils;
 import android.util.Log;
 
 import java.io.File;
-import java.io.FilenameFilter;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 import java.util.StringTokenizer;
 
 public class GeckoThread extends Thread {
@@ -121,18 +120,20 @@ public class GeckoThread extends Thread 
     @WrapForJNI
     private static final ClassLoader clsLoader = GeckoThread.class.getClassLoader();
     @WrapForJNI
     private static MessageQueue msgQueue;
     @WrapForJNI
     private static int uiThreadId;
 
     // Main process parameters
-    public static final int FLAG_DEBUGGING = 1; // Debugging mode.
-    public static final int FLAG_PRELOAD_CHILD = 2; // Preload child during main thread start.
+    public static final int FLAG_DEBUGGING = 1 << 0; // Debugging mode.
+    public static final int FLAG_PRELOAD_CHILD = 1 << 1; // Preload child during main thread start.
+    public static final int FLAG_ENABLE_NATIVE_CRASHREPORTER = 1 << 2; // Enable native crash reporting
+    public static final int FLAG_ENABLE_JAVA_CRASHREPORTER = 1 << 3; // Enable java crash reporting
 
     private static final String EXTRA_ARGS = "args";
     private static final String EXTRA_PREFS_FD = "prefsFd";
     private static final String EXTRA_IPC_FD = "ipcFd";
     private static final String EXTRA_CRASH_FD = "crashFd";
     private static final String EXTRA_CRASH_ANNOTATION_FD = "crashAnnotationFd";
 
     private boolean mInitialized;
@@ -178,21 +179,24 @@ public class GeckoThread extends Thread 
     }
 
     public static boolean initMainProcess(final GeckoProfile profile, final String[] args,
                                           final Bundle extras, final int flags) {
         return INSTANCE.init(profile, args, extras, flags, /* fd */ -1,
                              /* fd */ -1, /* fd */ -1, /* fd */ -1);
     }
 
-    public static boolean initChildProcess(final String[] args, final Bundle extras,
-                                           final int prefsFd, final int ipcFd,
+    public static boolean initChildProcess(final String[] args,
+                                           final Bundle extras,
+                                           final int flags,
+                                           final int prefsFd,
+                                           final int ipcFd,
                                            final int crashFd,
                                            final int crashAnnotationFd) {
-        return INSTANCE.init(/* profile */ null, args, extras, /* flags */ 0,
+        return INSTANCE.init(/* profile */ null, args, extras, flags,
                              prefsFd, ipcFd, crashFd, crashAnnotationFd);
     }
 
     private static boolean canUseProfile(final Context context, final GeckoProfile profile,
                                          final String profileName, final File profileDir) {
         if (profileDir != null && !profileDir.isDirectory()) {
             return false;
         }
@@ -374,17 +378,27 @@ public class GeckoThread extends Thread 
         return mProfile;
     }
 
     public static @Nullable Bundle getActiveExtras() {
         synchronized (INSTANCE) {
             if (!INSTANCE.mInitialized) {
                 return null;
             }
-            return INSTANCE.mExtras;
+            return new Bundle(INSTANCE.mExtras);
+        }
+    }
+
+    public static int getActiveFlags() {
+        synchronized (INSTANCE) {
+            if (!INSTANCE.mInitialized) {
+                return 0;
+            }
+
+            return INSTANCE.mFlags;
         }
     }
 
     private static ArrayList<String> getEnvFromExtras(final Bundle extras) {
         if (extras == null) {
             return new ArrayList<>();
         }
 
@@ -459,17 +473,30 @@ public class GeckoThread extends Thread 
 
         final Context context = GeckoAppShell.getApplicationContext();
         final String[] args = isChildProcess() ? mArgs : getMainProcessArgs();
 
         if ((mFlags & FLAG_DEBUGGING) != 0) {
             Log.i(LOGTAG, "RunGecko - args = " + TextUtils.join(" ", args));
         }
 
-        final ArrayList<String> env = getEnvFromExtras(mExtras);
+        final List<String> env = getEnvFromExtras(mExtras);
+
+        // In Gecko, the native crash reporter is enabled by default in opt builds, and
+        // disabled by default in debug builds.
+        if ((mFlags & FLAG_ENABLE_NATIVE_CRASHREPORTER) == 0 && !BuildConfig.DEBUG_BUILD) {
+            env.add(0, "MOZ_CRASHREPORTER_DISABLE=1");
+        } else if ((mFlags & FLAG_ENABLE_NATIVE_CRASHREPORTER) != 0 && BuildConfig.DEBUG_BUILD) {
+            env.add(0, "MOZ_CRASHREPORTER=1");
+        }
+
+        if ((mFlags & FLAG_ENABLE_JAVA_CRASHREPORTER) != 0) {
+            GeckoAppShell.ensureCrashHandling();
+        }
+
         GeckoLoader.setupGeckoEnvironment(context, context.getFilesDir().getPath(), env);
 
         // And go.
         GeckoLoader.nativeRun(args,
                               mExtras.getInt(EXTRA_PREFS_FD, -1),
                               mExtras.getInt(EXTRA_IPC_FD, -1),
                               mExtras.getInt(EXTRA_CRASH_FD, -1),
                               mExtras.getInt(EXTRA_CRASH_ANNOTATION_FD, -1));
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoProcessManager.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoProcessManager.java
@@ -176,16 +176,21 @@ public final class GeckoProcessManager e
 
     @WrapForJNI
     private static int start(final String type, final String[] args,
                              final int prefsFd, final int ipcFd,
                              final int crashFd, final int crashAnnotationFd) {
         return INSTANCE.start(type, args, prefsFd, ipcFd, crashFd, crashAnnotationFd, /* retry */ false);
     }
 
+    private int filterFlagsForChild(int flags) {
+        return flags & (GeckoThread.FLAG_ENABLE_JAVA_CRASHREPORTER |
+                GeckoThread.FLAG_ENABLE_NATIVE_CRASHREPORTER);
+    }
+
     private int start(final String type, final String[] args, final int prefsFd,
                       final int ipcFd, final int crashFd,
                       final int crashAnnotationFd, final boolean retry) {
         final ChildConnection connection = getConnection(type);
         final IChildProcess child = connection.bind();
         if (child == null) {
             return 0;
         }
@@ -200,19 +205,21 @@ public final class GeckoProcessManager e
             ipcPfd = ParcelFileDescriptor.fromFd(ipcFd);
             crashPfd = (crashFd >= 0) ? ParcelFileDescriptor.fromFd(crashFd) : null;
             crashAnnotationPfd = (crashAnnotationFd >= 0) ? ParcelFileDescriptor.fromFd(crashAnnotationFd) : null;
         } catch (final IOException e) {
             Log.e(LOGTAG, "Cannot create fd for " + type, e);
             return 0;
         }
 
+        final int flags = filterFlagsForChild(GeckoThread.getActiveFlags());
+
         boolean started = false;
         try {
-            started = child.start(this, args, extras, prefsPfd, ipcPfd, crashPfd,
+            started = child.start(this, args, extras, flags, prefsPfd, ipcPfd, crashPfd,
                                   crashAnnotationPfd);
         } catch (final RemoteException e) {
         }
 
         if (!started) {
             if (retry) {
                 Log.e(LOGTAG, "Cannot restart child " + type);
                 return 0;
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoServiceChildProcess.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoServiceChildProcess.java
@@ -19,19 +19,17 @@ import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
 import android.util.Log;
 
 public class GeckoServiceChildProcess extends Service {
-
     private static final String LOGTAG = "GeckoServiceChildProcess";
-
     private static IProcessManager sProcessManager;
 
     @WrapForJNI(calledFrom = "gecko")
     private static IGeckoEditableParent getEditableParent(final long contentId,
                                                           final long tabId) {
         try {
             return sProcessManager.getEditableParent(contentId, tabId);
         } catch (final RemoteException e) {
@@ -39,17 +37,16 @@ public class GeckoServiceChildProcess ex
             return null;
         }
     }
 
     @Override
     public void onCreate() {
         super.onCreate();
 
-        GeckoAppShell.ensureCrashHandling();
         GeckoAppShell.setApplicationContext(getApplicationContext());
     }
 
     @Override
     public int onStartCommand(final Intent intent, final int flags, final int startId) {
         return Service.START_NOT_STICKY;
     }
 
@@ -58,16 +55,17 @@ public class GeckoServiceChildProcess ex
         public int getPid() {
             return Process.myPid();
         }
 
         @Override
         public boolean start(final IProcessManager procMan,
                              final String[] args,
                              final Bundle extras,
+                             final int flags,
                              final ParcelFileDescriptor prefsPfd,
                              final ParcelFileDescriptor ipcPfd,
                              final ParcelFileDescriptor crashReporterPfd,
                              final ParcelFileDescriptor crashAnnotationPfd) {
             synchronized (GeckoServiceChildProcess.class) {
                 if (sProcessManager != null) {
                     Log.e(LOGTAG, "Child process already started");
                     return false;
@@ -80,17 +78,17 @@ public class GeckoServiceChildProcess ex
             final int crashReporterFd = crashReporterPfd != null ?
                                         crashReporterPfd.detachFd() : -1;
             final int crashAnnotationFd = crashAnnotationPfd != null ?
                                           crashAnnotationPfd.detachFd() : -1;
 
             ThreadUtils.postToUiThread(new Runnable() {
                 @Override
                 public void run() {
-                    if (GeckoThread.initChildProcess(args, extras, prefsFd, ipcFd, crashReporterFd,
+                    if (GeckoThread.initChildProcess(args, extras, flags, prefsFd, ipcFd, crashReporterFd,
                                                      crashAnnotationFd)) {
                         GeckoThread.launch();
                     }
                 }
             });
             return true;
         }
 
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java
@@ -79,19 +79,29 @@ public final class GeckoRuntime implemen
             }
         }
     };
 
     /* package */ boolean init(final @NonNull GeckoRuntimeSettings settings) {
         if (DEBUG) {
             Log.d(LOGTAG, "init");
         }
-        final int flags = settings.getUseContentProcessHint()
-                          ? GeckoThread.FLAG_PRELOAD_CHILD
-                          : 0;
+        int flags = 0;
+        if (settings.getUseContentProcessHint()) {
+            flags |= GeckoThread.FLAG_PRELOAD_CHILD;
+        }
+
+        if (settings.getNativeCrashReportingEnabled()) {
+            flags |= GeckoThread.FLAG_ENABLE_NATIVE_CRASHREPORTER;
+        }
+
+        if (settings.getJavaCrashReportingEnabled()) {
+            flags |= GeckoThread.FLAG_ENABLE_JAVA_CRASHREPORTER;
+        }
+
         if (GeckoThread.initMainProcess(/* profile */ null,
                                         settings.getArguments(),
                                         settings.getExtras(),
                                         flags)) {
             if (!GeckoThread.launch()) {
                 Log.d(LOGTAG, "init failed (GeckoThread already launched)");
                 return false;
             }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java
@@ -35,17 +35,19 @@ public final class GeckoRuntimeSettings 
         public @NonNull GeckoRuntimeSettings build() {
             return new GeckoRuntimeSettings(mSettings);
         }
 
         /**
          * Set the content process hint flag.
          *
          * @param use If true, this will reload the content process for future use.
+         *            Default is false.
          * @return This Builder instance.
+
          */
         public @NonNull Builder useContentProcessHint(final boolean use) {
             mSettings.mUseContentProcess = use;
             return this;
         }
 
         /**
          * Set the custom Gecko process arguments.
@@ -74,16 +76,17 @@ public final class GeckoRuntimeSettings 
             mSettings.mExtras = extras;
             return this;
         }
 
         /**
          * Set whether JavaScript support should be enabled.
          *
          * @param flag A flag determining whether JavaScript should be enabled.
+         *             Default is true.
          * @return This Builder instance.
          */
         public @NonNull Builder javaScriptEnabled(final boolean flag) {
             mSettings.mJavaScript.set(flag);
             return this;
         }
 
         /**
@@ -96,22 +99,51 @@ public final class GeckoRuntimeSettings 
             mSettings.mRemoteDebugging.set(enabled);
             return this;
         }
 
         /**
          * Set whether support for web fonts should be enabled.
          *
          * @param flag A flag determining whether web fonts should be enabled.
+         *             Default is true.
          * @return This Builder instance.
          */
         public @NonNull Builder webFontsEnabled(final boolean flag) {
             mSettings.mWebFonts.set(flag);
             return this;
         }
+
+        /**
+         * Set whether crash reporting for native code should be enabled. This will cause
+         * a SIGSEGV handler to be installed, and any crash encountered there will be
+         * reported to Mozilla.
+         *
+         * @param enabled A flag determining whether native crash reporting should be enabled.
+         *                Defaults to false.
+         * @return This Builder.
+         */
+        public @NonNull Builder nativeCrashReportingEnabled(final boolean enabled) {
+            mSettings.mNativeCrashReporting = enabled;
+            return this;
+        }
+
+        /**
+         * Set whether crash reporting for Java code should be enabled. This will cause
+         * a default unhandled exception handler to be installed, and any exceptions encountered
+         * will automatically reported to Mozilla.
+         *
+         * @param enabled A flag determining whether Java crash reporting should be enabled.
+         *                Defaults to false.
+         * @return This Builder.
+         */
+        public @NonNull Builder javaCrashReportingEnabled(final boolean enabled) {
+            mSettings.mJavaCrashReporting = enabled;
+            return this;
+        }
     }
 
     /* package */ GeckoRuntime runtime;
     /* package */ boolean mUseContentProcess;
     /* package */ String[] mArgs;
     /* package */ Bundle mExtras;
     /* package */ int prefCount;
 
@@ -145,16 +177,18 @@ public final class GeckoRuntimeSettings 
     }
 
     /* package */ Pref<Boolean> mJavaScript = new Pref<Boolean>(
         "javascript.enabled", true);
     /* package */ Pref<Boolean> mRemoteDebugging = new Pref<Boolean>(
         "devtools.debugger.remote-enabled", false);
     /* package */ Pref<Boolean> mWebFonts = new Pref<Boolean>(
         "browser.display.use_document_fonts", true);
+    /* package */ boolean mNativeCrashReporting;
+    /* package */ boolean mJavaCrashReporting;
 
     private final Pref<?>[] mPrefs = new Pref<?>[] {
         mJavaScript, mRemoteDebugging, mWebFonts
     };
 
     /* package */ GeckoRuntimeSettings() {
         this(null);
     }
@@ -175,16 +209,19 @@ public final class GeckoRuntimeSettings 
         mExtras = new Bundle(settings.getExtras());
 
         for (int i = 0; i < mPrefs.length; i++) {
             // We know this is safe.
             @SuppressWarnings("unchecked")
             final Pref<Object> uncheckedPref = (Pref<Object>) mPrefs[i];
             uncheckedPref.set(settings.mPrefs[i].get());
         }
+
+        mNativeCrashReporting = settings.mNativeCrashReporting;
+        mJavaCrashReporting = settings.mJavaCrashReporting;
     }
 
     /* package */ void flush() {
         for (final Pref<?> pref: mPrefs) {
             pref.flush();
         }
     }
 
@@ -270,44 +307,68 @@ public final class GeckoRuntimeSettings 
      * @param flag A flag determining whether web fonts should be enabled.
      * @return This GeckoRuntimeSettings instance.
      */
     public @NonNull GeckoRuntimeSettings setWebFontsEnabled(final boolean flag) {
         mWebFonts.set(flag);
         return this;
     }
 
+    /**
+     * Get whether native crash reporting is enabled or not.
+     *
+     * @return True if native crash reporting is enabled.
+     */
+    public boolean getNativeCrashReportingEnabled() {
+        return mNativeCrashReporting;
+    }
+
+    /**
+     * Get whether Java crash reporting is enabled or not.
+     *
+     * @return True if Java crash reporting is enabled.
+     */
+    public boolean getJavaCrashReportingEnabled() {
+        return mJavaCrashReporting;
+    }
+
     @Override // Parcelable
     public int describeContents() {
         return 0;
     }
 
     @Override // Parcelable
     public void writeToParcel(Parcel out, int flags) {
-        out.writeByte((byte) (mUseContentProcess ? 1 : 0));
+        ParcelableUtils.writeBoolean(out, mUseContentProcess);
         out.writeStringArray(mArgs);
         mExtras.writeToParcel(out, flags);
 
         for (final Pref<?> pref : mPrefs) {
             out.writeValue(pref.get());
         }
+
+        ParcelableUtils.writeBoolean(out, mNativeCrashReporting);
+        ParcelableUtils.writeBoolean(out, mJavaCrashReporting);
     }
 
     // AIDL code may call readFromParcel even though it's not part of Parcelable.
     public void readFromParcel(final Parcel source) {
-        mUseContentProcess = source.readByte() == 1;
+        mUseContentProcess = ParcelableUtils.readBoolean(source);
         mArgs = source.createStringArray();
         mExtras.readFromParcel(source);
 
         for (final Pref<?> pref : mPrefs) {
             // We know this is safe.
             @SuppressWarnings("unchecked")
             final Pref<Object> uncheckedPref = (Pref<Object>) pref;
             uncheckedPref.set(source.readValue(getClass().getClassLoader()));
         }
+
+        mNativeCrashReporting = ParcelableUtils.readBoolean(source);
+        mJavaCrashReporting = ParcelableUtils.readBoolean(source);
     }
 
     public static final Parcelable.Creator<GeckoRuntimeSettings> CREATOR
         = new Parcelable.Creator<GeckoRuntimeSettings>() {
         @Override
         public GeckoRuntimeSettings createFromParcel(final Parcel in) {
             final GeckoRuntimeSettings settings = new GeckoRuntimeSettings();
             settings.readFromParcel(in);
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/ParcelableUtils.java
@@ -0,0 +1,19 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * vim: ts=4 sw=4 expandtab:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.geckoview;
+
+import android.os.Parcel;
+
+class ParcelableUtils {
+    public static void writeBoolean(Parcel out, boolean val) {
+        out.writeByte((byte) (val ? 1 : 0));
+    }
+
+    public static boolean readBoolean(Parcel source) {
+        return source.readByte() == 1;
+    }
+}
--- a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
+++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
@@ -62,17 +62,22 @@ public class GeckoViewActivity extends A
                 // each build.
                 runtimeSettingsBuilder.arguments(new String[] { "-purgecaches" });
             }
 
             final Bundle extras = getIntent().getExtras();
             if (extras != null) {
                 runtimeSettingsBuilder.extras(extras);
             }
-            runtimeSettingsBuilder.useContentProcessHint(useMultiprocess);
+
+            runtimeSettingsBuilder
+                    .useContentProcessHint(useMultiprocess)
+                    .nativeCrashReportingEnabled(true)
+                    .javaCrashReportingEnabled(true);
+
             sGeckoRuntime = GeckoRuntime.create(this, runtimeSettingsBuilder.build());
         }
 
         final GeckoSessionSettings sessionSettings = new GeckoSessionSettings();
         sessionSettings.setBoolean(GeckoSessionSettings.USE_MULTIPROCESS,
                                    useMultiprocess);
         mGeckoSession = new GeckoSession(sessionSettings);
 
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -402,16 +402,17 @@ nsAppShell::nsAppShell()
     {
         MutexAutoLock lock(*sAppShellLock);
         sAppShell = this;
     }
 
     if (!XRE_IsParentProcess()) {
         if (jni::IsAvailable()) {
             GeckoThreadSupport::Init();
+            GeckoAppShellSupport::Init();
 
             // Set the corresponding state in GeckoThread.
             java::GeckoThread::SetState(java::GeckoThread::State::RUNNING());
         }
         return;
     }
 
     if (jni::IsAvailable()) {