Skip to content

Commit

Permalink
Merge da8fc6d into cd268a3
Browse files Browse the repository at this point in the history
  • Loading branch information
markushi authored Sep 6, 2023
2 parents cd268a3 + da8fc6d commit 4881e6a
Show file tree
Hide file tree
Showing 19 changed files with 657 additions and 109 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Breaking changes:
- Automatic user interaction tracking: every click now starts a new automatic transaction ([#2891](https://github.com/getsentry/sentry-java/pull/2891))
- Previously performing a click on the same UI widget twice would keep the existing transaction running, the new behavior now better aligns with other SDKs
- Android only: If global hub mode is enabled, Sentry.getSpan() returns the root span instead of the latest span ([#2855](https://github.com/getsentry/sentry-java/pull/2855))
- Android only: Observe network state to upload any unsent envelopes ([#2910](https://github.com/getsentry/sentry-java/pull/2910))

### Fixes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
import io.sentry.DeduplicateMultithreadedEventProcessor;
import io.sentry.DefaultTransactionPerformanceCollector;
import io.sentry.ILogger;
import io.sentry.NoOpConnectionStatusProvider;
import io.sentry.SendFireAndForgetEnvelopeSender;
import io.sentry.SendFireAndForgetOutboxSender;
import io.sentry.SentryLevel;
import io.sentry.android.core.cache.AndroidEnvelopeCache;
import io.sentry.android.core.internal.debugmeta.AssetsDebugMetaLoader;
import io.sentry.android.core.internal.gestures.AndroidViewGestureTargetLocator;
import io.sentry.android.core.internal.modules.AssetsModulesLoader;
import io.sentry.android.core.internal.util.AndroidConnectionStatusProvider;
import io.sentry.android.core.internal.util.AndroidMainThreadChecker;
import io.sentry.android.core.internal.util.SentryFrameMetricsCollector;
import io.sentry.android.fragment.FragmentLifecycleIntegration;
Expand Down Expand Up @@ -130,14 +132,19 @@ static void initializeIntegrationsAndProcessors(
options.setEnvelopeDiskCache(new AndroidEnvelopeCache(options));
}

if (options.getConnectionStatusProvider() instanceof NoOpConnectionStatusProvider) {
options.setConnectionStatusProvider(
new AndroidConnectionStatusProvider(context, options.getLogger(), buildInfoProvider));
}

options.addEventProcessor(new DeduplicateMultithreadedEventProcessor(options));
options.addEventProcessor(
new DefaultAndroidEventProcessor(context, buildInfoProvider, options));
options.addEventProcessor(new PerformanceAndroidEventProcessor(options, activityFramesTracker));
options.addEventProcessor(new ScreenshotEventProcessor(options, buildInfoProvider));
options.addEventProcessor(new ViewHierarchyEventProcessor(options));
options.addEventProcessor(new AnrV2EventProcessor(context, options, buildInfoProvider));
options.setTransportGate(new AndroidTransportGate(context, options.getLogger()));
options.setTransportGate(new AndroidTransportGate(options));
final SentryFrameMetricsCollector frameMetricsCollector =
new SentryFrameMetricsCollector(context, options, buildInfoProvider);
options.setTransactionProfiler(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,26 @@
package io.sentry.android.core;

import android.content.Context;
import io.sentry.ILogger;
import io.sentry.android.core.internal.util.ConnectivityChecker;
import io.sentry.IConnectionStatusProvider;
import io.sentry.SentryOptions;
import io.sentry.transport.ITransportGate;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.TestOnly;

final class AndroidTransportGate implements ITransportGate {

private final Context context;
private final @NotNull ILogger logger;
private final @NotNull SentryOptions options;

AndroidTransportGate(final @NotNull Context context, final @NotNull ILogger logger) {
this.context = context;
this.logger = logger;
AndroidTransportGate(final @NotNull SentryOptions options) {
this.options = options;
}

@Override
public boolean isConnected() {
return isConnected(ConnectivityChecker.getConnectionStatus(context, logger));
return isConnected(options.getConnectionStatusProvider().getConnectionStatus());
}

@TestOnly
boolean isConnected(final @NotNull ConnectivityChecker.Status status) {
boolean isConnected(final @NotNull IConnectionStatusProvider.ConnectionStatus status) {
// let's assume its connected if there's no permission or something as we can't really know
// whether is online or not.
switch (status) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import android.util.DisplayMetrics;
import io.sentry.DateUtils;
import io.sentry.SentryLevel;
import io.sentry.android.core.internal.util.ConnectivityChecker;
import io.sentry.android.core.internal.util.CpuInfoUtils;
import io.sentry.android.core.internal.util.DeviceOrientations;
import io.sentry.android.core.internal.util.RootChecker;
Expand Down Expand Up @@ -180,8 +179,8 @@ private void setDeviceIO(final @NotNull Device device, final boolean includeDyna
}

Boolean connected;
switch (ConnectivityChecker.getConnectionStatus(context, options.getLogger())) {
case NOT_CONNECTED:
switch (options.getConnectionStatusProvider().getConnectionStatus()) {
case DISCONNECTED:
connected = false;
break;
case CONNECTED:
Expand Down Expand Up @@ -223,8 +222,7 @@ private void setDeviceIO(final @NotNull Device device, final boolean includeDyna

if (device.getConnectionType() == null) {
// wifi, ethernet or cellular, null if none
device.setConnectionType(
ConnectivityChecker.getConnectionType(context, options.getLogger(), buildInfoProvider));
device.setConnectionType(options.getConnectionStatusProvider().getConnectionType());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import io.sentry.SentryLevel;
import io.sentry.SentryOptions;
import io.sentry.TypeCheckHint;
import io.sentry.android.core.internal.util.ConnectivityChecker;
import io.sentry.android.core.internal.util.AndroidConnectionStatusProvider;
import io.sentry.util.Objects;
import java.io.Closeable;
import java.io.IOException;
Expand Down Expand Up @@ -69,7 +69,7 @@ public void register(final @NotNull IHub hub, final @NotNull SentryOptions optio

networkCallback = new NetworkBreadcrumbsNetworkCallback(hub, buildInfoProvider);
final boolean registered =
ConnectivityChecker.registerNetworkCallback(
AndroidConnectionStatusProvider.registerNetworkCallback(
context, logger, buildInfoProvider, networkCallback);

// The specific error is logged in the ConnectivityChecker method
Expand All @@ -86,7 +86,7 @@ public void register(final @NotNull IHub hub, final @NotNull SentryOptions optio
@Override
public void close() throws IOException {
if (networkCallback != null) {
ConnectivityChecker.unregisterNetworkCallback(
AndroidConnectionStatusProvider.unregisterNetworkCallback(
context, logger, buildInfoProvider, networkCallback);
logger.log(SentryLevel.DEBUG, "NetworkBreadcrumbsIntegration remove.");
}
Expand Down Expand Up @@ -208,7 +208,7 @@ static class NetworkBreadcrumbConnectionDetail {
this.signalStrength = strength > -100 ? strength : 0;
this.isVpn = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
String connectionType =
ConnectivityChecker.getConnectionType(networkCapabilities, buildInfoProvider);
AndroidConnectionStatusProvider.getConnectionType(networkCapabilities, buildInfoProvider);
this.type = connectionType != null ? connectionType : "";
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
package io.sentry.android.core;

import io.sentry.IConnectionStatusProvider;
import io.sentry.IHub;
import io.sentry.Integration;
import io.sentry.SendCachedEnvelopeFireAndForgetIntegration;
import io.sentry.SentryLevel;
import io.sentry.SentryOptions;
import io.sentry.util.LazyEvaluator;
import io.sentry.util.Objects;
import java.io.Closeable;
import java.io.IOException;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

final class SendCachedEnvelopeIntegration implements Integration {
final class SendCachedEnvelopeIntegration
implements Integration, IConnectionStatusProvider.IConnectionStatusObserver, Closeable {

private final @NotNull SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForgetFactory
factory;
private final @NotNull LazyEvaluator<Boolean> startupCrashMarkerEvaluator;
private final AtomicBoolean startupCrashHandled = new AtomicBoolean(false);
private @Nullable IConnectionStatusProvider connectionStatusProvider;
private @Nullable IHub hub;
private @Nullable SentryAndroidOptions options;

public SendCachedEnvelopeIntegration(
final @NotNull SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForgetFactory factory,
Expand All @@ -29,7 +39,8 @@ public SendCachedEnvelopeIntegration(
@Override
public void register(@NotNull IHub hub, @NotNull SentryOptions options) {
Objects.requireNonNull(hub, "Hub is required");
final SentryAndroidOptions androidOptions =
this.hub = hub;
this.options =
Objects.requireNonNull(
(options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null,
"SentryAndroidOptions is required");
Expand All @@ -40,51 +51,82 @@ public void register(@NotNull IHub hub, @NotNull SentryOptions options) {
return;
}

connectionStatusProvider = options.getConnectionStatusProvider();
connectionStatusProvider.addConnectionStatusObserver(this);

sendCachedEnvelopes(hub, this.options);
}

@Override
public void close() throws IOException {
if (connectionStatusProvider != null) {
connectionStatusProvider.removeConnectionStatusObserver(this);
}
}

@Override
public void onConnectionStatusChanged(IConnectionStatusProvider.ConnectionStatus status) {
if (hub != null && options != null) {
sendCachedEnvelopes(hub, options);
}
}

private synchronized void sendCachedEnvelopes(
final @NotNull IHub hub, final @NotNull SentryAndroidOptions options) {

if (connectionStatusProvider != null
&& connectionStatusProvider.getConnectionStatus()
== IConnectionStatusProvider.ConnectionStatus.DISCONNECTED) {
options.getLogger().log(SentryLevel.INFO, "SendCachedEnvelopeIntegration, no connection.");
return;
}

final SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForget sender =
factory.create(hub, androidOptions);
factory.create(hub, options);

if (sender == null) {
androidOptions.getLogger().log(SentryLevel.ERROR, "SendFireAndForget factory is null.");
options.getLogger().log(SentryLevel.ERROR, "SendCachedEnvelopeIntegration factory is null.");
return;
}

try {
Future<?> future =
androidOptions
final Future<?> future =
options
.getExecutorService()
.submit(
() -> {
try {
sender.send();
} catch (Throwable e) {
androidOptions
options
.getLogger()
.log(SentryLevel.ERROR, "Failed trying to send cached events.", e);
}
});

if (startupCrashMarkerEvaluator.getValue()) {
androidOptions
.getLogger()
.log(SentryLevel.DEBUG, "Startup Crash marker exists, blocking flush.");
// startupCrashMarkerEvaluator remains true on subsequent runs, let's ensure we only block on
// the very first execution (=app start)
if (startupCrashMarkerEvaluator.getValue()
&& startupCrashHandled.compareAndSet(false, true)) {
options.getLogger().log(SentryLevel.DEBUG, "Startup Crash marker exists, blocking flush.");
try {
future.get(androidOptions.getStartupCrashFlushTimeoutMillis(), TimeUnit.MILLISECONDS);
future.get(options.getStartupCrashFlushTimeoutMillis(), TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
androidOptions
options
.getLogger()
.log(SentryLevel.DEBUG, "Synchronous send timed out, continuing in the background.");
}
}
androidOptions.getLogger().log(SentryLevel.DEBUG, "SendCachedEnvelopeIntegration installed.");
options.getLogger().log(SentryLevel.DEBUG, "SendCachedEnvelopeIntegration installed.");
} catch (RejectedExecutionException e) {
androidOptions
options
.getLogger()
.log(
SentryLevel.ERROR,
"Failed to call the executor. Cached events will not be sent. Did you call Sentry.close()?",
e);
} catch (Throwable e) {
androidOptions
options
.getLogger()
.log(SentryLevel.ERROR, "Failed to call the executor. Cached events will not be sent", e);
}
Expand Down
Loading

0 comments on commit 4881e6a

Please sign in to comment.