diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/DiskToZipkinExporter.java b/splunk-otel-android/src/main/java/com/splunk/rum/DiskToZipkinExporter.java index 82023cff7..9d5eb0cf4 100644 --- a/splunk-otel-android/src/main/java/com/splunk/rum/DiskToZipkinExporter.java +++ b/splunk-otel-android/src/main/java/com/splunk/rum/DiskToZipkinExporter.java @@ -22,6 +22,7 @@ import android.util.Log; import androidx.annotation.Nullable; +import io.opentelemetry.rum.internal.instrumentation.network.CurrentNetworkProvider; import java.io.File; import java.util.Comparator; import java.util.List; @@ -39,7 +40,7 @@ class DiskToZipkinExporter { static final double DEFAULT_MAX_UNCOMPRESSED_BANDWIDTH = 15.0 * 1024; private final ScheduledExecutorService threadPool; - private final ConnectionUtil connectionUtil; + private final CurrentNetworkProvider currentNetworkProvider; private final FileSender fileSender; private final File spanFilesPath; private final FileUtils fileUtils; @@ -48,7 +49,7 @@ class DiskToZipkinExporter { DiskToZipkinExporter(Builder builder) { this.threadPool = builder.threadPool; - this.connectionUtil = requireNonNull(builder.connectionUtil); + this.currentNetworkProvider = requireNonNull(builder.currentNetworkProvider); this.fileSender = requireNonNull(builder.fileSender); this.spanFilesPath = requireNonNull(builder.spanFilesPath); this.fileUtils = builder.fileUtils; @@ -72,7 +73,7 @@ void doExportCycle() { } private void exportPendingFiles() { - if (!connectionUtil.refreshNetworkStatus().isOnline()) { + if (!currentNetworkProvider.refreshNetworkStatus().isOnline()) { Log.i( SplunkRum.LOG_TAG, "Network offline, leaving spans on disk for for eventual export."); @@ -123,7 +124,7 @@ static class Builder { @Nullable private FileSender fileSender; @Nullable private BandwidthTracker bandwidthTracker; private ScheduledExecutorService threadPool = Executors.newSingleThreadScheduledExecutor(); - @Nullable private ConnectionUtil connectionUtil; + @Nullable private CurrentNetworkProvider currentNetworkProvider; @Nullable private File spanFilesPath; private FileUtils fileUtils = new FileUtils(); private double bandwidthLimit = DEFAULT_MAX_UNCOMPRESSED_BANDWIDTH; @@ -133,8 +134,8 @@ Builder threadPool(ScheduledExecutorService threadPool) { return this; } - Builder connectionUtil(ConnectionUtil connectionUtil) { - this.connectionUtil = connectionUtil; + Builder connectionUtil(CurrentNetworkProvider currentNetworkProvider) { + this.currentNetworkProvider = currentNetworkProvider; return this; } diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/MemoryBufferingExporter.java b/splunk-otel-android/src/main/java/com/splunk/rum/MemoryBufferingExporter.java index 08ecf26ab..61d79f06c 100644 --- a/splunk-otel-android/src/main/java/com/splunk/rum/MemoryBufferingExporter.java +++ b/splunk-otel-android/src/main/java/com/splunk/rum/MemoryBufferingExporter.java @@ -18,6 +18,7 @@ import android.util.Log; import androidx.annotation.NonNull; +import io.opentelemetry.rum.internal.instrumentation.network.CurrentNetworkProvider; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.sdk.trace.export.SpanExporter; @@ -30,21 +31,21 @@ class MemoryBufferingExporter implements SpanExporter { private static final int MAX_BACKLOG_SIZE = 100; - private final ConnectionUtil connectionUtil; + private final CurrentNetworkProvider currentNetworkProvider; private final SpanExporter delegate; // note: no need to make this queue thread-safe since it will only ever be called from the // BatchSpanProcessor worker thread. private final Queue backlog = new ArrayDeque<>(MAX_BACKLOG_SIZE); - MemoryBufferingExporter(ConnectionUtil connectionUtil, SpanExporter delegate) { - this.connectionUtil = connectionUtil; + MemoryBufferingExporter(CurrentNetworkProvider currentNetworkProvider, SpanExporter delegate) { + this.currentNetworkProvider = currentNetworkProvider; this.delegate = delegate; } @Override public CompletableResultCode export(Collection spans) { backlog.addAll(spans); - if (!connectionUtil.refreshNetworkStatus().isOnline()) { + if (!currentNetworkProvider.refreshNetworkStatus().isOnline()) { Log.i( SplunkRum.LOG_TAG, "Network offline, buffering " + spans.size() + " spans for eventual export."); diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/NetworkMonitor.java b/splunk-otel-android/src/main/java/com/splunk/rum/NetworkMonitor.java deleted file mode 100644 index a958ef378..000000000 --- a/splunk-otel-android/src/main/java/com/splunk/rum/NetworkMonitor.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.splunk.rum; - -import static io.opentelemetry.api.common.AttributeKey.stringKey; -import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_HOST_CONNECTION_TYPE; - -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.api.trace.Tracer; -import io.opentelemetry.rum.internal.instrumentation.ApplicationStateListener; -import java.util.concurrent.atomic.AtomicBoolean; - -class NetworkMonitor implements ApplicationStateListener { - static final AttributeKey NETWORK_STATUS_KEY = stringKey("network.status"); - - private final ConnectionUtil connectionUtil; - private final AtomicBoolean shouldEmitChangeEvents = new AtomicBoolean(true); - - NetworkMonitor(ConnectionUtil connectionUtil) { - this.connectionUtil = connectionUtil; - } - - void addConnectivityListener(Tracer tracer) { - connectionUtil.addNetworkChangeListener( - new TracingNetworkChangeListener(tracer, shouldEmitChangeEvents)); - } - - @Override - public void onApplicationForegrounded() { - shouldEmitChangeEvents.set(true); - } - - @Override - public void onApplicationBackgrounded() { - shouldEmitChangeEvents.set(false); - } - - // visibleForTesting - static class TracingNetworkChangeListener implements NetworkChangeListener { - - private final Tracer tracer; - private final AtomicBoolean shouldEmitChangeEvents; - private final CurrentNetworkAttributesExtractor networkAttributesExtractor = - new CurrentNetworkAttributesExtractor(); - - TracingNetworkChangeListener(Tracer tracer, AtomicBoolean shouldEmitChangeEvents) { - this.tracer = tracer; - this.shouldEmitChangeEvents = shouldEmitChangeEvents; - } - - @Override - public void onNetworkChange(CurrentNetwork activeNetwork) { - if (!shouldEmitChangeEvents.get()) { - return; - } - if (activeNetwork.getState() == NetworkState.NO_NETWORK_AVAILABLE) { - tracer.spanBuilder("network.change") - .setAttribute(NETWORK_STATUS_KEY, "lost") - .startSpan() - // put this after span start to override what might be set in the - // RumAttributeAppender. - .setAttribute( - NET_HOST_CONNECTION_TYPE, activeNetwork.getState().getHumanName()) - .end(); - } else { - Span available = - tracer.spanBuilder("network.change") - .setAttribute(NETWORK_STATUS_KEY, "available") - .startSpan(); - // put these after span start to override what might be set in the - // RumAttributeAppender. - available.setAllAttributes(networkAttributesExtractor.extract(activeNetwork)); - available.end(); - } - } - } -} diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/RumInitializer.java b/splunk-otel-android/src/main/java/com/splunk/rum/RumInitializer.java index 7bb0160ae..7a3cd4df5 100644 --- a/splunk-otel-android/src/main/java/com/splunk/rum/RumInitializer.java +++ b/splunk-otel-android/src/main/java/com/splunk/rum/RumInitializer.java @@ -47,11 +47,15 @@ import io.opentelemetry.rum.internal.OpenTelemetryRumBuilder; import io.opentelemetry.rum.internal.instrumentation.anr.AnrDetector; import io.opentelemetry.rum.internal.instrumentation.crash.CrashReporter; +import io.opentelemetry.rum.internal.instrumentation.network.CurrentNetworkProvider; +import io.opentelemetry.rum.internal.instrumentation.network.NetworkAttributesSpanAppender; +import io.opentelemetry.rum.internal.instrumentation.network.NetworkChangeMonitor; import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.resources.ResourceBuilder; import io.opentelemetry.sdk.trace.SpanLimits; +import io.opentelemetry.sdk.trace.SpanProcessor; import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; @@ -62,6 +66,7 @@ import java.util.Collection; import java.util.List; import java.util.concurrent.TimeUnit; +import java.util.function.Function; import java.util.function.Supplier; import java.util.logging.Level; import zipkin2.reporter.Sender; @@ -87,7 +92,9 @@ class RumInitializer { this.timingClock = startupTimer.startupClock; } - SplunkRum initialize(ConnectionUtil.Factory connectionUtilFactory, Looper mainLooper) { + SplunkRum initialize( + Function currentNetworkProviderFactory, + Looper mainLooper) { VisibleScreenTracker visibleScreenTracker = new VisibleScreenTracker(); long startTimeNanos = timingClock.now(); @@ -97,7 +104,8 @@ SplunkRum initialize(ConnectionUtil.Factory connectionUtilFactory, Looper mainLo initializationEvents.add( new RumInitializer.InitializationEvent("resourceInitialized", timingClock.now())); - ConnectionUtil connectionUtil = connectionUtilFactory.createAndStart(application); + CurrentNetworkProvider currentNetworkProvider = + currentNetworkProviderFactory.apply(application); initializationEvents.add( new InitializationEvent("connectionUtilInitialized", timingClock.now())); @@ -105,15 +113,15 @@ SplunkRum initialize(ConnectionUtil.Factory connectionUtilFactory, Looper mainLo GlobalAttributesSpanAppender.create(builder.globalAttributes); otelRumBuilder.addTracerProviderCustomizer( (tracerProviderBuilder, app) -> { - NetworkAttributesAppender networkAttributesAppender = - new NetworkAttributesAppender(connectionUtil); + SpanProcessor networkAttributesSpanAppender = + NetworkAttributesSpanAppender.create(currentNetworkProvider); ScreenAttributesAppender screenAttributesAppender = new ScreenAttributesAppender(visibleScreenTracker); initializationEvents.add( new RumInitializer.InitializationEvent( "attributeAppenderInitialized", timingClock.now())); - SpanExporter zipkinExporter = buildFilteringExporter(connectionUtil); + SpanExporter zipkinExporter = buildFilteringExporter(currentNetworkProvider); initializationEvents.add( new RumInitializer.InitializationEvent( "exporterInitialized", timingClock.now())); @@ -126,7 +134,7 @@ SplunkRum initialize(ConnectionUtil.Factory connectionUtilFactory, Looper mainLo tracerProviderBuilder .addSpanProcessor(globalAttributesSpanAppender) - .addSpanProcessor(networkAttributesAppender) + .addSpanProcessor(networkAttributesSpanAppender) .addSpanProcessor(screenAttributesAppender) .addSpanProcessor(batchSpanProcessor) .setSpanLimits( @@ -175,12 +183,8 @@ SplunkRum initialize(ConnectionUtil.Factory connectionUtilFactory, Looper mainLo if (builder.networkMonitorEnabled) { otelRumBuilder.addInstrumentation( instrumentedApplication -> { - NetworkMonitor networkMonitor = new NetworkMonitor(connectionUtil); - networkMonitor.addConnectivityListener( - instrumentedApplication - .getOpenTelemetrySdk() - .getTracer(SplunkRum.RUM_TRACER_NAME)); - instrumentedApplication.registerApplicationStateListener(networkMonitor); + NetworkChangeMonitor.create(currentNetworkProvider) + .installOn(instrumentedApplication); initializationEvents.add( new RumInitializer.InitializationEvent( "networkMonitorInitialized", timingClock.now())); @@ -336,8 +340,8 @@ private Resource buildResource(String applicationName, String rumVersion) { } // visible for testing - SpanExporter buildFilteringExporter(ConnectionUtil connectionUtil) { - SpanExporter exporter = buildExporter(connectionUtil); + SpanExporter buildFilteringExporter(CurrentNetworkProvider currentNetworkProvider) { + SpanExporter exporter = buildExporter(currentNetworkProvider); SpanExporter splunkTranslatedExporter = new SplunkSpanDataModifier(exporter, builder.reactNativeSupportEnabled); SpanExporter filteredExporter = builder.decorateWithSpanFilter(splunkTranslatedExporter); @@ -346,7 +350,7 @@ SpanExporter buildFilteringExporter(ConnectionUtil connectionUtil) { return filteredExporter; } - private SpanExporter buildExporter(ConnectionUtil connectionUtil) { + private SpanExporter buildExporter(CurrentNetworkProvider currentNetworkProvider) { if (builder.debugEnabled) { // tell the Zipkin exporter to shut up already. We're on mobile, network stuff happens. // we'll do our best to hang on to the spans with the wrapping BufferingExporter. @@ -356,13 +360,14 @@ private SpanExporter buildExporter(ConnectionUtil connectionUtil) { } if (builder.diskBufferingEnabled) { - return buildStorageBufferingExporter(connectionUtil); + return buildStorageBufferingExporter(currentNetworkProvider); } - return buildMemoryBufferingThrottledExporter(connectionUtil); + return buildMemoryBufferingThrottledExporter(currentNetworkProvider); } - private SpanExporter buildStorageBufferingExporter(ConnectionUtil connectionUtil) { + private SpanExporter buildStorageBufferingExporter( + CurrentNetworkProvider currentNetworkProvider) { Sender sender = OkHttpSender.newBuilder().endpoint(getEndpoint()).build(); File spanFilesPath = FileUtils.getSpansDirectory(application); BandwidthTracker bandwidthTracker = new BandwidthTracker(); @@ -371,7 +376,7 @@ private SpanExporter buildStorageBufferingExporter(ConnectionUtil connectionUtil FileSender.builder().sender(sender).bandwidthTracker(bandwidthTracker).build(); DiskToZipkinExporter diskToZipkinExporter = DiskToZipkinExporter.builder() - .connectionUtil(connectionUtil) + .connectionUtil(currentNetworkProvider) .fileSender(fileSender) .bandwidthTracker(bandwidthTracker) .spanFilesPath(spanFilesPath) @@ -386,11 +391,12 @@ private String getEndpoint() { return builder.beaconEndpoint + "?auth=" + builder.rumAccessToken; } - private SpanExporter buildMemoryBufferingThrottledExporter(ConnectionUtil connectionUtil) { + private SpanExporter buildMemoryBufferingThrottledExporter( + CurrentNetworkProvider currentNetworkProvider) { String endpoint = getEndpoint(); SpanExporter zipkinSpanExporter = getCoreSpanExporter(endpoint); return ThrottlingExporter.newBuilder( - new MemoryBufferingExporter(connectionUtil, zipkinSpanExporter)) + new MemoryBufferingExporter(currentNetworkProvider, zipkinSpanExporter)) .categorizeByAttribute(SplunkRum.COMPONENT_KEY) .maxSpansInWindow(100) .windowSize(Duration.ofSeconds(30)) diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/SplunkRum.java b/splunk-otel-android/src/main/java/com/splunk/rum/SplunkRum.java index 3054a665f..b7365db7b 100644 --- a/splunk-otel-android/src/main/java/com/splunk/rum/SplunkRum.java +++ b/splunk-otel-android/src/main/java/com/splunk/rum/SplunkRum.java @@ -36,9 +36,11 @@ import io.opentelemetry.instrumentation.okhttp.v3_0.OkHttpTelemetry; import io.opentelemetry.rum.internal.GlobalAttributesSpanAppender; import io.opentelemetry.rum.internal.OpenTelemetryRum; +import io.opentelemetry.rum.internal.instrumentation.network.CurrentNetworkProvider; import io.opentelemetry.sdk.OpenTelemetrySdk; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import java.util.function.Function; import okhttp3.Call; import okhttp3.OkHttpClient; @@ -100,7 +102,7 @@ public static SplunkRumBuilder builder() { static SplunkRum initialize( SplunkRumBuilder builder, Application application, - ConnectionUtil.Factory connectionUtilFactory) { + Function currentNetworkProviderFactory) { if (INSTANCE != null) { Log.w(LOG_TAG, "Singleton SplunkRum instance has already been initialized."); return INSTANCE; @@ -108,7 +110,7 @@ static SplunkRum initialize( INSTANCE = new RumInitializer(builder, application, startupTimer) - .initialize(connectionUtilFactory, Looper.getMainLooper()); + .initialize(currentNetworkProviderFactory, Looper.getMainLooper()); if (builder.debugEnabled) { Log.i( diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/SplunkRumBuilder.java b/splunk-otel-android/src/main/java/com/splunk/rum/SplunkRumBuilder.java index d7cae4bb1..da63d79b4 100644 --- a/splunk-otel-android/src/main/java/com/splunk/rum/SplunkRumBuilder.java +++ b/splunk-otel-android/src/main/java/com/splunk/rum/SplunkRumBuilder.java @@ -22,6 +22,7 @@ import android.util.Log; import androidx.annotation.Nullable; import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.rum.internal.instrumentation.network.CurrentNetworkProvider; import io.opentelemetry.sdk.trace.export.SpanExporter; import java.time.Duration; import java.util.function.Consumer; @@ -314,7 +315,7 @@ public SplunkRum build(Application application) { throw new IllegalStateException( "You must provide a rumAccessToken, a realm (or full beaconEndpoint), and an applicationName to create a valid Config instance."); } - return SplunkRum.initialize(this, application, new ConnectionUtil.Factory()); + return SplunkRum.initialize(this, application, CurrentNetworkProvider::createAndStart); } SpanExporter decorateWithSpanFilter(SpanExporter exporter) { diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/Carrier.java b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/Carrier.java similarity index 98% rename from splunk-otel-android/src/main/java/com/splunk/rum/Carrier.java rename to splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/Carrier.java index a1080ca6e..a6f61d292 100644 --- a/splunk-otel-android/src/main/java/com/splunk/rum/Carrier.java +++ b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/Carrier.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.splunk.rum; +package io.opentelemetry.rum.internal.instrumentation.network; import android.os.Build; import android.telephony.TelephonyManager; diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/CarrierFinder.java b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/CarrierFinder.java similarity index 96% rename from splunk-otel-android/src/main/java/com/splunk/rum/CarrierFinder.java rename to splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/CarrierFinder.java index 9215bf902..23ca02a88 100644 --- a/splunk-otel-android/src/main/java/com/splunk/rum/CarrierFinder.java +++ b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/CarrierFinder.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.splunk.rum; +package io.opentelemetry.rum.internal.instrumentation.network; import android.os.Build; import android.telephony.TelephonyManager; diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/CurrentNetwork.java b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/CurrentNetwork.java similarity index 83% rename from splunk-otel-android/src/main/java/com/splunk/rum/CurrentNetwork.java rename to splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/CurrentNetwork.java index 86e095839..4a7ab9d56 100644 --- a/splunk-otel-android/src/main/java/com/splunk/rum/CurrentNetwork.java +++ b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/CurrentNetwork.java @@ -14,13 +14,15 @@ * limitations under the License. */ -package com.splunk.rum; +package io.opentelemetry.rum.internal.instrumentation.network; import android.os.Build; import androidx.annotation.Nullable; import java.util.Objects; -final class CurrentNetwork { +/** A value class representing the current network that the device is connected to. */ +public final class CurrentNetwork { + @Nullable private final Carrier carrier; private final NetworkState state; @Nullable private final String subType; @@ -31,7 +33,8 @@ private CurrentNetwork(Builder builder) { this.subType = builder.subType; } - boolean isOnline() { + /** Returns {@code true} if the device has internet connection; {@code false} otherwise. */ + public boolean isOnline() { return getState() != NetworkState.NO_NETWORK_AVAILABLE; } @@ -44,55 +47,27 @@ String getSubType() { return subType; } - @Override - public String toString() { - return "CurrentNetwork{" - + "carrier=" - + carrier - + ", state=" - + state - + ", subType='" - + subType - + '\'' - + '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - CurrentNetwork that = (CurrentNetwork) o; - return Objects.equals(carrier, that.carrier) - && state == that.state - && Objects.equals(subType, that.subType); - } - - @Override - public int hashCode() { - return Objects.hash(carrier, state, subType); - } - @SuppressWarnings("NullAway") @Nullable - public String getCarrierCountryCode() { + String getCarrierCountryCode() { return haveCarrier() ? carrier.getMobileCountryCode() : null; } @SuppressWarnings("NullAway") @Nullable - public String getCarrierIsoCountryCode() { + String getCarrierIsoCountryCode() { return haveCarrier() ? carrier.getIsoCountryCode() : null; } @SuppressWarnings("NullAway") @Nullable - public String getCarrierNetworkCode() { + String getCarrierNetworkCode() { return haveCarrier() ? carrier.getMobileNetworkCode() : null; } @SuppressWarnings("NullAway") @Nullable - public String getCarrierName() { + String getCarrierName() { return haveCarrier() ? carrier.getName() : null; } @@ -100,6 +75,34 @@ private boolean haveCarrier() { return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) && (carrier != null); } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CurrentNetwork that = (CurrentNetwork) o; + return Objects.equals(carrier, that.carrier) + && state == that.state + && Objects.equals(subType, that.subType); + } + + @Override + public int hashCode() { + return Objects.hash(carrier, state, subType); + } + + @Override + public String toString() { + return "CurrentNetwork{" + + "carrier=" + + carrier + + ", state=" + + state + + ", subType='" + + subType + + '\'' + + '}'; + } + static Builder builder(NetworkState state) { return new Builder(state); } @@ -109,22 +112,22 @@ static class Builder { private final NetworkState state; @Nullable private String subType; - public Builder(NetworkState state) { + private Builder(NetworkState state) { this.state = state; } - CurrentNetwork build() { - return new CurrentNetwork(this); - } - - public Builder carrier(@Nullable Carrier carrier) { + Builder carrier(@Nullable Carrier carrier) { this.carrier = carrier; return this; } - public Builder subType(@Nullable String subType) { + Builder subType(@Nullable String subType) { this.subType = subType; return this; } + + CurrentNetwork build() { + return new CurrentNetwork(this); + } } } diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/CurrentNetworkAttributesExtractor.java b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/CurrentNetworkAttributesExtractor.java similarity index 95% rename from splunk-otel-android/src/main/java/com/splunk/rum/CurrentNetworkAttributesExtractor.java rename to splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/CurrentNetworkAttributesExtractor.java index c3de25fa5..3662cb1ae 100644 --- a/splunk-otel-android/src/main/java/com/splunk/rum/CurrentNetworkAttributesExtractor.java +++ b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/CurrentNetworkAttributesExtractor.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.splunk.rum; +package io.opentelemetry.rum.internal.instrumentation.network; import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_HOST_CARRIER_ICC; import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_HOST_CARRIER_MCC; @@ -28,7 +28,7 @@ import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; -class CurrentNetworkAttributesExtractor { +final class CurrentNetworkAttributesExtractor { Attributes extract(CurrentNetwork network) { AttributesBuilder builder = diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/ConnectionUtil.java b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/CurrentNetworkProvider.java similarity index 70% rename from splunk-otel-android/src/main/java/com/splunk/rum/ConnectionUtil.java rename to splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/CurrentNetworkProvider.java index ba5fce573..0b9e1b726 100644 --- a/splunk-otel-android/src/main/java/com/splunk/rum/ConnectionUtil.java +++ b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/CurrentNetworkProvider.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.splunk.rum; +package io.opentelemetry.rum.internal.instrumentation.network; import android.app.Application; import android.content.Context; @@ -31,22 +31,43 @@ // note: based on ideas from stack overflow: // https://stackoverflow.com/questions/32547006/connectivitymanager-getnetworkinfoint-deprecated -class ConnectionUtil { + +/** + * A provider of {@link CurrentNetwork} information. Registers itself in the Android {@link + * ConnectivityManager} and listens for network changes. + */ +public final class CurrentNetworkProvider { static final CurrentNetwork NO_NETWORK = CurrentNetwork.builder(NetworkState.NO_NETWORK_AVAILABLE).build(); static final CurrentNetwork UNKNOWN_NETWORK = CurrentNetwork.builder(NetworkState.TRANSPORT_UNKNOWN).build(); + /** + * Creates a new {@link CurrentNetworkProvider} instance and registers network callbacks in the + * Android {@link ConnectivityManager}. + */ + public static CurrentNetworkProvider createAndStart(Application application) { + Context context = application.getApplicationContext(); + CurrentNetworkProvider currentNetworkProvider = + new CurrentNetworkProvider(NetworkDetector.create(context)); + currentNetworkProvider.startMonitoring( + CurrentNetworkProvider::createNetworkMonitoringRequest, + (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)); + return currentNetworkProvider; + } + private final NetworkDetector networkDetector; private volatile CurrentNetwork currentNetwork = UNKNOWN_NETWORK; private final List listeners = new CopyOnWriteArrayList<>(); - ConnectionUtil(NetworkDetector networkDetector) { + // visible for tests + CurrentNetworkProvider(NetworkDetector networkDetector) { this.networkDetector = networkDetector; } + // visible for tests void startMonitoring( Supplier createNetworkMonitoringRequest, ConnectivityManager connectivityManager) { @@ -56,7 +77,7 @@ void startMonitoring( } catch (Exception e) { // if this fails, we'll go without network change events. Log.w( - SplunkRum.LOG_TAG, + "OpenTelemetryRum", "Failed to register network callbacks. Automatic network monitoring is disabled.", e); } @@ -73,7 +94,8 @@ private void registerNetworkCallbacks( } } - CurrentNetwork refreshNetworkStatus() { + /** Returns up-to-date {@linkplain CurrentNetwork current network information}. */ + public CurrentNetwork refreshNetworkStatus() { try { currentNetwork = networkDetector.detectCurrentNetwork(); } catch (Exception e) { @@ -84,7 +106,7 @@ CurrentNetwork refreshNetworkStatus() { return currentNetwork; } - static NetworkRequest createNetworkMonitoringRequest() { + private static NetworkRequest createNetworkMonitoringRequest() { // note: this throws an NPE when running in junit without robolectric, due to Android return new NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) @@ -95,7 +117,7 @@ static NetworkRequest createNetworkMonitoringRequest() { .build(); } - CurrentNetwork getActiveNetwork() { + CurrentNetwork getCurrentNetwork() { return currentNetwork; } @@ -109,39 +131,27 @@ private void notifyListeners(CurrentNetwork activeNetwork) { } } - private class ConnectionMonitor extends ConnectivityManager.NetworkCallback { + private final class ConnectionMonitor extends ConnectivityManager.NetworkCallback { @Override public void onAvailable(@NonNull Network network) { CurrentNetwork activeNetwork = refreshNetworkStatus(); - Log.d(SplunkRum.LOG_TAG, " onAvailable: activeNetwork=" + activeNetwork); + Log.d("OpenTelemetryRum", " onAvailable: currentNetwork=" + activeNetwork); notifyListeners(activeNetwork); } @Override public void onLost(@NonNull Network network) { - // it seems that the "currentActiveNetwork" is still the one that is being lost, so for + // it seems that the "currentNetwork" is still the one that is being lost, so for // this method, we'll force it to be NO_NETWORK, rather than relying on the // ConnectivityManager to have the right // state at the right time during this event. - CurrentNetwork activeNetwork = NO_NETWORK; - currentNetwork = activeNetwork; - Log.d(SplunkRum.LOG_TAG, " onLost: activeNetwork=" + activeNetwork); - - notifyListeners(activeNetwork); - } - } - - static class Factory { + CurrentNetwork currentNetwork = NO_NETWORK; + CurrentNetworkProvider.this.currentNetwork = currentNetwork; + Log.d("OpenTelemetryRum", " onLost: currentNetwork=" + currentNetwork); - ConnectionUtil createAndStart(Application application) { - Context context = application.getApplicationContext(); - ConnectionUtil connectionUtil = new ConnectionUtil(NetworkDetector.create(context)); - connectionUtil.startMonitoring( - ConnectionUtil::createNetworkMonitoringRequest, - (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)); - return connectionUtil; + notifyListeners(currentNetwork); } } } diff --git a/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkApplicationListener.java b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkApplicationListener.java new file mode 100644 index 000000000..6cdd19919 --- /dev/null +++ b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkApplicationListener.java @@ -0,0 +1,73 @@ +/* + * Copyright Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.rum.internal.instrumentation.network; + +import static io.opentelemetry.api.common.AttributeKey.stringKey; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.rum.internal.instrumentation.ApplicationStateListener; +import java.util.concurrent.atomic.AtomicBoolean; + +class NetworkApplicationListener implements ApplicationStateListener { + static final AttributeKey NETWORK_STATUS_KEY = stringKey("network.status"); + + private final CurrentNetworkProvider currentNetworkProvider; + private final AtomicBoolean shouldEmitChangeEvents = new AtomicBoolean(true); + + NetworkApplicationListener(CurrentNetworkProvider currentNetworkProvider) { + this.currentNetworkProvider = currentNetworkProvider; + } + + void startMonitoring(Instrumenter instrumenter) { + currentNetworkProvider.addNetworkChangeListener( + new TracingNetworkChangeListener(instrumenter, shouldEmitChangeEvents)); + } + + @Override + public void onApplicationForegrounded() { + shouldEmitChangeEvents.set(true); + } + + @Override + public void onApplicationBackgrounded() { + shouldEmitChangeEvents.set(false); + } + + private static final class TracingNetworkChangeListener implements NetworkChangeListener { + + private final Instrumenter instrumenter; + private final AtomicBoolean shouldEmitChangeEvents; + + TracingNetworkChangeListener( + Instrumenter instrumenter, + AtomicBoolean shouldEmitChangeEvents) { + this.instrumenter = instrumenter; + this.shouldEmitChangeEvents = shouldEmitChangeEvents; + } + + @Override + public void onNetworkChange(CurrentNetwork currentNetwork) { + if (!shouldEmitChangeEvents.get()) { + return; + } + Context context = instrumenter.start(Context.current(), currentNetwork); + instrumenter.end(context, currentNetwork, null, null); + } + } +} diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/NetworkAttributesAppender.java b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkAttributesSpanAppender.java similarity index 57% rename from splunk-otel-android/src/main/java/com/splunk/rum/NetworkAttributesAppender.java rename to splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkAttributesSpanAppender.java index e802c6dda..948286c51 100644 --- a/splunk-otel-android/src/main/java/com/splunk/rum/NetworkAttributesAppender.java +++ b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkAttributesSpanAppender.java @@ -14,26 +14,38 @@ * limitations under the License. */ -package com.splunk.rum; +package io.opentelemetry.rum.internal.instrumentation.network; +import io.opentelemetry.api.common.Attributes; import io.opentelemetry.context.Context; import io.opentelemetry.sdk.trace.ReadWriteSpan; import io.opentelemetry.sdk.trace.ReadableSpan; import io.opentelemetry.sdk.trace.SpanProcessor; -final class NetworkAttributesAppender implements SpanProcessor { +/** + * A {@link SpanProcessor} implementation that appends a set of {@linkplain Attributes attributes} + * describing the {@linkplain CurrentNetwork current network} to every span that is exported. + * + *

This class is internal and not for public use. Its APIs are unstable and can change at any + * time. + */ +public final class NetworkAttributesSpanAppender implements SpanProcessor { + + public static SpanProcessor create(CurrentNetworkProvider currentNetworkProvider) { + return new NetworkAttributesSpanAppender(currentNetworkProvider); + } - private final ConnectionUtil connectionUtil; + private final CurrentNetworkProvider currentNetworkProvider; private final CurrentNetworkAttributesExtractor networkAttributesExtractor = new CurrentNetworkAttributesExtractor(); - NetworkAttributesAppender(ConnectionUtil connectionUtil) { - this.connectionUtil = connectionUtil; + NetworkAttributesSpanAppender(CurrentNetworkProvider currentNetworkProvider) { + this.currentNetworkProvider = currentNetworkProvider; } @Override public void onStart(Context parentContext, ReadWriteSpan span) { - CurrentNetwork currentNetwork = connectionUtil.getActiveNetwork(); + CurrentNetwork currentNetwork = currentNetworkProvider.getCurrentNetwork(); span.setAllAttributes(networkAttributesExtractor.extract(currentNetwork)); } diff --git a/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkChangeAttributesExtractor.java b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkChangeAttributesExtractor.java new file mode 100644 index 000000000..e2eed40b1 --- /dev/null +++ b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkChangeAttributesExtractor.java @@ -0,0 +1,59 @@ +/* + * Copyright Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.rum.internal.instrumentation.network; + +import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_HOST_CONNECTION_TYPE; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; + +final class NetworkChangeAttributesExtractor implements AttributesExtractor { + + static final AttributeKey NETWORK_STATUS_KEY = stringKey("network.status"); + + private final CurrentNetworkAttributesExtractor networkAttributesExtractor = + new CurrentNetworkAttributesExtractor(); + + @Override + public void onStart( + AttributesBuilder attributes, Context parentContext, CurrentNetwork currentNetwork) { + String status = + currentNetwork.getState() == NetworkState.NO_NETWORK_AVAILABLE + ? "lost" + : "available"; + attributes.put(NETWORK_STATUS_KEY, status); + } + + @Override + public void onEnd( + AttributesBuilder attributes, + Context context, + CurrentNetwork currentNetwork, + Void unused, + Throwable error) { + // put these after span start to override what might be set in the + // NetworkAttributesSpanAppender. + if (currentNetwork.getState() == NetworkState.NO_NETWORK_AVAILABLE) { + attributes.put(NET_HOST_CONNECTION_TYPE, currentNetwork.getState().getHumanName()); + } else { + attributes.putAll(networkAttributesExtractor.extract(currentNetwork)); + } + } +} diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/NetworkChangeListener.java b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkChangeListener.java similarity index 91% rename from splunk-otel-android/src/main/java/com/splunk/rum/NetworkChangeListener.java rename to splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkChangeListener.java index d108a6ee6..8121b3cca 100644 --- a/splunk-otel-android/src/main/java/com/splunk/rum/NetworkChangeListener.java +++ b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkChangeListener.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.splunk.rum; +package io.opentelemetry.rum.internal.instrumentation.network; interface NetworkChangeListener { diff --git a/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkChangeMonitor.java b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkChangeMonitor.java new file mode 100644 index 000000000..7cd12e03d --- /dev/null +++ b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkChangeMonitor.java @@ -0,0 +1,70 @@ +/* + * Copyright Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.rum.internal.instrumentation.network; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.rum.internal.instrumentation.InstrumentedApplication; +import java.util.ArrayList; +import java.util.List; + +/** + * Entrypoint for installing the network change monitoring instrumentation. + * + *

This class is internal and not for public use. Its APIs are unstable and can change at any + * time. + */ +public final class NetworkChangeMonitor { + + public static NetworkChangeMonitor create(CurrentNetworkProvider currentNetworkProvider) { + return builder(currentNetworkProvider).build(); + } + + public static NetworkChangeMonitorBuilder builder( + CurrentNetworkProvider currentNetworkProvider) { + return new NetworkChangeMonitorBuilder(currentNetworkProvider); + } + + private final CurrentNetworkProvider currentNetworkProvider; + private final List> additionalExtractors; + + NetworkChangeMonitor(NetworkChangeMonitorBuilder builder) { + this.currentNetworkProvider = builder.currentNetworkProvider; + this.additionalExtractors = new ArrayList<>(builder.additionalExtractors); + } + + /** + * Installs the network change monitoring instrumentation on the given {@link + * InstrumentedApplication}. + */ + public void installOn(InstrumentedApplication instrumentedApplication) { + NetworkApplicationListener networkApplicationListener = + new NetworkApplicationListener(currentNetworkProvider); + networkApplicationListener.startMonitoring( + buildInstrumenter(instrumentedApplication.getOpenTelemetrySdk())); + instrumentedApplication.registerApplicationStateListener(networkApplicationListener); + } + + private Instrumenter buildInstrumenter(OpenTelemetry openTelemetry) { + return Instrumenter.builder( + openTelemetry, "io.opentelemetry.network", network -> "network.change") + .addAttributesExtractor(new NetworkChangeAttributesExtractor()) + .addAttributesExtractors(additionalExtractors) + .buildInstrumenter(); + } +} diff --git a/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkChangeMonitorBuilder.java b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkChangeMonitorBuilder.java new file mode 100644 index 000000000..992562287 --- /dev/null +++ b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkChangeMonitorBuilder.java @@ -0,0 +1,48 @@ +/* + * Copyright Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.rum.internal.instrumentation.network; + +import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; +import java.util.ArrayList; +import java.util.List; + +/** + * A builder of {@link NetworkChangeMonitor}. + * + *

This class is internal and not for public use. Its APIs are unstable and can change at any + * time. + */ +public final class NetworkChangeMonitorBuilder { + + final CurrentNetworkProvider currentNetworkProvider; + final List> additionalExtractors = new ArrayList<>(); + + NetworkChangeMonitorBuilder(CurrentNetworkProvider currentNetworkProvider) { + this.currentNetworkProvider = currentNetworkProvider; + } + + /** Adds an {@link AttributesExtractor} that will extract additional attributes. */ + public NetworkChangeMonitorBuilder addAttributesExtractor( + AttributesExtractor extractor) { + additionalExtractors.add(extractor); + return this; + } + + public NetworkChangeMonitor build() { + return new NetworkChangeMonitor(this); + } +} diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/NetworkDetector.java b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkDetector.java similarity index 95% rename from splunk-otel-android/src/main/java/com/splunk/rum/NetworkDetector.java rename to splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkDetector.java index 4e1bdd66b..151c2538c 100644 --- a/splunk-otel-android/src/main/java/com/splunk/rum/NetworkDetector.java +++ b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkDetector.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.splunk.rum; +package io.opentelemetry.rum.internal.instrumentation.network; import android.content.Context; import android.net.ConnectivityManager; diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/NetworkState.java b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkState.java similarity index 93% rename from splunk-otel-android/src/main/java/com/splunk/rum/NetworkState.java rename to splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkState.java index 4e64beadc..45c348027 100644 --- a/splunk-otel-android/src/main/java/com/splunk/rum/NetworkState.java +++ b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkState.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.splunk.rum; +package io.opentelemetry.rum.internal.instrumentation.network; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; @@ -32,7 +32,7 @@ enum NetworkState { this.humanName = humanName; } - public String getHumanName() { + String getHumanName() { return humanName; } } diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/PostApi28NetworkDetector.java b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/PostApi28NetworkDetector.java similarity index 94% rename from splunk-otel-android/src/main/java/com/splunk/rum/PostApi28NetworkDetector.java rename to splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/PostApi28NetworkDetector.java index 25c78dfa2..1bd9589a8 100644 --- a/splunk-otel-android/src/main/java/com/splunk/rum/PostApi28NetworkDetector.java +++ b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/PostApi28NetworkDetector.java @@ -14,10 +14,10 @@ * limitations under the License. */ -package com.splunk.rum; +package io.opentelemetry.rum.internal.instrumentation.network; -import static com.splunk.rum.ConnectionUtil.NO_NETWORK; -import static com.splunk.rum.ConnectionUtil.UNKNOWN_NETWORK; +import static io.opentelemetry.rum.internal.instrumentation.network.CurrentNetworkProvider.NO_NETWORK; +import static io.opentelemetry.rum.internal.instrumentation.network.CurrentNetworkProvider.UNKNOWN_NETWORK; import android.Manifest; import android.annotation.SuppressLint; diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/SimpleNetworkDetector.java b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/SimpleNetworkDetector.java similarity index 88% rename from splunk-otel-android/src/main/java/com/splunk/rum/SimpleNetworkDetector.java rename to splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/SimpleNetworkDetector.java index edcdf32e9..d24666c16 100644 --- a/splunk-otel-android/src/main/java/com/splunk/rum/SimpleNetworkDetector.java +++ b/splunk-otel-android/src/main/java/io/opentelemetry/rum/internal/instrumentation/network/SimpleNetworkDetector.java @@ -14,10 +14,10 @@ * limitations under the License. */ -package com.splunk.rum; +package io.opentelemetry.rum.internal.instrumentation.network; -import static com.splunk.rum.ConnectionUtil.NO_NETWORK; -import static com.splunk.rum.ConnectionUtil.UNKNOWN_NETWORK; +import static io.opentelemetry.rum.internal.instrumentation.network.CurrentNetworkProvider.NO_NETWORK; +import static io.opentelemetry.rum.internal.instrumentation.network.CurrentNetworkProvider.UNKNOWN_NETWORK; import android.net.ConnectivityManager; import android.net.NetworkInfo; diff --git a/splunk-otel-android/src/test/java/com/splunk/rum/DiskToZipkinExporterTest.java b/splunk-otel-android/src/test/java/com/splunk/rum/DiskToZipkinExporterTest.java index b3c46cf81..f8cb4b222 100644 --- a/splunk-otel-android/src/test/java/com/splunk/rum/DiskToZipkinExporterTest.java +++ b/splunk-otel-android/src/test/java/com/splunk/rum/DiskToZipkinExporterTest.java @@ -23,6 +23,8 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import io.opentelemetry.rum.internal.instrumentation.network.CurrentNetwork; +import io.opentelemetry.rum.internal.instrumentation.network.CurrentNetworkProvider; import java.io.File; import java.util.stream.Stream; import org.junit.jupiter.api.BeforeEach; @@ -44,7 +46,7 @@ class DiskToZipkinExporterTest { private final File imposter = new File(spanFilesPath.getAbsolutePath() + File.separator + "someImposterFile.dll"); - @Mock private ConnectionUtil connectionUtil; + @Mock private CurrentNetworkProvider currentNetworkProvider; @Mock private FileUtils fileUtils; @Mock private CurrentNetwork currentNetwork; @Mock FileSender sender; @@ -52,7 +54,7 @@ class DiskToZipkinExporterTest { @BeforeEach void setup() throws Exception { - when(connectionUtil.refreshNetworkStatus()).thenReturn(currentNetwork); + when(currentNetworkProvider.refreshNetworkStatus()).thenReturn(currentNetwork); when(currentNetwork.isOnline()).thenReturn(true); Stream files = Stream.of(file1, imposter, file2); when(fileUtils.listSpanFiles(spanFilesPath)).thenReturn(files); @@ -124,7 +126,7 @@ private DiskToZipkinExporter buildExporter() { .bandwidthLimit(BANDWIDTH_LIMIT) .bandwidthTracker(bandwidthTracker) .spanFilesPath(spanFilesPath) - .connectionUtil(connectionUtil) + .connectionUtil(currentNetworkProvider) .build(); } } diff --git a/splunk-otel-android/src/test/java/com/splunk/rum/MemoryBufferingExporterTest.java b/splunk-otel-android/src/test/java/com/splunk/rum/MemoryBufferingExporterTest.java index 4bedf010e..e8ebd73ad 100644 --- a/splunk-otel-android/src/test/java/com/splunk/rum/MemoryBufferingExporterTest.java +++ b/splunk-otel-android/src/test/java/com/splunk/rum/MemoryBufferingExporterTest.java @@ -26,6 +26,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import io.opentelemetry.rum.internal.instrumentation.network.CurrentNetwork; +import io.opentelemetry.rum.internal.instrumentation.network.CurrentNetworkProvider; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.sdk.trace.export.SpanExporter; @@ -39,19 +41,22 @@ import org.mockito.ArgumentCaptor; class MemoryBufferingExporterTest { - private final ConnectionUtil connectionUtil = mock(ConnectionUtil.class); + private final CurrentNetworkProvider currentNetworkProvider = + mock(CurrentNetworkProvider.class); + private final CurrentNetwork currentNetwork = mock(CurrentNetwork.class); @BeforeEach void setUp() { - when(connectionUtil.refreshNetworkStatus()) - .thenReturn(CurrentNetwork.builder(NetworkState.TRANSPORT_CELLULAR).build()); + when(currentNetworkProvider.refreshNetworkStatus()).thenReturn(currentNetwork); } @Test void happyPath() { + when(currentNetwork.isOnline()).thenReturn(true); + SpanExporter delegate = mock(SpanExporter.class); MemoryBufferingExporter bufferingExporter = - new MemoryBufferingExporter(connectionUtil, delegate); + new MemoryBufferingExporter(currentNetworkProvider, delegate); Collection spans = Arrays.asList(mock(SpanData.class), mock(SpanData.class)); when(delegate.export(spans)).thenReturn(CompletableResultCode.ofSuccess()); @@ -62,13 +67,11 @@ void happyPath() { @Test void offlinePath() { - when(connectionUtil.refreshNetworkStatus()) - .thenReturn(CurrentNetwork.builder(NetworkState.NO_NETWORK_AVAILABLE).build()) - .thenReturn(CurrentNetwork.builder(NetworkState.TRANSPORT_UNKNOWN).build()); + when(currentNetwork.isOnline()).thenReturn(false, true); SpanExporter delegate = mock(SpanExporter.class); MemoryBufferingExporter bufferingExporter = - new MemoryBufferingExporter(connectionUtil, delegate); + new MemoryBufferingExporter(currentNetworkProvider, delegate); Collection spans = Arrays.asList(mock(SpanData.class), mock(SpanData.class)); @@ -90,9 +93,11 @@ void offlinePath() { @Test void retryPath() { + when(currentNetwork.isOnline()).thenReturn(true); + SpanExporter delegate = mock(SpanExporter.class); MemoryBufferingExporter bufferingExporter = - new MemoryBufferingExporter(connectionUtil, delegate); + new MemoryBufferingExporter(currentNetworkProvider, delegate); SpanData one = mock(SpanData.class); SpanData two = mock(SpanData.class); @@ -112,9 +117,11 @@ void retryPath() { @Test void flush_withBacklog() { + when(currentNetwork.isOnline()).thenReturn(true); + SpanExporter delegate = mock(SpanExporter.class); MemoryBufferingExporter bufferingExporter = - new MemoryBufferingExporter(connectionUtil, delegate); + new MemoryBufferingExporter(currentNetworkProvider, delegate); SpanData one = mock(SpanData.class); SpanData two = mock(SpanData.class); @@ -134,9 +141,11 @@ void flush_withBacklog() { @Test void flush() { + when(currentNetwork.isOnline()).thenReturn(true); + SpanExporter delegate = mock(SpanExporter.class); MemoryBufferingExporter bufferingExporter = - new MemoryBufferingExporter(connectionUtil, delegate); + new MemoryBufferingExporter(currentNetworkProvider, delegate); when(delegate.flush()).thenReturn(CompletableResultCode.ofSuccess()); CompletableResultCode secondResult = bufferingExporter.flush(); @@ -147,9 +156,11 @@ void flush() { @SuppressWarnings("unchecked") @Test void maxBacklog() { + when(currentNetwork.isOnline()).thenReturn(true); + SpanExporter delegate = mock(SpanExporter.class); MemoryBufferingExporter bufferingExporter = - new MemoryBufferingExporter(connectionUtil, delegate); + new MemoryBufferingExporter(currentNetworkProvider, delegate); List firstSet = new ArrayList<>(); for (int i = 0; i < 110; i++) { @@ -181,7 +192,7 @@ void maxBacklog() { void shutdown() { SpanExporter delegate = mock(SpanExporter.class); MemoryBufferingExporter bufferingExporter = - new MemoryBufferingExporter(connectionUtil, delegate); + new MemoryBufferingExporter(currentNetworkProvider, delegate); bufferingExporter.shutdown(); verify(delegate).shutdown(); diff --git a/splunk-otel-android/src/test/java/com/splunk/rum/RumInitializerTest.java b/splunk-otel-android/src/test/java/com/splunk/rum/RumInitializerTest.java index 161e5072c..308cebce3 100644 --- a/splunk-otel-android/src/test/java/com/splunk/rum/RumInitializerTest.java +++ b/splunk-otel-android/src/test/java/com/splunk/rum/RumInitializerTest.java @@ -34,6 +34,8 @@ import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.rum.internal.instrumentation.network.CurrentNetwork; +import io.opentelemetry.rum.internal.instrumentation.network.CurrentNetworkProvider; import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; import io.opentelemetry.sdk.testing.trace.TestSpanData; @@ -71,13 +73,13 @@ void initializationSpan() { RumInitializer testInitializer = new RumInitializer(splunkRumBuilder, application, startupTimer) { @Override - SpanExporter buildFilteringExporter(ConnectionUtil connectionUtil) { + SpanExporter buildFilteringExporter(CurrentNetworkProvider connectionUtil) { return testExporter; } }; SplunkRum splunkRum = testInitializer.initialize( - mock(ConnectionUtil.Factory.class, RETURNS_DEEP_STUBS), mainLooper); + app -> mock(CurrentNetworkProvider.class, RETURNS_DEEP_STUBS), mainLooper); startupTimer.runCompletionCallback(); splunkRum.flushSpans(); @@ -125,13 +127,13 @@ void spanLimitsAreConfigured() { RumInitializer testInitializer = new RumInitializer(splunkRumBuilder, application, startupTimer) { @Override - SpanExporter buildFilteringExporter(ConnectionUtil connectionUtil) { + SpanExporter buildFilteringExporter(CurrentNetworkProvider connectionUtil) { return testExporter; } }; SplunkRum splunkRum = testInitializer.initialize( - mock(ConnectionUtil.Factory.class, RETURNS_DEEP_STUBS), mainLooper); + app -> mock(CurrentNetworkProvider.class, RETURNS_DEEP_STUBS), mainLooper); splunkRum.flushSpans(); testExporter.reset(); @@ -172,15 +174,15 @@ SpanExporter getCoreSpanExporter(String endpoint) { } }; - ConnectionUtil connectionUtil = mock(ConnectionUtil.class); + CurrentNetworkProvider currentNetworkProvider = mock(CurrentNetworkProvider.class); + CurrentNetwork currentNetwork = mock(CurrentNetwork.class); - CurrentNetwork offline = CurrentNetwork.builder(NetworkState.NO_NETWORK_AVAILABLE).build(); - CurrentNetwork online = CurrentNetwork.builder(NetworkState.TRANSPORT_WIFI).build(); - when(connectionUtil.refreshNetworkStatus()).thenReturn(offline, online); + when(currentNetworkProvider.refreshNetworkStatus()).thenReturn(currentNetwork); + when(currentNetwork.isOnline()).thenReturn(false, true); long currentTimeNanos = MILLISECONDS.toNanos(System.currentTimeMillis()); - SpanExporter spanExporter = testInitializer.buildFilteringExporter(connectionUtil); + SpanExporter spanExporter = testInitializer.buildFilteringExporter(currentNetworkProvider); List batch1 = new ArrayList<>(); for (int i = 0; i < 99; i++) { batch1.add(createTestSpan(currentTimeNanos - MINUTES.toNanos(1))); @@ -221,10 +223,9 @@ void shouldTranslateExceptionEventsToSpanAttributes() { when(application.getApplicationContext()).thenReturn(context); - ConnectionUtil connectionUtil = mock(ConnectionUtil.class, RETURNS_DEEP_STUBS); - when(connectionUtil.refreshNetworkStatus().isOnline()).thenReturn(true); - ConnectionUtil.Factory connectionUtilFactory = mock(ConnectionUtil.Factory.class); - when(connectionUtilFactory.createAndStart(application)).thenReturn(connectionUtil); + CurrentNetworkProvider currentNetworkProvider = + mock(CurrentNetworkProvider.class, RETURNS_DEEP_STUBS); + when(currentNetworkProvider.refreshNetworkStatus().isOnline()).thenReturn(true); AppStartupTimer appStartupTimer = new AppStartupTimer(); RumInitializer initializer = @@ -235,7 +236,7 @@ SpanExporter getCoreSpanExporter(String endpoint) { } }; - SplunkRum splunkRum = initializer.initialize(connectionUtilFactory, mainLooper); + SplunkRum splunkRum = initializer.initialize(app -> currentNetworkProvider, mainLooper); appStartupTimer.runCompletionCallback(); Exception e = new IllegalArgumentException("booom!"); diff --git a/splunk-otel-android/src/test/java/com/splunk/rum/SplunkRumTest.java b/splunk-otel-android/src/test/java/com/splunk/rum/SplunkRumTest.java index 7b060876e..2de376466 100644 --- a/splunk-otel-android/src/test/java/com/splunk/rum/SplunkRumTest.java +++ b/splunk-otel-android/src/test/java/com/splunk/rum/SplunkRumTest.java @@ -44,6 +44,7 @@ import io.opentelemetry.context.Scope; import io.opentelemetry.rum.internal.GlobalAttributesSpanAppender; import io.opentelemetry.rum.internal.OpenTelemetryRum; +import io.opentelemetry.rum.internal.instrumentation.network.CurrentNetworkProvider; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; import io.opentelemetry.sdk.testing.junit5.OpenTelemetryExtension; @@ -80,8 +81,8 @@ public void setup() { @Test void initialization_onlyOnce() { Application application = mock(Application.class, RETURNS_DEEP_STUBS); - ConnectionUtil.Factory connectionUtilFactory = - mock(ConnectionUtil.Factory.class, RETURNS_DEEP_STUBS); + CurrentNetworkProvider currentNetworkProvider = + mock(CurrentNetworkProvider.class, RETURNS_DEEP_STUBS); Context context = mock(Context.class); SplunkRumBuilder splunkRumBuilder = @@ -95,7 +96,7 @@ void initialization_onlyOnce() { when(context.getFilesDir()).thenReturn(new File("/my/storage/spot")); SplunkRum singleton = - SplunkRum.initialize(splunkRumBuilder, application, connectionUtilFactory); + SplunkRum.initialize(splunkRumBuilder, application, app -> currentNetworkProvider); SplunkRum sameInstance = splunkRumBuilder.build(application); assertSame(singleton, sameInstance); @@ -110,8 +111,8 @@ void getInstance_preConfig() { @Test void getInstance() { Application application = mock(Application.class, RETURNS_DEEP_STUBS); - ConnectionUtil.Factory connectionUtilFactory = - mock(ConnectionUtil.Factory.class, RETURNS_DEEP_STUBS); + CurrentNetworkProvider currentNetworkProvider = + mock(CurrentNetworkProvider.class, RETURNS_DEEP_STUBS); Context context = mock(Context.class); SplunkRumBuilder splunkRumBuilder = @@ -125,7 +126,7 @@ void getInstance() { when(context.getFilesDir()).thenReturn(new File("/my/storage/spot")); SplunkRum singleton = - SplunkRum.initialize(splunkRumBuilder, application, connectionUtilFactory); + SplunkRum.initialize(splunkRumBuilder, application, app -> currentNetworkProvider); assertSame(singleton, SplunkRum.getInstance()); } @@ -137,8 +138,8 @@ void newBuilder() { @Test void nonNullMethods() { Application application = mock(Application.class, RETURNS_DEEP_STUBS); - ConnectionUtil.Factory connectionUtilFactory = - mock(ConnectionUtil.Factory.class, RETURNS_DEEP_STUBS); + CurrentNetworkProvider currentNetworkProvider = + mock(CurrentNetworkProvider.class, RETURNS_DEEP_STUBS); Context context = mock(Context.class); when(application.getApplicationContext()).thenReturn(context); @@ -152,7 +153,7 @@ void nonNullMethods() { .disableAnrDetection(); SplunkRum splunkRum = - SplunkRum.initialize(splunkRumBuilder, application, connectionUtilFactory); + SplunkRum.initialize(splunkRumBuilder, application, app -> currentNetworkProvider); assertNotNull(splunkRum.getOpenTelemetry()); assertNotNull(splunkRum.getRumSessionId()); } @@ -233,8 +234,8 @@ void createAndEnd() { @Test void integrateWithBrowserRum() { Application application = mock(Application.class, RETURNS_DEEP_STUBS); - ConnectionUtil.Factory connectionUtilFactory = - mock(ConnectionUtil.Factory.class, RETURNS_DEEP_STUBS); + CurrentNetworkProvider currentNetworkProvider = + mock(CurrentNetworkProvider.class, RETURNS_DEEP_STUBS); Context context = mock(Context.class); WebView webView = mock(WebView.class); @@ -249,7 +250,7 @@ void integrateWithBrowserRum() { .disableAnrDetection(); SplunkRum splunkRum = - SplunkRum.initialize(splunkRumBuilder, application, connectionUtilFactory); + SplunkRum.initialize(splunkRumBuilder, application, app -> currentNetworkProvider); splunkRum.integrateWithBrowserRum(webView); verify(webView) diff --git a/splunk-otel-android/src/test/java/com/splunk/rum/CarrierFinderTest.java b/splunk-otel-android/src/test/java/io/opentelemetry/rum/internal/instrumentation/network/CarrierFinderTest.java similarity index 97% rename from splunk-otel-android/src/test/java/com/splunk/rum/CarrierFinderTest.java rename to splunk-otel-android/src/test/java/io/opentelemetry/rum/internal/instrumentation/network/CarrierFinderTest.java index f6f645a93..805b98a06 100644 --- a/splunk-otel-android/src/test/java/com/splunk/rum/CarrierFinderTest.java +++ b/splunk-otel-android/src/test/java/io/opentelemetry/rum/internal/instrumentation/network/CarrierFinderTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.splunk.rum; +package io.opentelemetry.rum.internal.instrumentation.network; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; diff --git a/splunk-otel-android/src/test/java/com/splunk/rum/CurrentNetworkAttributesExtractorTest.java b/splunk-otel-android/src/test/java/io/opentelemetry/rum/internal/instrumentation/network/CurrentNetworkAttributesExtractorTest.java similarity index 98% rename from splunk-otel-android/src/test/java/com/splunk/rum/CurrentNetworkAttributesExtractorTest.java rename to splunk-otel-android/src/test/java/io/opentelemetry/rum/internal/instrumentation/network/CurrentNetworkAttributesExtractorTest.java index 3d62d2263..9df8d8819 100644 --- a/splunk-otel-android/src/test/java/com/splunk/rum/CurrentNetworkAttributesExtractorTest.java +++ b/splunk-otel-android/src/test/java/io/opentelemetry/rum/internal/instrumentation/network/CurrentNetworkAttributesExtractorTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.splunk.rum; +package io.opentelemetry.rum.internal.instrumentation.network; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static org.assertj.core.api.Assertions.entry; diff --git a/splunk-otel-android/src/test/java/com/splunk/rum/ConnectionUtilTest.java b/splunk-otel-android/src/test/java/io/opentelemetry/rum/internal/instrumentation/network/CurrentNetworkProviderTest.java similarity index 82% rename from splunk-otel-android/src/test/java/com/splunk/rum/ConnectionUtilTest.java rename to splunk-otel-android/src/test/java/io/opentelemetry/rum/internal/instrumentation/network/CurrentNetworkProviderTest.java index f8000e602..435b0c1ec 100644 --- a/splunk-otel-android/src/test/java/com/splunk/rum/ConnectionUtilTest.java +++ b/splunk-otel-android/src/test/java/io/opentelemetry/rum/internal/instrumentation/network/CurrentNetworkProviderTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.splunk.rum; +package io.opentelemetry.rum.internal.instrumentation.network; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; @@ -41,7 +41,7 @@ @RunWith(RobolectricTestRunner.class) @Config(maxSdk = Build.VERSION_CODES.S) -public class ConnectionUtilTest { +public class CurrentNetworkProviderTest { @Test @Config(maxSdk = Build.VERSION_CODES.LOLLIPOP) @@ -59,12 +59,12 @@ public void lollipop() { .subType("LTE") .build()); - ConnectionUtil connectionUtil = new ConnectionUtil(networkDetector); - connectionUtil.startMonitoring(() -> networkRequest, connectivityManager); + CurrentNetworkProvider currentNetworkProvider = new CurrentNetworkProvider(networkDetector); + currentNetworkProvider.startMonitoring(() -> networkRequest, connectivityManager); assertEquals( CurrentNetwork.builder(NetworkState.TRANSPORT_WIFI).build(), - connectionUtil.getActiveNetwork()); + currentNetworkProvider.getCurrentNetwork()); ArgumentCaptor monitorCaptor = ArgumentCaptor.forClass(NetworkCallback.class); @@ -72,7 +72,7 @@ public void lollipop() { .registerNetworkCallback(eq(networkRequest), monitorCaptor.capture()); AtomicInteger notified = new AtomicInteger(0); - connectionUtil.addNetworkChangeListener( + currentNetworkProvider.addNetworkChangeListener( (currentNetwork) -> { int timesCalled = notified.incrementAndGet(); if (timesCalled == 1) { @@ -109,12 +109,12 @@ public void modernSdks() { .subType("LTE") .build()); - ConnectionUtil connectionUtil = new ConnectionUtil(networkDetector); - connectionUtil.startMonitoring(() -> networkRequest, connectivityManager); + CurrentNetworkProvider currentNetworkProvider = new CurrentNetworkProvider(networkDetector); + currentNetworkProvider.startMonitoring(() -> networkRequest, connectivityManager); assertEquals( CurrentNetwork.builder(NetworkState.TRANSPORT_WIFI).build(), - connectionUtil.getActiveNetwork()); + currentNetworkProvider.getCurrentNetwork()); verify(connectivityManager, never()) .registerNetworkCallback(eq(networkRequest), isA(NetworkCallback.class)); @@ -123,7 +123,7 @@ public void modernSdks() { verify(connectivityManager).registerDefaultNetworkCallback(monitorCaptor.capture()); AtomicInteger notified = new AtomicInteger(0); - connectionUtil.addNetworkChangeListener( + currentNetworkProvider.addNetworkChangeListener( (currentNetwork) -> { int timesCalled = notified.incrementAndGet(); if (timesCalled == 1) { @@ -151,8 +151,10 @@ public void networkDetectorException() { NetworkDetector networkDetector = mock(NetworkDetector.class); when(networkDetector.detectCurrentNetwork()).thenThrow(new SecurityException("bug")); - ConnectionUtil connectionUtil = new ConnectionUtil(networkDetector); - assertEquals(ConnectionUtil.UNKNOWN_NETWORK, connectionUtil.refreshNetworkStatus()); + CurrentNetworkProvider currentNetworkProvider = new CurrentNetworkProvider(networkDetector); + assertEquals( + CurrentNetworkProvider.UNKNOWN_NETWORK, + currentNetworkProvider.refreshNetworkStatus()); } @Test @@ -167,11 +169,12 @@ public void networkDetectorExceptionOnCallbackRegistration() { .when(connectivityManager) .registerDefaultNetworkCallback(isA(NetworkCallback.class)); - ConnectionUtil connectionUtil = new ConnectionUtil(networkDetector); - connectionUtil.startMonitoring(() -> mock(NetworkRequest.class), connectivityManager); + CurrentNetworkProvider currentNetworkProvider = new CurrentNetworkProvider(networkDetector); + currentNetworkProvider.startMonitoring( + () -> mock(NetworkRequest.class), connectivityManager); assertEquals( CurrentNetwork.builder(NetworkState.TRANSPORT_WIFI).build(), - connectionUtil.refreshNetworkStatus()); + currentNetworkProvider.refreshNetworkStatus()); } @Test @@ -187,11 +190,11 @@ public void networkDetectorExceptionOnCallbackRegistration_lollipop() { .when(connectivityManager) .registerNetworkCallback(eq(networkRequest), isA(NetworkCallback.class)); - ConnectionUtil connectionUtil = new ConnectionUtil(networkDetector); - connectionUtil.startMonitoring(() -> networkRequest, connectivityManager); + CurrentNetworkProvider currentNetworkProvider = new CurrentNetworkProvider(networkDetector); + currentNetworkProvider.startMonitoring(() -> networkRequest, connectivityManager); assertEquals( CurrentNetwork.builder(NetworkState.TRANSPORT_WIFI).build(), - connectionUtil.refreshNetworkStatus()); + currentNetworkProvider.refreshNetworkStatus()); } @Test @@ -210,8 +213,8 @@ public void shouldNotFailOnImmediateConnectionManagerCall_lollipop() { .when(connectivityManager) .registerNetworkCallback(eq(networkRequest), any(NetworkCallback.class)); - ConnectionUtil connectionUtil = new ConnectionUtil(networkDetector); - connectionUtil.startMonitoring(() -> networkRequest, connectivityManager); + CurrentNetworkProvider currentNetworkProvider = new CurrentNetworkProvider(networkDetector); + currentNetworkProvider.startMonitoring(() -> networkRequest, connectivityManager); } @Test @@ -230,7 +233,7 @@ public void shouldNotFailOnImmediateConnectionManagerCall() { .when(connectivityManager) .registerDefaultNetworkCallback(any(NetworkCallback.class)); - ConnectionUtil connectionUtil = new ConnectionUtil(networkDetector); - connectionUtil.startMonitoring(() -> networkRequest, connectivityManager); + CurrentNetworkProvider currentNetworkProvider = new CurrentNetworkProvider(networkDetector); + currentNetworkProvider.startMonitoring(() -> networkRequest, connectivityManager); } } diff --git a/splunk-otel-android/src/test/java/com/splunk/rum/NetworkAttributesAppenderTest.java b/splunk-otel-android/src/test/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkAttributesSpanAppenderTest.java similarity index 87% rename from splunk-otel-android/src/test/java/com/splunk/rum/NetworkAttributesAppenderTest.java rename to splunk-otel-android/src/test/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkAttributesSpanAppenderTest.java index 32f79c288..4cb37f121 100644 --- a/splunk-otel-android/src/test/java/com/splunk/rum/NetworkAttributesAppenderTest.java +++ b/splunk-otel-android/src/test/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkAttributesSpanAppenderTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.splunk.rum; +package io.opentelemetry.rum.internal.instrumentation.network; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -32,16 +32,16 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) -class NetworkAttributesAppenderTest { +class NetworkAttributesSpanAppenderTest { - @Mock ConnectionUtil connectionUtil; + @Mock CurrentNetworkProvider currentNetworkProvider; @Mock ReadWriteSpan span; - @InjectMocks NetworkAttributesAppender underTest; + @InjectMocks NetworkAttributesSpanAppender underTest; @Test void shouldAppendNetworkAttributes() { - when(connectionUtil.getActiveNetwork()) + when(currentNetworkProvider.getCurrentNetwork()) .thenReturn( CurrentNetwork.builder(NetworkState.TRANSPORT_CELLULAR) .subType("LTE") diff --git a/splunk-otel-android/src/test/java/com/splunk/rum/NetworkMonitorTest.java b/splunk-otel-android/src/test/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkChangeMonitorTest.java similarity index 56% rename from splunk-otel-android/src/test/java/com/splunk/rum/NetworkMonitorTest.java rename to splunk-otel-android/src/test/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkChangeMonitorTest.java index cf40e68bb..037176342 100644 --- a/splunk-otel-android/src/test/java/com/splunk/rum/NetworkMonitorTest.java +++ b/splunk-otel-android/src/test/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkChangeMonitorTest.java @@ -14,44 +14,68 @@ * limitations under the License. */ -package com.splunk.rum; +package io.opentelemetry.rum.internal.instrumentation.network; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.os.Build; -import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.rum.internal.instrumentation.ApplicationStateListener; +import io.opentelemetry.rum.internal.instrumentation.InstrumentedApplication; +import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.testing.junit4.OpenTelemetryRule; import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; @Config(sdk = Build.VERSION_CODES.P) @RunWith(RobolectricTestRunner.class) -public class NetworkMonitorTest { +public class NetworkChangeMonitorTest { @Rule public OpenTelemetryRule otelTesting = OpenTelemetryRule.create(); - private Tracer tracer; + @Captor ArgumentCaptor applicationStateListenerCaptor; + @Captor ArgumentCaptor networkChangeListenerCaptor; + + @Mock CurrentNetworkProvider currentNetworkProvider; + @Mock InstrumentedApplication instrumentedApplication; + + AutoCloseable mocks; @Before - public void setup() { - tracer = otelTesting.getOpenTelemetry().getTracer("testTracer"); + public void setUp() { + mocks = MockitoAnnotations.openMocks(this); + when(instrumentedApplication.getOpenTelemetrySdk()) + .thenReturn((OpenTelemetrySdk) otelTesting.getOpenTelemetry()); + } + + @After + public void tearDown() throws Exception { + mocks.close(); } @Test public void networkAvailable_wifi() { - NetworkMonitor.TracingNetworkChangeListener listener = - new NetworkMonitor.TracingNetworkChangeListener(tracer, new AtomicBoolean(true)); + NetworkChangeMonitor.create(currentNetworkProvider).installOn(instrumentedApplication); + + verify(currentNetworkProvider) + .addNetworkChangeListener(networkChangeListenerCaptor.capture()); + NetworkChangeListener listener = networkChangeListenerCaptor.getValue(); listener.onNetworkChange(CurrentNetwork.builder(NetworkState.TRANSPORT_WIFI).build()); @@ -60,14 +84,17 @@ public void networkAvailable_wifi() { assertThat(spans.get(0)) .hasName("network.change") .hasAttributesSatisfyingExactly( - equalTo(NetworkMonitor.NETWORK_STATUS_KEY, "available"), + equalTo(NetworkApplicationListener.NETWORK_STATUS_KEY, "available"), equalTo(SemanticAttributes.NET_HOST_CONNECTION_TYPE, "wifi")); } @Test public void networkAvailable_cellular() { - NetworkMonitor.TracingNetworkChangeListener listener = - new NetworkMonitor.TracingNetworkChangeListener(tracer, new AtomicBoolean(true)); + NetworkChangeMonitor.create(currentNetworkProvider).installOn(instrumentedApplication); + + verify(currentNetworkProvider) + .addNetworkChangeListener(networkChangeListenerCaptor.capture()); + NetworkChangeListener listener = networkChangeListenerCaptor.getValue(); CurrentNetwork network = CurrentNetwork.builder(NetworkState.TRANSPORT_CELLULAR) @@ -89,7 +116,7 @@ public void networkAvailable_cellular() { assertThat(spans.get(0)) .hasName("network.change") .hasAttributesSatisfyingExactly( - equalTo(NetworkMonitor.NETWORK_STATUS_KEY, "available"), + equalTo(NetworkApplicationListener.NETWORK_STATUS_KEY, "available"), equalTo(SemanticAttributes.NET_HOST_CONNECTION_TYPE, "cell"), equalTo(SemanticAttributes.NET_HOST_CONNECTION_SUBTYPE, "LTE"), equalTo(SemanticAttributes.NET_HOST_CARRIER_NAME, "ShadyTel"), @@ -100,8 +127,11 @@ public void networkAvailable_cellular() { @Test public void networkLost() { - NetworkMonitor.TracingNetworkChangeListener listener = - new NetworkMonitor.TracingNetworkChangeListener(tracer, new AtomicBoolean(true)); + NetworkChangeMonitor.create(currentNetworkProvider).installOn(instrumentedApplication); + + verify(currentNetworkProvider) + .addNetworkChangeListener(networkChangeListenerCaptor.capture()); + NetworkChangeListener listener = networkChangeListenerCaptor.getValue(); listener.onNetworkChange(CurrentNetwork.builder(NetworkState.NO_NETWORK_AVAILABLE).build()); @@ -110,25 +140,35 @@ public void networkLost() { assertThat(spans.get(0)) .hasName("network.change") .hasAttributesSatisfyingExactly( - equalTo(NetworkMonitor.NETWORK_STATUS_KEY, "lost"), + equalTo(NetworkApplicationListener.NETWORK_STATUS_KEY, "lost"), equalTo(SemanticAttributes.NET_HOST_CONNECTION_TYPE, "unavailable")); } @Test public void noEventsPlease() { - AtomicBoolean shouldEmitChangeEvents = new AtomicBoolean(false); + NetworkChangeMonitor.create(currentNetworkProvider).installOn(instrumentedApplication); - NetworkMonitor.TracingNetworkChangeListener listener = - new NetworkMonitor.TracingNetworkChangeListener(tracer, shouldEmitChangeEvents); + verify(currentNetworkProvider) + .addNetworkChangeListener(networkChangeListenerCaptor.capture()); + NetworkChangeListener networkListener = networkChangeListenerCaptor.getValue(); - listener.onNetworkChange(CurrentNetwork.builder(NetworkState.NO_NETWORK_AVAILABLE).build()); + verify(instrumentedApplication) + .registerApplicationStateListener(applicationStateListenerCaptor.capture()); + ApplicationStateListener applicationListener = applicationStateListenerCaptor.getValue(); + + applicationListener.onApplicationBackgrounded(); + + networkListener.onNetworkChange( + CurrentNetwork.builder(NetworkState.NO_NETWORK_AVAILABLE).build()); assertTrue(otelTesting.getSpans().isEmpty()); - listener.onNetworkChange( + networkListener.onNetworkChange( CurrentNetwork.builder(NetworkState.TRANSPORT_CELLULAR).subType("LTE").build()); assertTrue(otelTesting.getSpans().isEmpty()); - shouldEmitChangeEvents.set(true); - listener.onNetworkChange(CurrentNetwork.builder(NetworkState.NO_NETWORK_AVAILABLE).build()); + applicationListener.onApplicationForegrounded(); + + networkListener.onNetworkChange( + CurrentNetwork.builder(NetworkState.NO_NETWORK_AVAILABLE).build()); assertEquals(1, otelTesting.getSpans().size()); } } diff --git a/splunk-otel-android/src/test/java/com/splunk/rum/NetworkDetectorTest.java b/splunk-otel-android/src/test/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkDetectorTest.java similarity index 96% rename from splunk-otel-android/src/test/java/com/splunk/rum/NetworkDetectorTest.java rename to splunk-otel-android/src/test/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkDetectorTest.java index f1e9be8fa..28cb8b5f6 100644 --- a/splunk-otel-android/src/test/java/com/splunk/rum/NetworkDetectorTest.java +++ b/splunk-otel-android/src/test/java/io/opentelemetry/rum/internal/instrumentation/network/NetworkDetectorTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.splunk.rum; +package io.opentelemetry.rum.internal.instrumentation.network; import static org.junit.Assert.assertTrue; diff --git a/splunk-otel-android/src/test/java/com/splunk/rum/PostApi28NetworkDetectorTest.java b/splunk-otel-android/src/test/java/io/opentelemetry/rum/internal/instrumentation/network/PostApi28NetworkDetectorTest.java similarity index 98% rename from splunk-otel-android/src/test/java/com/splunk/rum/PostApi28NetworkDetectorTest.java rename to splunk-otel-android/src/test/java/io/opentelemetry/rum/internal/instrumentation/network/PostApi28NetworkDetectorTest.java index b09109548..d8f1528f8 100644 --- a/splunk-otel-android/src/test/java/com/splunk/rum/PostApi28NetworkDetectorTest.java +++ b/splunk-otel-android/src/test/java/io/opentelemetry/rum/internal/instrumentation/network/PostApi28NetworkDetectorTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.splunk.rum; +package io.opentelemetry.rum.internal.instrumentation.network; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static org.junit.Assert.assertEquals; diff --git a/splunk-otel-android/src/test/java/com/splunk/rum/SimpleNetworkDetectorTest.java b/splunk-otel-android/src/test/java/io/opentelemetry/rum/internal/instrumentation/network/SimpleNetworkDetectorTest.java similarity index 98% rename from splunk-otel-android/src/test/java/com/splunk/rum/SimpleNetworkDetectorTest.java rename to splunk-otel-android/src/test/java/io/opentelemetry/rum/internal/instrumentation/network/SimpleNetworkDetectorTest.java index e9f39b636..04a9c5419 100644 --- a/splunk-otel-android/src/test/java/com/splunk/rum/SimpleNetworkDetectorTest.java +++ b/splunk-otel-android/src/test/java/io/opentelemetry/rum/internal/instrumentation/network/SimpleNetworkDetectorTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.splunk.rum; +package io.opentelemetry.rum.internal.instrumentation.network; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock;