summaryrefslogtreecommitdiffstats
path: root/mobile/android/android-components/samples/crash
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/android-components/samples/crash')
-rw-r--r--mobile/android/android-components/samples/crash/build.gradle47
-rw-r--r--mobile/android/android-components/samples/crash/lint.xml12
-rw-r--r--mobile/android/android-components/samples/crash/proguard-rules.pro21
-rw-r--r--mobile/android/android-components/samples/crash/src/main/AndroidManifest.xml40
-rw-r--r--mobile/android/android-components/samples/crash/src/main/ic_launcher-web.pngbin0 -> 5650 bytes
-rw-r--r--mobile/android/android-components/samples/crash/src/main/java/org/mozilla/samples/crash/CrashActivity.kt168
-rw-r--r--mobile/android/android-components/samples/crash/src/main/java/org/mozilla/samples/crash/CrashApplication.kt152
-rw-r--r--mobile/android/android-components/samples/crash/src/main/java/org/mozilla/samples/crash/CrashListActivity.kt21
-rw-r--r--mobile/android/android-components/samples/crash/src/main/java/org/mozilla/samples/crash/CrashService.kt82
-rw-r--r--mobile/android/android-components/samples/crash/src/main/res/layout/activity_crash.xml40
-rw-r--r--mobile/android/android-components/samples/crash/src/main/res/mipmap-anydpi-v26/ic_launcher.xml9
-rw-r--r--mobile/android/android-components/samples/crash/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml9
-rw-r--r--mobile/android/android-components/samples/crash/src/main/res/mipmap-hdpi/ic_launcher.pngbin0 -> 1156 bytes
-rw-r--r--mobile/android/android-components/samples/crash/src/main/res/mipmap-hdpi/ic_launcher_foreground.pngbin0 -> 295 bytes
-rw-r--r--mobile/android/android-components/samples/crash/src/main/res/mipmap-hdpi/ic_launcher_round.pngbin0 -> 3041 bytes
-rw-r--r--mobile/android/android-components/samples/crash/src/main/res/mipmap-mdpi/ic_launcher.pngbin0 -> 858 bytes
-rw-r--r--mobile/android/android-components/samples/crash/src/main/res/mipmap-mdpi/ic_launcher_foreground.pngbin0 -> 225 bytes
-rw-r--r--mobile/android/android-components/samples/crash/src/main/res/mipmap-mdpi/ic_launcher_round.pngbin0 -> 1897 bytes
-rw-r--r--mobile/android/android-components/samples/crash/src/main/res/mipmap-xhdpi/ic_launcher.pngbin0 -> 1600 bytes
-rw-r--r--mobile/android/android-components/samples/crash/src/main/res/mipmap-xhdpi/ic_launcher_foreground.pngbin0 -> 403 bytes
-rw-r--r--mobile/android/android-components/samples/crash/src/main/res/mipmap-xhdpi/ic_launcher_round.pngbin0 -> 4303 bytes
-rw-r--r--mobile/android/android-components/samples/crash/src/main/res/mipmap-xxhdpi/ic_launcher.pngbin0 -> 2415 bytes
-rw-r--r--mobile/android/android-components/samples/crash/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.pngbin0 -> 716 bytes
-rw-r--r--mobile/android/android-components/samples/crash/src/main/res/mipmap-xxhdpi/ic_launcher_round.pngbin0 -> 6677 bytes
-rw-r--r--mobile/android/android-components/samples/crash/src/main/res/mipmap-xxxhdpi/ic_launcher.pngbin0 -> 3395 bytes
-rw-r--r--mobile/android/android-components/samples/crash/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.pngbin0 -> 1092 bytes
-rw-r--r--mobile/android/android-components/samples/crash/src/main/res/mipmap-xxxhdpi/ic_launcher_round.pngbin0 -> 9675 bytes
-rw-r--r--mobile/android/android-components/samples/crash/src/main/res/values/ic_launcher_background.xml8
-rw-r--r--mobile/android/android-components/samples/crash/src/main/res/values/strings.xml12
-rw-r--r--mobile/android/android-components/samples/crash/src/main/res/values/styles.xml19
-rw-r--r--mobile/android/android-components/samples/crash/src/main/res/xml/backup_rules.xml8
-rw-r--r--mobile/android/android-components/samples/crash/src/main/res/xml/data_extraction_rules.xml9
32 files changed, 657 insertions, 0 deletions
diff --git a/mobile/android/android-components/samples/crash/build.gradle b/mobile/android/android-components/samples/crash/build.gradle
new file mode 100644
index 0000000000..9ea07c4d9f
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/build.gradle
@@ -0,0 +1,47 @@
+/* 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/. */
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+
+android {
+ defaultConfig {
+ applicationId "org.mozilla.samples.crash"
+ minSdkVersion config.minSdkVersion
+ compileSdk config.compileSdkVersion
+ targetSdkVersion config.targetSdkVersion
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ buildFeatures {
+ viewBinding true
+ }
+
+ namespace 'org.mozilla.samples.crash'
+}
+
+
+dependencies {
+ implementation project(':lib-crash')
+ implementation project(':lib-fetch-httpurlconnection')
+ implementation project(':service-glean')
+ implementation project(':support-base')
+ implementation project(':support-utils')
+
+ implementation ComponentsDependencies.kotlin_coroutines
+
+ implementation ComponentsDependencies.androidx_appcompat
+ implementation ComponentsDependencies.google_material
+ implementation ComponentsDependencies.androidx_recyclerview
+}
diff --git a/mobile/android/android-components/samples/crash/lint.xml b/mobile/android/android-components/samples/crash/lint.xml
new file mode 100644
index 0000000000..33cf423701
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/lint.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+<lint>
+ <issue id="IconMissingDensityFolder" severity="ignore">
+ <!-- Suppress lint warnings on mdpi -->
+ <ignore path="src/debug/res/drawable-mdpi"/>
+ </issue>
+
+ <issue id="GoogleAppIndexingWarning" severity="ignore" />
+</lint> \ No newline at end of file
diff --git a/mobile/android/android-components/samples/crash/proguard-rules.pro b/mobile/android/android-components/samples/crash/proguard-rules.pro
new file mode 100644
index 0000000000..f1b424510d
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/mobile/android/android-components/samples/crash/src/main/AndroidManifest.xml b/mobile/android/android-components/samples/crash/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..29c01303bd
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools">
+
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
+
+ <application
+ android:name="org.mozilla.samples.crash.CrashApplication"
+ android:allowBackup="true"
+ android:fullBackupContent="@xml/backup_rules"
+ android:icon="@mipmap/ic_launcher"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.AppCompat"
+ android:dataExtractionRules="@xml/data_extraction_rules"
+ tools:targetApi="s"
+ tools:ignore="DataExtractionRules">
+ <activity android:name="org.mozilla.samples.crash.CrashActivity"
+ android:launchMode="singleTask"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="org.mozilla.samples.crash.CrashListActivity" android:exported="false" />
+
+ <service android:name="org.mozilla.samples.crash.CrashService"
+ android:foregroundServiceType="specialUse"
+ android:process=":samples.crash.service" />
+ </application>
+</manifest>
diff --git a/mobile/android/android-components/samples/crash/src/main/ic_launcher-web.png b/mobile/android/android-components/samples/crash/src/main/ic_launcher-web.png
new file mode 100644
index 0000000000..b8f772f66a
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/ic_launcher-web.png
Binary files differ
diff --git a/mobile/android/android-components/samples/crash/src/main/java/org/mozilla/samples/crash/CrashActivity.kt b/mobile/android/android-components/samples/crash/src/main/java/org/mozilla/samples/crash/CrashActivity.kt
new file mode 100644
index 0000000000..2bcce48acc
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/java/org/mozilla/samples/crash/CrashActivity.kt
@@ -0,0 +1,168 @@
+/* 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.samples.crash
+
+import android.content.BroadcastReceiver
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.Bundle
+import android.view.View
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.content.ContextCompat
+import com.google.android.material.snackbar.Snackbar
+import mozilla.components.concept.base.crash.Breadcrumb
+import mozilla.components.lib.crash.Crash
+import mozilla.components.support.utils.ext.registerReceiverCompat
+import org.mozilla.samples.crash.databinding.ActivityCrashBinding
+
+class CrashActivity : AppCompatActivity(), View.OnClickListener {
+ private val receiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (!Crash.isCrashIntent(intent)) {
+ return
+ }
+
+ val crash = Crash.fromIntent(intent)
+
+ Snackbar.make(
+ findViewById(android.R.id.content),
+ "Sorry. We crashed.",
+ Snackbar.LENGTH_LONG,
+ )
+ .setAction("Report") { crashReporter.submitReport(crash) }
+ .show()
+ }
+ }
+ private lateinit var binding: ActivityCrashBinding
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = ActivityCrashBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ binding.fatalCrashButton.setOnClickListener(this)
+ binding.crashButton.setOnClickListener(this)
+ binding.fatalServiceCrashButton.setOnClickListener(this)
+ binding.crashList.setOnClickListener(this)
+
+ crashReporter.recordCrashBreadcrumb(
+ Breadcrumb(
+ "CrashActivity onCreate",
+ emptyMap(),
+ "sample",
+ Breadcrumb.Level.DEBUG,
+ Breadcrumb.Type.NAVIGATION,
+ ),
+ )
+ }
+
+ override fun onResume() {
+ super.onResume()
+
+ registerReceiverCompat(
+ receiver,
+ IntentFilter(CrashApplication.NON_FATAL_CRASH_BROADCAST),
+ ContextCompat.RECEIVER_NOT_EXPORTED,
+ )
+
+ crashReporter.recordCrashBreadcrumb(
+ Breadcrumb(
+ "CrashActivity onResume",
+ emptyMap(),
+ "sample",
+ Breadcrumb.Level.DEBUG,
+ Breadcrumb.Type.NAVIGATION,
+ ),
+ )
+ }
+
+ override fun onPause() {
+ super.onPause()
+ unregisterReceiver(receiver)
+
+ crashReporter.recordCrashBreadcrumb(
+ Breadcrumb(
+ "CrashActivity onPause",
+ emptyMap(),
+ "sample",
+ Breadcrumb.Level.DEBUG,
+ Breadcrumb.Type.NAVIGATION,
+ ),
+ )
+ }
+
+ @Suppress("TooGenericExceptionThrown")
+ override fun onClick(view: View) {
+ when (view) {
+ binding.fatalCrashButton -> {
+ crashReporter.recordCrashBreadcrumb(
+ Breadcrumb(
+ "fatal crash button clicked",
+ emptyMap(),
+ "sample",
+ Breadcrumb.Level.INFO,
+ Breadcrumb.Type.USER,
+ ),
+ )
+
+ throw RuntimeException("Boom!")
+ }
+
+ binding.crashButton -> {
+ crashReporter.recordCrashBreadcrumb(
+ Breadcrumb(
+ "crash button clicked",
+ emptyMap(),
+ "sample",
+ Breadcrumb.Level.INFO,
+ Breadcrumb.Type.USER,
+ ),
+ )
+
+ // Pretend GeckoView has crashed by re-building a crash Intent and launching the CrashHandlerService.
+ val intent = Intent("org.mozilla.gecko.ACTION_CRASHED")
+ intent.component = ComponentName(
+ packageName,
+ "mozilla.components.lib.crash.handler.CrashHandlerService",
+ )
+ intent.putExtra(
+ "minidumpPath",
+ "${filesDir.path}/mozilla/Crash Reports/pending/3ba5f665-8422-dc8e-a88e-fc65c081d304.dmp",
+ )
+ intent.putExtra("fatal", false)
+ intent.putExtra(
+ "extrasPath",
+ "${filesDir.path}/mozilla/Crash Reports/pending/3ba5f665-8422-dc8e-a88e-fc65c081d304.extra",
+ )
+ intent.putExtra("minidumpSuccess", true)
+
+ ContextCompat.startForegroundService(this, intent)
+ }
+
+ binding.fatalServiceCrashButton -> {
+ crashReporter.recordCrashBreadcrumb(
+ Breadcrumb(
+ "fatal service crash button clicked",
+ emptyMap(),
+ "sample",
+ Breadcrumb.Level.INFO,
+ Breadcrumb.Type.USER,
+ ),
+ )
+
+ startService(Intent(this, CrashService::class.java))
+ finish()
+ }
+
+ binding.crashList -> {
+ startActivity(Intent(this, CrashListActivity::class.java))
+ }
+
+ else -> throw java.lang.RuntimeException("Unknown ID")
+ }
+ }
+}
diff --git a/mobile/android/android-components/samples/crash/src/main/java/org/mozilla/samples/crash/CrashApplication.kt b/mobile/android/android-components/samples/crash/src/main/java/org/mozilla/samples/crash/CrashApplication.kt
new file mode 100644
index 0000000000..2d50615dd7
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/java/org/mozilla/samples/crash/CrashApplication.kt
@@ -0,0 +1,152 @@
+/* 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.samples.crash
+
+import android.app.Application
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.widget.Toast
+import androidx.core.app.NotificationManagerCompat
+import kotlinx.coroutines.DelicateCoroutinesApi
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
+import mozilla.components.concept.base.crash.Breadcrumb
+import mozilla.components.lib.crash.Crash
+import mozilla.components.lib.crash.CrashReporter
+import mozilla.components.lib.crash.service.CrashReporterService
+import mozilla.components.lib.crash.service.GleanCrashReporterService
+import mozilla.components.lib.fetch.httpurlconnection.HttpURLConnectionClient
+import mozilla.components.service.glean.BuildInfo
+import mozilla.components.service.glean.Glean
+import mozilla.components.service.glean.config.Configuration
+import mozilla.components.service.glean.net.ConceptFetchHttpUploader
+import mozilla.components.support.base.android.NotificationsDelegate
+import mozilla.components.support.base.log.Log
+import mozilla.components.support.base.log.sink.AndroidLogSink
+import mozilla.components.support.utils.PendingIntentUtils
+import java.util.Calendar
+import java.util.TimeZone
+import java.util.UUID
+
+@Suppress("MagicNumber")
+internal object GleanBuildInfo {
+ val buildInfo: BuildInfo by lazy {
+ BuildInfo(
+ versionCode = "0.0.1",
+ versionName = "0.0.1",
+ buildDate = Calendar.getInstance(
+ TimeZone.getTimeZone("GMT+0"),
+ ).also { cal -> cal.set(2019, 9, 23, 12, 52, 8) },
+ )
+ }
+}
+
+class CrashApplication : Application() {
+ internal lateinit var crashReporter: CrashReporter
+
+ override fun onCreate() {
+ super.onCreate()
+
+ // We want the log messages of all builds to go to Android logcat
+ Log.addSink(AndroidLogSink())
+
+ val notificationManagerCompat = NotificationManagerCompat.from(applicationContext)
+
+ val notificationsDelegate: NotificationsDelegate by lazy {
+ NotificationsDelegate(
+ notificationManagerCompat,
+ )
+ }
+
+ crashReporter = CrashReporter(
+ context = this,
+ services = listOf(
+ createDummyCrashService(this),
+ ),
+ telemetryServices = listOf(GleanCrashReporterService(applicationContext)),
+ shouldPrompt = CrashReporter.Prompt.ALWAYS,
+ promptConfiguration = CrashReporter.PromptConfiguration(
+ appName = "Sample App",
+ organizationName = "Mozilla",
+ message = "As a private browser, we never save and cannot restore your last browsing session.",
+ theme = R.style.CrashDialogTheme,
+ ),
+ nonFatalCrashIntent = createNonFatalPendingIntent(this),
+ enabled = true,
+ notificationsDelegate = notificationsDelegate,
+ ).install(this)
+
+ // Initialize Glean for recording by the GleanCrashReporterService
+ val httpClient = ConceptFetchHttpUploader(lazy { HttpURLConnectionClient() })
+ val config = Configuration(httpClient = httpClient)
+ Glean.initialize(
+ applicationContext,
+ uploadEnabled = true,
+ configuration = config,
+ buildInfo = GleanBuildInfo.buildInfo,
+ )
+ }
+
+ companion object {
+ const val NON_FATAL_CRASH_BROADCAST = "org.mozilla.samples.crash.CRASH"
+ }
+}
+
+@OptIn(DelicateCoroutinesApi::class)
+private fun createDummyCrashService(context: Context): CrashReporterService {
+ // For this sample we create a dummy service. In a real application this would be an instance of SentryCrashService
+ // or SocorroCrashService.
+ return object : CrashReporterService {
+ override val id: String = "dummy"
+
+ override val name: String = "Dummy"
+
+ override fun createCrashReportUrl(identifier: String): String? {
+ return "https://example.org/$identifier"
+ }
+
+ override fun report(crash: Crash.UncaughtExceptionCrash): String? {
+ GlobalScope.launch(Dispatchers.Main) {
+ Toast.makeText(context, "Uploading uncaught exception crash...", Toast.LENGTH_SHORT).show()
+ }
+ return createDummyId()
+ }
+
+ override fun report(crash: Crash.NativeCodeCrash): String? {
+ GlobalScope.launch(Dispatchers.Main) {
+ Toast.makeText(context, "Uploading native crash...", Toast.LENGTH_SHORT).show()
+ }
+ return createDummyId()
+ }
+
+ override fun report(throwable: Throwable, breadcrumbs: ArrayList<Breadcrumb>): String? {
+ GlobalScope.launch(Dispatchers.Main) {
+ Toast.makeText(context, "Uploading caught exception...", Toast.LENGTH_SHORT).show()
+ }
+ return createDummyId()
+ }
+
+ private fun createDummyId(): String {
+ return "dummy${UUID.randomUUID().toString().hashCode()}"
+ }
+ }
+}
+
+private fun createNonFatalPendingIntent(context: Context): PendingIntent {
+ // The PendingIntent can launch whatever you want - an activity, a service... Here we pick a broadcast. Our main
+ // activity will listener for the broadcast and show an in-app snackbar to ask the user whether we should send
+ // this crash report.
+ return PendingIntent.getBroadcast(
+ context,
+ 0,
+ Intent(CrashApplication.NON_FATAL_CRASH_BROADCAST),
+ PendingIntentUtils.defaultFlags,
+ )
+}
+
+val Context.crashReporter: CrashReporter
+ get() = (applicationContext as CrashApplication).crashReporter
diff --git a/mobile/android/android-components/samples/crash/src/main/java/org/mozilla/samples/crash/CrashListActivity.kt b/mobile/android/android-components/samples/crash/src/main/java/org/mozilla/samples/crash/CrashListActivity.kt
new file mode 100644
index 0000000000..9a0d4d1d1d
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/java/org/mozilla/samples/crash/CrashListActivity.kt
@@ -0,0 +1,21 @@
+/* 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.samples.crash
+
+import android.widget.Toast
+import mozilla.components.lib.crash.CrashReporter
+import mozilla.components.lib.crash.ui.AbstractCrashListActivity
+
+/**
+ * Activity showing list of past crashes.
+ */
+class CrashListActivity : AbstractCrashListActivity() {
+ override val crashReporter: CrashReporter
+ get() = (application as CrashApplication).crashReporter
+
+ override fun onCrashServiceSelected(url: String) {
+ Toast.makeText(this, "Go to: $url", Toast.LENGTH_SHORT).show()
+ }
+}
diff --git a/mobile/android/android-components/samples/crash/src/main/java/org/mozilla/samples/crash/CrashService.kt b/mobile/android/android-components/samples/crash/src/main/java/org/mozilla/samples/crash/CrashService.kt
new file mode 100644
index 0000000000..270f9d505d
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/java/org/mozilla/samples/crash/CrashService.kt
@@ -0,0 +1,82 @@
+/* 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.samples.crash
+
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.Service
+import android.content.Context
+import android.content.Intent
+import android.os.Build
+import android.os.IBinder
+import android.widget.Toast
+import androidx.core.app.NotificationCompat
+import kotlinx.coroutines.DelicateCoroutinesApi
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import mozilla.components.support.base.ids.SharedIdsHelper
+
+private const val NOTIFICATION_CHANNEL_ID = "mozac.lib.crash.notification"
+private const val NOTIFICATION_TAG = "mozac.lib.crash.foreground-service"
+private const val DELAY_CRASH_MS = 10000L
+
+/**
+ * This service will wait 10 seconds and then crash. We need to wait some time because Android still allows to launch
+ * an activity from a background service if the app was in the foreground a couple of seconds ago.
+ */
+class CrashService : Service() {
+ override fun onBind(intent: Intent?): IBinder? = null
+
+ @OptIn(DelicateCoroutinesApi::class) // GlobalScope usage
+ @Suppress("TooGenericExceptionThrown")
+ override fun onCreate() {
+ Toast.makeText(this, "Crashing from background soonish...", Toast.LENGTH_SHORT).show()
+
+ // We need to put this service into foreground because otherwise Android may kill it (with no visible app UI)
+ // before we can crash.
+ startForeground(SharedIdsHelper.getIdForTag(this, NOTIFICATION_TAG), createNotification())
+
+ GlobalScope.launch(Dispatchers.Main) {
+ delay(DELAY_CRASH_MS)
+
+ throw RuntimeException("Background crash")
+ }
+ }
+
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ return START_NOT_STICKY
+ }
+
+ private fun createNotification(): Notification {
+ val channel = ensureChannelExists()
+
+ return NotificationCompat.Builder(this, channel)
+ .setContentTitle("Crash Service")
+ .setPriority(NotificationCompat.PRIORITY_DEFAULT)
+ .setCategory(NotificationCompat.CATEGORY_ERROR)
+ .build()
+ }
+
+ private fun ensureChannelExists(): String {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val notificationManager: NotificationManager = getSystemService(
+ Context.NOTIFICATION_SERVICE,
+ ) as NotificationManager
+
+ val channel = NotificationChannel(
+ NOTIFICATION_CHANNEL_ID,
+ "Crash Service",
+ NotificationManager.IMPORTANCE_DEFAULT,
+ )
+
+ notificationManager.createNotificationChannel(channel)
+ }
+
+ return NOTIFICATION_CHANNEL_ID
+ }
+}
diff --git a/mobile/android/android-components/samples/crash/src/main/res/layout/activity_crash.xml b/mobile/android/android-components/samples/crash/src/main/res/layout/activity_crash.xml
new file mode 100644
index 0000000000..4921aa4849
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/res/layout/activity_crash.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <Button
+ android:id="@+id/crashButton"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/crash_nonfatal"
+ android:textAlignment="center" />
+
+ <Button
+ android:id="@+id/fatalCrashButton"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/crash_fatal"
+ android:textAlignment="center" />
+
+ <Button
+ android:id="@+id/fatalServiceCrashButton"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/crash_fatal_service"
+ android:textAlignment="center" />
+
+ <Button
+ android:id="@+id/crashList"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/list_of_crashes"
+ android:textAlignment="center" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/mobile/android/android-components/samples/crash/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/mobile/android/android-components/samples/crash/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000000..fb7d4e724b
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@color/ic_launcher_background"/>
+ <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon> \ No newline at end of file
diff --git a/mobile/android/android-components/samples/crash/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/mobile/android/android-components/samples/crash/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000000..fb7d4e724b
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@color/ic_launcher_background"/>
+ <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon> \ No newline at end of file
diff --git a/mobile/android/android-components/samples/crash/src/main/res/mipmap-hdpi/ic_launcher.png b/mobile/android/android-components/samples/crash/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000000..506f4c3598
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/mobile/android/android-components/samples/crash/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/mobile/android/android-components/samples/crash/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000000..361ce175fe
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/mobile/android/android-components/samples/crash/src/main/res/mipmap-hdpi/ic_launcher_round.png b/mobile/android/android-components/samples/crash/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..302220d5c0
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/res/mipmap-hdpi/ic_launcher_round.png
Binary files differ
diff --git a/mobile/android/android-components/samples/crash/src/main/res/mipmap-mdpi/ic_launcher.png b/mobile/android/android-components/samples/crash/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000000..777f4f5b51
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/mobile/android/android-components/samples/crash/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/mobile/android/android-components/samples/crash/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000000..c158a5aa08
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/mobile/android/android-components/samples/crash/src/main/res/mipmap-mdpi/ic_launcher_round.png b/mobile/android/android-components/samples/crash/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..3f578142cd
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/res/mipmap-mdpi/ic_launcher_round.png
Binary files differ
diff --git a/mobile/android/android-components/samples/crash/src/main/res/mipmap-xhdpi/ic_launcher.png b/mobile/android/android-components/samples/crash/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000000..fd7337a647
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/mobile/android/android-components/samples/crash/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/mobile/android/android-components/samples/crash/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000000..2233cd4525
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/mobile/android/android-components/samples/crash/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/mobile/android/android-components/samples/crash/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..91bc706ebe
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Binary files differ
diff --git a/mobile/android/android-components/samples/crash/src/main/res/mipmap-xxhdpi/ic_launcher.png b/mobile/android/android-components/samples/crash/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..7863ad9b3f
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/mobile/android/android-components/samples/crash/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/mobile/android/android-components/samples/crash/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000000..ea69f75cd7
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/mobile/android/android-components/samples/crash/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/mobile/android/android-components/samples/crash/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..7fdbb4bede
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/mobile/android/android-components/samples/crash/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/mobile/android/android-components/samples/crash/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..6e28647885
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/mobile/android/android-components/samples/crash/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/mobile/android/android-components/samples/crash/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000000..beccb228ea
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/mobile/android/android-components/samples/crash/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/mobile/android/android-components/samples/crash/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..83a223b8a8
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/mobile/android/android-components/samples/crash/src/main/res/values/ic_launcher_background.xml b/mobile/android/android-components/samples/crash/src/main/res/values/ic_launcher_background.xml
new file mode 100644
index 0000000000..bce8f507b6
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/res/values/ic_launcher_background.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+
+<resources>
+ <color name="ic_launcher_background">#FFF31A</color>
+</resources> \ No newline at end of file
diff --git a/mobile/android/android-components/samples/crash/src/main/res/values/strings.xml b/mobile/android/android-components/samples/crash/src/main/res/values/strings.xml
new file mode 100644
index 0000000000..ac69e68e60
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/res/values/strings.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+<resources>
+ <string name="app_name">Crash Sample</string>
+
+ <string name="crash_fatal">Crash (Fatal)</string>
+ <string name="crash_nonfatal">Crash (Non-Fatal)</string>
+ <string name="crash_fatal_service">Crash (Fatal; background service)</string>
+ <string name="list_of_crashes">List of crashes</string>
+</resources>
diff --git a/mobile/android/android-components/samples/crash/src/main/res/values/styles.xml b/mobile/android/android-components/samples/crash/src/main/res/values/styles.xml
new file mode 100644
index 0000000000..bea300304b
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/res/values/styles.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+<resources>
+ <!-- inherit from the AppCompat theme -->
+ <style name="CrashDialogTheme" parent="Theme.Mozac.CrashReporter">
+
+ <!-- your app branding color for the app bar -->
+ <item name="colorPrimary">#8BC34A</item>
+
+ <!-- darker variant for the status bar and contextual app bars -->
+ <item name="colorPrimaryDark">#689F38</item>
+
+ <!-- theme UI controls like checkboxes and text fields -->
+ <item name="colorAccent">#E040FB</item>
+
+ </style>
+</resources> \ No newline at end of file
diff --git a/mobile/android/android-components/samples/crash/src/main/res/xml/backup_rules.xml b/mobile/android/android-components/samples/crash/src/main/res/xml/backup_rules.xml
new file mode 100644
index 0000000000..820ae61afa
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+
+<full-backup-content>
+ <include domain="sharedpref" path="."/>
+</full-backup-content> \ No newline at end of file
diff --git a/mobile/android/android-components/samples/crash/src/main/res/xml/data_extraction_rules.xml b/mobile/android/android-components/samples/crash/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 0000000000..55da967560
--- /dev/null
+++ b/mobile/android/android-components/samples/crash/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+<data-extraction-rules>
+ <cloud-backup>
+ <include domain="sharedpref" path="."/>
+ </cloud-backup>
+</data-extraction-rules> \ No newline at end of file