Skip to content

Commit 241e3d5

Browse files
authored
Merge 5e5a8d5 into 27d7cf8
2 parents 27d7cf8 + 5e5a8d5 commit 241e3d5

File tree

2 files changed

+78
-60
lines changed

2 files changed

+78
-60
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
### Miscellaneous
6+
7+
- [ANR] Update Connection Status cache in the background ([#4832](https://github.com/getsentry/sentry-java/pull/4832))
8+
39
## 8.24.0
410

511
### Features

sentry-android-core/src/main/java/io/sentry/android/core/internal/util/AndroidConnectionStatusProvider.java

Lines changed: 72 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public final class AndroidConnectionStatusProvider
5454
private static final @NotNull AutoClosableReentrantLock childCallbacksLock =
5555
new AutoClosableReentrantLock();
5656
private static final @NotNull List<NetworkCallback> childCallbacks = new ArrayList<>();
57+
private static final AtomicBoolean isUpdatingCache = new AtomicBoolean(false);
5758

5859
private static final int[] transports = {
5960
NetworkCapabilities.TRANSPORT_WIFI,
@@ -268,10 +269,19 @@ private void updateCacheAndNotifyObservers(
268269

269270
// Only notify observers if something meaningful changed
270271
if (shouldUpdate) {
271-
updateCache(networkCapabilities);
272-
273-
final @NotNull ConnectionStatus status = getConnectionStatusFromCache();
274272
try (final @NotNull ISentryLifecycleToken ignored = lock.acquire()) {
273+
cachedNetworkCapabilities = networkCapabilities;
274+
lastCacheUpdateTime = timeProvider.getCurrentTimeMillis();
275+
final @NotNull ConnectionStatus status = getConnectionStatusFromCache();
276+
options
277+
.getLogger()
278+
.log(
279+
SentryLevel.DEBUG,
280+
"Cache updated - Status: "
281+
+ status
282+
+ ", Type: "
283+
+ getConnectionTypeFromCache());
284+
275285
for (final @NotNull IConnectionStatusObserver observer :
276286
connectionStatusObservers) {
277287
observer.onConnectionStatusChanged(status);
@@ -349,56 +359,57 @@ private boolean hasSignificantTransportChanges(
349359
}
350360

351361
@SuppressLint({"NewApi", "MissingPermission"})
352-
private void updateCache(@Nullable NetworkCapabilities networkCapabilities) {
362+
private void updateCache() {
353363
try (final @NotNull ISentryLifecycleToken ignored = lock.acquire()) {
354-
try {
355-
if (networkCapabilities != null) {
356-
cachedNetworkCapabilities = networkCapabilities;
357-
} else {
358-
if (!Permissions.hasPermission(context, Manifest.permission.ACCESS_NETWORK_STATE)) {
359-
options
360-
.getLogger()
361-
.log(
362-
SentryLevel.INFO,
363-
"No permission (ACCESS_NETWORK_STATE) to check network status.");
364-
cachedNetworkCapabilities = null;
365-
lastCacheUpdateTime = timeProvider.getCurrentTimeMillis();
366-
return;
367-
}
364+
cachedNetworkCapabilities = null;
365+
lastCacheUpdateTime = timeProvider.getCurrentTimeMillis();
366+
}
367+
try {
368+
if (!Permissions.hasPermission(context, Manifest.permission.ACCESS_NETWORK_STATE)) {
369+
options
370+
.getLogger()
371+
.log(SentryLevel.INFO, "No permission (ACCESS_NETWORK_STATE) to check network status.");
372+
return;
373+
}
368374

369-
if (buildInfoProvider.getSdkInfoVersion() < Build.VERSION_CODES.M) {
370-
cachedNetworkCapabilities = null;
371-
lastCacheUpdateTime = timeProvider.getCurrentTimeMillis();
372-
return;
373-
}
375+
if (buildInfoProvider.getSdkInfoVersion() < Build.VERSION_CODES.M) {
376+
return;
377+
}
374378

375-
// Fallback: query current active network
376-
final ConnectivityManager connectivityManager =
377-
getConnectivityManager(context, options.getLogger());
378-
if (connectivityManager != null) {
379-
final Network activeNetwork = connectivityManager.getActiveNetwork();
380-
381-
cachedNetworkCapabilities =
382-
activeNetwork != null
383-
? connectivityManager.getNetworkCapabilities(activeNetwork)
384-
: null;
385-
} else {
386-
cachedNetworkCapabilities =
387-
null; // Clear cached capabilities if connectivity manager is null
388-
}
389-
}
390-
lastCacheUpdateTime = timeProvider.getCurrentTimeMillis();
379+
// Fallback: query current active network in the background
380+
submitSafe(
381+
() -> {
382+
// Avoid concurrent updates
383+
if (!isUpdatingCache.getAndSet(true)) {
384+
final ConnectivityManager connectivityManager =
385+
getConnectivityManager(context, options.getLogger());
386+
if (connectivityManager != null) {
387+
final @Nullable NetworkCapabilities capabilities =
388+
getNetworkCapabilities(connectivityManager);
389+
390+
try (final @NotNull ISentryLifecycleToken ignored2 = lock.acquire()) {
391+
cachedNetworkCapabilities = capabilities;
392+
lastCacheUpdateTime = timeProvider.getCurrentTimeMillis();
393+
394+
if (capabilities != null) {
395+
options
396+
.getLogger()
397+
.log(
398+
SentryLevel.DEBUG,
399+
"Cache updated - Status: "
400+
+ getConnectionStatusFromCache()
401+
+ ", Type: "
402+
+ getConnectionTypeFromCache());
403+
}
404+
}
405+
}
406+
isUpdatingCache.set(false);
407+
}
408+
});
391409

392-
options
393-
.getLogger()
394-
.log(
395-
SentryLevel.DEBUG,
396-
"Cache updated - Status: "
397-
+ getConnectionStatusFromCache()
398-
+ ", Type: "
399-
+ getConnectionTypeFromCache());
400-
} catch (Throwable t) {
401-
options.getLogger().log(SentryLevel.WARNING, "Failed to update connection status cache", t);
410+
} catch (Throwable t) {
411+
options.getLogger().log(SentryLevel.WARNING, "Failed to update connection status cache", t);
412+
try (final @NotNull ISentryLifecycleToken ignored = lock.acquire()) {
402413
cachedNetworkCapabilities = null;
403414
lastCacheUpdateTime = timeProvider.getCurrentTimeMillis();
404415
}
@@ -412,15 +423,15 @@ private boolean isCacheValid() {
412423
@Override
413424
public @NotNull ConnectionStatus getConnectionStatus() {
414425
if (!isCacheValid()) {
415-
updateCache(null);
426+
updateCache();
416427
}
417428
return getConnectionStatusFromCache();
418429
}
419430

420431
@Override
421432
public @Nullable String getConnectionType() {
422433
if (!isCacheValid()) {
423-
updateCache(null);
434+
updateCache();
424435
}
425436
return getConnectionTypeFromCache();
426437
}
@@ -490,7 +501,7 @@ public void onForeground() {
490501
() -> {
491502
// proactively update cache and notify observers on foreground to ensure connectivity
492503
// state is not stale
493-
updateCache(null);
504+
updateCache();
494505

495506
final @NotNull ConnectionStatus status = getConnectionStatusFromCache();
496507
if (status == ConnectionStatus.DISCONNECTED) {
@@ -575,6 +586,14 @@ public NetworkCapabilities getCachedNetworkCapabilities() {
575586
}
576587
}
577588

589+
@RequiresApi(Build.VERSION_CODES.M)
590+
@SuppressLint("MissingPermission")
591+
private static @Nullable NetworkCapabilities getNetworkCapabilities(
592+
final @NotNull ConnectivityManager connectivityManager) {
593+
final Network activeNetwork = connectivityManager.getActiveNetwork();
594+
return activeNetwork != null ? connectivityManager.getNetworkCapabilities(activeNetwork) : null;
595+
}
596+
578597
/**
579598
* Check the connection type of the active network
580599
*
@@ -603,14 +622,7 @@ public NetworkCapabilities getCachedNetworkCapabilities() {
603622
boolean cellular = false;
604623

605624
if (buildInfoProvider.getSdkInfoVersion() >= Build.VERSION_CODES.M) {
606-
607-
final Network activeNetwork = connectivityManager.getActiveNetwork();
608-
if (activeNetwork == null) {
609-
logger.log(SentryLevel.INFO, "Network is null and cannot check network status");
610-
return null;
611-
}
612-
final NetworkCapabilities networkCapabilities =
613-
connectivityManager.getNetworkCapabilities(activeNetwork);
625+
final NetworkCapabilities networkCapabilities = getNetworkCapabilities(connectivityManager);
614626
if (networkCapabilities == null) {
615627
logger.log(SentryLevel.INFO, "NetworkCapabilities is null and cannot check network type");
616628
return null;

0 commit comments

Comments
 (0)