Make ServiceProvider.getService thread safe #2012
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Description
One Line Summary
Make
ServiceProvider
thread safe, to avoid cases where it can unexpectedly return different instances for the same interface.Details
Motivation
We are getting a number of crash reports with
appContext
beingnull
and I believeServiceProvider.getService
not being thread safe is cause. (but unknown if the only one)Scope
Only thread safely changes to
ServiceProvider.kt
.SDK Internal Details
When investigating we found a case where two instances of
IApplicationService
could be created. Specifically if an instance ofReceiveReceiptWorker
is created by the Android Operating System whenOneSignal.initWithContext
is called (by the app developer or internally by the SDK). This can result in two thread callingServiceProvider.getService()
, creating the two instances of a class unexpectedly.What This May Fix
While this fix addresses a single known cause of
OneSignal.initWithContext
throwing on anull
Context
error, it is unclear if this is the only source of this, or by how much this fix will help lower these crashes.Reproducing the Crash in an app
While I could not reproduce the issue in a real world scenario there is a unit test in this PR ServiceProviderTest. Also a integration style test can be done by running the example app in the crash-with-null-context-on-initWithContext branch, see aa32927 through for more details.
Crashes
This PR may address NullPointerException from
initWithContext
like this one:#1990
Testing
Unit testing
Added a new test
Created a new ServiceProviderTest test confirm that
ServiceBuilder.getService()
should return the same instance, even when accessed from two threads. To ensure the test is consistent we made the constructor slow so both threads end up on the same line of the production code we want to test.Manual testing
Tested on an Android 6 and Android 14 emulator, ensuring notifications display.
Affected code checklist
Checklist
Overview
Testing
Final pass
This change is