commit 96a3d28b3f9368e5d9333b04ccff020d4fa27d2b
parent 943d62471a99c72daf644b74aef288d729746a3d
Author: Makoto Kato <m_kato@ga2.so-net.ne.jp>
Date: Tue, 25 Nov 2025 00:35:31 +0000
Bug 2000513 - Clean up duplicated crash reporter launcher. r=geckoview-reviewers,tcampbell
Currently, we have two crash launcher in `GeckoRuntime` and `CrashHandler`,
so this fix merge both with one.
Also, `Context.startForegroundService` might throws
`ForegroundServiceStartNotAllowedException` if Android 12+. So we should
handle it instead of crashing parent process.
Differential Revision: https://phabricator.services.mozilla.com/D272835
Diffstat:
3 files changed, 100 insertions(+), 47 deletions(-)
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/CrashHandler.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/CrashHandler.java
@@ -6,12 +6,14 @@
package org.mozilla.geckoview;
import android.annotation.SuppressLint;
+import android.app.ForegroundServiceStartNotAllowedException;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Process;
import android.util.Log;
@@ -26,11 +28,14 @@ import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.UUID;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.GeckoAppShell;
+import org.mozilla.gecko.util.GeckoBundle;
/** Handler for processing application crashes and sending crash reports. */
public class CrashHandler implements Thread.UncaughtExceptionHandler {
@@ -351,57 +356,90 @@ public class CrashHandler implements Thread.UncaughtExceptionHandler {
@AnyThread
public boolean launchCrashReporter(
@NonNull final String dumpFile, @NonNull final String extraFile) {
- try {
- final Context context = getAppContext();
+ if (mHandlerService == null) {
+ Log.w(LOGTAG, "No crash handler service defined, unable to report crash");
+ return false;
+ }
- if (mHandlerService == null) {
- Log.w(LOGTAG, "No crash handler service defined, unable to report crash");
- return false;
- }
+ return launchCrashReporter(
+ mHandlerService,
+ getAppContext(),
+ getAppPackageName(),
+ dumpFile,
+ extraFile,
+ GeckoRuntime.CRASHED_PROCESS_VISIBILITY_MAIN,
+ "main",
+ null);
+ }
+ @AnyThread
+ private static boolean launchCrashReporter(
+ @NonNull final Class<?> handlerService,
+ @Nullable final Context context,
+ @NonNull final String appPackageName,
+ @NonNull final String dumpFile,
+ @NonNull final String extraFile,
+ @NonNull final String visibility,
+ @NonNull final String processType,
+ @Nullable final String remoteType) {
+ try {
if (context != null) {
- final Intent intent = new Intent(GeckoRuntime.ACTION_CRASHED);
+ final Intent intent =
+ new Intent(GeckoRuntime.ACTION_CRASHED, null, context, handlerService);
intent.putExtra(GeckoRuntime.EXTRA_MINIDUMP_PATH, dumpFile);
intent.putExtra(GeckoRuntime.EXTRA_EXTRAS_PATH, extraFile);
- intent.putExtra(
- GeckoRuntime.EXTRA_CRASH_PROCESS_VISIBILITY,
- GeckoRuntime.CRASHED_PROCESS_VISIBILITY_MAIN);
- intent.putExtra(GeckoRuntime.EXTRA_CRASH_PROCESS_TYPE, "main");
- intent.setClass(context, mHandlerService);
-
+ intent.putExtra(GeckoRuntime.EXTRA_CRASH_PROCESS_VISIBILITY, visibility);
+ intent.putExtra(GeckoRuntime.EXTRA_CRASH_PROCESS_TYPE, processType);
+ if (remoteType != null) {
+ intent.putExtra(GeckoRuntime.EXTRA_CRASH_REMOTE_TYPE, remoteType);
+ }
context.startForegroundService(intent);
return true;
}
- final ProcessBuilder pb =
- new ProcessBuilder(
- "/system/bin/am",
- "start-foreground-service",
- "--user",
- /* USER_CURRENT_OR_SELF */ "-3",
- "-a",
- GeckoRuntime.ACTION_CRASHED,
- "-n",
- getAppPackageName() + '/' + mHandlerService.getName(),
- "--es",
- GeckoRuntime.EXTRA_MINIDUMP_PATH,
- dumpFile,
- "--es",
- GeckoRuntime.EXTRA_EXTRAS_PATH,
- extraFile,
- "--es",
- GeckoRuntime.EXTRA_CRASH_PROCESS_VISIBILITY,
- GeckoRuntime.CRASHED_PROCESS_VISIBILITY_MAIN,
- "--es",
- GeckoRuntime.EXTRA_CRASH_PROCESS_TYPE,
- "main");
-
- pb.start().waitFor();
+ final List<String> args =
+ new ArrayList<>(
+ Arrays.asList(
+ "/system/bin/am",
+ "start-foreground-service",
+ "--user",
+ /* USER_CURRENT_OR_SELF */ "-3",
+ "-a",
+ GeckoRuntime.ACTION_CRASHED,
+ "-n",
+ appPackageName + '/' + handlerService.getName(),
+ "--es",
+ GeckoRuntime.EXTRA_MINIDUMP_PATH,
+ dumpFile,
+ "--es",
+ GeckoRuntime.EXTRA_EXTRAS_PATH,
+ extraFile,
+ "--es",
+ GeckoRuntime.EXTRA_CRASH_PROCESS_VISIBILITY,
+ visibility,
+ "--es",
+ GeckoRuntime.EXTRA_CRASH_PROCESS_TYPE,
+ processType));
+ if (remoteType != null) {
+ args.add("--es");
+ args.add(GeckoRuntime.EXTRA_CRASH_REMOTE_TYPE);
+ args.add(remoteType);
+ }
+ final ProcessBuilder pb = new ProcessBuilder();
+ pb.command(args).start().waitFor();
} catch (final IOException e) {
Log.e(LOGTAG, "Error launching crash reporter", e);
return false;
+ } catch (final IllegalStateException e) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
+ && e instanceof ForegroundServiceStartNotAllowedException) {
+ Log.e(LOGTAG, "Error launching crash reporter", e);
+ return false;
+ }
+ throw e;
+
} catch (final InterruptedException e) {
Log.i(LOGTAG, "Interrupted while waiting to launch crash reporter", e);
// Fall-through
@@ -409,6 +447,20 @@ public class CrashHandler implements Thread.UncaughtExceptionHandler {
return true;
}
+ @AnyThread
+ /* package */ static boolean launchCrashReporter(
+ final Class<?> handlerService, final Context context, final GeckoBundle bundle) {
+ return launchCrashReporter(
+ handlerService,
+ context,
+ context.getPackageName(),
+ bundle.getString(GeckoRuntime.EXTRA_MINIDUMP_PATH),
+ bundle.getString(GeckoRuntime.EXTRA_EXTRAS_PATH),
+ bundle.getString(GeckoRuntime.EXTRA_CRASH_PROCESS_VISIBILITY),
+ bundle.getString(GeckoRuntime.EXTRA_CRASH_PROCESS_TYPE),
+ bundle.getString(GeckoRuntime.EXTRA_CRASH_REMOTE_TYPE));
+ }
+
/**
* Report an exception to Socorro.
*
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java
@@ -356,16 +356,10 @@ public final class GeckoRuntime implements Parcelable {
return null;
});
} else if ("GeckoView:ChildCrashReport".equals(event) && crashHandler != null) {
- final Context context = GeckoAppShell.getApplicationContext();
- final Intent i = new Intent(ACTION_CRASHED, null, context, crashHandler);
- i.putExtra(EXTRA_MINIDUMP_PATH, message.getString(EXTRA_MINIDUMP_PATH));
- i.putExtra(EXTRA_EXTRAS_PATH, message.getString(EXTRA_EXTRAS_PATH));
- i.putExtra(
- EXTRA_CRASH_PROCESS_VISIBILITY, message.getString(EXTRA_CRASH_PROCESS_VISIBILITY));
- i.putExtra(EXTRA_CRASH_PROCESS_TYPE, message.getString(EXTRA_CRASH_PROCESS_TYPE));
- i.putExtra(EXTRA_CRASH_REMOTE_TYPE, message.getString(EXTRA_CRASH_REMOTE_TYPE));
-
- context.startForegroundService(i);
+ CrashHandler.launchCrashReporter(
+ crashHandler, GeckoAppShell.getApplicationContext(), message);
+ // TODO(m_kato):
+ // If returning false, we cannot send crash data, should we re-try it later?
} else if ("GeckoView:ServiceWorkerOpenWindow".equals(event)) {
final String url = message.getString("url", "about:blank");
serviceWorkerOpenWindow(url)
diff --git a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/ExampleCrashHandler.java b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/ExampleCrashHandler.java
@@ -49,6 +49,13 @@ public class ExampleCrashHandler extends Service {
LOGTAG,
"Process Visibility: "
+ mCrashIntent.getStringExtra(GeckoRuntime.EXTRA_CRASH_PROCESS_VISIBILITY));
+ Log.d(
+ LOGTAG,
+ "Process Type: " + mCrashIntent.getStringExtra(GeckoRuntime.EXTRA_CRASH_PROCESS_TYPE));
+ final String remoteType = mCrashIntent.getStringExtra(GeckoRuntime.EXTRA_CRASH_REMOTE_TYPE);
+ if (remoteType != null) {
+ Log.d(LOGTAG, "Remote Type: " + remoteType);
+ }
String id = createNotificationChannel();