Skip to content

Commit 06adc56

Browse files
committed
WIP: Idea for exit cleanly on expected crash
1 parent f024090 commit 06adc56

File tree

3 files changed

+89
-4
lines changed

3 files changed

+89
-4
lines changed

firebase-sessions/test-app/src/main/AndroidManifest.xml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
android:exported="true"
2929
android:label="@string/app_name"
3030
android:name=".MainActivity"
31-
android:process=":main"
3231
android:theme="@style/Theme.Widget_test_app.NoActionBar">
3332
<intent-filter>
3433
<action android:name="android.intent.action.MAIN" />
@@ -51,6 +50,15 @@
5150
android:name="firebase_sessions_sessions_restart_timeout"
5251
android:value="5" />
5352

53+
<provider
54+
android:authorities="${applicationId}.preFirebase"
55+
android:directBootAware="true"
56+
android:exported="false"
57+
android:initOrder="101"
58+
android:name="com.google.firebase.testing.sessions.PreFirebaseProvider" />
59+
60+
<receiver android:name=".CrashBroadcastReceiver" />
61+
5462
<receiver
5563
android:exported="false"
5664
android:name="CrashWidgetProvider"
@@ -63,8 +71,6 @@
6371
android:resource="@xml/homescreen_widget" />
6472
</receiver>
6573

66-
<receiver android:name=".CrashBroadcastReceiver" />
67-
6874
<service
6975
android:enabled="true"
7076
android:exported="false"

firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/FirstFragment.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@ class FirstFragment : Fragment() {
5454

5555
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
5656
super.onViewCreated(view, savedInstanceState)
57+
PreFirebaseProvider.expectedMessage = "i am expected"
5758

58-
binding.buttonCrash.setOnClickListener { throw RuntimeException("CRASHED") }
59+
binding.buttonCrash.setOnClickListener { throw RuntimeException("so i am expected 123") }
5960
binding.buttonNonFatal.setOnClickListener {
6061
crashlytics.recordException(IllegalStateException())
6162
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.firebase.testing.sessions
18+
19+
import android.content.ContentProvider
20+
import android.content.ContentValues
21+
import android.database.Cursor
22+
import android.net.Uri
23+
import java.lang.Thread.UncaughtExceptionHandler
24+
import kotlin.system.exitProcess
25+
26+
class PreFirebaseProvider : ContentProvider(), UncaughtExceptionHandler {
27+
private var defaultUncaughtExceptionHandler: UncaughtExceptionHandler? = null
28+
29+
companion object {
30+
/**
31+
* Set an expected exception message. If the uncaught exception contains the given string, then
32+
* the app will exit cleanly. Otherwise, it will propagate up to the default exception handler.
33+
*/
34+
var expectedMessage: String? = null
35+
}
36+
37+
override fun onCreate(): Boolean {
38+
defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler()
39+
Thread.setDefaultUncaughtExceptionHandler(this)
40+
41+
return false
42+
}
43+
44+
/* Returns if the given exception contains the expectedMessage in its message. */
45+
private fun isExpected(throwable: Throwable): Boolean =
46+
expectedMessage?.run { throwable.message?.contains(this) } ?: false
47+
48+
override fun uncaughtException(thread: Thread, throwable: Throwable) {
49+
if (isExpected(throwable)) {
50+
// Exit cleanly
51+
exitProcess(0)
52+
} else {
53+
// Propagate up to the default exception handler
54+
defaultUncaughtExceptionHandler?.uncaughtException(thread, throwable)
55+
}
56+
}
57+
58+
override fun query(
59+
uri: Uri,
60+
projection: Array<out String>?,
61+
selection: String?,
62+
selectionArgs: Array<out String>?,
63+
sortOrder: String?
64+
): Cursor? = null
65+
66+
override fun getType(uri: Uri): String? = null
67+
68+
override fun insert(uri: Uri, values: ContentValues?): Uri? = null
69+
70+
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int = 0
71+
72+
override fun update(
73+
uri: Uri,
74+
values: ContentValues?,
75+
selection: String?,
76+
selectionArgs: Array<out String>?
77+
): Int = 0
78+
}

0 commit comments

Comments
 (0)