44import android .annotation .SuppressLint ;
55import android .content .Context ;
66import android .net .ConnectivityManager ;
7+ import android .net .ConnectivityManager .NetworkCallback ;
78import android .net .Network ;
89import android .net .NetworkCapabilities ;
910import android .os .Build ;
10- import androidx .annotation .NonNull ;
1111import io .sentry .IConnectionStatusProvider ;
1212import io .sentry .ILogger ;
13+ import io .sentry .ISentryLifecycleToken ;
1314import io .sentry .SentryLevel ;
1415import io .sentry .android .core .BuildInfoProvider ;
1516import io .sentry .android .core .ContextUtils ;
16- import java .util .HashMap ;
17- import java .util .Map ;
17+ import io .sentry .util .AutoClosableReentrantLock ;
18+ import java .util .ArrayList ;
19+ import java .util .List ;
1820import org .jetbrains .annotations .ApiStatus ;
1921import org .jetbrains .annotations .NotNull ;
2022import org .jetbrains .annotations .Nullable ;
@@ -31,8 +33,9 @@ public final class AndroidConnectionStatusProvider implements IConnectionStatusP
3133 private final @ NotNull Context context ;
3234 private final @ NotNull ILogger logger ;
3335 private final @ NotNull BuildInfoProvider buildInfoProvider ;
34- private final @ NotNull Map <IConnectionStatusObserver , ConnectivityManager .NetworkCallback >
35- registeredCallbacks ;
36+ private final @ NotNull List <IConnectionStatusObserver > connectionStatusObservers ;
37+ private final @ NotNull AutoClosableReentrantLock lock = new AutoClosableReentrantLock ();
38+ private volatile @ Nullable NetworkCallback networkCallback ;
3639
3740 public AndroidConnectionStatusProvider (
3841 @ NotNull Context context ,
@@ -41,7 +44,7 @@ public AndroidConnectionStatusProvider(
4144 this .context = ContextUtils .getApplicationContext (context );
4245 this .logger = logger ;
4346 this .buildInfoProvider = buildInfoProvider ;
44- this .registeredCallbacks = new HashMap <>();
47+ this .connectionStatusObservers = new ArrayList <>();
4548 }
4649
4750 @ Override
@@ -65,40 +68,64 @@ public AndroidConnectionStatusProvider(
6568
6669 @ Override
6770 public boolean addConnectionStatusObserver (final @ NotNull IConnectionStatusObserver observer ) {
71+ try (final @ NotNull ISentryLifecycleToken ignored = lock .acquire ()) {
72+ connectionStatusObservers .add (observer );
73+ }
6874
69- final ConnectivityManager .NetworkCallback callback =
70- new ConnectivityManager .NetworkCallback () {
71- @ Override
72- public void onAvailable (@ NonNull Network network ) {
73- observer .onConnectionStatusChanged (getConnectionStatus ());
74- }
75-
76- @ Override
77- public void onLosing (@ NonNull Network network , int maxMsToLive ) {
78- observer .onConnectionStatusChanged (getConnectionStatus ());
79- }
80-
81- @ Override
82- public void onLost (@ NonNull Network network ) {
83- observer .onConnectionStatusChanged (getConnectionStatus ());
84- }
85-
86- @ Override
87- public void onUnavailable () {
88- observer .onConnectionStatusChanged (getConnectionStatus ());
75+ if (networkCallback == null ) {
76+ try (final @ NotNull ISentryLifecycleToken ignored = lock .acquire ()) {
77+ if (networkCallback == null ) {
78+ final @ NotNull NetworkCallback newNetworkCallback =
79+ new NetworkCallback () {
80+ @ Override
81+ public void onAvailable (final @ NotNull Network network ) {
82+ updateObservers ();
83+ }
84+
85+ @ Override
86+ public void onUnavailable () {
87+ updateObservers ();
88+ }
89+
90+ @ Override
91+ public void onLost (final @ NotNull Network network ) {
92+ updateObservers ();
93+ }
94+
95+ public void updateObservers () {
96+ final @ NotNull ConnectionStatus status = getConnectionStatus ();
97+ try (final @ NotNull ISentryLifecycleToken ignored = lock .acquire ()) {
98+ for (final @ NotNull IConnectionStatusObserver observer :
99+ connectionStatusObservers ) {
100+ observer .onConnectionStatusChanged (status );
101+ }
102+ }
103+ }
104+ };
105+
106+ if (registerNetworkCallback (context , logger , buildInfoProvider , newNetworkCallback )) {
107+ networkCallback = newNetworkCallback ;
108+ return true ;
109+ } else {
110+ return false ;
89111 }
90- };
91-
92- registeredCallbacks .put (observer , callback );
93- return registerNetworkCallback (context , logger , buildInfoProvider , callback );
112+ }
113+ }
114+ }
115+ // networkCallback is already registered, so we can safely return true
116+ return true ;
94117 }
95118
96119 @ Override
97120 public void removeConnectionStatusObserver (final @ NotNull IConnectionStatusObserver observer ) {
98- final @ Nullable ConnectivityManager .NetworkCallback callback =
99- registeredCallbacks .remove (observer );
100- if (callback != null ) {
101- unregisterNetworkCallback (context , logger , callback );
121+ try (final @ NotNull ISentryLifecycleToken ignored = lock .acquire ()) {
122+ connectionStatusObservers .remove (observer );
123+ if (connectionStatusObservers .isEmpty ()) {
124+ if (networkCallback != null ) {
125+ unregisterNetworkCallback (context , logger , networkCallback );
126+ networkCallback = null ;
127+ }
128+ }
102129 }
103130 }
104131
@@ -281,7 +308,7 @@ public static boolean registerNetworkCallback(
281308 final @ NotNull Context context ,
282309 final @ NotNull ILogger logger ,
283310 final @ NotNull BuildInfoProvider buildInfoProvider ,
284- final @ NotNull ConnectivityManager . NetworkCallback networkCallback ) {
311+ final @ NotNull NetworkCallback networkCallback ) {
285312 if (buildInfoProvider .getSdkInfoVersion () < Build .VERSION_CODES .N ) {
286313 logger .log (SentryLevel .DEBUG , "NetworkCallbacks need Android N+." );
287314 return false ;
@@ -307,7 +334,7 @@ public static boolean registerNetworkCallback(
307334 public static void unregisterNetworkCallback (
308335 final @ NotNull Context context ,
309336 final @ NotNull ILogger logger ,
310- final @ NotNull ConnectivityManager . NetworkCallback networkCallback ) {
337+ final @ NotNull NetworkCallback networkCallback ) {
311338
312339 final ConnectivityManager connectivityManager = getConnectivityManager (context , logger );
313340 if (connectivityManager == null ) {
@@ -322,8 +349,13 @@ public static void unregisterNetworkCallback(
322349
323350 @ TestOnly
324351 @ NotNull
325- public Map <IConnectionStatusObserver , ConnectivityManager .NetworkCallback >
326- getRegisteredCallbacks () {
327- return registeredCallbacks ;
352+ public List <IConnectionStatusObserver > getStatusObservers () {
353+ return connectionStatusObservers ;
354+ }
355+
356+ @ TestOnly
357+ @ Nullable
358+ public NetworkCallback getNetworkCallback () {
359+ return networkCallback ;
328360 }
329361}
0 commit comments