Backed out 3 changesets (Bug 1433968) for causing Bug 1459349. a=backout FIREFOX_NIGHTLY_61_END
authorNarcis Beleuzu <nbeleuzu@mozilla.com>
Mon, 07 May 2018 17:06:30 +0300
changeset 417109 93443d36d4bd53dba004f7b73430879f96daa681
parent 417108 0e52749acd376489bb551928d2012dde10bfc41b
child 417110 f286b7752b9639c3b294da44b22fe25c3b801919
child 417122 b83a4e36ddde0bc977fda734b97744dc4ad394b5
child 417166 aaf5e5f97ea32f654996047ef4b623e8ae5e40ce
push id33959
push usernbeleuzu@mozilla.com
push dateMon, 07 May 2018 14:09:41 +0000
treeherdermozilla-central@93443d36d4bd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1433968, 1459349
milestone61.0a1
backs out82b765c5a02febf502a638586d24d4065b37bb45
73ec9bb3e17ee1e54a10bcd17680f9e360849f70
0812ac0376daea88d8f19d6cf3cd43608a0a6475
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 3 changesets (Bug 1433968) for causing Bug 1459349. a=backout Backed out changeset 82b765c5a02f (bug 1433968) Backed out changeset 73ec9bb3e17e (bug 1433968) Backed out changeset 0812ac0376da (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/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/geckoview/build.gradle
+++ b/mobile/android/geckoview/build.gradle
@@ -130,20 +130,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,20 +119,18 @@ 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.
 
     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;
@@ -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,29 +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;
-        }
-
+        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,51 +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;
-        }
     }
 
     /* package */ GeckoRuntime runtime;
     /* package */ boolean mUseContentProcess;
     /* package */ String[] mArgs;
     /* package */ Bundle mExtras;
     /* package */ int prefCount;
 
@@ -177,18 +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;
 
     private final Pref<?>[] mPrefs = new Pref<?>[] {
         mJavaScript, mRemoteDebugging, mWebFonts
     };
 
     /* package */ GeckoRuntimeSettings() {
         this(null);
     }
@@ -209,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();
         }
     }
 
@@ -307,68 +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;
-    }
-
     @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);
     }
 
     // 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);
     }
 
     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
@@ -401,17 +401,16 @@ nsAppShell::nsAppShell()
 {
     {
         MutexAutoLock lock(*sAppShellLock);
         sAppShell = this;
     }
 
     if (!XRE_IsParentProcess()) {
         if (jni::IsAvailable()) {
-            GeckoAppShellSupport::Init();
             GeckoThreadSupport::Init();
 
             // Set the corresponding state in GeckoThread.
             java::GeckoThread::SetState(java::GeckoThread::State::RUNNING());
         }
         return;
     }