Backed out 6 changesets (bug 1433968) for Android failures on GeckoRuntimeSettings on a CLOSED TREE
authorTiberius Oros <toros@mozilla.com>
Thu, 10 May 2018 18:11:55 +0300
changeset 417754 3e7ffd532e0ac7d7fc52a52f80a2e1b839a7f915
parent 417753 bba1deb626b53d9c044a30bdfce49a15f5c00780
child 417755 0e0e6097f282da9d82bc97960931922cfeb38690
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)
bugs1433968
milestone62.0a1
backs outbba1deb626b53d9c044a30bdfce49a15f5c00780
7a8616427aea19089fffd9423577751deae186e2
c94c48d763506bc36126b95e6073f62c757a085b
64cccb490a2a93cf744421cf37c485415df44798
62f865eed952837e551e28ad76f9472525ad0a06
5ed1e3af37bac190cc189ae3c1aedb40f152410a
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
Backed out 6 changesets (bug 1433968) for Android failures on GeckoRuntimeSettings on a CLOSED TREE Backed out changeset bba1deb626b5 (bug 1433968) Backed out changeset 7a8616427aea (bug 1433968) Backed out changeset c94c48d76350 (bug 1433968) Backed out changeset 64cccb490a2a (bug 1433968) Backed out changeset 62f865eed952 (bug 1433968) Backed out changeset 5ed1e3af37ba (bug 1433968)
mobile/android/app/build.gradle
mobile/android/app/lint.xml
mobile/android/base/AndroidManifest.xml.in
mobile/android/base/java/org/mozilla/gecko/CrashReporter.java
mobile/android/base/java/org/mozilla/gecko/CrashReporterActivity.java
mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
mobile/android/base/java/org/mozilla/gecko/GeckoService.java
mobile/android/base/java/org/mozilla/gecko/RemotePresentationService.java
mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
mobile/android/geckoview/build.gradle
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/AndroidManifest.xml
mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/process/IChildProcess.aidl
mobile/android/geckoview/src/main/java/org/mozilla/gecko/CrashHandler.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/CrashReporterService.java
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/mozglue/GeckoLoader.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
toolkit/crashreporter/nsExceptionHandler.cpp
widget/android/nsAppShell.cpp
--- a/mobile/android/app/build.gradle
+++ b/mobile/android/app/build.gradle
@@ -117,17 +117,17 @@ android {
                 srcDir "${topsrcdir}/mobile/android/base/java"
                 srcDir "${topsrcdir}/mobile/android/services/src/main/java"
 
                 if (mozconfig.substs.MOZ_ANDROID_MLS_STUMBLER) {
                     srcDir "${topsrcdir}/mobile/android/stumbler/java"
                 }
 
                 if (!mozconfig.substs.MOZ_CRASHREPORTER) {
-                    exclude 'org/mozilla/gecko/CrashReporterActivity.java'
+                    exclude 'org/mozilla/gecko/CrashReporter.java'
                 }
 
                 if (!mozconfig.substs.MOZ_NATIVE_DEVICES) {
                     exclude 'org/mozilla/gecko/ChromeCastDisplay.java'
                     exclude 'org/mozilla/gecko/ChromeCastPlayer.java'
                     exclude 'org/mozilla/gecko/GeckoMediaPlayer.java'
                     exclude 'org/mozilla/gecko/GeckoPresentationDisplay.java'
                     exclude 'org/mozilla/gecko/MediaPlayerManager.java'
--- a/mobile/android/app/lint.xml
+++ b/mobile/android/app/lint.xml
@@ -59,17 +59,17 @@
         <ignore path="**/mobile/android/base/java/org/mozilla/gecko/util/ViewUtil.java"/>
         <ignore path="**/mobile/android/base/java/org/mozilla/gecko/IntentHelper.java"/>
         <ignore path="**/media/webrtc/trunk/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioEffects.java"/>
         <ignore path="**/media/webrtc/trunk/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioRecord.java"/>
         <ignore path="src/photon/res/values/styles.xml"/>
     </issue>
 
     <!-- We fixed all "Registered" lint errors. However the current gradle plugin has a bug where
-         it ignores @SuppressLint annotations for this check. See CrashReporterActivity class and
+         it ignores @SuppressLint annotations for this check. See CrashReporter class and
          https://code.google.com/p/android/issues/detail?id=204846 -->
     <issue id="Registered" severity="warning" />
 
     <issue id="ObjectAnimatorBinding" severity="error">
         <!-- Two animated properties are provided by the underlying
              View implementation. -->
         <ignore path="**/mobile/android/base/java/org/mozilla/gecko/firstrun/FirstrunPager.java"/>
     </issue>
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -267,17 +267,17 @@
                 <data android:scheme="moz-notification" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </receiver>
 
 #include ../services/manifests/FxAccountAndroidManifest_activities.xml.in
 
 #ifdef MOZ_CRASHREPORTER
-  <activity android:name="org.mozilla.gecko.CrashReporterActivity"
+  <activity android:name="org.mozilla.gecko.CrashReporter"
             android:process="@ANDROID_PACKAGE_NAME@.CrashReporter"
             android:label="@string/crash_reporter_title"
             android:icon="@drawable/crash_reporter"
             android:theme="@style/Gecko"
             android:exported="false"
             android:excludeFromRecents="true">
           <intent-filter>
             <action android:name="org.mozilla.gecko.reportCrash" />
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/CrashReporter.java
@@ -0,0 +1,599 @@
+/* -*- Mode: Java; tab-width: 20; indent-tabs-mode: nil; -*-
+ * 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.gecko;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
+import java.security.MessageDigest;
+import java.util.zip.GZIPOutputStream;
+
+import org.mozilla.gecko.AppConstants.Versions;
+import org.mozilla.gecko.GeckoProfile;
+import org.mozilla.gecko.mozglue.GeckoLoader;
+import org.mozilla.gecko.mozglue.MinidumpAnalyzer;
+import org.mozilla.gecko.telemetry.pingbuilders.TelemetryCrashPingBuilder;
+import org.mozilla.gecko.telemetry.TelemetryDispatcher;
+import org.mozilla.gecko.util.INIParser;
+import org.mozilla.gecko.util.INISection;
+import org.mozilla.gecko.util.ProxySelector;
+
+import android.annotation.SuppressLint;
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v7.app.AppCompatActivity;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+
+// Registered: This activity is only registered in the manifest if MOZ_CRASHREPORTER is set.
+// CutPasteId: This lint is not worth fixing.  To fix it, cache all the findViewById results.
+@SuppressLint("Registered,CutPasteId")
+public class CrashReporter extends AppCompatActivity
+{
+    private static final String LOGTAG = "GeckoCrashReporter";
+
+    private static final String PASSED_MINI_DUMP_KEY = "minidumpPath";
+    private static final String PASSED_MINI_DUMP_SUCCESS_KEY = "minidumpSuccess";
+    private static final String MINI_DUMP_PATH_KEY = "upload_file_minidump";
+    private static final String PAGE_URL_KEY = "URL";
+    private static final String NOTES_KEY = "Notes";
+    private static final String SERVER_URL_KEY = "ServerURL";
+
+    private static final String CRASH_REPORT_SUFFIX = "/mozilla/Crash Reports/";
+    private static final String PENDING_SUFFIX = CRASH_REPORT_SUFFIX + "pending";
+    private static final String SUBMITTED_SUFFIX = CRASH_REPORT_SUFFIX + "submitted";
+
+    private static final String PREFS_SEND_REPORT   = "sendReport";
+    private static final String PREFS_INCLUDE_URL   = "includeUrl";
+    private static final String PREFS_ALLOW_CONTACT = "allowContact";
+    private static final String PREFS_CONTACT_EMAIL = "contactEmail";
+
+    private Handler mHandler;
+    private ProgressDialog mProgressDialog;
+    private File mPendingMinidumpFile;
+    private File mPendingExtrasFile;
+    private HashMap<String, String> mExtrasStringMap;
+    private boolean mMinidumpSucceeded;
+
+    private boolean moveFile(File inFile, File outFile) {
+        Log.i(LOGTAG, "moving " + inFile + " to " + outFile);
+        if (inFile.renameTo(outFile))
+            return true;
+        try {
+            outFile.createNewFile();
+            Log.i(LOGTAG, "couldn't rename minidump file");
+            // so copy it instead
+            FileChannel inChannel = new FileInputStream(inFile).getChannel();
+            FileChannel outChannel = new FileOutputStream(outFile).getChannel();
+            long transferred = inChannel.transferTo(0, inChannel.size(), outChannel);
+            inChannel.close();
+            outChannel.close();
+
+            if (transferred > 0)
+                inFile.delete();
+        } catch (Exception e) {
+            Log.e(LOGTAG, "exception while copying minidump file: ", e);
+            return false;
+        }
+        return true;
+    }
+
+    private void doFinish() {
+        if (mHandler != null) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    finish();
+                }
+            });
+        }
+    }
+
+    @Override
+    public void finish() {
+        try {
+            if (mProgressDialog.isShowing()) {
+                mProgressDialog.dismiss();
+            }
+        } catch (Exception e) {
+            Log.e(LOGTAG, "exception while closing progress dialog: ", e);
+        }
+        super.finish();
+    }
+
+    @Override
+    @SuppressLint("WrongThread") // We don't have a worker thread for the TelemetryDispatcher
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        // mHandler is created here so runnables can be run on the main thread
+        mHandler = new Handler();
+        setContentView(R.layout.crash_reporter);
+        mProgressDialog = new ProgressDialog(this);
+        mProgressDialog.setMessage(getString(R.string.sending_crash_report));
+
+        mMinidumpSucceeded = getIntent().getBooleanExtra(PASSED_MINI_DUMP_SUCCESS_KEY, false);
+        if (!mMinidumpSucceeded) {
+            Log.i(LOGTAG, "Failed to get minidump.");
+        }
+        String passedMinidumpPath = getIntent().getStringExtra(PASSED_MINI_DUMP_KEY);
+        File passedMinidumpFile = new File(passedMinidumpPath);
+        File pendingDir = new File(getFilesDir(), PENDING_SUFFIX);
+        pendingDir.mkdirs();
+        mPendingMinidumpFile = new File(pendingDir, passedMinidumpFile.getName());
+        moveFile(passedMinidumpFile, mPendingMinidumpFile);
+
+        File extrasFile = new File(passedMinidumpPath.replaceAll("\\.dmp", ".extra"));
+        mPendingExtrasFile = new File(pendingDir, extrasFile.getName());
+        moveFile(extrasFile, mPendingExtrasFile);
+
+        // Compute the minidump hash and generate the stack traces
+        computeMinidumpHash(mPendingExtrasFile, mPendingMinidumpFile);
+
+        try {
+            GeckoLoader.loadMozGlue(this);
+
+            if (!MinidumpAnalyzer.GenerateStacks(mPendingMinidumpFile.getPath(), /* fullStacks */ false)) {
+                Log.e(LOGTAG, "Could not generate stacks for this minidump: " + passedMinidumpPath);
+            }
+        } catch (UnsatisfiedLinkError e) {
+            Log.e(LOGTAG, "Could not load libmozglue.so, stacks for this crash won't be generated");
+        }
+
+        // Extract the annotations from the .extra file
+        mExtrasStringMap = new HashMap<String, String>();
+        readStringsFromFile(mPendingExtrasFile.getPath(), mExtrasStringMap);
+
+        try {
+            // Find the profile name and path. Since we don't have any other way of getting it within
+            // this context we extract it from the crash dump path.
+            final File profileDir = passedMinidumpFile.getParentFile().getParentFile();
+            final String profileName = getProfileName(profileDir);
+
+            if (profileName != null) {
+                // Extract the crash dump ID and telemetry client ID, we need profile access for the latter.
+                final String passedMinidumpName = passedMinidumpFile.getName();
+                // Strip the .dmp suffix from the minidump name to obtain the crash ID.
+                final String crashId = passedMinidumpName.substring(0, passedMinidumpName.length() - 4);
+                final GeckoProfile profile = GeckoProfile.get(this, profileName, profileDir);
+                final String clientId = profile.getClientId();
+
+                // Assemble and send the crash ping
+                final TelemetryCrashPingBuilder pingBuilder =
+                    new TelemetryCrashPingBuilder(crashId, clientId, mExtrasStringMap);
+                final TelemetryDispatcher dispatcher = new TelemetryDispatcher(profileDir.getPath(), profileName);
+                dispatcher.queuePingForUpload(this, pingBuilder);
+            }
+        } catch (GeckoProfileDirectories.NoMozillaDirectoryException | IOException e) {
+            Log.e(LOGTAG, "Cannot send the crash ping: ", e);
+        }
+
+        // Notify GeckoApp that we've crashed, so it can react appropriately during the next start.
+        try {
+            File crashFlag = new File(GeckoProfileDirectories.getMozillaDirectory(this), "CRASHED");
+            crashFlag.createNewFile();
+        } catch (GeckoProfileDirectories.NoMozillaDirectoryException | IOException e) {
+            Log.e(LOGTAG, "Cannot set crash flag: ", e);
+        }
+
+        final CheckBox allowContactCheckBox = (CheckBox) findViewById(R.id.allow_contact);
+        final CheckBox includeUrlCheckBox = (CheckBox) findViewById(R.id.include_url);
+        final CheckBox sendReportCheckBox = (CheckBox) findViewById(R.id.send_report);
+        final EditText commentsEditText = (EditText) findViewById(R.id.comment);
+        final EditText emailEditText = (EditText) findViewById(R.id.email);
+
+        // Load CrashReporter preferences to avoid redundant user input.
+        SharedPreferences prefs = GeckoSharedPrefs.forCrashReporter(this);
+        final boolean sendReport   = prefs.getBoolean(PREFS_SEND_REPORT, true);
+        final boolean includeUrl   = prefs.getBoolean(PREFS_INCLUDE_URL, false);
+        final boolean allowContact = prefs.getBoolean(PREFS_ALLOW_CONTACT, false);
+        final String contactEmail  = prefs.getString(PREFS_CONTACT_EMAIL, "");
+
+        allowContactCheckBox.setChecked(allowContact);
+        includeUrlCheckBox.setChecked(includeUrl);
+        sendReportCheckBox.setChecked(sendReport);
+        emailEditText.setText(contactEmail);
+
+        sendReportCheckBox.setOnCheckedChangeListener(new CheckBox.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton checkbox, boolean isChecked) {
+                commentsEditText.setEnabled(isChecked);
+                commentsEditText.requestFocus();
+
+                includeUrlCheckBox.setEnabled(isChecked);
+                allowContactCheckBox.setEnabled(isChecked);
+                emailEditText.setEnabled(isChecked && allowContactCheckBox.isChecked());
+            }
+        });
+
+        allowContactCheckBox.setOnCheckedChangeListener(new CheckBox.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton checkbox, boolean isChecked) {
+                // We need to check isEnabled() here because this listener is
+                // fired on rotation -- even when the checkbox is disabled.
+                emailEditText.setEnabled(checkbox.isEnabled() && isChecked);
+                emailEditText.requestFocus();
+            }
+        });
+
+        emailEditText.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                // Even if the email EditText is disabled, allow it to be
+                // clicked and focused.
+                if (sendReportCheckBox.isChecked() && !v.isEnabled()) {
+                    allowContactCheckBox.setChecked(true);
+                    v.setEnabled(true);
+                    v.requestFocus();
+                }
+            }
+        });
+    }
+
+    @Override
+    public void onBackPressed() {
+        AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setMessage(R.string.crash_closing_alert);
+        builder.setNegativeButton(R.string.button_cancel, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                dialog.dismiss();
+            }
+        });
+        builder.setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                CrashReporter.this.finish();
+            }
+        });
+        builder.show();
+    }
+
+    private void backgroundSendReport() {
+        final CheckBox sendReportCheckbox = (CheckBox) findViewById(R.id.send_report);
+        if (!sendReportCheckbox.isChecked()) {
+            doFinish();
+            return;
+        }
+
+        // Persist settings to avoid redundant user input.
+        savePrefs();
+
+        mProgressDialog.show();
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                sendReport(mPendingMinidumpFile, mExtrasStringMap, mPendingExtrasFile);
+            }
+        }, "CrashReporter Thread").start();
+    }
+
+    private void savePrefs() {
+        SharedPreferences.Editor editor = GeckoSharedPrefs.forCrashReporter(this).edit();
+
+        final boolean allowContact = ((CheckBox) findViewById(R.id.allow_contact)).isChecked();
+        final boolean includeUrl   = ((CheckBox) findViewById(R.id.include_url)).isChecked();
+        final boolean sendReport   = ((CheckBox) findViewById(R.id.send_report)).isChecked();
+        final String contactEmail  = ((EditText) findViewById(R.id.email)).getText().toString();
+
+        editor.putBoolean(PREFS_ALLOW_CONTACT, allowContact);
+        editor.putBoolean(PREFS_INCLUDE_URL, includeUrl);
+        editor.putBoolean(PREFS_SEND_REPORT, sendReport);
+        editor.putString(PREFS_CONTACT_EMAIL, contactEmail);
+
+        // A slight performance improvement via async apply() vs. blocking on commit().
+        editor.apply();
+    }
+
+    public void onCloseClick(View v) {  // bound via crash_reporter.xml
+        backgroundSendReport();
+    }
+
+    public void onRestartClick(View v) {  // bound via crash_reporter.xml
+        doRestart();
+        backgroundSendReport();
+    }
+
+    private String getProfileName(File profileDir) throws GeckoProfileDirectories.NoMozillaDirectoryException {
+        final File mozillaDir = GeckoProfileDirectories.getMozillaDirectory(this);
+        final INIParser parser = GeckoProfileDirectories.getProfilesINI(mozillaDir);
+        String profileName = null;
+
+        if (parser.getSections() != null) {
+            for (Enumeration<INISection> e = parser.getSections().elements(); e.hasMoreElements(); ) {
+                final INISection section = e.nextElement();
+                final String path = section.getStringProperty("Path");
+                final boolean isRelative = (section.getIntProperty("IsRelative") == 1);
+
+                if ((isRelative && path.equals(profileDir.getName())) ||
+                    path.equals(profileDir.getPath())) {
+                    profileName = section.getStringProperty("Name");
+                    break;
+                }
+            }
+        }
+
+        return profileName;
+    }
+
+
+    private void computeMinidumpHash(File extraFile, File minidump) {
+        try {
+            FileInputStream stream = new FileInputStream(minidump);
+            MessageDigest md = MessageDigest.getInstance("SHA-256");
+
+            try {
+                byte[] buffer = new byte[4096];
+                int readBytes;
+
+                while ((readBytes = stream.read(buffer)) != -1) {
+                    md.update(buffer, 0, readBytes);
+                }
+            } finally {
+              stream.close();
+            }
+
+            byte[] digest = md.digest();
+            StringBuilder hash = new StringBuilder(84);
+
+            hash.append("MinidumpSha256Hash=");
+
+            for (int i = 0; i < digest.length; i++) {
+              hash.append(Integer.toHexString((digest[i] & 0xf0) >> 4));
+              hash.append(Integer.toHexString(digest[i] & 0x0f));
+            }
+
+            hash.append('\n');
+
+            FileWriter writer = new FileWriter(extraFile, /* append */ true);
+
+            try {
+                writer.write(hash.toString());
+            } finally {
+                writer.close();
+            }
+        } catch (Exception e) {
+            Log.e(LOGTAG, "exception while computing the minidump hash: ", e);
+        }
+    }
+
+    private boolean readStringsFromFile(String filePath, Map<String, String> stringMap) {
+        try {
+            BufferedReader reader = new BufferedReader(new FileReader(filePath));
+            return readStringsFromReader(reader, stringMap);
+        } catch (Exception e) {
+            Log.e(LOGTAG, "exception while reading strings: ", e);
+            return false;
+        }
+    }
+
+    private boolean readStringsFromReader(BufferedReader reader, Map<String, String> stringMap) throws IOException {
+        String line;
+        while ((line = reader.readLine()) != null) {
+            int equalsPos = -1;
+            if ((equalsPos = line.indexOf('=')) != -1) {
+                String key = line.substring(0, equalsPos);
+                String val = unescape(line.substring(equalsPos + 1));
+                stringMap.put(key, val);
+            }
+        }
+        reader.close();
+        return true;
+    }
+
+    private String generateBoundary() {
+        // Generate some random numbers to fill out the boundary
+        int r0 = (int)(Integer.MAX_VALUE * Math.random());
+        int r1 = (int)(Integer.MAX_VALUE * Math.random());
+        return String.format("---------------------------%08X%08X", r0, r1);
+    }
+
+    private void sendPart(OutputStream os, String boundary, String name, String data) {
+        try {
+            os.write(("--" + boundary + "\r\n" +
+                      "Content-Disposition: form-data; name=\"" + name + "\"\r\n" +
+                      "\r\n" +
+                      data + "\r\n"
+                     ).getBytes());
+        } catch (Exception ex) {
+            Log.e(LOGTAG, "Exception when sending \"" + name + "\"", ex);
+        }
+    }
+
+    private void sendFile(OutputStream os, String boundary, String name, File file) throws IOException {
+        os.write(("--" + boundary + "\r\n" +
+                  "Content-Disposition: form-data; name=\"" + name + "\"; " +
+                  "filename=\"" + file.getName() + "\"\r\n" +
+                  "Content-Type: application/octet-stream\r\n" +
+                  "\r\n"
+                 ).getBytes());
+        FileChannel fc = new FileInputStream(file).getChannel();
+        fc.transferTo(0, fc.size(), Channels.newChannel(os));
+        fc.close();
+    }
+
+    private String readLogcat() {
+        final String crashReporterProc = " " + android.os.Process.myPid() + ' ';
+        BufferedReader br = null;
+        try {
+            // get at most the last 400 lines of logcat
+            Process proc = Runtime.getRuntime().exec(new String[] {
+                "logcat", "-v", "threadtime", "-t", "400", "-d", "*:D"
+            });
+            StringBuilder sb = new StringBuilder();
+            br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
+            for (String s = br.readLine(); s != null; s = br.readLine()) {
+                if (s.contains(crashReporterProc)) {
+                    // Don't include logs from the crash reporter's process.
+                    break;
+                }
+                sb.append(s).append('\n');
+            }
+            return sb.toString();
+        } catch (Exception e) {
+            return "Unable to get logcat: " + e.toString();
+        } finally {
+            if (br != null) {
+                try {
+                    br.close();
+                } catch (Exception e) {
+                    // ignore
+                }
+            }
+        }
+    }
+
+    private void sendReport(File minidumpFile, Map<String, String> extras, File extrasFile) {
+        Log.i(LOGTAG, "sendReport: " + minidumpFile.getPath());
+        final CheckBox includeURLCheckbox = (CheckBox) findViewById(R.id.include_url);
+
+        String spec = extras.get(SERVER_URL_KEY);
+        if (spec == null) {
+            doFinish();
+            return;
+        }
+
+        Log.i(LOGTAG, "server url: " + spec);
+        try {
+            final URL url = new URL(URLDecoder.decode(spec, "UTF-8"));
+            final URI uri = new URI(url.getProtocol(), url.getUserInfo(),
+                                    url.getHost(), url.getPort(),
+                                    url.getPath(), url.getQuery(), url.getRef());
+            HttpURLConnection conn = (HttpURLConnection)ProxySelector.openConnectionWithProxy(uri);
+            conn.setRequestMethod("POST");
+            String boundary = generateBoundary();
+            conn.setDoOutput(true);
+            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
+            conn.setRequestProperty("Content-Encoding", "gzip");
+
+            OutputStream os = new GZIPOutputStream(conn.getOutputStream());
+            for (String key : extras.keySet()) {
+                if (key.equals(PAGE_URL_KEY)) {
+                    if (includeURLCheckbox.isChecked())
+                        sendPart(os, boundary, key, extras.get(key));
+                } else if (!key.equals(SERVER_URL_KEY) && !key.equals(NOTES_KEY)) {
+                    sendPart(os, boundary, key, extras.get(key));
+                }
+            }
+
+            // Add some extra information to notes so its displayed by
+            // crash-stats.mozilla.org. Remove this when bug 607942 is fixed.
+            StringBuilder sb = new StringBuilder();
+            sb.append(extras.containsKey(NOTES_KEY) ? extras.get(NOTES_KEY) + "\n" : "");
+            sb.append(Build.MANUFACTURER).append(' ')
+              .append(Build.MODEL).append('\n')
+              .append(Build.FINGERPRINT);
+            sendPart(os, boundary, NOTES_KEY, sb.toString());
+
+            sendPart(os, boundary, "Android_Manufacturer", Build.MANUFACTURER);
+            sendPart(os, boundary, "Android_Model", Build.MODEL);
+            sendPart(os, boundary, "Android_Board", Build.BOARD);
+            sendPart(os, boundary, "Android_Brand", Build.BRAND);
+            sendPart(os, boundary, "Android_Device", Build.DEVICE);
+            sendPart(os, boundary, "Android_Display", Build.DISPLAY);
+            sendPart(os, boundary, "Android_Fingerprint", Build.FINGERPRINT);
+            sendPart(os, boundary, "Android_APP_ABI", AppConstants.MOZ_APP_ABI);
+            sendPart(os, boundary, "Android_CPU_ABI", Build.CPU_ABI);
+            sendPart(os, boundary, "Android_MIN_SDK", Integer.toString(AppConstants.Versions.MIN_SDK_VERSION));
+            sendPart(os, boundary, "Android_MAX_SDK", Integer.toString(AppConstants.Versions.MAX_SDK_VERSION));
+            try {
+                sendPart(os, boundary, "Android_CPU_ABI2", Build.CPU_ABI2);
+                sendPart(os, boundary, "Android_Hardware", Build.HARDWARE);
+            } catch (Exception ex) {
+                Log.e(LOGTAG, "Exception while sending SDK version 8 keys", ex);
+            }
+            sendPart(os, boundary, "Android_Version",  Build.VERSION.SDK_INT + " (" + Build.VERSION.CODENAME + ")");
+            if (Versions.feature16Plus && includeURLCheckbox.isChecked()) {
+                sendPart(os, boundary, "Android_Logcat", readLogcat());
+            }
+
+            String comment = ((EditText) findViewById(R.id.comment)).getText().toString();
+            if (!TextUtils.isEmpty(comment)) {
+                sendPart(os, boundary, "Comments", comment);
+            }
+
+            if (((CheckBox) findViewById(R.id.allow_contact)).isChecked()) {
+                String email = ((EditText) findViewById(R.id.email)).getText().toString();
+                sendPart(os, boundary, "Email", email);
+            }
+
+            sendPart(os, boundary, PASSED_MINI_DUMP_SUCCESS_KEY, mMinidumpSucceeded ? "True" : "False");
+            sendFile(os, boundary, MINI_DUMP_PATH_KEY, minidumpFile);
+            os.write(("\r\n--" + boundary + "--\r\n").getBytes());
+            os.flush();
+            os.close();
+            BufferedReader br = new BufferedReader(
+                new InputStreamReader(conn.getInputStream()));
+            HashMap<String, String>  responseMap = new HashMap<String, String>();
+            readStringsFromReader(br, responseMap);
+
+            if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
+                File submittedDir = new File(getFilesDir(),
+                                             SUBMITTED_SUFFIX);
+                submittedDir.mkdirs();
+                minidumpFile.delete();
+                extrasFile.delete();
+                String crashid = responseMap.get("CrashID");
+                File file = new File(submittedDir, crashid + ".txt");
+                FileOutputStream fos = new FileOutputStream(file);
+                fos.write("Crash ID: ".getBytes());
+                fos.write(crashid.getBytes());
+                fos.close();
+            } else {
+                Log.i(LOGTAG, "Received failure HTTP response code from server: " + conn.getResponseCode());
+            }
+        } catch (IOException e) {
+            Log.e(LOGTAG, "exception during send: ", e);
+        } catch (URISyntaxException e) {
+            Log.e(LOGTAG, "exception during new URI: ", e);
+        }
+
+        doFinish();
+    }
+
+    private void doRestart() {
+        try {
+            String action = "android.intent.action.MAIN";
+            Intent intent = new Intent(action);
+            intent.setClassName(AppConstants.ANDROID_PACKAGE_NAME,
+                                AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS);
+            intent.putExtra("didRestart", true);
+            Log.i(LOGTAG, intent.toString());
+            startActivity(intent);
+        } catch (Exception e) {
+            Log.e(LOGTAG, "error while trying to restart", e);
+        }
+    }
+
+    private String unescape(String string) {
+        return string.replaceAll("\\\\\\\\", "\\").replaceAll("\\\\n", "\n").replaceAll("\\\\t", "\t");
+    }
+}
deleted file mode 100644
--- a/mobile/android/base/java/org/mozilla/gecko/CrashReporterActivity.java
+++ /dev/null
@@ -1,598 +0,0 @@
-/* -*- Mode: Java; tab-width: 20; indent-tabs-mode: nil; -*-
- * 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.gecko;
-
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.InputStreamReader;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.net.URLDecoder;
-import java.nio.channels.Channels;
-import java.nio.channels.FileChannel;
-import java.security.MessageDigest;
-import java.util.zip.GZIPOutputStream;
-
-import org.mozilla.gecko.AppConstants.Versions;
-import org.mozilla.gecko.mozglue.GeckoLoader;
-import org.mozilla.gecko.mozglue.MinidumpAnalyzer;
-import org.mozilla.gecko.telemetry.pingbuilders.TelemetryCrashPingBuilder;
-import org.mozilla.gecko.telemetry.TelemetryDispatcher;
-import org.mozilla.gecko.util.INIParser;
-import org.mozilla.gecko.util.INISection;
-import org.mozilla.gecko.util.ProxySelector;
-
-import android.annotation.SuppressLint;
-import android.app.AlertDialog;
-import android.app.ProgressDialog;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.support.v7.app.AppCompatActivity;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.View;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.EditText;
-
-// Registered: This activity is only registered in the manifest if MOZ_CRASHREPORTER is set.
-// CutPasteId: This lint is not worth fixing.  To fix it, cache all the findViewById results.
-@SuppressLint("Registered,CutPasteId")
-public class CrashReporterActivity extends AppCompatActivity
-{
-    private static final String LOGTAG = "GeckoCrashReporter";
-
-    private static final String PASSED_MINI_DUMP_KEY = "minidumpPath";
-    private static final String PASSED_MINI_DUMP_SUCCESS_KEY = "minidumpSuccess";
-    private static final String MINI_DUMP_PATH_KEY = "upload_file_minidump";
-    private static final String PAGE_URL_KEY = "URL";
-    private static final String NOTES_KEY = "Notes";
-    private static final String SERVER_URL_KEY = "ServerURL";
-
-    private static final String CRASH_REPORT_SUFFIX = "/mozilla/Crash Reports/";
-    private static final String PENDING_SUFFIX = CRASH_REPORT_SUFFIX + "pending";
-    private static final String SUBMITTED_SUFFIX = CRASH_REPORT_SUFFIX + "submitted";
-
-    private static final String PREFS_SEND_REPORT   = "sendReport";
-    private static final String PREFS_INCLUDE_URL   = "includeUrl";
-    private static final String PREFS_ALLOW_CONTACT = "allowContact";
-    private static final String PREFS_CONTACT_EMAIL = "contactEmail";
-
-    private Handler mHandler;
-    private ProgressDialog mProgressDialog;
-    private File mPendingMinidumpFile;
-    private File mPendingExtrasFile;
-    private HashMap<String, String> mExtrasStringMap;
-    private boolean mMinidumpSucceeded;
-
-    private boolean moveFile(File inFile, File outFile) {
-        Log.i(LOGTAG, "moving " + inFile + " to " + outFile);
-        if (inFile.renameTo(outFile))
-            return true;
-        try {
-            outFile.createNewFile();
-            Log.i(LOGTAG, "couldn't rename minidump file");
-            // so copy it instead
-            FileChannel inChannel = new FileInputStream(inFile).getChannel();
-            FileChannel outChannel = new FileOutputStream(outFile).getChannel();
-            long transferred = inChannel.transferTo(0, inChannel.size(), outChannel);
-            inChannel.close();
-            outChannel.close();
-
-            if (transferred > 0)
-                inFile.delete();
-        } catch (Exception e) {
-            Log.e(LOGTAG, "exception while copying minidump file: ", e);
-            return false;
-        }
-        return true;
-    }
-
-    private void doFinish() {
-        if (mHandler != null) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    finish();
-                }
-            });
-        }
-    }
-
-    @Override
-    public void finish() {
-        try {
-            if (mProgressDialog.isShowing()) {
-                mProgressDialog.dismiss();
-            }
-        } catch (Exception e) {
-            Log.e(LOGTAG, "exception while closing progress dialog: ", e);
-        }
-        super.finish();
-    }
-
-    @Override
-    @SuppressLint("WrongThread") // We don't have a worker thread for the TelemetryDispatcher
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        // mHandler is created here so runnables can be run on the main thread
-        mHandler = new Handler();
-        setContentView(R.layout.crash_reporter);
-        mProgressDialog = new ProgressDialog(this);
-        mProgressDialog.setMessage(getString(R.string.sending_crash_report));
-
-        mMinidumpSucceeded = getIntent().getBooleanExtra(PASSED_MINI_DUMP_SUCCESS_KEY, false);
-        if (!mMinidumpSucceeded) {
-            Log.i(LOGTAG, "Failed to get minidump.");
-        }
-        String passedMinidumpPath = getIntent().getStringExtra(PASSED_MINI_DUMP_KEY);
-        File passedMinidumpFile = new File(passedMinidumpPath);
-        File pendingDir = new File(getFilesDir(), PENDING_SUFFIX);
-        pendingDir.mkdirs();
-        mPendingMinidumpFile = new File(pendingDir, passedMinidumpFile.getName());
-        moveFile(passedMinidumpFile, mPendingMinidumpFile);
-
-        File extrasFile = new File(passedMinidumpPath.replaceAll("\\.dmp", ".extra"));
-        mPendingExtrasFile = new File(pendingDir, extrasFile.getName());
-        moveFile(extrasFile, mPendingExtrasFile);
-
-        // Compute the minidump hash and generate the stack traces
-        computeMinidumpHash(mPendingExtrasFile, mPendingMinidumpFile);
-
-        try {
-            GeckoLoader.loadMozGlue(this);
-
-            if (!MinidumpAnalyzer.GenerateStacks(mPendingMinidumpFile.getPath(), /* fullStacks */ false)) {
-                Log.e(LOGTAG, "Could not generate stacks for this minidump: " + passedMinidumpPath);
-            }
-        } catch (UnsatisfiedLinkError e) {
-            Log.e(LOGTAG, "Could not load libmozglue.so, stacks for this crash won't be generated");
-        }
-
-        // Extract the annotations from the .extra file
-        mExtrasStringMap = new HashMap<String, String>();
-        readStringsFromFile(mPendingExtrasFile.getPath(), mExtrasStringMap);
-
-        try {
-            // Find the profile name and path. Since we don't have any other way of getting it within
-            // this context we extract it from the crash dump path.
-            final File profileDir = passedMinidumpFile.getParentFile().getParentFile();
-            final String profileName = getProfileName(profileDir);
-
-            if (profileName != null) {
-                // Extract the crash dump ID and telemetry client ID, we need profile access for the latter.
-                final String passedMinidumpName = passedMinidumpFile.getName();
-                // Strip the .dmp suffix from the minidump name to obtain the crash ID.
-                final String crashId = passedMinidumpName.substring(0, passedMinidumpName.length() - 4);
-                final GeckoProfile profile = GeckoProfile.get(this, profileName, profileDir);
-                final String clientId = profile.getClientId();
-
-                // Assemble and send the crash ping
-                final TelemetryCrashPingBuilder pingBuilder =
-                    new TelemetryCrashPingBuilder(crashId, clientId, mExtrasStringMap);
-                final TelemetryDispatcher dispatcher = new TelemetryDispatcher(profileDir.getPath(), profileName);
-                dispatcher.queuePingForUpload(this, pingBuilder);
-            }
-        } catch (GeckoProfileDirectories.NoMozillaDirectoryException | IOException e) {
-            Log.e(LOGTAG, "Cannot send the crash ping: ", e);
-        }
-
-        // Notify GeckoApp that we've crashed, so it can react appropriately during the next start.
-        try {
-            File crashFlag = new File(GeckoProfileDirectories.getMozillaDirectory(this), "CRASHED");
-            crashFlag.createNewFile();
-        } catch (GeckoProfileDirectories.NoMozillaDirectoryException | IOException e) {
-            Log.e(LOGTAG, "Cannot set crash flag: ", e);
-        }
-
-        final CheckBox allowContactCheckBox = (CheckBox) findViewById(R.id.allow_contact);
-        final CheckBox includeUrlCheckBox = (CheckBox) findViewById(R.id.include_url);
-        final CheckBox sendReportCheckBox = (CheckBox) findViewById(R.id.send_report);
-        final EditText commentsEditText = (EditText) findViewById(R.id.comment);
-        final EditText emailEditText = (EditText) findViewById(R.id.email);
-
-        // Load CrashReporterActivity preferences to avoid redundant user input.
-        SharedPreferences prefs = GeckoSharedPrefs.forCrashReporter(this);
-        final boolean sendReport   = prefs.getBoolean(PREFS_SEND_REPORT, true);
-        final boolean includeUrl   = prefs.getBoolean(PREFS_INCLUDE_URL, false);
-        final boolean allowContact = prefs.getBoolean(PREFS_ALLOW_CONTACT, false);
-        final String contactEmail  = prefs.getString(PREFS_CONTACT_EMAIL, "");
-
-        allowContactCheckBox.setChecked(allowContact);
-        includeUrlCheckBox.setChecked(includeUrl);
-        sendReportCheckBox.setChecked(sendReport);
-        emailEditText.setText(contactEmail);
-
-        sendReportCheckBox.setOnCheckedChangeListener(new CheckBox.OnCheckedChangeListener() {
-            @Override
-            public void onCheckedChanged(CompoundButton checkbox, boolean isChecked) {
-                commentsEditText.setEnabled(isChecked);
-                commentsEditText.requestFocus();
-
-                includeUrlCheckBox.setEnabled(isChecked);
-                allowContactCheckBox.setEnabled(isChecked);
-                emailEditText.setEnabled(isChecked && allowContactCheckBox.isChecked());
-            }
-        });
-
-        allowContactCheckBox.setOnCheckedChangeListener(new CheckBox.OnCheckedChangeListener() {
-            @Override
-            public void onCheckedChanged(CompoundButton checkbox, boolean isChecked) {
-                // We need to check isEnabled() here because this listener is
-                // fired on rotation -- even when the checkbox is disabled.
-                emailEditText.setEnabled(checkbox.isEnabled() && isChecked);
-                emailEditText.requestFocus();
-            }
-        });
-
-        emailEditText.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                // Even if the email EditText is disabled, allow it to be
-                // clicked and focused.
-                if (sendReportCheckBox.isChecked() && !v.isEnabled()) {
-                    allowContactCheckBox.setChecked(true);
-                    v.setEnabled(true);
-                    v.requestFocus();
-                }
-            }
-        });
-    }
-
-    @Override
-    public void onBackPressed() {
-        AlertDialog.Builder builder = new AlertDialog.Builder(this);
-        builder.setMessage(R.string.crash_closing_alert);
-        builder.setNegativeButton(R.string.button_cancel, new DialogInterface.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-                dialog.dismiss();
-            }
-        });
-        builder.setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-                CrashReporterActivity.this.finish();
-            }
-        });
-        builder.show();
-    }
-
-    private void backgroundSendReport() {
-        final CheckBox sendReportCheckbox = (CheckBox) findViewById(R.id.send_report);
-        if (!sendReportCheckbox.isChecked()) {
-            doFinish();
-            return;
-        }
-
-        // Persist settings to avoid redundant user input.
-        savePrefs();
-
-        mProgressDialog.show();
-        new Thread(new Runnable() {
-            @Override
-            public void run() {
-                sendReport(mPendingMinidumpFile, mExtrasStringMap, mPendingExtrasFile);
-            }
-        }, "CrashReporterActivity Thread").start();
-    }
-
-    private void savePrefs() {
-        SharedPreferences.Editor editor = GeckoSharedPrefs.forCrashReporter(this).edit();
-
-        final boolean allowContact = ((CheckBox) findViewById(R.id.allow_contact)).isChecked();
-        final boolean includeUrl   = ((CheckBox) findViewById(R.id.include_url)).isChecked();
-        final boolean sendReport   = ((CheckBox) findViewById(R.id.send_report)).isChecked();
-        final String contactEmail  = ((EditText) findViewById(R.id.email)).getText().toString();
-
-        editor.putBoolean(PREFS_ALLOW_CONTACT, allowContact);
-        editor.putBoolean(PREFS_INCLUDE_URL, includeUrl);
-        editor.putBoolean(PREFS_SEND_REPORT, sendReport);
-        editor.putString(PREFS_CONTACT_EMAIL, contactEmail);
-
-        // A slight performance improvement via async apply() vs. blocking on commit().
-        editor.apply();
-    }
-
-    public void onCloseClick(View v) {  // bound via crash_reporter.xml
-        backgroundSendReport();
-    }
-
-    public void onRestartClick(View v) {  // bound via crash_reporter.xml
-        doRestart();
-        backgroundSendReport();
-    }
-
-    private String getProfileName(File profileDir) throws GeckoProfileDirectories.NoMozillaDirectoryException {
-        final File mozillaDir = GeckoProfileDirectories.getMozillaDirectory(this);
-        final INIParser parser = GeckoProfileDirectories.getProfilesINI(mozillaDir);
-        String profileName = null;
-
-        if (parser.getSections() != null) {
-            for (Enumeration<INISection> e = parser.getSections().elements(); e.hasMoreElements(); ) {
-                final INISection section = e.nextElement();
-                final String path = section.getStringProperty("Path");
-                final boolean isRelative = (section.getIntProperty("IsRelative") == 1);
-
-                if ((isRelative && path.equals(profileDir.getName())) ||
-                    path.equals(profileDir.getPath())) {
-                    profileName = section.getStringProperty("Name");
-                    break;
-                }
-            }
-        }
-
-        return profileName;
-    }
-
-
-    private void computeMinidumpHash(File extraFile, File minidump) {
-        try {
-            FileInputStream stream = new FileInputStream(minidump);
-            MessageDigest md = MessageDigest.getInstance("SHA-256");
-
-            try {
-                byte[] buffer = new byte[4096];
-                int readBytes;
-
-                while ((readBytes = stream.read(buffer)) != -1) {
-                    md.update(buffer, 0, readBytes);
-                }
-            } finally {
-              stream.close();
-            }
-
-            byte[] digest = md.digest();
-            StringBuilder hash = new StringBuilder(84);
-
-            hash.append("MinidumpSha256Hash=");
-
-            for (int i = 0; i < digest.length; i++) {
-              hash.append(Integer.toHexString((digest[i] & 0xf0) >> 4));
-              hash.append(Integer.toHexString(digest[i] & 0x0f));
-            }
-
-            hash.append('\n');
-
-            FileWriter writer = new FileWriter(extraFile, /* append */ true);
-
-            try {
-                writer.write(hash.toString());
-            } finally {
-                writer.close();
-            }
-        } catch (Exception e) {
-            Log.e(LOGTAG, "exception while computing the minidump hash: ", e);
-        }
-    }
-
-    private boolean readStringsFromFile(String filePath, Map<String, String> stringMap) {
-        try {
-            BufferedReader reader = new BufferedReader(new FileReader(filePath));
-            return readStringsFromReader(reader, stringMap);
-        } catch (Exception e) {
-            Log.e(LOGTAG, "exception while reading strings: ", e);
-            return false;
-        }
-    }
-
-    private boolean readStringsFromReader(BufferedReader reader, Map<String, String> stringMap) throws IOException {
-        String line;
-        while ((line = reader.readLine()) != null) {
-            int equalsPos = -1;
-            if ((equalsPos = line.indexOf('=')) != -1) {
-                String key = line.substring(0, equalsPos);
-                String val = unescape(line.substring(equalsPos + 1));
-                stringMap.put(key, val);
-            }
-        }
-        reader.close();
-        return true;
-    }
-
-    private String generateBoundary() {
-        // Generate some random numbers to fill out the boundary
-        int r0 = (int)(Integer.MAX_VALUE * Math.random());
-        int r1 = (int)(Integer.MAX_VALUE * Math.random());
-        return String.format("---------------------------%08X%08X", r0, r1);
-    }
-
-    private void sendPart(OutputStream os, String boundary, String name, String data) {
-        try {
-            os.write(("--" + boundary + "\r\n" +
-                      "Content-Disposition: form-data; name=\"" + name + "\"\r\n" +
-                      "\r\n" +
-                      data + "\r\n"
-                     ).getBytes());
-        } catch (Exception ex) {
-            Log.e(LOGTAG, "Exception when sending \"" + name + "\"", ex);
-        }
-    }
-
-    private void sendFile(OutputStream os, String boundary, String name, File file) throws IOException {
-        os.write(("--" + boundary + "\r\n" +
-                  "Content-Disposition: form-data; name=\"" + name + "\"; " +
-                  "filename=\"" + file.getName() + "\"\r\n" +
-                  "Content-Type: application/octet-stream\r\n" +
-                  "\r\n"
-                 ).getBytes());
-        FileChannel fc = new FileInputStream(file).getChannel();
-        fc.transferTo(0, fc.size(), Channels.newChannel(os));
-        fc.close();
-    }
-
-    private String readLogcat() {
-        final String crashReporterProc = " " + android.os.Process.myPid() + ' ';
-        BufferedReader br = null;
-        try {
-            // get at most the last 400 lines of logcat
-            Process proc = Runtime.getRuntime().exec(new String[] {
-                "logcat", "-v", "threadtime", "-t", "400", "-d", "*:D"
-            });
-            StringBuilder sb = new StringBuilder();
-            br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
-            for (String s = br.readLine(); s != null; s = br.readLine()) {
-                if (s.contains(crashReporterProc)) {
-                    // Don't include logs from the crash reporter's process.
-                    break;
-                }
-                sb.append(s).append('\n');
-            }
-            return sb.toString();
-        } catch (Exception e) {
-            return "Unable to get logcat: " + e.toString();
-        } finally {
-            if (br != null) {
-                try {
-                    br.close();
-                } catch (Exception e) {
-                    // ignore
-                }
-            }
-        }
-    }
-
-    private void sendReport(File minidumpFile, Map<String, String> extras, File extrasFile) {
-        Log.i(LOGTAG, "sendReport: " + minidumpFile.getPath());
-        final CheckBox includeURLCheckbox = (CheckBox) findViewById(R.id.include_url);
-
-        String spec = extras.get(SERVER_URL_KEY);
-        if (spec == null) {
-            doFinish();
-            return;
-        }
-
-        Log.i(LOGTAG, "server url: " + spec);
-        try {
-            final URL url = new URL(URLDecoder.decode(spec, "UTF-8"));
-            final URI uri = new URI(url.getProtocol(), url.getUserInfo(),
-                                    url.getHost(), url.getPort(),
-                                    url.getPath(), url.getQuery(), url.getRef());
-            HttpURLConnection conn = (HttpURLConnection)ProxySelector.openConnectionWithProxy(uri);
-            conn.setRequestMethod("POST");
-            String boundary = generateBoundary();
-            conn.setDoOutput(true);
-            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
-            conn.setRequestProperty("Content-Encoding", "gzip");
-
-            OutputStream os = new GZIPOutputStream(conn.getOutputStream());
-            for (String key : extras.keySet()) {
-                if (key.equals(PAGE_URL_KEY)) {
-                    if (includeURLCheckbox.isChecked())
-                        sendPart(os, boundary, key, extras.get(key));
-                } else if (!key.equals(SERVER_URL_KEY) && !key.equals(NOTES_KEY)) {
-                    sendPart(os, boundary, key, extras.get(key));
-                }
-            }
-
-            // Add some extra information to notes so its displayed by
-            // crash-stats.mozilla.org. Remove this when bug 607942 is fixed.
-            StringBuilder sb = new StringBuilder();
-            sb.append(extras.containsKey(NOTES_KEY) ? extras.get(NOTES_KEY) + "\n" : "");
-            sb.append(Build.MANUFACTURER).append(' ')
-              .append(Build.MODEL).append('\n')
-              .append(Build.FINGERPRINT);
-            sendPart(os, boundary, NOTES_KEY, sb.toString());
-
-            sendPart(os, boundary, "Android_Manufacturer", Build.MANUFACTURER);
-            sendPart(os, boundary, "Android_Model", Build.MODEL);
-            sendPart(os, boundary, "Android_Board", Build.BOARD);
-            sendPart(os, boundary, "Android_Brand", Build.BRAND);
-            sendPart(os, boundary, "Android_Device", Build.DEVICE);
-            sendPart(os, boundary, "Android_Display", Build.DISPLAY);
-            sendPart(os, boundary, "Android_Fingerprint", Build.FINGERPRINT);
-            sendPart(os, boundary, "Android_APP_ABI", AppConstants.MOZ_APP_ABI);
-            sendPart(os, boundary, "Android_CPU_ABI", Build.CPU_ABI);
-            sendPart(os, boundary, "Android_MIN_SDK", Integer.toString(AppConstants.Versions.MIN_SDK_VERSION));
-            sendPart(os, boundary, "Android_MAX_SDK", Integer.toString(AppConstants.Versions.MAX_SDK_VERSION));
-            try {
-                sendPart(os, boundary, "Android_CPU_ABI2", Build.CPU_ABI2);
-                sendPart(os, boundary, "Android_Hardware", Build.HARDWARE);
-            } catch (Exception ex) {
-                Log.e(LOGTAG, "Exception while sending SDK version 8 keys", ex);
-            }
-            sendPart(os, boundary, "Android_Version",  Build.VERSION.SDK_INT + " (" + Build.VERSION.CODENAME + ")");
-            if (Versions.feature16Plus && includeURLCheckbox.isChecked()) {
-                sendPart(os, boundary, "Android_Logcat", readLogcat());
-            }
-
-            String comment = ((EditText) findViewById(R.id.comment)).getText().toString();
-            if (!TextUtils.isEmpty(comment)) {
-                sendPart(os, boundary, "Comments", comment);
-            }
-
-            if (((CheckBox) findViewById(R.id.allow_contact)).isChecked()) {
-                String email = ((EditText) findViewById(R.id.email)).getText().toString();
-                sendPart(os, boundary, "Email", email);
-            }
-
-            sendPart(os, boundary, PASSED_MINI_DUMP_SUCCESS_KEY, mMinidumpSucceeded ? "True" : "False");
-            sendFile(os, boundary, MINI_DUMP_PATH_KEY, minidumpFile);
-            os.write(("\r\n--" + boundary + "--\r\n").getBytes());
-            os.flush();
-            os.close();
-            BufferedReader br = new BufferedReader(
-                new InputStreamReader(conn.getInputStream()));
-            HashMap<String, String>  responseMap = new HashMap<String, String>();
-            readStringsFromReader(br, responseMap);
-
-            if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
-                File submittedDir = new File(getFilesDir(),
-                                             SUBMITTED_SUFFIX);
-                submittedDir.mkdirs();
-                minidumpFile.delete();
-                extrasFile.delete();
-                String crashid = responseMap.get("CrashID");
-                File file = new File(submittedDir, crashid + ".txt");
-                FileOutputStream fos = new FileOutputStream(file);
-                fos.write("Crash ID: ".getBytes());
-                fos.write(crashid.getBytes());
-                fos.close();
-            } else {
-                Log.i(LOGTAG, "Received failure HTTP response code from server: " + conn.getResponseCode());
-            }
-        } catch (IOException e) {
-            Log.e(LOGTAG, "exception during send: ", e);
-        } catch (URISyntaxException e) {
-            Log.e(LOGTAG, "exception during new URI: ", e);
-        }
-
-        doFinish();
-    }
-
-    private void doRestart() {
-        try {
-            String action = "android.intent.action.MAIN";
-            Intent intent = new Intent(action);
-            intent.setClassName(AppConstants.ANDROID_PACKAGE_NAME,
-                                AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS);
-            intent.putExtra("didRestart", true);
-            Log.i(LOGTAG, intent.toString());
-            startActivity(intent);
-        } catch (Exception e) {
-            Log.e(LOGTAG, "error while trying to restart", e);
-        }
-    }
-
-    private String unescape(String string) {
-        return string.replaceAll("\\\\\\\\", "\\").replaceAll("\\\\n", "\n").replaceAll("\\\\t", "\t");
-    }
-}
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -118,16 +118,17 @@ public abstract class GeckoApp extends G
                                           ViewTreeObserver.OnGlobalLayoutListener {
 
     private static final String LOGTAG = "GeckoApp";
     private static final long ONE_DAY_MS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS);
 
     public static final String ACTION_ALERT_CALLBACK       = "org.mozilla.gecko.ALERT_CALLBACK";
     public static final String ACTION_HOMESCREEN_SHORTCUT  = "org.mozilla.gecko.BOOKMARK";
     public static final String ACTION_WEBAPP               = "org.mozilla.gecko.WEBAPP";
+    public static final String ACTION_DEBUG                = "org.mozilla.gecko.DEBUG";
     public static final String ACTION_LAUNCH_SETTINGS      = "org.mozilla.gecko.SETTINGS";
     public static final String ACTION_LOAD                 = "org.mozilla.gecko.LOAD";
     public static final String ACTION_INIT_PW              = "org.mozilla.gecko.INIT_PW";
     public static final String ACTION_SWITCH_TAB           = "org.mozilla.gecko.SWITCH_TAB";
     public static final String ACTION_SHUTDOWN             = "org.mozilla.gecko.SHUTDOWN";
 
     public static final String INTENT_REGISTER_STUMBLER_LISTENER = "org.mozilla.gecko.STUMBLER_REGISTER_LOCAL_LISTENER";
 
@@ -992,59 +993,62 @@ public abstract class GeckoApp extends G
         // restart, and will be propagated to Gecko accordingly, so there's
         // no need to touch that here.
         if (BrowserLocaleManager.getInstance().systemLocaleDidChange()) {
             Log.i(LOGTAG, "System locale changed. Restarting.");
             finishAndShutdown(/* restart */ true);
             return;
         }
 
-        // To prevent races, register startup events before launching the Gecko thread.
-        EventDispatcher.getInstance().registerGeckoThreadListener(this,
-                "Gecko:Ready",
-                null);
-
-        EventDispatcher.getInstance().registerUiThreadListener(this,
-                "Gecko:CorruptAPK",
-                "Update:Check",
-                "Update:Download",
-                "Update:Install",
-                null);
-
         if (sAlreadyLoaded) {
             // This happens when the GeckoApp activity is destroyed by Android
             // without killing the entire application (see Bug 769269).
             // In case we have multiple GeckoApp-based activities, this can
             // also happen if we're not the first activity to run within a session.
             mIsRestoringActivity = true;
             Telemetry.addToHistogram("FENNEC_RESTORING_ACTIVITY", 1);
+
         } else {
             final String action = intent.getAction();
             final String[] args = GeckoApplication.getDefaultGeckoArgs();
+            final int flags = ACTION_DEBUG.equals(action) ? GeckoThread.FLAG_DEBUGGING : 0;
 
             sAlreadyLoaded = true;
-            if (GeckoApplication.getRuntime() == null) {
-                GeckoApplication.createRuntime(this, intent);
-            }
+            GeckoThread.initMainProcess(/* profile */ null, args,
+                                        intent.getExtras(), flags);
 
             // Speculatively pre-fetch the profile in the background.
             ThreadUtils.postToBackgroundThread(new Runnable() {
                 @Override
                 public void run() {
                     getProfile();
                 }
             });
 
             final String uri = getURIFromIntent(intent);
             if (!TextUtils.isEmpty(uri)) {
                 // Start a speculative connection as soon as Gecko loads.
                 GeckoThread.speculativeConnect(uri);
             }
         }
 
+        // To prevent races, register startup events before launching the Gecko thread.
+        EventDispatcher.getInstance().registerGeckoThreadListener(this,
+            "Gecko:Ready",
+            null);
+
+        EventDispatcher.getInstance().registerUiThreadListener(this,
+            "Gecko:CorruptAPK",
+            "Update:Check",
+            "Update:Download",
+            "Update:Install",
+            null);
+
+        GeckoThread.launch();
+
         Bundle stateBundle = IntentUtils.getBundleExtraSafe(getIntent(), EXTRA_STATE_BUNDLE);
         if (stateBundle != null) {
             // Use the state bundle if it was given as an intent extra. This is
             // only intended to be used internally via Robocop, so a boolean
             // is read from a private shared pref to prevent other apps from
             // injecting states.
             final SharedPreferences prefs = getSharedPreferences();
             if (prefs.getBoolean(PREFS_ALLOW_STATE_BUNDLE, false)) {
@@ -1072,17 +1076,17 @@ public abstract class GeckoApp extends G
         session.getSettings().setString(GeckoSessionSettings.CHROME_URI,
                                         "chrome://browser/content/browser.xul");
         session.setContentDelegate(this);
 
         // If the view already has a session, we need to ensure it is closed.
         if (mLayerView.getSession() != null) {
             mLayerView.getSession().close();
         }
-        mLayerView.setSession(session, GeckoApplication.getRuntime());
+        mLayerView.setSession(session, GeckoRuntime.getDefault(this));
         mLayerView.setOverScrollMode(View.OVER_SCROLL_NEVER);
 
         getAppEventDispatcher().registerGeckoThreadListener(this,
             "Locale:Set",
             "PrivateBrowsing:Data",
             null);
 
         getAppEventDispatcher().registerUiThreadListener(this,
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
@@ -8,22 +8,20 @@ import android.Manifest;
 import android.app.Activity;
 import android.app.Application;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.net.Uri;
-import android.os.Bundle;
 import android.os.Environment;
 import android.os.Process;
 import android.os.SystemClock;
 import android.provider.MediaStore;
-import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.design.widget.Snackbar;
 import android.text.TextUtils;
 import android.util.Base64;
 import android.util.Log;
 
 import com.squareup.leakcanary.LeakCanary;
 import com.squareup.leakcanary.RefWatcher;
@@ -35,47 +33,43 @@ import org.mozilla.gecko.db.LocalBrowser
 import org.mozilla.gecko.distribution.Distribution;
 import org.mozilla.gecko.home.HomePanelsManager;
 import org.mozilla.gecko.icons.IconCallback;
 import org.mozilla.gecko.icons.IconResponse;
 import org.mozilla.gecko.icons.Icons;
 import org.mozilla.gecko.lwt.LightweightTheme;
 import org.mozilla.gecko.mdns.MulticastDNSManager;
 import org.mozilla.gecko.media.AudioFocusAgent;
-import org.mozilla.gecko.mozglue.SafeIntent;
 import org.mozilla.gecko.notifications.NotificationClient;
 import org.mozilla.gecko.notifications.NotificationHelper;
 import org.mozilla.gecko.permissions.Permissions;
 import org.mozilla.gecko.preferences.DistroSharedPrefsImport;
 import org.mozilla.gecko.pwa.PwaUtils;
 import org.mozilla.gecko.telemetry.TelemetryBackgroundReceiver;
 import org.mozilla.gecko.util.ActivityResultHandler;
 import org.mozilla.gecko.util.BitmapUtils;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.util.PRNGFixes;
 import org.mozilla.gecko.util.ShortcutUtils;
 import org.mozilla.gecko.util.ThreadUtils;
-import org.mozilla.geckoview.GeckoRuntime;
-import org.mozilla.geckoview.GeckoRuntimeSettings;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.InputStream;
 import java.io.IOException;
 import java.lang.reflect.Method;
 import java.net.URL;
 import java.util.UUID;
 
 public class GeckoApplication extends Application
                               implements HapticFeedbackDelegate {
     private static final String LOG_TAG = "GeckoApplication";
-    public static final String ACTION_DEBUG = "org.mozilla.gecko.DEBUG";
     private static final String MEDIA_DECODING_PROCESS_CRASH = "MEDIA_DECODING_PROCESS_CRASH";
 
     private boolean mInBackground;
     private boolean mPausedGecko;
     private boolean mIsInitialResume;
 
     private LightweightTheme mLightweightTheme;
 
@@ -211,60 +205,16 @@ public class GeckoApplication extends Ap
             GeckoThread.onResume();
             mPausedGecko = false;
             GeckoNetworkManager.getInstance().start(this);
         }
 
         mInBackground = false;
     }
 
-    private static GeckoRuntime sGeckoRuntime;
-    public static GeckoRuntime getRuntime() {
-        return sGeckoRuntime;
-    }
-
-    public static GeckoRuntime ensureRuntime(@NonNull Context context) {
-        if (sGeckoRuntime != null) {
-            return sGeckoRuntime;
-        }
-
-        return createRuntime(context, null);
-    }
-
-    private static GeckoRuntimeSettings.Builder createSettingsBuilder() {
-        return new GeckoRuntimeSettings.Builder()
-                .javaCrashReportingEnabled(true)
-                .nativeCrashReportingEnabled(true)
-                .arguments(getDefaultGeckoArgs());
-    }
-
-    public static GeckoRuntime createRuntime(@NonNull Context context,
-                                             @Nullable SafeIntent intent) {
-        if (sGeckoRuntime != null) {
-            throw new IllegalStateException("Already have a GeckoRuntime!");
-        }
-
-        if (context == null) {
-            throw new IllegalArgumentException("Context must not be null");
-        }
-
-        GeckoRuntimeSettings.Builder builder = createSettingsBuilder();
-        if (intent != null) {
-            builder.pauseForDebugger(ACTION_DEBUG.equals(intent.getAction()));
-
-            Bundle extras = intent.getExtras();
-            if (extras != null) {
-                builder.extras(extras);
-            }
-        }
-
-        sGeckoRuntime = GeckoRuntime.create(context, builder.build());
-        return sGeckoRuntime;
-    }
-
     @Override
     public void onCreate() {
         Log.i(LOG_TAG, "zerdatime " + SystemClock.elapsedRealtime() +
               " - application start");
 
         final Context oldContext = GeckoAppShell.getApplicationContext();
         if (oldContext instanceof GeckoApplication) {
             ((GeckoApplication) oldContext).onDestroy();
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoService.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoService.java
@@ -13,17 +13,16 @@ import android.content.Intent;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Log;
 
 import java.io.File;
 
-import org.mozilla.gecko.mozglue.SafeIntent;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.EventCallback;
 
 public class GeckoService extends Service {
 
     private static final String LOGTAG = "GeckoService";
     private static final boolean DEBUG = false;
@@ -162,45 +161,44 @@ public class GeckoService extends Servic
 
         final String profileName = intent.getStringExtra(INTENT_PROFILE_NAME);
         final String profileDir = intent.getStringExtra(INTENT_PROFILE_DIR);
 
         if (profileName == null) {
             throw new IllegalArgumentException("Intent must specify profile.");
         }
 
-        if (GeckoApplication.getRuntime() != null) {
-            // Gecko has already been initialized, make sure it's using the
-            // expected profile.
-            return GeckoThread.canUseProfile(profileName,
-                    profileDir != null ? new File(profileDir) : null);
-        }
+        if (!GeckoThread.initMainProcessWithProfile(
+                profileName, profileDir != null ? new File(profileDir) : null,
+                GeckoApplication.getDefaultGeckoArgs(), intent.getExtras())) {
+            Log.w(LOGTAG, "Ignoring due to profile mismatch: " +
+                          profileName + " [" + profileDir + ']');
 
-        String args;
-        if (profileDir != null) {
-            args = "-profile " + profileDir;
-        } else {
-            args = "-P " + profileName;
+            final GeckoProfile profile = GeckoThread.getActiveProfile();
+            if (profile != null) {
+                Log.w(LOGTAG, "Current profile is " + profile.getName() +
+                              " [" + profile.getDir().getAbsolutePath() + ']');
+            }
+            return false;
         }
-
-        intent.putExtra(GeckoThread.EXTRA_ARGS, args);
-        GeckoApplication.createRuntime(this, new SafeIntent(intent));
         return true;
     }
 
     private int handleIntent(final Intent intent, final int startId) {
         if (DEBUG) {
             Log.d(LOGTAG, "Handling " + intent.getAction());
         }
 
         if (!initGecko(intent)) {
             stopSelf(startId);
             return Service.START_NOT_STICKY;
         }
 
+        GeckoThread.launch();
+
         switch (intent.getAction()) {
         case INTENT_ACTION_UPDATE_ADDONS:
             // Run the add-on update service. Because the service is automatically invoked
             // when loading Gecko, we don't have to do anything else here.
         case INTENT_ACTION_LOAD_LIBS:
             // Load libs only. Don't take any additional actions.
         case INTENT_ACTION_START_GECKO:
             // Load libs and start Gecko. Don't take any additional actions.
--- a/mobile/android/base/java/org/mozilla/gecko/RemotePresentationService.java
+++ b/mobile/android/base/java/org/mozilla/gecko/RemotePresentationService.java
@@ -127,17 +127,17 @@ class VirtualPresentation extends CastPr
 
         final GeckoSession session = new GeckoSession();
         session.getSettings().setString(GeckoSessionSettings.CHROME_URI,
                                         PRESENTATION_VIEW_URI + "#" + deviceId);
         session.getSettings().setInt(GeckoSessionSettings.SCREEN_ID, screenId);
 
         // Create new GeckoView
         view = new GeckoView(getContext());
-        view.setSession(session, GeckoApplication.ensureRuntime(getContext()));
+        view.setSession(session, GeckoRuntime.getDefault(getContext()));
         view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
                                               LayoutParams.MATCH_PARENT));
 
         // Create new layout to put the GeckoView
         layout = new RelativeLayout(getContext());
         layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
                                                 LayoutParams.MATCH_PARENT));
         layout.addView(view);
--- a/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
@@ -32,17 +32,16 @@ import android.view.View;
 import android.widget.ProgressBar;
 
 import org.mozilla.gecko.ActivityHandlerHelper;
 import org.mozilla.gecko.BrowserApp;
 import org.mozilla.gecko.Clipboard;
 import org.mozilla.gecko.DoorHangerPopup;
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.FormAssistPopup;
-import org.mozilla.gecko.GeckoApplication;
 import org.mozilla.gecko.GeckoSharedPrefs;
 import org.mozilla.gecko.preferences.GeckoPreferences;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.SnackbarBuilder;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.menu.GeckoMenuInflater;
@@ -127,17 +126,17 @@ public class CustomTabsActivity extends 
 
         final GeckoSessionSettings settings = new GeckoSessionSettings();
         settings.setBoolean(GeckoSessionSettings.USE_MULTIPROCESS, false);
         mGeckoSession = new GeckoSession(settings);
         mGeckoSession.setNavigationDelegate(this);
         mGeckoSession.setProgressDelegate(this);
         mGeckoSession.setContentDelegate(this);
 
-        mGeckoView.setSession(mGeckoSession, GeckoApplication.ensureRuntime(this));
+        mGeckoView.setSession(mGeckoSession, GeckoRuntime.getDefault(this));
 
         mPromptService = new PromptService(this, mGeckoView.getEventDispatcher());
         mDoorHangerPopup = new DoorHangerPopup(this, mGeckoView.getEventDispatcher());
 
         mFormAssistPopup = (FormAssistPopup) findViewById(R.id.form_assist_popup);
         mFormAssistPopup.create(mGeckoView);
 
         mTextSelection = TextSelection.Factory.create(mGeckoView, this);
--- a/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
@@ -21,17 +21,16 @@ import android.view.Window;
 import android.view.WindowManager;
 import android.widget.Toast;
 
 import org.mozilla.gecko.ActivityHandlerHelper;
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.BrowserApp;
 import org.mozilla.gecko.DoorHangerPopup;
 import org.mozilla.gecko.FormAssistPopup;
-import org.mozilla.gecko.GeckoApplication;
 import org.mozilla.gecko.GeckoScreenOrientation;
 import org.mozilla.gecko.GeckoSharedPrefs;
 import org.mozilla.gecko.preferences.GeckoPreferences;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.customtabs.CustomTabsActivity;
 import org.mozilla.gecko.permissions.Permissions;
 import org.mozilla.gecko.prompts.PromptService;
 import org.mozilla.gecko.text.TextSelection;
@@ -93,17 +92,17 @@ public class WebAppActivity extends AppC
 
         super.onCreate(savedInstanceState);
         setContentView(R.layout.webapp_activity);
         mGeckoView = (GeckoView) findViewById(R.id.pwa_gecko_view);
 
         final GeckoSessionSettings settings = new GeckoSessionSettings();
         settings.setBoolean(GeckoSessionSettings.USE_MULTIPROCESS, false);
         mGeckoSession = new GeckoSession(settings);
-        mGeckoView.setSession(mGeckoSession, GeckoApplication.ensureRuntime(this));
+        mGeckoView.setSession(mGeckoSession, GeckoRuntime.getDefault(this));
 
         mGeckoSession.setNavigationDelegate(this);
         mGeckoSession.setContentDelegate(this);
         mGeckoSession.setProgressDelegate(new GeckoSession.ProgressDelegate() {
             @Override
             public void onPageStart(GeckoSession session, String url) {
 
             }
--- a/mobile/android/geckoview/build.gradle
+++ b/mobile/android/geckoview/build.gradle
@@ -134,20 +134,16 @@ android {
                     exclude 'com/google/android/exoplayer2/**'
                     exclude 'org/mozilla/gecko/media/GeckoHlsAudioRenderer.java'
                     exclude 'org/mozilla/gecko/media/GeckoHlsPlayer.java'
                     exclude 'org/mozilla/gecko/media/GeckoHlsRendererBase.java'
                     exclude 'org/mozilla/gecko/media/GeckoHlsVideoRenderer.java'
                     exclude 'org/mozilla/gecko/media/Utils.java'
                 }
 
-                if (!mozconfig.substs.MOZ_CRASHREPORTER) {
-                    exclude 'org/mozilla/gecko/CrashReporterService.java'
-                }
-
                 if (mozconfig.substs.MOZ_WEBRTC) {
                     srcDir "${topsrcdir}/media/webrtc/trunk/webrtc/base/java/src"
                     srcDir "${topsrcdir}/media/webrtc/trunk/webrtc/modules/audio_device/android/java/src"
                     srcDir "${topsrcdir}/media/webrtc/trunk/webrtc/modules/video_capture/android/java/src"
                 }
             }
 
             assets {
--- 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,20 +115,16 @@ 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,20 +842,17 @@ 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())
-                    .nativeCrashReportingEnabled(true)
-                    .javaCrashReportingEnabled(true);
-
+                                  .extras(InstrumentationRegistry.getArguments());
             sRuntime = GeckoRuntime.create(
                 InstrumentationRegistry.getTargetContext(),
                 runtimeSettingsBuilder.build());
         }
 
         sRuntime.getSettings().setRemoteDebuggingEnabled(mWithDevTools);
 
         mMainSession = new GeckoSession(settings);
--- a/mobile/android/geckoview/src/main/AndroidManifest.xml
+++ b/mobile/android/geckoview/src/main/AndroidManifest.xml
@@ -1,100 +1,73 @@
-<?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="org.mozilla.geckoview">
+    package="org.mozilla.geckoview">
 
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
     <uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"/>
+
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
     <uses-permission android:name="android.permission.VIBRATE"/>
 
-    <uses-feature
-            android:name="android.hardware.location"
-            android:required="false"/>
-    <uses-feature
-            android:name="android.hardware.location.gps"
-            android:required="false"/>
+    <uses-feature android:name="android.hardware.location" android:required="false"/>
+    <uses-feature android:name="android.hardware.location.gps" android:required="false"/>
     <uses-feature android:name="android.hardware.touchscreen"/>
 
-    <uses-permission android:name="android.permission.CAMERA"/>
-
-    <uses-feature
-            android:name="android.hardware.camera"
-            android:required="false"/>
-    <uses-feature
-            android:name="android.hardware.camera.autofocus"
-            android:required="false"/>
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-feature android:name="android.hardware.camera" android:required="false"/>
+    <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
 
-    <!-- #ifdef MOZ_WEBRTC -->
-    <!--
-         TODO preprocess AndroidManifest.xml so that we can
-         conditionally include WebRTC permissions based on MOZ_WEBRTC.
-    -->
-    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
-    -->
-    <uses-feature
-            android:name="android.hardware.audio.low_latency"
-            android:required="false"/>
-    -->
-    <uses-feature
-            android:name="android.hardware.microphone"
-            android:required="false"/>
-    -->
-    <uses-feature
-            android:name="android.hardware.camera.any"
-            android:required="false"/>
-    -->
-    <!-- #endif -->
-
+    <!--#ifdef MOZ_WEBRTC-->
+    <!-- TODO preprocess AndroidManifest.xml so that we can
+         conditionally include WebRTC permissions based on MOZ_WEBRTC. -->
+    <uses-permission android:name="android.permission.RECORD_AUDIO"/>-->
+    <uses-feature android:name="android.hardware.audio.low_latency" android:required="false"/>-->
+    <uses-feature android:name="android.hardware.microphone" android:required="false"/>-->
+    <uses-feature android:name="android.hardware.camera.any" android:required="false"/>-->
+    <!--#endif-->
 
     <!-- App requires OpenGL ES 2.0 -->
-    <uses-feature
-            android:glEsVersion="0x00020000"
-            android:required="true"/>
+    <uses-feature android:glEsVersion="0x00020000" android:required="true" />
 
     <application>
-
         <!-- New child services must also be added to the Fennec AndroidManifest.xml.in -->
         <service
-                android:name="org.mozilla.gecko.media.MediaManager"
-                android:enabled="true"
-                android:exported="false"
-                android:isolatedProcess="false"
-                android:process=":media">
+            android:name="org.mozilla.gecko.media.MediaManager"
+            android:enabled="true"
+            android:exported="false"
+            android:process=":media"
+            android:isolatedProcess="false">
         </service>
+
         <service
-                android:name="org.mozilla.gecko.process.GeckoServiceChildProcess$geckomediaplugin"
-                android:enabled="true"
-                android:exported="false"
-                android:isolatedProcess="false"
-                android:process=":geckomediaplugin">
+            android:name="org.mozilla.gecko.process.GeckoServiceChildProcess$geckomediaplugin"
+            android:enabled="true"
+            android:exported="false"
+            android:process=":geckomediaplugin"
+            android:isolatedProcess="false">
         </service>
+
         <service
-                android:name="org.mozilla.gecko.process.GeckoServiceChildProcess$tab"
-                android:enabled="true"
-                android:exported="false"
-                android:isolatedProcess="false"
-                android:process=":tab">
+            android:name="org.mozilla.gecko.process.GeckoServiceChildProcess$tab"
+            android:enabled="true"
+            android:exported="false"
+            android:process=":tab"
+            android:isolatedProcess="false">
         </service>
+
         <service
-                android:name="org.mozilla.gecko.gfx.SurfaceAllocatorService"
-                android:enabled="true"
-                android:exported="false"
-                android:isolatedProcess="false">
+            android:name="org.mozilla.gecko.gfx.SurfaceAllocatorService"
+            android:enabled="true"
+            android:exported="false"
+            android:isolatedProcess="false">
         </service>
-        <service
-                android:name="org.mozilla.gecko.CrashReporterService"
-                android:exported="false"
-                android:process=":crashreporter">
-        </service>
-    </application>
+   </application>
 
-</manifest>
\ No newline at end of file
+</manifest>
--- 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, int flags,
+    boolean start(in IProcessManager procMan, in String[] args, in Bundle extras,
                   in ParcelFileDescriptor prefsPfd, in ParcelFileDescriptor ipcPfd,
                   in ParcelFileDescriptor crashReporterPfd,
                   in ParcelFileDescriptor crashAnnotationPfd);
 
     void crash();
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/CrashHandler.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/CrashHandler.java
@@ -292,25 +292,26 @@ public class CrashHandler implements Thr
      * @param extraFile Path for the crash extra file
      * @return Whether the crash reporter was successfully launched
      */
     protected boolean launchCrashReporter(final String dumpFile, final String extraFile) {
         try {
             final Context context = getAppContext();
             final String javaPkg = getJavaPackageName();
             final String pkg = getAppPackageName();
-            final String component = javaPkg + ".CrashReporterService";
+            final String component = javaPkg + ".CrashReporter";
             final String action = javaPkg + ".reportCrash";
             final ProcessBuilder pb;
 
             if (context != null) {
                 final Intent intent = new Intent(action);
                 intent.setComponent(new ComponentName(pkg, component));
                 intent.putExtra("minidumpPath", dumpFile);
-                context.startService(intent);
+                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                context.startActivity(intent);
                 return true;
             }
 
             if (Build.VERSION.SDK_INT < 17) {
                 pb = new ProcessBuilder(
                     "/system/bin/am", "start",
                     "-a", action,
                     "-n", pkg + '/' + component,
deleted file mode 100644
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/CrashReporterService.java
+++ /dev/null
@@ -1,373 +0,0 @@
-package org.mozilla.gecko;
-
-import org.mozilla.gecko.mozglue.GeckoLoader;
-import org.mozilla.gecko.mozglue.MinidumpAnalyzer;
-import org.mozilla.gecko.util.INIParser;
-import org.mozilla.gecko.util.INISection;
-import org.mozilla.gecko.util.ProxySelector;
-
-import android.app.IntentService;
-import android.content.Intent;
-import android.os.Build;
-import android.util.Log;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.net.URLDecoder;
-import java.nio.channels.Channels;
-import java.nio.channels.FileChannel;
-import java.security.MessageDigest;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.zip.GZIPOutputStream;
-
-public class CrashReporterService extends IntentService {
-    private static final String LOGTAG = "CrashReporter";
-    private static final String ACTION_REPORT_CRASH = "org.mozilla.gecko.reportCrash";
-    private static final String PASSED_MINI_DUMP_KEY = "minidumpPath";
-    private static final String PASSED_MINI_DUMP_SUCCESS_KEY = "minidumpSuccess";
-    private static final String MINI_DUMP_PATH_KEY = "upload_file_minidump";
-    private static final String PAGE_URL_KEY = "URL";
-    private static final String NOTES_KEY = "Notes";
-    private static final String SERVER_URL_KEY = "ServerURL";
-
-    private static final String CRASH_REPORT_SUFFIX = "/mozilla/Crash Reports/";
-    private static final String PENDING_SUFFIX = CRASH_REPORT_SUFFIX + "pending";
-    private static final String SUBMITTED_SUFFIX = CRASH_REPORT_SUFFIX + "submitted";
-
-    private File mPendingMinidumpFile;
-    private File mPendingExtrasFile;
-    private HashMap<String, String> mExtrasStringMap;
-    private boolean mMinidumpSucceeded;
-
-    public CrashReporterService() {
-        super("CrashReporterService");
-    }
-
-    @Override
-    protected void onHandleIntent(Intent intent) {
-        if (intent == null || !intent.getAction().equals(ACTION_REPORT_CRASH)) {
-            Log.d(LOGTAG, "Invalid or unknown action");
-            return;
-        }
-
-        Class<?> reporterActivityCls = getFennecReporterActivity();
-        if (reporterActivityCls != null) {
-            intent.setClass(this, reporterActivityCls);
-            startActivity(intent);
-            return;
-        }
-
-        submitCrash(intent);
-    }
-
-    private Class<?> getFennecReporterActivity() {
-        try {
-            return Class.forName("org.mozilla.gecko.CrashReporterActivity");
-        } catch (ClassNotFoundException e) {
-            return null;
-        }
-    }
-
-    private boolean moveFile(File inFile, File outFile) {
-        Log.i(LOGTAG, "moving " + inFile + " to " + outFile);
-        if (inFile.renameTo(outFile))
-            return true;
-        try {
-            outFile.createNewFile();
-            Log.i(LOGTAG, "couldn't rename minidump file");
-            // so copy it instead
-            FileChannel inChannel = new FileInputStream(inFile).getChannel();
-            FileChannel outChannel = new FileOutputStream(outFile).getChannel();
-            long transferred = inChannel.transferTo(0, inChannel.size(), outChannel);
-            inChannel.close();
-            outChannel.close();
-
-            if (transferred > 0)
-                inFile.delete();
-        } catch (Exception e) {
-            Log.e(LOGTAG, "exception while copying minidump file: ", e);
-            return false;
-        }
-        return true;
-    }
-
-    private void submitCrash(Intent intent) {
-        mMinidumpSucceeded = intent.getBooleanExtra(PASSED_MINI_DUMP_SUCCESS_KEY, false);
-        if (!mMinidumpSucceeded) {
-            Log.i(LOGTAG, "Failed to get minidump.");
-        }
-        String passedMinidumpPath = intent.getStringExtra(PASSED_MINI_DUMP_KEY);
-        File passedMinidumpFile = new File(passedMinidumpPath);
-        File pendingDir = new File(getFilesDir(), PENDING_SUFFIX);
-        pendingDir.mkdirs();
-        mPendingMinidumpFile = new File(pendingDir, passedMinidumpFile.getName());
-        moveFile(passedMinidumpFile, mPendingMinidumpFile);
-
-        File extrasFile = new File(passedMinidumpPath.replaceAll("\\.dmp", ".extra"));
-        mPendingExtrasFile = new File(pendingDir, extrasFile.getName());
-        moveFile(extrasFile, mPendingExtrasFile);
-
-        // Compute the minidump hash and generate the stack traces
-        computeMinidumpHash(mPendingExtrasFile, mPendingMinidumpFile);
-
-        try {
-            GeckoLoader.loadMozGlue(this);
-
-            if (!MinidumpAnalyzer.GenerateStacks(mPendingMinidumpFile.getPath(), /* fullStacks */ false)) {
-                Log.e(LOGTAG, "Could not generate stacks for this minidump: " + passedMinidumpPath);
-            }
-        } catch (UnsatisfiedLinkError e) {
-            Log.e(LOGTAG, "Could not load libmozglue.so, stacks for this crash won't be generated");
-        }
-
-        // Extract the annotations from the .extra file
-        mExtrasStringMap = new HashMap<String, String>();
-        readStringsFromFile(mPendingExtrasFile.getPath(), mExtrasStringMap);
-
-        try {
-            // Find the profile name and path. Since we don't have any other way of getting it within
-            // this context we extract it from the crash dump path.
-            final File profileDir = passedMinidumpFile.getParentFile().getParentFile();
-            final String profileName = getProfileName(profileDir);
-
-            if (profileName != null) {
-                // Extract the crash dump ID and telemetry client ID, we need profile access for the latter.
-                final String passedMinidumpName = passedMinidumpFile.getName();
-                // Strip the .dmp suffix from the minidump name to obtain the crash ID.
-                final String crashId = passedMinidumpName.substring(0, passedMinidumpName.length() - 4);
-                final GeckoProfile profile = GeckoProfile.get(this, profileName, profileDir);
-                final String clientId = profile.getClientId();
-            }
-        } catch (GeckoProfileDirectories.NoMozillaDirectoryException | IOException e) {
-            Log.e(LOGTAG, "Cannot send the crash ping: ", e);
-        }
-
-        // Notify GeckoApp that we've crashed, so it can react appropriately during the next start.
-        try {
-            File crashFlag = new File(GeckoProfileDirectories.getMozillaDirectory(this), "CRASHED");
-            crashFlag.createNewFile();
-        } catch (GeckoProfileDirectories.NoMozillaDirectoryException | IOException e) {
-            Log.e(LOGTAG, "Cannot set crash flag: ", e);
-        }
-
-        sendReport(mPendingMinidumpFile, mExtrasStringMap, mPendingExtrasFile);
-    }
-
-
-    private String getProfileName(File profileDir) throws GeckoProfileDirectories.NoMozillaDirectoryException {
-        final File mozillaDir = GeckoProfileDirectories.getMozillaDirectory(this);
-        final INIParser parser = GeckoProfileDirectories.getProfilesINI(mozillaDir);
-        String profileName = null;
-
-        if (parser.getSections() != null) {
-            for (Enumeration<INISection> e = parser.getSections().elements(); e.hasMoreElements(); ) {
-                final INISection section = e.nextElement();
-                final String path = section.getStringProperty("Path");
-                final boolean isRelative = (section.getIntProperty("IsRelative") == 1);
-
-                if ((isRelative && path.equals(profileDir.getName())) ||
-                        path.equals(profileDir.getPath())) {
-                    profileName = section.getStringProperty("Name");
-                    break;
-                }
-            }
-        }
-
-        return profileName;
-    }
-
-
-    private void computeMinidumpHash(File extraFile, File minidump) {
-        try {
-            FileInputStream stream = new FileInputStream(minidump);
-            MessageDigest md = MessageDigest.getInstance("SHA-256");
-
-            try {
-                byte[] buffer = new byte[4096];
-                int readBytes;
-
-                while ((readBytes = stream.read(buffer)) != -1) {
-                    md.update(buffer, 0, readBytes);
-                }
-            } finally {
-                stream.close();
-            }
-
-            byte[] digest = md.digest();
-            StringBuilder hash = new StringBuilder(84);
-
-            hash.append("MinidumpSha256Hash=");
-
-            for (int i = 0; i < digest.length; i++) {
-                hash.append(Integer.toHexString((digest[i] & 0xf0) >> 4));
-                hash.append(Integer.toHexString(digest[i] & 0x0f));
-            }
-
-            hash.append('\n');
-
-            FileWriter writer = new FileWriter(extraFile, /* append */ true);
-
-            try {
-                writer.write(hash.toString());
-            } finally {
-                writer.close();
-            }
-        } catch (Exception e) {
-            Log.e(LOGTAG, "exception while computing the minidump hash: ", e);
-        }
-    }
-
-    private boolean readStringsFromFile(String filePath, Map<String, String> stringMap) {
-        try {
-            BufferedReader reader = new BufferedReader(new FileReader(filePath));
-            return readStringsFromReader(reader, stringMap);
-        } catch (Exception e) {
-            Log.e(LOGTAG, "exception while reading strings: ", e);
-            return false;
-        }
-    }
-
-    private boolean readStringsFromReader(BufferedReader reader, Map<String, String> stringMap) throws IOException {
-        String line;
-        while ((line = reader.readLine()) != null) {
-            int equalsPos = -1;
-            if ((equalsPos = line.indexOf('=')) != -1) {
-                String key = line.substring(0, equalsPos);
-                String val = unescape(line.substring(equalsPos + 1));
-                stringMap.put(key, val);
-            }
-        }
-        reader.close();
-        return true;
-    }
-
-    private String generateBoundary() {
-        // Generate some random numbers to fill out the boundary
-        int r0 = (int)(Integer.MAX_VALUE * Math.random());
-        int r1 = (int)(Integer.MAX_VALUE * Math.random());
-        return String.format("---------------------------%08X%08X", r0, r1);
-    }
-
-    private void sendPart(OutputStream os, String boundary, String name, String data) {
-        try {
-            os.write(("--" + boundary + "\r\n" +
-                    "Content-Disposition: form-data; name=\"" + name + "\"\r\n" +
-                    "\r\n" +
-                    data + "\r\n"
-            ).getBytes());
-        } catch (Exception ex) {
-            Log.e(LOGTAG, "Exception when sending \"" + name + "\"", ex);
-        }
-    }
-
-    private void sendFile(OutputStream os, String boundary, String name, File file) throws IOException {
-        os.write(("--" + boundary + "\r\n" +
-                "Content-Disposition: form-data; name=\"" + name + "\"; " +
-                "filename=\"" + file.getName() + "\"\r\n" +
-                "Content-Type: application/octet-stream\r\n" +
-                "\r\n"
-        ).getBytes());
-        FileChannel fc = new FileInputStream(file).getChannel();
-        fc.transferTo(0, fc.size(), Channels.newChannel(os));
-        fc.close();
-    }
-
-    private void sendReport(File minidumpFile, Map<String, String> extras, File extrasFile) {
-        Log.i(LOGTAG, "sendReport: " + minidumpFile.getPath());
-
-        String spec = extras.get(SERVER_URL_KEY);
-        if (spec == null) {
-            return;
-        }
-
-        try {
-            final URL url = new URL(URLDecoder.decode(spec, "UTF-8"));
-            final URI uri = new URI(url.getProtocol(), url.getUserInfo(),
-                    url.getHost(), url.getPort(),
-                    url.getPath(), url.getQuery(), url.getRef());
-            HttpURLConnection conn = (HttpURLConnection) ProxySelector.openConnectionWithProxy(uri);
-            conn.setRequestMethod("POST");
-            String boundary = generateBoundary();
-            conn.setDoOutput(true);
-            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
-            conn.setRequestProperty("Content-Encoding", "gzip");
-
-            OutputStream os = new GZIPOutputStream(conn.getOutputStream());
-            for (String key : extras.keySet()) {
-                if (!key.equals(SERVER_URL_KEY) && !key.equals(NOTES_KEY)) {
-                    sendPart(os, boundary, key, extras.get(key));
-                }
-            }
-
-            StringBuilder sb = new StringBuilder();
-            sb.append(extras.containsKey(NOTES_KEY) ? extras.get(NOTES_KEY) + "\n" : "");
-            sb.append(Build.MANUFACTURER).append(' ')
-                    .append(Build.MODEL).append('\n')
-                    .append(Build.FINGERPRINT);
-            sendPart(os, boundary, NOTES_KEY, sb.toString());
-
-            sendPart(os, boundary, "Android_Manufacturer", Build.MANUFACTURER);
-            sendPart(os, boundary, "Android_Model", Build.MODEL);
-            sendPart(os, boundary, "Android_Board", Build.BOARD);
-            sendPart(os, boundary, "Android_Brand", Build.BRAND);
-            sendPart(os, boundary, "Android_Device", Build.DEVICE);
-            sendPart(os, boundary, "Android_Display", Build.DISPLAY);
-            sendPart(os, boundary, "Android_Fingerprint", Build.FINGERPRINT);
-            sendPart(os, boundary, "Android_CPU_ABI", Build.CPU_ABI);
-            try {
-                sendPart(os, boundary, "Android_CPU_ABI2", Build.CPU_ABI2);
-                sendPart(os, boundary, "Android_Hardware", Build.HARDWARE);
-            } catch (Exception ex) {
-                Log.e(LOGTAG, "Exception while sending SDK version 8 keys", ex);
-            }
-            sendPart(os, boundary, "Android_Version",  Build.VERSION.SDK_INT + " (" + Build.VERSION.CODENAME + ")");
-            sendPart(os, boundary, PASSED_MINI_DUMP_SUCCESS_KEY, mMinidumpSucceeded ? "True" : "False");
-            sendFile(os, boundary, MINI_DUMP_PATH_KEY, minidumpFile);
-            os.write(("\r\n--" + boundary + "--\r\n").getBytes());
-            os.flush();
-            os.close();
-            BufferedReader br = new BufferedReader(
-                    new InputStreamReader(conn.getInputStream()));
-            HashMap<String, String>  responseMap = new HashMap<String, String>();
-            readStringsFromReader(br, responseMap);
-
-            if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
-                File submittedDir = new File(getFilesDir(),
-                        SUBMITTED_SUFFIX);
-                submittedDir.mkdirs();
-                minidumpFile.delete();
-                extrasFile.delete();
-                String crashid = responseMap.get("CrashID");
-                File file = new File(submittedDir, crashid + ".txt");
-                FileOutputStream fos = new FileOutputStream(file);
-                fos.write("Crash ID: ".getBytes());
-                fos.write(crashid.getBytes());
-                fos.close();
-                Log.i(LOGTAG, "Successfully sent crash report: " + crashid);
-            } else {
-                Log.w(LOGTAG, "Received failure HTTP response code from server: " + conn.getResponseCode());
-            }
-        } catch (IOException e) {
-            Log.e(LOGTAG, "exception during send: ", e);
-        } catch (URISyntaxException e) {
-            Log.e(LOGTAG, "exception during new URI: ", e);
-        }
-    }
-
-    private String unescape(String string) {
-        return string.replaceAll("\\\\\\\\", "\\").replaceAll("\\\\n", "\n").replaceAll("\\\\t", "\t");
-    }
-}
--- 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 class GeckoCrashHandler extends CrashHandler {
+    private static final CrashHandler CRASH_HANDLER = new CrashHandler() {
         @Override
         protected String getAppPackageName() {
             final Context appContext = getAppContext();
             if (appContext == null) {
                 return "<unknown>";
             }
             return appContext.getPackageName();
         }
@@ -169,28 +169,20 @@ 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 synchronized CrashHandler ensureCrashHandling() {
-        if (sCrashHandler == null) {
-            sCrashHandler = new GeckoCrashHandler();
-        }
-
-        return sCrashHandler;
-    }
-
-    public static synchronized boolean isCrashHandlingEnabled() {
-        return sCrashHandler != null;
+    public static CrashHandler ensureCrashHandling() {
+        // Crash handling is automatically enabled when GeckoAppShell is loaded.
+        return CRASH_HANDLER;
     }
 
     @WrapForJNI(exceptionMode = "ignore")
     /* package */ static synchronized String getAppNotes() {
         return sAppNotes;
     }
 
     public static synchronized void appendAppNotesToCrashReport(final String notes) {
@@ -283,20 +275,18 @@ public class GeckoAppShell
      */
 
     @WrapForJNI(exceptionMode = "ignore")
     private static String getExceptionStackTrace(Throwable e) {
         return CrashHandler.getExceptionStackTrace(CrashHandler.getRootException(e));
     }
 
     @WrapForJNI(exceptionMode = "ignore")
-    private static synchronized void handleUncaughtException(Throwable e) {
-        if (sCrashHandler != null) {
-            sCrashHandler.uncaughtException(null, e);
-        }
+    private static void handleUncaughtException(Throwable e) {
+        CRASH_HANDLER.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
@@ -7,36 +7,35 @@ package org.mozilla.gecko;
 
 import org.mozilla.gecko.annotation.RobocopTarget;
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.mozglue.GeckoLoader;
 import org.mozilla.gecko.process.GeckoProcessManager;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.FileUtils;
 import org.mozilla.gecko.util.ThreadUtils;
-import org.mozilla.geckoview.BuildConfig;
 
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.Bundle;
 import android.os.Handler;
 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 {
     private static final String LOGTAG = "GeckoThread";
 
     public enum State implements NativeQueue.State {
         // After being loaded by class loader.
@@ -120,22 +119,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 << 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
+    public static final int FLAG_DEBUGGING = 1; // Debugging mode.
+    public static final int FLAG_PRELOAD_CHILD = 2; // Preload child during main thread start.
 
-    /* package */ static final String EXTRA_ARGS = "args";
+    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;
     private GeckoProfile mProfile;
     private String[] mArgs;
@@ -179,24 +176,21 @@ 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 flags,
-                                           final int prefsFd,
-                                           final int ipcFd,
+    public static boolean initChildProcess(final String[] args, final Bundle extras,
+                                           final int prefsFd, final int ipcFd,
                                            final int crashFd,
                                            final int crashAnnotationFd) {
-        return INSTANCE.init(/* profile */ null, args, extras, flags,
+        return INSTANCE.init(/* profile */ null, args, extras, /* flags */ 0,
                              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;
         }
@@ -378,50 +372,20 @@ public class GeckoThread extends Thread 
         return mProfile;
     }
 
     public static @Nullable Bundle getActiveExtras() {
         synchronized (INSTANCE) {
             if (!INSTANCE.mInitialized) {
                 return null;
             }
-            return new Bundle(INSTANCE.mExtras);
-        }
-    }
-
-    public static int getActiveFlags() {
-        synchronized (INSTANCE) {
-            if (!INSTANCE.mInitialized) {
-                return 0;
-            }
-
-            return INSTANCE.mFlags;
+            return INSTANCE.mExtras;
         }
     }
 
-    private static ArrayList<String> getEnvFromExtras(final Bundle extras) {
-        if (extras == null) {
-            return new ArrayList<>();
-        }
-
-        ArrayList<String> result = new ArrayList<>();
-        if (extras != null) {
-            String env = extras.getString("env0");
-            for (int c = 1; env != null; c++) {
-                if (BuildConfig.DEBUG) {
-                    Log.d(LOGTAG, "env var: " + env);
-                }
-                result.add(env);
-                env = extras.getString("env" + c);
-            }
-        }
-
-        return result;
-    }
-
     @Override
     public void run() {
         Log.i(LOGTAG, "preparing to run Gecko");
 
         Looper.prepare();
         GeckoThread.msgQueue = Looper.myQueue();
         ThreadUtils.sGeckoThread = this;
         ThreadUtils.sGeckoHandler = new Handler();
@@ -473,31 +437,17 @@ 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 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);
+        GeckoLoader.setupGeckoEnvironment(context, context.getFilesDir().getPath(), mExtras);
 
         // 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/mozglue/GeckoLoader.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/GeckoLoader.java
@@ -3,17 +3,16 @@
  * 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.gecko.mozglue;
 
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.InputStream;
-import java.util.Collection;
 import java.util.Locale;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
 import android.content.Context;
 import android.content.Intent;
 import android.os.Build;
 import android.os.Bundle;
@@ -88,19 +87,27 @@ public final class GeckoLoader {
         if (oldDir.exists()) {
             delTree(oldDir);
         }
         return tmpDir;
     }
 
     public synchronized static void setupGeckoEnvironment(final Context context,
                                                           final String profilePath,
-                                                          final Collection<String> env) {
-        for (final String e : env) {
-            putenv(e);
+                                                          final Bundle extras) {
+        // if we have an intent (we're being launched by an activity)
+        // read in any environmental variables from it here
+        if (extras != null) {
+            String env = extras.getString("env0");
+            Log.d(LOGTAG, "Gecko environment env0: " + env);
+            for (int c = 1; env != null; c++) {
+                putenv(env);
+                env = extras.getString("env" + c);
+                Log.d(LOGTAG, "env" + c + ": " + env);
+            }
         }
 
         try {
             final File dataDir = new File(context.getApplicationInfo().dataDir);
             putenv("MOZ_ANDROID_DATA_DIR=" + dataDir.getCanonicalPath());
         } catch (final java.io.IOException e) {
             Log.e(LOGTAG, "Failed to resolve app data directory");
         }
--- 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,21 +176,16 @@ 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;
         }
@@ -205,21 +200,19 @@ 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, flags, prefsPfd, ipcPfd, crashPfd,
+            started = child.start(this, args, extras, 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,17 +19,19 @@ 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) {
@@ -37,16 +39,17 @@ 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;
     }
 
@@ -55,17 +58,16 @@ 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;
@@ -78,17 +80,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, flags, prefsFd, ipcFd, crashReporterFd,
+                    if (GeckoThread.initChildProcess(args, extras, 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,33 +79,19 @@ public final class GeckoRuntime implemen
             }
         }
     };
 
     /* package */ boolean init(final @NonNull GeckoRuntimeSettings settings) {
         if (DEBUG) {
             Log.d(LOGTAG, "init");
         }
-        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 (settings.getPauseForDebuggerEnabled()) {
-            flags |= GeckoThread.FLAG_DEBUGGING;
-        }
-
+        final int flags = settings.getUseContentProcessHint()
+                          ? GeckoThread.FLAG_PRELOAD_CHILD
+                          : 0;
         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,19 +35,17 @@ 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.
@@ -76,17 +74,16 @@ 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;
         }
 
         /**
@@ -99,64 +96,22 @@ 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;
-        }
-
-        /**
-         * Set whether there should be a pause during startup. This is useful if you need to
-         * wait for a debugger to attach.
-         *
-         * @param enabled A flag determining whether there will be a pause early in startup.
-         *                Defaults to false.
-         * @return This Builder.
-         */
-        public @NonNull Builder pauseForDebugger(boolean enabled) {
-            mSettings.mDebugPause = enabled;
-            return this;
-        }
     }
 
     /* package */ GeckoRuntime runtime;
     /* package */ boolean mUseContentProcess;
     /* package */ String[] mArgs;
     /* package */ Bundle mExtras;
     /* package */ int prefCount;
 
@@ -190,19 +145,16 @@ 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;
-    /* package */ boolean mDebugPause;
 
     private final Pref<?>[] mPrefs = new Pref<?>[] {
         mJavaScript, mRemoteDebugging, mWebFonts
     };
 
     /* package */ GeckoRuntimeSettings() {
         this(null);
     }
@@ -223,19 +175,16 @@ 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();
         }
     }
 
@@ -321,77 +270,44 @@ 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;
-    }
-
-    /**
-     * Gets whether the pause-for-debugger is enabled or not.
-     *
-     * @return True if the pause is enabled.
-     */
-    public boolean getPauseForDebuggerEnabled() { return mDebugPause; }
-
     @Override // Parcelable
     public int describeContents() {
         return 0;
     }
 
     @Override // Parcelable
     public void writeToParcel(Parcel out, int flags) {
-        ParcelableUtils.writeBoolean(out, mUseContentProcess);
+        out.writeByte((byte) (mUseContentProcess ? 1 : 0));
         out.writeStringArray(mArgs);
         mExtras.writeToParcel(out, flags);
 
         for (final Pref<?> pref : mPrefs) {
             out.writeValue(pref.get());
         }
-
-        ParcelableUtils.writeBoolean(out, mNativeCrashReporting);
-        ParcelableUtils.writeBoolean(out, mJavaCrashReporting);
-        ParcelableUtils.writeBoolean(out, mDebugPause);
     }
 
     // AIDL code may call readFromParcel even though it's not part of Parcelable.
     public void readFromParcel(final Parcel source) {
-        mUseContentProcess = ParcelableUtils.readBoolean(source);
+        mUseContentProcess = source.readByte() == 1;
         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);
-        mDebugPause = 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);
deleted file mode 100644
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/ParcelableUtils.java
+++ /dev/null
@@ -1,19 +0,0 @@
-/* -*- 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,22 +62,17 @@ 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)
-                    .nativeCrashReportingEnabled(true)
-                    .javaCrashReportingEnabled(true);
-
+            runtimeSettingsBuilder.useContentProcessHint(useMultiprocess);
             sGeckoRuntime = GeckoRuntime.create(this, runtimeSettingsBuilder.build());
         }
 
         final GeckoSessionSettings sessionSettings = new GeckoSessionSettings();
         sessionSettings.setBoolean(GeckoSessionSettings.USE_MULTIPROCESS,
                                    useMultiprocess);
         mGeckoSession = new GeckoSession(sessionSettings);
 
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -835,27 +835,27 @@ LaunchCrashReporterActivity(XP_CHAR* aPr
 
   if (pid == -1)
     return false;
   else if (pid == 0) {
     // Invoke the reportCrash activity using am
     if (androidUserSerial) {
       Unused << execlp("/system/bin/am",
                        "/system/bin/am",
-                       "startservice",
+                       "start",
                        "--user", androidUserSerial,
                        "-a", "org.mozilla.gecko.reportCrash",
                        "-n", aProgramPath,
                        "--es", "minidumpPath", aMinidumpPath,
                        "--ez", "minidumpSuccess", aSucceeded ? "true" : "false",
                        (char*)0);
     } else {
       Unused << execlp("/system/bin/am",
                        "/system/bin/am",
-                       "startservice",
+                       "start",
                        "-a", "org.mozilla.gecko.reportCrash",
                        "-n", aProgramPath,
                        "--es", "minidumpPath", aMinidumpPath,
                        "--ez", "minidumpSuccess", aSucceeded ? "true" : "false",
                        (char*)0);
     }
     _exit(1);
 
@@ -1539,20 +1539,20 @@ nsresult SetExceptionHandler(nsIFile* aX
 #endif // XP_WIN32
 #else
   // On Android, we launch using the application package name instead of a
   // filename, so use the dynamically set MOZ_ANDROID_PACKAGE_NAME, or fall
   // back to the static ANDROID_PACKAGE_NAME.
   const char* androidPackageName = PR_GetEnv("MOZ_ANDROID_PACKAGE_NAME");
   if (androidPackageName != nullptr) {
     nsCString package(androidPackageName);
-    package.AppendLiteral("/org.mozilla.gecko.CrashReporterService");
+    package.AppendLiteral("/org.mozilla.gecko.CrashReporter");
     crashReporterPath = ToNewCString(package);
   } else {
-    nsCString package(ANDROID_PACKAGE_NAME "/org.mozilla.gecko.CrashReporterService");
+    nsCString package(ANDROID_PACKAGE_NAME "/org.mozilla.gecko.CrashReporter");
     crashReporterPath = ToNewCString(package);
   }
 #endif // !defined(MOZ_WIDGET_ANDROID)
 
   // get temp path to use for minidump path
 #if defined(XP_WIN32)
   nsString tempPath;
 #else
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -402,17 +402,16 @@ 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()) {