Skip to content

Commit d68e020

Browse files
author
viyakovlev
committed
fix: safe unregister SystemEventsBroadcastReceiver
1 parent 22f4345 commit d68e020

File tree

2 files changed

+45
-20
lines changed

2 files changed

+45
-20
lines changed

sentry-android-core/src/main/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegration.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -203,13 +203,13 @@ private void scheduleUnregisterReceiver() {
203203
}
204204

205205
try {
206-
options.getExecutorService().submit(() -> unregisterReceiver());
206+
options.getExecutorService().submit(() -> unregisterReceiver(options));
207207
} catch (RejectedExecutionException e) {
208-
unregisterReceiver();
208+
unregisterReceiver(options);
209209
}
210210
}
211211

212-
private void unregisterReceiver() {
212+
private void unregisterReceiver(final @NotNull SentryAndroidOptions options) {
213213
final @Nullable SystemEventsBroadcastReceiver receiverRef;
214214
try (final @NotNull ISentryLifecycleToken ignored = receiverLock.acquire()) {
215215
isStopped = true;
@@ -218,7 +218,17 @@ private void unregisterReceiver() {
218218
}
219219

220220
if (receiverRef != null) {
221-
context.unregisterReceiver(receiverRef);
221+
try {
222+
context.unregisterReceiver(receiverRef);
223+
} catch (IllegalArgumentException exception) {
224+
options
225+
.getLogger()
226+
.log(
227+
SentryLevel.ERROR,
228+
exception,
229+
"Failed to unregister SystemEventsBroadcastReceiver: the receiver is already unregistered or was never registered."
230+
);
231+
}
222232
}
223233
}
224234

sentry-android-core/src/test/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegrationTest.kt

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,6 @@ import io.sentry.ISentryExecutorService
1616
import io.sentry.SentryLevel
1717
import io.sentry.test.DeferredExecutorService
1818
import io.sentry.test.ImmediateExecutorService
19-
import java.util.concurrent.CountDownLatch
20-
import kotlin.test.AfterTest
21-
import kotlin.test.BeforeTest
22-
import kotlin.test.Test
23-
import kotlin.test.assertEquals
24-
import kotlin.test.assertFalse
25-
import kotlin.test.assertNotNull
26-
import kotlin.test.assertNull
27-
import kotlin.test.assertTrue
2819
import org.junit.runner.RunWith
2920
import org.mockito.kotlin.any
3021
import org.mockito.kotlin.anyOrNull
@@ -41,6 +32,15 @@ import org.robolectric.annotation.Config
4132
import org.robolectric.shadow.api.Shadow
4233
import org.robolectric.shadows.ShadowActivityManager
4334
import org.robolectric.shadows.ShadowBuild
35+
import java.util.concurrent.CountDownLatch
36+
import kotlin.test.AfterTest
37+
import kotlin.test.BeforeTest
38+
import kotlin.test.Test
39+
import kotlin.test.assertEquals
40+
import kotlin.test.assertFalse
41+
import kotlin.test.assertNotNull
42+
import kotlin.test.assertNull
43+
import kotlin.test.assertTrue
4444

4545
@RunWith(AndroidJUnit4::class)
4646
@Config(sdk = [Build.VERSION_CODES.TIRAMISU])
@@ -52,6 +52,7 @@ class SystemEventsBreadcrumbsIntegrationTest {
5252
lateinit var shadowActivityManager: ShadowActivityManager
5353

5454
fun getSut(
55+
contextForSut: Context = context,
5556
enableSystemEventBreadcrumbs: Boolean = true,
5657
enableSystemEventBreadcrumbsExtras: Boolean = false,
5758
executorService: ISentryExecutorService = ImmediateExecutorService(),
@@ -64,7 +65,7 @@ class SystemEventsBreadcrumbsIntegrationTest {
6465
this.executorService = executorService
6566
}
6667
return SystemEventsBreadcrumbsIntegration(
67-
context,
68+
contextForSut,
6869
SystemEventsBreadcrumbsIntegration.getDefaultActions().toTypedArray(),
6970
handler,
7071
)
@@ -313,6 +314,20 @@ class SystemEventsBreadcrumbsIntegrationTest {
313314
assertFalse(fixture.options.isEnableSystemEventBreadcrumbs)
314315
}
315316

317+
@Test
318+
fun `Do not crash if receiver already unregistered`() {
319+
val realContext = ApplicationProvider.getApplicationContext<Context>()
320+
val sut = fixture.getSut(realContext)
321+
322+
sut.register(fixture.scopes, fixture.options)
323+
324+
realContext.unregisterReceiver(sut.receiver)
325+
326+
val result = runCatching { sut.onBackground() }
327+
328+
assertFalse(result.isFailure)
329+
}
330+
316331
@Test
317332
fun `when str has full package, return last string after dot`() {
318333
val sut = fixture.getSut()
@@ -421,9 +436,9 @@ class SystemEventsBreadcrumbsIntegrationTest {
421436
sut.register(fixture.scopes, fixture.options)
422437

423438
Thread {
424-
sut.close()
425-
latch.countDown()
426-
}
439+
sut.close()
440+
latch.countDown()
441+
}
427442
.start()
428443

429444
latch.await()
@@ -505,9 +520,9 @@ class SystemEventsBreadcrumbsIntegrationTest {
505520
assertNotNull(sut.receiver)
506521

507522
Thread {
508-
sut.close()
509-
latch.countDown()
510-
}
523+
sut.close()
524+
latch.countDown()
525+
}
511526
.start()
512527

513528
latch.await()

0 commit comments

Comments
 (0)