From 9f6f89b607f2e11987940e378a766f38d558b09f Mon Sep 17 00:00:00 2001 From: Divya Latha Date: Fri, 30 Jan 2026 11:25:12 +0530 Subject: [PATCH 01/10] chore: remove pricing information from README --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index ba2cc5cd..e87220e4 100644 --- a/README.md +++ b/README.md @@ -209,10 +209,6 @@ As noted in our [security policy](../../security/policy), New Relic is committed If you believe you have found a security vulnerability in this project or any of New Relic's products or websites, we welcome and greatly appreciate you reporting it to New Relic through [HackerOne](https://hackerone.com/newrelic). -## Pricing - -Important: Ingesting video telemetry data via this video agent requires a subscription to an Advanced Compute. Contact your New Relic account representative for more details on pricing and entitlement. - ## License New Relic Video Agent is licensed under the [Apache 2.0](http://apache.org/licenses/LICENSE-2.0.txt) License. From b5b7e623fbe36c7a6a3a5d0942789ebdc3d095a5 Mon Sep 17 00:00:00 2001 From: mavinash Date: Tue, 3 Feb 2026 14:30:32 +0530 Subject: [PATCH 02/10] fix: lint error in sample app --- app/src/main/AndroidManifest.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ae9a0c9a..5767e420 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -26,4 +26,6 @@ + + \ No newline at end of file From 8402de189711763739ec4d6fd58ebd5992ead5a5 Mon Sep 17 00:00:00 2001 From: mavinash Date: Wed, 4 Feb 2026 12:06:08 +0530 Subject: [PATCH 03/10] fix: Lint issues --- .../exoplayer/tracker/NRTrackerExoPlayer.java | 7 ++++++ .../nrvideoproject/VideoPlayerAds.java | 22 ++++++++++++------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/NRExoPlayerTracker/src/main/java/com/newrelic/videoagent/exoplayer/tracker/NRTrackerExoPlayer.java b/NRExoPlayerTracker/src/main/java/com/newrelic/videoagent/exoplayer/tracker/NRTrackerExoPlayer.java index 81602546..b4700330 100644 --- a/NRExoPlayerTracker/src/main/java/com/newrelic/videoagent/exoplayer/tracker/NRTrackerExoPlayer.java +++ b/NRExoPlayerTracker/src/main/java/com/newrelic/videoagent/exoplayer/tracker/NRTrackerExoPlayer.java @@ -5,6 +5,8 @@ import android.os.Looper; import androidx.annotation.NonNull; +import androidx.annotation.OptIn; +import androidx.media3.common.util.UnstableApi; import androidx.media3.common.C; import androidx.media3.common.MediaLibraryInfo; import androidx.media3.common.MediaMetadata; @@ -37,7 +39,12 @@ /** * New Relic Video tracker for ExoPlayer. + *

+ * @OptIn is required for Media3 APIs which are marked as @UnstableApi. + * This is by design as per Google's Media3 documentation. + * @see Media3 Unstable API Documentation */ +@OptIn(markerClass = UnstableApi.class) public class NRTrackerExoPlayer extends NRVideoTracker implements Player.Listener, AnalyticsListener { protected ExoPlayer player; diff --git a/app/src/main/java/com/newrelic/nrvideoproject/VideoPlayerAds.java b/app/src/main/java/com/newrelic/nrvideoproject/VideoPlayerAds.java index 11717447..31f2e157 100644 --- a/app/src/main/java/com/newrelic/nrvideoproject/VideoPlayerAds.java +++ b/app/src/main/java/com/newrelic/nrvideoproject/VideoPlayerAds.java @@ -8,21 +8,28 @@ import com.newrelic.videoagent.core.NRVideoPlayerConfiguration; import androidx.appcompat.app.AppCompatActivity; import androidx.media3.common.MediaItem; -import androidx.media3.common.util.Util; import androidx.media3.datasource.DataSource; -import androidx.media3.datasource.DefaultDataSourceFactory; -import androidx.media3.exoplayer.SimpleExoPlayer; +import androidx.media3.datasource.DefaultDataSource; +import androidx.media3.exoplayer.ExoPlayer; import androidx.media3.exoplayer.ima.ImaAdsLoader; import androidx.media3.exoplayer.source.DefaultMediaSourceFactory; import androidx.media3.ui.PlayerView; +import androidx.annotation.OptIn; +import androidx.media3.common.util.UnstableApi; import android.util.Log; import com.newrelic.videoagent.core.NewRelicVideoAgent; import com.newrelic.videoagent.ima.tracker.NRTrackerIMA; +/** + * @OptIn is required for Media3 IMA ads integration APIs which are marked as @UnstableApi. + * This is by design as per Google's Media3 documentation. + * @see Media3 Unstable API Documentation + */ +@OptIn(markerClass = UnstableApi.class) public class VideoPlayerAds extends AppCompatActivity implements AdErrorEvent.AdErrorListener, AdEvent.AdEventListener { - private SimpleExoPlayer player; + private ExoPlayer player; private Integer trackerId; private ImaAdsLoader adsLoader; private PlayerView playerView; @@ -67,13 +74,12 @@ protected void onDestroy() { private void playVideo(String videoUrl) { // Set up the factory for media sources, passing the ads loader and ad view providers. - DataSource.Factory dataSourceFactory = - new DefaultDataSourceFactory(this, Util.getUserAgent(this, getString(R.string.app_name))); + DataSource.Factory dataSourceFactory = new DefaultDataSource.Factory(this); DefaultMediaSourceFactory mediaSourceFactory = new DefaultMediaSourceFactory(dataSourceFactory); mediaSourceFactory.setAdsLoaderProvider(unusedAdTagUri -> adsLoader); mediaSourceFactory.setAdViewProvider(playerView); - player = new SimpleExoPlayer.Builder(this).setMediaSourceFactory(mediaSourceFactory).build(); + player = new ExoPlayer.Builder(this).setMediaSourceFactory(mediaSourceFactory).build(); NRVideoPlayerConfiguration playerConfiguration = new NRVideoPlayerConfiguration("test-player-something-else", player, true, null); trackerId = NRVideo.addPlayer(playerConfiguration); adTracker = (NRTrackerIMA) NewRelicVideoAgent.getInstance().getAdTracker(trackerId); @@ -93,7 +99,7 @@ private void playVideo(String videoUrl) { Uri contentUri = Uri.parse(videoUrl); Uri adTagUri = Uri.parse(getString(R.string.ad_tag_url)); MediaItem mediaItem = new MediaItem.Builder().setUri(contentUri).setAdTagUri(adTagUri).build(); - // Prepare the content and ad to be played with the SimpleExoPlayer. + // Prepare the content and ad to be played with the ExoPlayer. player.setMediaItem(mediaItem); // Set PlayWhenReady. If true, content and ads will autoplay. player.setPlayWhenReady(true); From 6bde22ffa0e570e06cbb70b1ce4d84d74f994781 Mon Sep 17 00:00:00 2001 From: Bharat Saraswat Date: Thu, 5 Feb 2026 11:11:55 +0530 Subject: [PATCH 04/10] Merge pull request #104 from newrelic/fix/drop-bitrate-attributes-before-start fix: Drop bitrate attributes before content/ad start --- .../videoagent/core/tracker/NRVideoTracker.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/tracker/NRVideoTracker.java b/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/tracker/NRVideoTracker.java index 48ae67d2..574adbe9 100644 --- a/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/tracker/NRVideoTracker.java +++ b/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/tracker/NRVideoTracker.java @@ -163,8 +163,11 @@ public Map getAttributes(String action, Map attr if (state.isAd) { attr.put("adTitle", getTitle()); - attr.put("adBitrate", getBitrate()); - attr.put("adRenditionBitrate", getRenditionBitrate()); + // Only add bitrate attributes after ad has started (first frame shown) + if (state.isStarted) { + attr.put("adBitrate", getBitrate()); + attr.put("adRenditionBitrate", getRenditionBitrate()); + } attr.put("adRenditionWidth", getRenditionWidth()); attr.put("adRenditionHeight", getRenditionHeight()); attr.put("adDuration", getDuration()); @@ -199,9 +202,11 @@ public Map getAttributes(String action, Map attr } attr.put("contentTitle", getTitle()); -// attr.put("contentBitrate", getBitrate()); - attr.put("contentBitrate", getActualBitrate()); - attr.put("contentRenditionBitrate", getRenditionBitrate()); + // Only add bitrate attributes after content has started (first frame shown) + if (state.isStarted) { + attr.put("contentBitrate", getActualBitrate()); + attr.put("contentRenditionBitrate", getRenditionBitrate()); + } attr.put("contentRenditionWidth", getRenditionWidth()); attr.put("contentRenditionHeight", getRenditionHeight()); attr.put("contentDuration", getDuration()); From 4e7dde4f248009d98271aaf7ff02d6cd51b33c9b Mon Sep 17 00:00:00 2001 From: mavinash Date: Tue, 6 Jan 2026 11:11:18 +0530 Subject: [PATCH 05/10] fix: Staging credentials --- .../videoagent/core/NRVideoConfiguration.java | 77 +++++++++++++------ .../videoagent/core/auth/TokenManager.java | 18 +++-- .../core/harvest/OptimizedHttpClient.java | 22 +++--- 3 files changed, 78 insertions(+), 39 deletions(-) diff --git a/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/NRVideoConfiguration.java b/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/NRVideoConfiguration.java index 193cc799..86d9dc17 100644 --- a/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/NRVideoConfiguration.java +++ b/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/NRVideoConfiguration.java @@ -42,6 +42,7 @@ public final class NRVideoConfiguration { private final boolean memoryOptimized; private final boolean debugLoggingEnabled; private final boolean isTV; + private final String collectorAddress; // Performance optimization constants private static final int DEFAULT_HARVEST_CYCLE_SECONDS = 5 * 60; // 5 minutes @@ -78,6 +79,7 @@ private NRVideoConfiguration(Builder builder) { this.memoryOptimized = builder.memoryOptimized; this.debugLoggingEnabled = builder.debugLoggingEnabled; this.isTV = builder.isTV; + this.collectorAddress = builder.collectorAddress; } // Immutable getters @@ -91,6 +93,7 @@ private NRVideoConfiguration(Builder builder) { public boolean isMemoryOptimized() { return memoryOptimized; } public boolean isDebugLoggingEnabled() { return debugLoggingEnabled; } public boolean isTV() { return isTV; } + public String getCollectorAddress() { return collectorAddress; } /** * Get dead letter retry interval in milliseconds @@ -107,41 +110,56 @@ public long getDeadLetterRetryInterval() { } /** - * Enterprise-grade region identification with multiple fallback strategies - * Thread-safe and optimized for performance + * Parse region code from application token prefix + * Matches NewRelic iOS Agent pattern: extracts region prefix before 'x' + * Examples: "EUxABCD..." -> "EU", "APxABCD..." -> "AP", "AA..." -> "" + */ + private static String parseRegionFromToken(String applicationToken) { + if (applicationToken == null || applicationToken.length() < 3) { + return ""; + } + + // Find the first 'x' in the token + int xIndex = applicationToken.indexOf('x'); + if (xIndex == -1) { + return ""; // No region prefix found + } + + // Extract everything before the first 'x' + String regionCode = applicationToken.substring(0, xIndex); + + // Remove any trailing 'x' characters + while (regionCode.length() > 0 && regionCode.charAt(regionCode.length() - 1) == 'x') { + regionCode = regionCode.substring(0, regionCode.length() - 1); + } + + return regionCode; + } + + /** + * Identify region with proper token parsing and fallback logic + * Behavior similar to NewRelic iOS Agent's NRMAAgentConfiguration */ private static String identifyRegion(String applicationToken) { if (applicationToken == null || applicationToken.length() < 10) { return "US"; // Safe default } - String cleanToken = applicationToken.trim().toLowerCase(); + // First, try to parse region from token prefix (e.g., "EUx", "APx") + String regionCode = parseRegionFromToken(applicationToken); - // Strategy 1: Direct prefix matching (most reliable) - for (Map.Entry entry : REGION_MAPPINGS.entrySet()) { - String regionKey = entry.getKey().toLowerCase(); - if (cleanToken.startsWith(regionKey) || cleanToken.contains("-" + regionKey + "-")) { - return entry.getValue(); - } - } + if (regionCode != null && regionCode.length() > 0) { + // Convert region code to uppercase and validate + String upperRegion = regionCode.toUpperCase(); - // Strategy 2: Token structure analysis - if (cleanToken.length() >= 40) { // Standard NR token length - // EU tokens often have specific patterns - if (cleanToken.contains("eu") || cleanToken.contains("europe")) { - return "EU"; - } - // AP tokens often have specific patterns - if (cleanToken.contains("ap") || cleanToken.contains("asia") || cleanToken.contains("pacific")) { - return "AP"; - } - // Gov tokens have specific patterns - if (cleanToken.contains("gov") || cleanToken.contains("fed")) { - return "GOV"; + // Map region codes to standard regions + String mappedRegion = REGION_MAPPINGS.get(upperRegion); + if (mappedRegion != null) { + return mappedRegion; } } - // Strategy 3: Default to US for production stability + // Default to US for standard tokens without region prefix return "US"; } @@ -158,6 +176,7 @@ public static final class Builder { private boolean memoryOptimized = true; private boolean debugLoggingEnabled = false; private boolean isTV = false; + private String collectorAddress = null; public Builder(String applicationToken) { this.applicationToken = applicationToken; @@ -235,6 +254,16 @@ public Builder enableLogging() { return this; } + /** + * Set custom collector domain address for /connect and /data endpoints (optional) + * Example: "staging-mobile-collector.newrelic.com" or "mobile-collector.newrelic.com" + * If not set, will be auto-detected from application token region + */ + public Builder withCollectorAddress(String collectorAddress) { + this.collectorAddress = collectorAddress; + return this; + } + private void applyTVOptimizations() { this.harvestCycleSeconds = TV_HARVEST_CYCLE_SECONDS; this.liveHarvestCycleSeconds = TV_LIVE_HARVEST_CYCLE_SECONDS; diff --git a/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/auth/TokenManager.java b/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/auth/TokenManager.java index 6159d856..e87d407f 100644 --- a/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/auth/TokenManager.java +++ b/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/auth/TokenManager.java @@ -154,20 +154,26 @@ private boolean isTokenValid() { /** * Build token endpoint URL based on region + * If collectorAddress is explicitly set, use it for /connect endpoint + * Otherwise, auto-detect from region */ private String buildTokenEndpoint() { + // If collectorAddress is explicitly set, use it for /connect endpoint + if (configuration.getCollectorAddress() != null && !configuration.getCollectorAddress().isEmpty()) { + return "https://" + configuration.getCollectorAddress() + "/mobile/v5/connect"; + } + + // Otherwise, auto-detect from region String region = configuration.getRegion().toUpperCase(); switch (region) { case "EU": - return "https://mobile-collector.eu.newrelic.com/mobile/v5/connect"; + return "https://mobile-collector.eu.nr-data.net/mobile/v5/connect"; case "AP": - return "https://mobile-collector.ap.newrelic.com/mobile/v5/connect"; + return "https://mobile-collector.ap.nr-data.net/mobile/v5/connect"; case "GOV": - return "https://mobile-collector.gov.newrelic.com/mobile/v5/connect"; - case "STAGING": - return "https://mobile-collector.staging.newrelic.com/mobile/v5/connect"; + return "https://gov-mobile-collector.newrelic.com/mobile/v5/connect"; default: - return "https://mobile-collector.newrelic.com/mobile/v5/connect"; + return "https://mobile-collector.newrelic.com/mobile/v5/connect"; // US/DEFAULT } } diff --git a/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/harvest/OptimizedHttpClient.java b/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/harvest/OptimizedHttpClient.java index c92dd987..116c4cd0 100644 --- a/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/harvest/OptimizedHttpClient.java +++ b/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/harvest/OptimizedHttpClient.java @@ -40,10 +40,9 @@ public class OptimizedHttpClient implements HttpClientInterface { static { REGIONAL_ENDPOINTS.put("US", "https://mobile-collector.newrelic.com/mobile/v3/data"); - REGIONAL_ENDPOINTS.put("EU", "https://mobile-collector.eu.newrelic.com/mobile/v3/data"); - REGIONAL_ENDPOINTS.put("AP", "https://mobile-collector.ap.newrelic.com/mobile/v3/data"); - REGIONAL_ENDPOINTS.put("GOV", "https://mobile-collector.gov.newrelic.com/mobile/v3/data"); - REGIONAL_ENDPOINTS.put("STAGING", "https://mobile-collector.staging.newrelic.com/mobile/v3/data"); + REGIONAL_ENDPOINTS.put("EU", "https://mobile-collector.eu.nr-data.net/mobile/v3/data"); + REGIONAL_ENDPOINTS.put("AP", "https://mobile-collector.ap.nr-data.net/mobile/v3/data"); + REGIONAL_ENDPOINTS.put("GOV", "https://gov-mobile-collector.newrelic.com/mobile/v3/data"); REGIONAL_ENDPOINTS.put("DEFAULT", REGIONAL_ENDPOINTS.get("US")); } @@ -59,10 +58,15 @@ public OptimizedHttpClient(NRVideoConfiguration configuration, android.content.C this.tokenManager = new TokenManager(context, configuration); this.deviceInfo = DeviceInformation.getInstance(context); - // Set endpoint URL based on region, defaulting to US if not found - String region = configuration.getRegion().toUpperCase(); - String regionEndpoint = REGIONAL_ENDPOINTS.get(region); - this.endpointUrl = regionEndpoint != null ? regionEndpoint : REGIONAL_ENDPOINTS.get("DEFAULT"); + // If collectorAddress is explicitly set, use it + if (configuration.getCollectorAddress() != null && !configuration.getCollectorAddress().isEmpty()) { + this.endpointUrl = "https://" + configuration.getCollectorAddress() + "/mobile/v3/data"; + } else { + // Otherwise, auto-detect from region + String region = configuration.getRegion().toUpperCase(); + String regionEndpoint = REGIONAL_ENDPOINTS.get(region); + this.endpointUrl = regionEndpoint != null ? regionEndpoint : REGIONAL_ENDPOINTS.get("DEFAULT"); + } if (configuration.isMemoryOptimized()) { connectionTimeoutMs = 6000; @@ -74,7 +78,7 @@ public OptimizedHttpClient(NRVideoConfiguration configuration, android.content.C System.setProperty("http.keepAliveDuration", "300000"); // 5 minutes System.setProperty("http.maxConnections", "5"); - NRLog.d("Initialized with region: " + region + + NRLog.d("Initialized with region: " + configuration.getRegion() + ", endpoint URL: " + endpointUrl); } From 35a5b80d6197019000cb8a3edb0474c3eefb402f Mon Sep 17 00:00:00 2001 From: mavinash Date: Tue, 20 Jan 2026 10:22:55 +0530 Subject: [PATCH 06/10] fix: Regional Collector URLs --- .../com/newrelic/videoagent/core/auth/TokenManager.java | 6 +++--- .../videoagent/core/harvest/OptimizedHttpClient.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/auth/TokenManager.java b/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/auth/TokenManager.java index e87d407f..eddf4b8a 100644 --- a/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/auth/TokenManager.java +++ b/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/auth/TokenManager.java @@ -167,11 +167,11 @@ private String buildTokenEndpoint() { String region = configuration.getRegion().toUpperCase(); switch (region) { case "EU": - return "https://mobile-collector.eu.nr-data.net/mobile/v5/connect"; + return "https://mobile-collector.eu.newrelic.com/mobile/v5/connect"; case "AP": - return "https://mobile-collector.ap.nr-data.net/mobile/v5/connect"; + return "https://mobile-collector.ap.newrelic.com/mobile/v5/connect"; case "GOV": - return "https://gov-mobile-collector.newrelic.com/mobile/v5/connect"; + return "https://mobile-collector.gov.newrelic.com/mobile/v5/connect"; default: return "https://mobile-collector.newrelic.com/mobile/v5/connect"; // US/DEFAULT } diff --git a/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/harvest/OptimizedHttpClient.java b/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/harvest/OptimizedHttpClient.java index 116c4cd0..2f6a52a3 100644 --- a/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/harvest/OptimizedHttpClient.java +++ b/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/harvest/OptimizedHttpClient.java @@ -40,9 +40,9 @@ public class OptimizedHttpClient implements HttpClientInterface { static { REGIONAL_ENDPOINTS.put("US", "https://mobile-collector.newrelic.com/mobile/v3/data"); - REGIONAL_ENDPOINTS.put("EU", "https://mobile-collector.eu.nr-data.net/mobile/v3/data"); - REGIONAL_ENDPOINTS.put("AP", "https://mobile-collector.ap.nr-data.net/mobile/v3/data"); - REGIONAL_ENDPOINTS.put("GOV", "https://gov-mobile-collector.newrelic.com/mobile/v3/data"); + REGIONAL_ENDPOINTS.put("EU", "https://mobile-collector.eu.newrelic.com/mobile/v3/data"); + REGIONAL_ENDPOINTS.put("AP", "https://mobile-collector.ap.newrelic.com/mobile/v3/data"); + REGIONAL_ENDPOINTS.put("GOV", "https://mobile-collector.gov.newrelic.com/mobile/v3/data"); REGIONAL_ENDPOINTS.put("DEFAULT", REGIONAL_ENDPOINTS.get("US")); } From 1281e0ed088816078b5baa7c24be3cb3eec0363a Mon Sep 17 00:00:00 2001 From: mavinash Date: Tue, 20 Jan 2026 10:25:17 +0530 Subject: [PATCH 07/10] fix: Remove extra comment --- .../java/com/newrelic/videoagent/core/auth/TokenManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/auth/TokenManager.java b/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/auth/TokenManager.java index eddf4b8a..bc1f2e82 100644 --- a/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/auth/TokenManager.java +++ b/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/auth/TokenManager.java @@ -173,7 +173,7 @@ private String buildTokenEndpoint() { case "GOV": return "https://mobile-collector.gov.newrelic.com/mobile/v5/connect"; default: - return "https://mobile-collector.newrelic.com/mobile/v5/connect"; // US/DEFAULT + return "https://mobile-collector.newrelic.com/mobile/v5/connect"; } } From a7aad5f7fb66fdfb4a23d410eebf6b07282fe3b1 Mon Sep 17 00:00:00 2001 From: mavinash Date: Thu, 29 Jan 2026 01:46:17 +0530 Subject: [PATCH 08/10] fix: pr review comments --- .../com/newrelic/videoagent/core/auth/TokenManager.java | 3 ++- .../videoagent/core/harvest/OptimizedHttpClient.java | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/auth/TokenManager.java b/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/auth/TokenManager.java index bc1f2e82..b1ecda1d 100644 --- a/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/auth/TokenManager.java +++ b/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/auth/TokenManager.java @@ -164,7 +164,8 @@ private String buildTokenEndpoint() { } // Otherwise, auto-detect from region - String region = configuration.getRegion().toUpperCase(); + String region = configuration.getRegion(); + region = (region != null) ? region.toUpperCase() : "US"; switch (region) { case "EU": return "https://mobile-collector.eu.newrelic.com/mobile/v5/connect"; diff --git a/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/harvest/OptimizedHttpClient.java b/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/harvest/OptimizedHttpClient.java index 2f6a52a3..6f1c2d34 100644 --- a/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/harvest/OptimizedHttpClient.java +++ b/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/harvest/OptimizedHttpClient.java @@ -63,9 +63,9 @@ public OptimizedHttpClient(NRVideoConfiguration configuration, android.content.C this.endpointUrl = "https://" + configuration.getCollectorAddress() + "/mobile/v3/data"; } else { // Otherwise, auto-detect from region - String region = configuration.getRegion().toUpperCase(); - String regionEndpoint = REGIONAL_ENDPOINTS.get(region); - this.endpointUrl = regionEndpoint != null ? regionEndpoint : REGIONAL_ENDPOINTS.get("DEFAULT"); + String region = configuration.getRegion(); + region = (region != null) ? region.toUpperCase() : "US"; + this.endpointUrl = REGIONAL_ENDPOINTS.getOrDefault(region, REGIONAL_ENDPOINTS.get("DEFAULT")); } if (configuration.isMemoryOptimized()) { From 8aef92d844488364db40f2cd7cc7b7d421b88a8c Mon Sep 17 00:00:00 2001 From: mavinash Date: Thu, 29 Jan 2026 15:41:16 +0530 Subject: [PATCH 09/10] fix: revert use of getOrDefault --- .../newrelic/videoagent/core/harvest/OptimizedHttpClient.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/harvest/OptimizedHttpClient.java b/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/harvest/OptimizedHttpClient.java index 6f1c2d34..ac391141 100644 --- a/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/harvest/OptimizedHttpClient.java +++ b/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/harvest/OptimizedHttpClient.java @@ -65,7 +65,8 @@ public OptimizedHttpClient(NRVideoConfiguration configuration, android.content.C // Otherwise, auto-detect from region String region = configuration.getRegion(); region = (region != null) ? region.toUpperCase() : "US"; - this.endpointUrl = REGIONAL_ENDPOINTS.getOrDefault(region, REGIONAL_ENDPOINTS.get("DEFAULT")); + String endpoint = REGIONAL_ENDPOINTS.get(region); + this.endpointUrl = (endpoint != null) ? endpoint : REGIONAL_ENDPOINTS.get("DEFAULT"); } if (configuration.isMemoryOptimized()) { From f48b2e854325ad75a51459d7d32444bef6676003 Mon Sep 17 00:00:00 2001 From: mavinash Date: Sun, 1 Feb 2026 12:18:02 +0530 Subject: [PATCH 10/10] fix: Trigger HEARTBEAT at elapsed time 0 --- .../com/newrelic/videoagent/core/tracker/NRVideoTracker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/tracker/NRVideoTracker.java b/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/tracker/NRVideoTracker.java index 574adbe9..94866a50 100644 --- a/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/tracker/NRVideoTracker.java +++ b/NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/tracker/NRVideoTracker.java @@ -416,7 +416,7 @@ public void sendHeartbeat() { state.accumulatedVideoWatchTime = (Math.abs(state.accumulatedVideoWatchTime - heartbeatInterval) <= 5 ? heartbeatInterval : state.accumulatedVideoWatchTime); Map eventData = new HashMap<>(); eventData.put("elapsedTime", state.accumulatedVideoWatchTime); - if (state.accumulatedVideoWatchTime != null && state.accumulatedVideoWatchTime > 0L) { + if (state.accumulatedVideoWatchTime != null) { if (state.isAd) { sendVideoAdEvent(AD_HEARTBEAT,eventData); } else {